Ir al contenido

Módulo XII — calidad, evaluaciones y testing

El módulo XII responde a una sola pregunta — ¿sigue mi agente haciendo lo correcto? — puntuando salidas candidatas frente a suites golden versionadas y emitiendo el resultado como evidencia canónica entre módulos. Es un módulo de la capa de Inteligencia: mide, no ejecuta el sujeto ni actúa sobre la infraestructura. Esta página es la referencia de lo que el módulo de evaluaciones hace hoy, y sus límites honestos.

XII es una capa de medición, no una capa de ejecución. Una salida candidata le llega ya producida — desde el sandbox de testing (módulo XVII), desde CI, en línea en la petición, o como una señal muestreada de una sesión real — y XII la puntúa frente a los casos de una suite. El único modelo que XII invoca alguna vez es el juez (para el scorer llm_judge); nunca ejecuta por sí mismo el agente o modelo sujeto. Producir salidas es tarea del sandbox, no de XII.

El conjunto de scorers es conectable. Built-ins deterministas y puros cubren los contratos comunes — exact, contains, not_contains, regex, json_valid, json_equal y numeric_range. Junto a ellos hay un scorer llm_judge que invoca un modelo a través del puerto Judge para calificar frente a una rúbrica.

Suites, ejecuciones y el artefacto canónico

Sección titulada «Suites, ejecuciones y el artefacto canónico»

Una suite es un dataset golden versionado: contiene sus casos, un scorer por defecto, un umbral de aprobación y un umbral de regresión. Los casos son append-only e inmutables por versión — corregir un caso acuña una nueva suite_version, nunca una edición in situ, de modo que el dataset que produjo cualquier veredicto pasado siempre es reconstruible.

Una ejecución puntúa cada caso de una suite, agrega un score y un pass_rate, y persiste tres cosas: evidencia por caso append-only, un agregado de ejecución mutable, y un único EvalResult del núcleo — el artefacto canónico (Suite, SubjectKind, SubjectID, Score, Passed, OccurredAt, Metrics) que compliance (XIII) y la UI leen sin conocer las propias tablas de XII. Las ejecuciones se ejecutan de forma síncrona; el stream SSE de una ejecución reproduce la ejecución persistida (frames por caso, luego un resumen), no actúa. Una regresión es una comparación con una ejecución de baseline. Cuando la caída de score supera el regression_threshold de la suite y el intervalo al 95 % de la diferencia de pass-rate (Newcombe, diferencia de intervalos de Wilson) excluye el cero, la ejecución establece regressed y escribe un Finding del núcleo (Kind = eval_regression). Una caída sobre el umbral cuyo intervalo incluye el cero se marca inconclusive_noise, no como regresión; la comparación muestra el intervalo y ambos denominadores, así que las suites pequeñas generan menos findings. La baseline automática es la última ejecución completada de la misma suite y el mismo sujeto, y de la misma variante cuando hay una, sin contar las marcadas inconclusive_noise. Esa ejecución puede usar otra versión de suite, otro modelo u otro scorer, y la comparación no informa cuál cambió. Tras un cambio de versión de suite, pasa una baseline explícita o fija una (POST /baselines). El hallazgo se emite en best-effort en el bus como finding.reported para que los módulos de entrega (salud/notificaciones) lo enruten. Del lado de lectura, los scorecards agregan pass-rate, score medio y tendencia por sujeto y se exportan como CSV/JSON.

La salida candidata nunca se persiste — venga de donde venga. Un resultado por caso almacena solo un hash unidireccional del detalle y una etiqueta recortada y depurada para la UI; el expurgo lo hace el handler antes del almacenamiento, nunca se asume del store. El monitor puntúa señales de comportamiento de una sesión real — su estado, recuento de findings, severidad máxima y cifras de tokens/coste (extraídas de las señales del núcleo Session, Finding y CostRecord) — y nunca el texto de salida en bruto, que la plataforma no persiste en absoluto. Las fixtures golden son la única excepción acotada: contenido autorizado por el operador, opt-in, no de producción, recortado por el handler antes de escribir para que una suite pueda ejecutarse de verdad.

Calibración del juez, mitigación de sesgo y la puerta de regresión en CI

Sección titulada «Calibración del juez, mitigación de sesgo y la puerta de regresión en CI»

Los veredictos del juez se confían solo después de medirse. Un conjunto de calibración etiquetado por humanos (construido con la sesión guiada olivares evals label) alimenta una ejecución de calibración que mide al juez frente a la referencia humana: porcentaje de acuerdo con su intervalo de Wilson al 95 %, kappa de Cohen (el acuerdo por sí solo no es defendible bajo desbalanceo de clases), sensibilidad/especificidad con sus denominadores, y una correlación de sesgo por verbosidad. El informe es evidencia append-only; el objetivo — acuerdo ≥ 0.85 y un kappa definido ≥ 0.6 — puede elevarse por ejecución pero nunca bajarse. Un conjunto cuyas etiquetas humanas son todas de aprobado no puede medir el acuerdo corregido por azar y no certifica nada.

La mitigación de sesgo está integrada y medida: el prompt del juez fuerza el razonamiento antes del veredicto (el análisis se descarta sobre la marcha — datos mínimos) e instruye contra premiar la longitud; el modo pairwise opt-in de la comparación A/B juzga cada caso compartido dos veces con el orden de presentación invertido, declara un ganador solo cuando ambos órdenes coinciden, e informa de la tasa medida de position_consistency.

La puerta de regresión (POST /gate, CLI evals gate) convierte todo esto en un veredicto bloqueante de CI: una regresión frente a la baseline, un pass-rate por debajo del umbral de la suite, o un juez sin calibrar hacen fallar la puerta (exit 1); una credencial de juez ausente degrada a un warn declarado, nunca a un pase silencioso. La semilla de la puerta elige los casos de la muestra; no hace determinista a un modelo remoto. Un acierto de la caché de veredictos, con clave de contenido, pin del modelo del juez y versión del prompt, devuelve el veredicto guardado. Un fallo, y cualquier ejecución ordinaria de llm_judge fuera de esta puerta, vuelve a llamar al juez remoto y puede diferir. El pre-flight de FinOps corre solo en esta puerta. Cuando no se aplica a las llamadas al juez ningún presupuesto de FinOps que bloquee o limite, la puerta permite el gasto. Cuando no se puede leer el almacén de presupuestos, la puerta rechaza el gasto y falla con budget_blocked. Un presupuesto que bloquea o limita detiene el gasto. La única salida de una puerta fallida es el override gobernado — de nivel admin, con motivo escrito, auditado — que cambia el veredicto efectivo que CI re-comprueba, nunca el registrado. Cada tasa reportada se entrega con su denominador y su intervalo al 95 %, y la comparación de regresión también. Consulta docs/EVAL-METHODOLOGY.md en el repositorio para la metodología completa y las fuentes.