Complete English translation of the Agentic Readiness scoring module across frontend and backend codebases to improve code maintainability and international collaboration. Frontend changes: - agenticReadinessV2.ts: Translated all algorithm functions, subfactor names, and descriptions to English (repeatability, predictability, structuring, inverseComplexity, stability, ROI) - AgenticReadinessTab.tsx: Translated RED_FLAG_CONFIGS labels and descriptions - locales/en.json & es.json: Added new translation keys for subfactors with both English and Spanish versions Backend changes: - agentic_score.py: Translated all docstrings, comments, and reason codes from Spanish to English while maintaining API compatibility All changes tested with successful frontend build compilation (no errors). https://claude.ai/code/session_check-agent-readiness-status-Exnpc
404 lines
12 KiB
TypeScript
404 lines
12 KiB
TypeScript
/**
|
|
* Agentic Readiness Score v2.0
|
|
* Algorithm based on 6-dimension methodology with continuous normalization
|
|
*/
|
|
|
|
import type { TierKey, SubFactor, AgenticReadinessResult, CustomerSegment } from '../types';
|
|
import { AGENTIC_READINESS_WEIGHTS, AGENTIC_READINESS_THRESHOLDS } from '../constants';
|
|
|
|
export interface AgenticReadinessInput {
|
|
// Basic data (SILVER)
|
|
volumen_mes: number;
|
|
aht_values: number[];
|
|
escalation_rate: number;
|
|
cpi_humano: number;
|
|
volumen_anual: number;
|
|
|
|
// Advanced data (GOLD)
|
|
structured_fields_pct?: number;
|
|
exception_rate?: number;
|
|
hourly_distribution?: number[];
|
|
off_hours_pct?: number;
|
|
csat_values?: number[];
|
|
motivo_contacto_entropy?: number;
|
|
resolucion_entropy?: number;
|
|
|
|
// Tier
|
|
tier: TierKey;
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 1: REPEATABILITY (25%)
|
|
* Based on monthly volume with logistic normalization
|
|
*/
|
|
function calculateRepeatabilityScore(volumen_mes: number): SubFactor {
|
|
const { k, x0 } = AGENTIC_READINESS_THRESHOLDS.repetitividad;
|
|
|
|
// Logistic function: score = 10 / (1 + exp(-k * (volume - x0)))
|
|
const score = 10 / (1 + Math.exp(-k * (volumen_mes - x0)));
|
|
|
|
return {
|
|
name: 'repeatability',
|
|
displayName: 'Repeatability',
|
|
score: Math.round(score * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.repetitividad,
|
|
description: `Monthly volume: ${volumen_mes} interactions`,
|
|
details: {
|
|
volumen_mes,
|
|
threshold_medio: x0
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 2: PREDICTABILITY (20%)
|
|
* Based on AHT variability + escalation rate + input/output variability
|
|
*/
|
|
function calculatePredictabilityScore(
|
|
aht_values: number[],
|
|
escalation_rate: number,
|
|
motivo_contacto_entropy?: number,
|
|
resolucion_entropy?: number
|
|
): SubFactor {
|
|
const thresholds = AGENTIC_READINESS_THRESHOLDS.predictibilidad;
|
|
|
|
// 1. AHT VARIABILITY (40%)
|
|
const aht_mean = aht_values.reduce((a, b) => a + b, 0) / aht_values.length;
|
|
const aht_variance = aht_values.reduce((sum, val) => sum + Math.pow(val - aht_mean, 2), 0) / aht_values.length;
|
|
const aht_std = Math.sqrt(aht_variance);
|
|
const cv_aht = aht_std / aht_mean;
|
|
|
|
// Normalize CV to 0-10 scale
|
|
const score_aht = Math.max(0, Math.min(10,
|
|
10 * (1 - (cv_aht - thresholds.cv_aht_excellent) / (thresholds.cv_aht_poor - thresholds.cv_aht_excellent))
|
|
));
|
|
|
|
// 2. ESCALATION RATE (30%)
|
|
const score_escalacion = Math.max(0, Math.min(10,
|
|
10 * (1 - escalation_rate / thresholds.escalation_poor)
|
|
));
|
|
|
|
// 3. INPUT/OUTPUT VARIABILITY (30%)
|
|
let score_variabilidad: number;
|
|
if (motivo_contacto_entropy !== undefined && resolucion_entropy !== undefined) {
|
|
// High input entropy + Low output entropy = GOOD for automation
|
|
const input_normalized = Math.min(motivo_contacto_entropy / 3.0, 1.0);
|
|
const output_normalized = Math.min(resolucion_entropy / 3.0, 1.0);
|
|
score_variabilidad = 10 * (input_normalized * (1 - output_normalized));
|
|
} else {
|
|
// If no entropy data, use average of AHT and escalation
|
|
score_variabilidad = (score_aht + score_escalacion) / 2;
|
|
}
|
|
|
|
// FINAL WEIGHTING
|
|
const predictabilidad = (
|
|
0.40 * score_aht +
|
|
0.30 * score_escalacion +
|
|
0.30 * score_variabilidad
|
|
);
|
|
|
|
return {
|
|
name: 'predictability',
|
|
displayName: 'Predictability',
|
|
score: Math.round(predictabilidad * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.predictibilidad,
|
|
description: `AHT CV: ${(cv_aht * 100).toFixed(1)}%, Escalation: ${(escalation_rate * 100).toFixed(1)}%`,
|
|
details: {
|
|
cv_aht: Math.round(cv_aht * 1000) / 1000,
|
|
escalation_rate,
|
|
score_aht: Math.round(score_aht * 10) / 10,
|
|
score_escalacion: Math.round(score_escalacion * 10) / 10,
|
|
score_variabilidad: Math.round(score_variabilidad * 10) / 10
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 3: STRUCTURING (15%)
|
|
* Percentage of structured fields vs free text
|
|
*/
|
|
function calculateStructuringScore(structured_fields_pct: number): SubFactor {
|
|
const score = structured_fields_pct * 10;
|
|
|
|
return {
|
|
name: 'structuring',
|
|
displayName: 'Structuring',
|
|
score: Math.round(score * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.estructuracion,
|
|
description: `${(structured_fields_pct * 100).toFixed(0)}% structured fields`,
|
|
details: {
|
|
structured_fields_pct
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 4: INVERSE COMPLEXITY (15%)
|
|
* Based on exception rate
|
|
*/
|
|
function calculateInverseComplexityScore(exception_rate: number): SubFactor {
|
|
// Lower exception rate → Higher score
|
|
// < 5% → Excellent (score 10)
|
|
// > 30% → Very complex (score 0)
|
|
const score_excepciones = Math.max(0, Math.min(10, 10 * (1 - exception_rate / 0.30)));
|
|
|
|
return {
|
|
name: 'inverseComplexity',
|
|
displayName: 'Inverse Complexity',
|
|
score: Math.round(score_excepciones * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.complejidad_inversa,
|
|
description: `${(exception_rate * 100).toFixed(1)}% exceptions`,
|
|
details: {
|
|
exception_rate
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 5: STABILITY (10%)
|
|
* Based on hourly distribution and % off-hours calls
|
|
*/
|
|
function calculateStabilityScore(
|
|
hourly_distribution: number[],
|
|
off_hours_pct: number
|
|
): SubFactor {
|
|
// 1. HOURLY DISTRIBUTION UNIFORMITY (60%)
|
|
// Calculate Shannon entropy
|
|
const total = hourly_distribution.reduce((a, b) => a + b, 0);
|
|
let score_uniformidad = 0;
|
|
let entropy_normalized = 0;
|
|
|
|
if (total > 0) {
|
|
const probs = hourly_distribution.map(v => v / total).filter(p => p > 0);
|
|
const entropy = -probs.reduce((sum, p) => sum + p * Math.log2(p), 0);
|
|
const max_entropy = Math.log2(hourly_distribution.length);
|
|
entropy_normalized = entropy / max_entropy;
|
|
score_uniformidad = entropy_normalized * 10;
|
|
}
|
|
|
|
// 2. % OFF-HOURS CALLS (40%)
|
|
// More off-hours calls → Higher agent need → Higher score
|
|
const score_off_hours = Math.min(10, (off_hours_pct / 0.30) * 10);
|
|
|
|
// WEIGHTING
|
|
const estabilidad = (
|
|
0.60 * score_uniformidad +
|
|
0.40 * score_off_hours
|
|
);
|
|
|
|
return {
|
|
name: 'stability',
|
|
displayName: 'Stability',
|
|
score: Math.round(estabilidad * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.estabilidad,
|
|
description: `${(off_hours_pct * 100).toFixed(1)}% off-hours`,
|
|
details: {
|
|
entropy_normalized: Math.round(entropy_normalized * 1000) / 1000,
|
|
off_hours_pct,
|
|
score_uniformidad: Math.round(score_uniformidad * 10) / 10,
|
|
score_off_hours: Math.round(score_off_hours * 10) / 10
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SUB-FACTOR 6: ROI (15%)
|
|
* Based on annual potential savings
|
|
*/
|
|
function calculateROIScore(
|
|
volumen_anual: number,
|
|
cpi_humano: number,
|
|
automation_savings_pct: number = 0.70
|
|
): SubFactor {
|
|
const ahorro_anual = volumen_anual * cpi_humano * automation_savings_pct;
|
|
|
|
// Logistic normalization
|
|
const { k, x0 } = AGENTIC_READINESS_THRESHOLDS.roi;
|
|
const score = 10 / (1 + Math.exp(-k * (ahorro_anual - x0)));
|
|
|
|
return {
|
|
name: 'roi',
|
|
displayName: 'ROI',
|
|
score: Math.round(score * 10) / 10,
|
|
weight: AGENTIC_READINESS_WEIGHTS.roi,
|
|
description: `€${(ahorro_anual / 1000).toFixed(0)}K annual potential savings`,
|
|
details: {
|
|
ahorro_anual: Math.round(ahorro_anual),
|
|
volumen_anual,
|
|
cpi_humano,
|
|
automation_savings_pct
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* CSAT DISTRIBUTION ADJUSTMENT (Optional, ±10%)
|
|
* Normal distribution → Stable process
|
|
*/
|
|
function calculateCSATDistributionAdjustment(csat_values: number[]): number {
|
|
// Simplified normality test (based on skewness and kurtosis)
|
|
const n = csat_values.length;
|
|
const mean = csat_values.reduce((a, b) => a + b, 0) / n;
|
|
const variance = csat_values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / n;
|
|
const std = Math.sqrt(variance);
|
|
|
|
// Skewness
|
|
const skewness = csat_values.reduce((sum, val) => sum + Math.pow((val - mean) / std, 3), 0) / n;
|
|
|
|
// Kurtosis
|
|
const kurtosis = csat_values.reduce((sum, val) => sum + Math.pow((val - mean) / std, 4), 0) / n;
|
|
|
|
// Normality: skewness close to 0, kurtosis close to 3
|
|
const skewness_score = Math.max(0, 1 - Math.abs(skewness));
|
|
const kurtosis_score = Math.max(0, 1 - Math.abs(kurtosis - 3) / 3);
|
|
const normality_score = (skewness_score + kurtosis_score) / 2;
|
|
|
|
// Adjustment: +5% if very normal, -5% if very abnormal
|
|
const adjustment = 1 + ((normality_score - 0.5) * 0.10);
|
|
|
|
return adjustment;
|
|
}
|
|
|
|
/**
|
|
* COMPLETE ALGORITHM (Tier GOLD)
|
|
*/
|
|
export function calculateAgenticReadinessScoreGold(data: AgenticReadinessInput): AgenticReadinessResult {
|
|
const sub_factors: SubFactor[] = [];
|
|
|
|
// 1. REPEATABILITY
|
|
sub_factors.push(calculateRepeatabilityScore(data.volumen_mes));
|
|
|
|
// 2. PREDICTABILITY
|
|
sub_factors.push(calculatePredictabilityScore(
|
|
data.aht_values,
|
|
data.escalation_rate,
|
|
data.motivo_contacto_entropy,
|
|
data.resolucion_entropy
|
|
));
|
|
|
|
// 3. STRUCTURING
|
|
sub_factors.push(calculateStructuringScore(data.structured_fields_pct || 0.5));
|
|
|
|
// 4. INVERSE COMPLEXITY
|
|
sub_factors.push(calculateInverseComplexityScore(data.exception_rate || 0.15));
|
|
|
|
// 5. STABILITY
|
|
sub_factors.push(calculateStabilityScore(
|
|
data.hourly_distribution || Array(24).fill(1),
|
|
data.off_hours_pct || 0.2
|
|
));
|
|
|
|
// 6. ROI
|
|
sub_factors.push(calculateROIScore(
|
|
data.volumen_anual,
|
|
data.cpi_humano
|
|
));
|
|
|
|
// BASE WEIGHTING
|
|
const agentic_readiness_base = sub_factors.reduce(
|
|
(sum, factor) => sum + (factor.score * factor.weight),
|
|
0
|
|
);
|
|
|
|
// CSAT DISTRIBUTION ADJUSTMENT (Optional)
|
|
let agentic_readiness_final = agentic_readiness_base;
|
|
if (data.csat_values && data.csat_values.length > 10) {
|
|
const adjustment = calculateCSATDistributionAdjustment(data.csat_values);
|
|
agentic_readiness_final = agentic_readiness_base * adjustment;
|
|
}
|
|
|
|
// Limit to 0-10 range
|
|
agentic_readiness_final = Math.max(0, Math.min(10, agentic_readiness_final));
|
|
|
|
// Interpretation
|
|
let interpretation = '';
|
|
let confidence: 'high' | 'medium' | 'low' = 'high';
|
|
|
|
if (agentic_readiness_final >= 8) {
|
|
interpretation = 'Excellent candidate for complete automation (Automate)';
|
|
} else if (agentic_readiness_final >= 5) {
|
|
interpretation = 'Good candidate for agentic assistance (Assist)';
|
|
} else if (agentic_readiness_final >= 3) {
|
|
interpretation = 'Candidate for human augmentation (Augment)';
|
|
} else {
|
|
interpretation = 'Not recommended for automation at this time';
|
|
}
|
|
|
|
return {
|
|
score: Math.round(agentic_readiness_final * 10) / 10,
|
|
sub_factors,
|
|
tier: 'gold',
|
|
confidence,
|
|
interpretation
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SIMPLIFIED ALGORITHM (Tier SILVER)
|
|
*/
|
|
export function calculateAgenticReadinessScoreSilver(data: AgenticReadinessInput): AgenticReadinessResult {
|
|
const sub_factors: SubFactor[] = [];
|
|
|
|
// 1. REPEATABILITY (30%)
|
|
const repeatability = calculateRepeatabilityScore(data.volumen_mes);
|
|
repeatability.weight = 0.30;
|
|
sub_factors.push(repeatability);
|
|
|
|
// 2. SIMPLIFIED PREDICTABILITY (30%)
|
|
const predictability = calculatePredictabilityScore(
|
|
data.aht_values,
|
|
data.escalation_rate
|
|
);
|
|
predictability.weight = 0.30;
|
|
sub_factors.push(predictability);
|
|
|
|
// 3. ROI (40%)
|
|
const roi = calculateROIScore(data.volumen_anual, data.cpi_humano);
|
|
roi.weight = 0.40;
|
|
sub_factors.push(roi);
|
|
|
|
// SIMPLIFIED WEIGHTING
|
|
const agentic_readiness = sub_factors.reduce(
|
|
(sum, factor) => sum + (factor.score * factor.weight),
|
|
0
|
|
);
|
|
|
|
// Interpretation
|
|
let interpretation = '';
|
|
if (agentic_readiness >= 7) {
|
|
interpretation = 'Good candidate for automation';
|
|
} else if (agentic_readiness >= 4) {
|
|
interpretation = 'Candidate for agentic assistance';
|
|
} else {
|
|
interpretation = 'Requires deeper analysis (consider GOLD)';
|
|
}
|
|
|
|
return {
|
|
score: Math.round(agentic_readiness * 10) / 10,
|
|
sub_factors,
|
|
tier: 'silver',
|
|
confidence: 'medium',
|
|
interpretation
|
|
};
|
|
}
|
|
|
|
/**
|
|
* MAIN FUNCTION - Selects algorithm based on tier
|
|
*/
|
|
export function calculateAgenticReadinessScore(data: AgenticReadinessInput): AgenticReadinessResult {
|
|
if (data.tier === 'gold') {
|
|
return calculateAgenticReadinessScoreGold(data);
|
|
} else if (data.tier === 'silver') {
|
|
return calculateAgenticReadinessScoreSilver(data);
|
|
} else {
|
|
// BRONZE: No Agentic Readiness
|
|
return {
|
|
score: 0,
|
|
sub_factors: [],
|
|
tier: 'bronze',
|
|
confidence: 'low',
|
|
interpretation: 'Bronze analysis does not include Agentic Readiness Score'
|
|
};
|
|
}
|
|
}
|