Laboratorio
El eval que le puse al demo de fraude
Después de comparar dos modelos con dos transacciones, quedaba una pregunta incómoda: cómo sé que el demo decide bien. Dos casos ilustran, pero no prueban nada.
La respuesta no fue otro caso, fue una vara. Armé un eval de 60 casos sintéticos con la decisión esperada fijada antes de correr el sistema. El sistema recibe una transacción con su contexto y devuelve una de tres decisiones: aprobar directo, pedir doble factor o bloquear. Cada una tiene un costo distinto, y el eval mide acuerdo con esa etiqueta, no con una idea abstracta de lo correcto.
Las seis piezas
- Un golden set de 60 casos versionados.
- Un runner que los pasa por el sistema y guarda los resultados en CSV.
- Un grader que compara contra la etiqueta.
- Un juez LLM que aplica una rúbrica escrita y evalúa el razonamiento, no solo la respuesta.
- Un metrics que agrega todo: acierto global, acierto por clase, latencias, tokens.
- Un gate que compara la corrida nueva contra la anterior y falla si empeora.
El gate fue la parte que más me sirvió. Un cambio es aceptable si el acierto global no baja más de dos puntos, si el p95 no supera 800 ms y si la zona gris no se derrumba. Escribí esos umbrales antes de correr, no después de ver el resultado.
Los números
| Jev | Gemini | |
|---|---|---|
| Acierto global | 91,7% (55/60) | 81,5% (44/54) |
| Casos claros | 100% | 100% |
| Zona gris | 15/20 | 7/17 |
| Latencia p50 / p95 | 260 / 329 ms | 2653 / 11078 ms |
| Tokens | — | 45.527 (~760 por caso) |
Gemini dejó seis casos sin respuesta por la contingencia del demo, por eso su base es 54 y no 60.
La diferencia no estaba en los casos fáciles: los dos aciertan el 100% ahí. Estaba en la zona gris, en las transacciones donde hay señales pero ninguna concluyente. Ahí es donde se juega la fricción innecesaria y la disputa con un cliente legítimo.
Lo que aprendí
El acierto global es una mala métrica para este tipo de sistema. Un modelo que aprueba todo acierta la mitad del set y falla justo en los casos que importan. Por eso el eval separa legítimos, fraudes y grises, y el gate pesa la zona gris por separado.
También aprendí que el eval hay que escribirlo antes de mirar los resultados. Cuando las etiquetas se fijan después de ver la salida, medís la capacidad del modelo para coincidir contigo, no su capacidad de decidir.
Y una cosa más: un eval sin límites declarados se convierte en una garantía falsa. Este mide acuerdo con mi criterio sobre datos sintéticos, con 60 casos, y da una señal direccional. No prueba que el sistema funcione en producción.
El código del eval está publicado en GitHub.