Módulo 1 Cuándo una respuesta del modelo cuenta como tarea terminada
El incidente: un panel de métricas perfecto que mentía
9 min
El primer panel de métricas de Nexo marcaba un 100 % de éxito. Nueve emails de renovación procesados, nueve respuestas HTTP 200, nueve tareas contadas como hechas.
Cuando alguien abrió las respuestas una a una, dentro había de todo: un texto cortado a mitad de frase, una negativa del modelo, una petición para ejecutar una herramienta, un JSON con el cliente equivocado y varias interpretaciones que nadie había comprobado. Todo eso había entrado en el panel como éxito, porque el panel solo miraba si el servidor contestaba.
Medir cada paso por separado
Si en vez de una sola cifra preguntas en cada paso del camino, sale otra historia:
| Señal | Casos | Qué demuestra |
|---|---|---|
| HTTP correcto | 9 de 9 | El servidor recibió la petición y contestó |
| Respuesta terminada | 6 de 9 | El modelo acabó de escribir (stop_reason = end_turn) |
| Candidato válido | 4 de 9 | El JSON tiene la forma correcta, sus datos aparecen en el email y es del cliente del caso |
| Decisión verificada | 1 de 9 | Una fuente independiente confirma la interpretación |
| Ruta correcta | 9 de 9 | Cada caso acabó donde debía: éxito, revisión o rechazo |
Dos palabras que vas a ver todo el módulo:
- Candidato es lo que propone el modelo. Hasta que no se comprueba, es una propuesta y nada más.
- Revisión es el destino de los casos que el sistema no puede dar por buenos solo. Los mira una persona.
Ojo al leer la tabla. La tasa de éxito no ha caído del 100 % al 11,1 %. El panel original contaba respuestas del servidor y la fila de decisiones verificadas cuenta renovaciones bien resueltas. Son dos preguntas distintas y el panel respondía solo a la primera.
Y fíjate en la última fila. Que solo una renovación se pueda automatizar no significa que el sistema falle. Los otros ocho casos tampoco se han perdido: tres van a revisión porque falta evidencia y cinco se rechazan con un motivo concreto. El sistema funciona bien porque cada caso llega a donde tiene que llegar, aunque automatice poco.
Regla central: una respuesta recibida no equivale a una tarea completada.
Pruébalo: haz visible el falso éxito
En la carpeta del proyecto, ejecuta:
npm run demo:01
Primero sale una tabla con los nueve casos. Cada fila tiene cuatro columnas:
caseId: el caso.modelOutcome: cómo terminó la respuesta del modelo.businessOutcome: qué decide el negocio con ella.code: el motivo concreto.
Esto es lo que deberías ver, agrupado:
| Casos | modelOutcome |
businessOutcome |
code |
|---|---|---|---|
case_verified |
model_completed |
task_succeeded |
VERIFIED |
case_unverified, case_double_cancel, case_no_missing |
model_completed |
semantic_review_required |
SEMANTIC_EVIDENCE_MISSING |
case_garbage |
model_completed |
task_not_succeeded |
CANDIDATE_INVALID |
case_wrong_identity |
model_completed |
task_not_succeeded |
IDENTITY_MISMATCH |
case_truncated, case_refusal, case_action |
truncated, refused, action_requested |
task_not_succeeded |
MODEL_NOT_COMPLETED |
Lee la tabla de abajo arriba:
- Los tres últimos ni siquiera terminaron bien: uno se cortó, otro se negó y otro pidió ejecutar una herramienta para aplicar el descuento.
case_garbageterminó, pero su texto no es un JSON válido.case_wrong_identityes un JSON perfecto con uncustomer_idque no es el del caso.- Los tres que van a revisión tienen JSON válido, pero nadie ha confirmado la intención. Dos de ellos son trampas a propósito: «No queremos no renovar» y «No falta información». Una doble negación que se lee fácil al revés.
- Solo
case_verifiedtiene una confirmación independiente, hecha por una persona. En la demo esa revisión está escrita a mano en el fichero, como un registro conmethod: "human_review". En producción vendría de la herramienta donde tu equipo revisa los casos, con quién lo revisó y cuándo.
Debajo de la tabla sale un objeto con las métricas. Busca estas líneas:
httpSuccessRate: 1,
modelCompletionRate: 0.6666666666666666,
candidateValidRate: 0.4444444444444444,
semanticVerificationRate: 0.1111111111111111,
routingAccuracy: 1,
taskSuccessRate: 0.1111111111111111,
Son las filas de la tabla del principio, en tanto por uno. naiveHttpAndTextSuccessRate: 1 es lo que enseñaba el panel original. Las líneas de latencia y coste (p95LatencyMs, tokensPerSuccessfulTask, totalCost, costPerSuccessfulTask y el bloque pricing) las explica la lección 7. De momento, ignóralas.
Lo que te llevas
Si quieres ver de dónde sale cada cifra, abre demos/parte-1/01-incidente-runtime.ts. Los nueve casos están escritos a mano al principio del fichero, con la respuesta del modelo y el resultado que se espera de cada uno.
La decisión que tienes que interiorizar es esta: mandar a revisión un caso sin evidencia suficiente es un resultado correcto. El error sería contarlo como éxito o esconderlo de las métricas. En la siguiente lección ves cómo se representa cada uno de esos estados en el código.