Módulo 5: Evals de regresión como gate de producción

Módulo 5: Evals de regresión como gate de producción

Descripción

Los tres módulos anteriores construyeron, uno por uno, la capacidad de saber qué le pasó a un run del agente de Reservo: el Módulo 2 le dio un trace_id y un rastro completo, paso a paso, en RUN_LOG.jsonl. El Módulo 3 le puso un precio en centavos. El Módulo 4 le puso un tiempo, modelado, tool por tool. Con las tres piezas en su lugar, hoy puedes tomar cualquier run del agente de Reservo y responder, con números reales: cuántos pasos dio, qué costó, cuánto tardó. Eso es observar y medir — las dos primeras disciplinas del mapa que abrió el Módulo 1.

Pero ninguna de esas tres piezas responde una pregunta distinta, y más urgente, que cualquiera que ya haya tocado un sistema en producción conoce bien: "cambié algo — un system prompt, una descripción de tool, un modelo — ¿sigue funcionando el agente igual que ayer?" Medir un run, uno a la vez, después de que ya pasó, no contesta eso. Hace falta algo que corra antes de que un cambio llegue a producción, sobre un conjunto de casos que ya conoces, y que te diga, con un veredicto binario, si algo se rompió. Eso es un gate de regresión — la tercera disciplina de esta guía: gatear.

Este módulo construye exactamente eso: regression/harness.py, un harness determinista que corre un set fijo de casosregression/golden_cases.json— contra el agente de Reservo tal como quedó construido en agent-fundamentals-and-tool-calling-guide M8, y produce un veredicto PASS/FAIL para cada caso y para el lote completo. Nada de lo que construyas aquí llama a un modelo para calificar nada. Cada chequeo es una comparación literal y determinista contra un valor fijo: ¿el resultado de la tool tiene la forma correcta?, ¿el agente llamó exactamente a la tool que se esperaba, en el orden esperado?, ¿el costo y la latencia modelados se mantuvieron bajo un umbral? Este módulo es, con toda intención, la pieza más estrecha de alcance de toda la guía — y la más fácil de confundir con algo que no es. Aclarar esa confusión, con precisión, es tan importante como el código que construye el gate.

Conexión con el módulo

Este módulo es el primero de la guía que no agrega una nueva forma de medir — reusa, sin cambiar una línea, todo lo que ya construiste: reservo_agent.run_reservo_agent (agent-fundamentals M8), run_logger.traced_run (Módulo 2), cost_calculator.estimate_cost_cents y cost_for_run (Módulo 3), y el modelo de latencia por tool (Módulo 4). Lo nuevo es la capa que decide, con esas piezas ya construidas, si el comportamiento del agente sigue siendo el que se espera.


Dónde estamos en el ecosistema

Agentes en producción — operar el agente de Reservo
├── Módulo 1: Por qué operar es distinto de construir
├── Módulo 2: Logging estructurado y trazado de un run
├── Módulo 3: Medir costo y tokens por run
├── Módulo 4: Medir latencia con honestidad
├── Módulo 5: Evals de regresión como gate de producción  ← ESTÁS AQUÍ
│   → Un set fijo de casos, comparación literal de la tool
│     elegida, schema de salida, umbral de costo/latencia,
│     PASS/FAIL como un gate de CI
├── Módulo 6: Fallos a escala — backoff, circuit breakers y rate limits
├── Módulo 7: Versionado y rollout seguro
└── Módulo 8: Proyecto — el agente de Reservo en producción

Con las tres primeras disciplinas de la guía completas (Módulo 1 nombró el problema; Módulos 2-4 construyeron cómo observar y medir), este es el módulo que convierte esa medición en una decisión: PASS o FAIL, sin ambigüedad, sin criterio subjetivo, sin que nadie tenga que leer un run entero a mano para decidir si algo se rompió. El Módulo 6 va a usar el mismo criterio de "algo se rompió" pero aplicado a fallos repetidos de una tool específica entre runs (un circuit breaker); el Módulo 7 va a reusar este mismo gate, literalmente, para comparar una versión vieja del agente contra una nueva antes de decidir si un cambio se despliega. Todo lo que construyas aquí es la base de ambos.


La analogía central de este módulo: la inspección técnica, antes de sacar el auto a la calle

Antes de que un auto pueda circular legalmente, pasa por una inspección técnica. El inspector no se sienta al volante a evaluar si el auto es cómodo, si el tablero se ve elegante, o si el color combina con el gusto del dueño — nada de eso es su trabajo, y ni siquiera tiene un criterio para juzgarlo. Lo que hace es correr una lista fija de chequeos, cada uno con un criterio binario: ¿los frenos responden dentro de la distancia esperada?, ¿las luces encienden?, ¿los cinturones sujetan? Cada chequeo tiene un umbral exacto, medido con un instrumento, no con una opinión. Si el auto pasa los quince puntos de la lista, sale con la calcomanía. Si falla uno solo —aunque sea el más "menor" de todos, como una luz trasera fundida—, no sale, y el taller tiene que arreglar exactamente ese punto antes de volver a intentarlo.

Un gate de regresión hace, con un agente de LLM, exactamente lo mismo que esa inspección hace con un auto. No evalúa si la respuesta del agente es "buena", "clara" o "bien escrita" — eso no es lo que este módulo mide, y ni siquiera tiene un criterio para intentarlo (esa evaluación, la de calidad semántica, es el trabajo de una disciplina distinta, que esta lección nombra con precisión más abajo). Lo que hace es correr una lista fija de casos, cada uno con un criterio binario, medido con código: ¿el resultado de la tool tiene la forma correcta (el "schema" de salida, como el sensor que confirma que las luces encienden)?, ¿el agente llamó a la tool correcta, en el orden correcto (como confirmar que el volante gira la rueda correcta, no una prueba de si "se siente bien" manejarlo)?, ¿el costo y la latencia se mantuvieron bajo el umbral esperado (como la distancia de frenado, un número, no una impresión)? Si el agente pasa los cinco casos del set fijo, el build pasa. Si falla uno solo —aunque sea el "más simple" de los cinco—, el gate falla, y ese caso específico, con su motivo exacto, es lo que hay que arreglar antes de intentarlo de nuevo. Un cambio en el prompt que rompe la elección de tool es, con toda precisión, el equivalente de unos frenos que dejaron de responder: el gate lo para antes de que llegue a la calle.


Lo que este módulo construye, y lo que deliberadamente no toca

Vale la pena, antes de escribir una sola línea de código, ser preciso sobre qué agrega este módulo a lo que ya tienes:

  • regression/golden_cases.json — el set fijo de casos: preguntas reales del dominio de Reservo, cada una con el guion de turnos del modelo (concepto, ya escrito a mano, como en toda esta guía) y el resultado exacto que se espera. Fijo significa fijo: nadie genera casos nuevos al azar, nadie los actualiza "a ojo" — es el mismo archivo, corrido una y otra vez, cada vez que algo cambia.
  • regression/harness.py — las funciones que corren cada caso, aplican los chequeos, y agregan un veredicto: check_schema (¿la forma del resultado es correcta?), check_tool_choice (¿la tool elegida coincide, literalmente, con la esperada?), check_cost_threshold y check_latency_threshold (¿el costo y la latencia modelados están bajo el umbral fijo del caso?), y run_regression_gate, la función que corre el CASE_SET completo y devuelve un GateReport con el PASS/FAIL de cada caso y del lote entero.
  • Nada de reservo_tools.py, reservo_contracts.py, reservo_robust.py ni reservo_agent.py cambia. Este módulo, como cada uno de los anteriores, envuelve el agente que agent-fundamentals ya construyó — nunca reescribe su lógica.
  • Nada de run_logger.py ni cost_calculator.py cambia tampoco. Cada caso del gate corre bajo traced_run (Módulo 2, sin modificar) y su costo se calcula con cost_for_run (Módulo 3, sin modificar). El modelo de latencia por tool que este módulo usa es el mismo TOOL_LATENCY_MS ya establecido y ejecutado desde el Módulo 1.

🛑 La frontera más importante de todo este módulo: FORMA, nunca calidad

Este es el punto que hay que dejar completamente claro antes de avanzar, porque es, con precisión, el solape más peligroso de todo el ecosistema de guías agentic — y una confusión aquí contamina cada lección que sigue.

Lo que este módulo verifica es la FORMA. Tres preguntas, y solo tres, todas contestables con una comparación exacta contra un valor fijo, sin ninguna interpretación de por medio:

  1. ¿El output valida contra su schema de salida? — ¿get_quote devolvió un dict con la clave price_cents de tipo entero, o devolvió algo con una forma distinta?
  2. ¿El agente sigue eligiendo la tool correcta, para un turno guionado exacto? — comparación literal: la secuencia de tools que el agente llamó, ¿es exactamente igual, elemento por elemento, a la secuencia esperada?
  3. ¿El costo y la latencia modelados se mantienen bajo un umbral fijo? — un número comparado contra otro número, con <=.

Lo que este módulo NUNCA verifica es si la respuesta es buena. No hay, en ningún archivo de este módulo, una llamada a un modelo de lenguaje para que "opine" sobre la calidad de una respuesta. No hay ningún dataset con una verdad "más o menos correcta" que un juez tenga que interpretar. No hay ningún score de 1 a 10, ningún "¿esta respuesta suena natural?", ningún trajectory scoring que evalúe si el camino que tomó el agente fue razonable más allá de la comparación literal de qué tool llamó. Esa evaluación — la de calidad semántica, la de "¿la respuesta del agente responde bien a lo que el usuario preguntó?" — es un trabajo real, importante, y completamente distinto, que pertenece a evaluation-frameworks-guide: su módulo evaluating-agents cubre trajectory evaluation, tool-call accuracy juzgada (no comparada literalmente, sino evaluada con un criterio de "¿fue una buena decisión?"), y task-completion/reasoning-quality; su módulo evaluation-pipelines-in-production cubre datasets dorados con verdad difusa, LLM-as-judge, y RAGAS.

Guárdate esta frase, porque cada lección de este módulo la repite en un contexto distinto: este gate confirma que la forma no se rompió — schema, tool correcta, umbral —, nunca si la respuesta es buena. Eso es evaluation-frameworks-guide.


El caso que sigue acompañando la guía: Reservo, con un set de casos fijo

Las cuatro tools son las mismas de siempre — list_rooms(), get_quote(room, tier, hours), book_room(room, tier, hours, member), cancel_booking(id) — y las anclas de precio siguen intactas: Focus basic 3h = 7500 centavos, Focus pro 3h = 6000 centavos. Este módulo no declara ninguna tool nueva ni cambia la lógica de negocio de Reservo — construye, alrededor de ella, un set de cinco casos fijos que cubren cotizar (con las dos anclas), reservar (con dos salas distintas), y reservar-y-cancelar. Cada caso queda anclado, además de a la tool correcta, a un valor de salida exacto: price_cents=6000 para Focus pro 3h, booking_id=1 para la primera reserva de un run recién reseteado, cancelled=True para una cancelación válida.

Como en cada módulo de esta guía: identificadores y código en inglés; prosa y comentarios en español; dinero, cuando aparezca, en centavos int. Modelos actuales (claude-sonnet-5) cuando la lección mencione al modelo como concepto.


Prerequisitos

Conocimiento requerido:

  • ✅ Haber completado los Módulos 1-4 de esta guía. Este módulo asume que RUN_LOG.jsonl, estimate_cost_cents/cost_for_run, y el modelo TOOL_LATENCY_MS ya existen y funcionan — no los vuelve a explicar desde cero.
  • ✅ Haber completado (o conocer bien) agent-fundamentals-and-tool-calling-guide M8: la firma de run_reservo_agent(question, model_script, max_iterations=10, summarize=None), el formato de history, y los cuatro contratos de tools con sus input_schema.
  • ✅ Python: funciones, dict/list, dataclasses, json.load/json.dumps. Nada de este módulo usa ninguna librería fuera de la estándar.

Recomendado:

  • ✅ Haber sentido, alguna vez, la incertidumbre de cambiar un prompt o una descripción de tool y no saber si algo, en algún rincón del sistema, dejó de funcionar. Esa incertidumbre es exactamente el problema que este módulo resuelve con un procedimiento repetible.

NO requerido:

  • ❌ No necesitas una API key ni conexión a internet: la decisión del modelo sigue siendo concepto, y todo el harness de este módulo corre 100% local.
  • ❌ No necesitas conocer ningún framework de evaluación semántica (RAGAS, TruLens, LangSmith Evals). Los patrones de este módulo son deliberadamente más simples — y esa simplicidad es el punto: un gate de FORMA no necesita ninguna de esas herramientas.
  • ❌ No necesitas saber nada de CI/CD real (GitHub Actions, pipelines de despliegue). Este módulo construye el criterio de PASS/FAIL en Python puro — cómo conectarlo a un pipeline real es una decisión de infraestructura fuera del alcance de esta guía.

Entorno:

  • Python 3.14.0 con su librería estándar (json, dataclasses, itertools). Nada que instalar.
  • ✅ Un editor de texto y una terminal.

Roadmap del módulo

Lección 01 — Introducción al módulo (esta)

La analogía de la inspección técnica, la frontera FORMA-vs-calidad declarada con precisión, y el mapa de las ocho lecciones.

Lección 02 — Qué verifica un eval de regresión

Las tres preguntas exactas que este gate puede contestar, con ejemplos ejecutados de cada una en aislamiento, antes de juntarlas en un harness completo.

Lección 03 — El set fijo de casos

regression/golden_cases.json: por qué fijo, cómo se estructura cada caso, y la disciplina de aislar el estado de Reservo entre casos para que ninguno dependa del orden en que corrieron los demás.

Lección 04 — Forma, no calidad: la frontera

La lección central de la frontera con evaluation-frameworks-guide: check_schema construido y probado a fondo, con casos que pasan y casos que fallan, y la declaración explícita, con ejemplos, de qué preguntas este módulo nunca contesta.

Lección 05 — Verificando la elección de tool

check_tool_choice: comparación literal de la secuencia de tools llamadas, y la primera demostración completa de un FAIL real — una regresión de prompt que cambia qué tool se elige.

Lección 06 — Verificando umbrales de costo y latencia

check_cost_threshold y check_latency_threshold, reusando cost_for_run (Módulo 3) y el modelo de latencia (Módulo 4) sin modificarlos, con un caso que falla por exceder un umbral.

Lección 07 — El gate: PASS o FAIL el build

run_regression_gate completo, corriendo el CASE_SET de cinco casos de punta a punta: el veredicto PASS del lote limpio, y el veredicto FAIL cuando se sustituye un guion por uno "después de un cambio" que rompe la elección de tool.

Lección 08 — Mini-proyecto: un gate de regresión para Reservo

Ensamblas regression/harness.py completo, corres el gate sobre el CASE_SET real, y produces regression_report.json — el artefacto que el Módulo 7 va a reusar para comparar dos versiones del agente.

Mapa de progresión

Lección 01 (esta)  → La analogía, la frontera FORMA-vs-calidad, el mapa
Lección 02         → Las tres preguntas que este gate puede contestar
Lección 03         → golden_cases.json: el set fijo, aislado entre casos
Lección 04         → check_schema, y la frontera con evaluation-frameworks
Lección 05         → check_tool_choice, y el primer FAIL real
Lección 06         → check_cost_threshold / check_latency_threshold
Lección 07         → run_regression_gate: PASS/FAIL del lote completo
Lección 08         → Mini-proyecto: regression_report.json real

Dificultad: ⭐⭐ ──────────────────▶ ⭐⭐⭐

Qué lograrás en este módulo

Al completar las 8 lecciones, podrás:

  1. Explicar, con precisión y ejemplos, la diferencia entre un chequeo de FORMA y un juicio de calidad semántica — y decir, para cualquier pregunta nueva sobre un agente, a cuál de las dos categorías pertenece.
  2. Diseñar un set fijo de casos de regresión, cada uno con su tool esperada, su schema de salida, y sus umbrales de costo/latencia.
  3. Construir check_schema: validar la forma de un resultado de tool contra un schema de salida, sin evaluar si el valor "tiene sentido".
  4. Construir check_tool_choice: comparar, literalmente, la secuencia de tools que un agente llamó contra la secuencia esperada.
  5. Construir check_cost_threshold y check_latency_threshold, reusando la ingeniería de costo y latencia de los Módulos 3 y 4 sin duplicarla.
  6. Correr un gate de regresión completo sobre el agente de Reservo, leer un GateReport, y diagnosticar exactamente por qué un caso específico falló.
  7. Trazar, sin dudar, la frontera con evaluation-frameworks-guide cada vez que una pregunta sobre un agente empiece a sonar a "¿la respuesta es buena?" en vez de "¿la forma se rompió?".

El antes y después

ANTES del módulo:
→ "si el agente responde algo razonable, ya está bien"
→ "probar un cambio es correr el agente a mano un par de veces
  y ver si 'se ve bien'"
→ "un eval siempre necesita un modelo que juzgue la respuesta"
→ "si algo se rompe, ya se va a notar en producción"

DESPUÉS del módulo:
→ un gate de regresión es un criterio BINARIO, determinista,
  sin ningún juicio de calidad de por medio
→ un set FIJO de casos, corrido siempre igual, reemplaza la
  prueba manual "a ojo" con un procedimiento repetible
→ FORMA (schema, tool elegida, umbral) y CALIDAD (¿la respuesta
  es buena?) son dos preguntas distintas, con dos disciplinas
  distintas -- esta guía solo resuelve la primera
→ un cambio que rompe algo se detecta ANTES de producción,
  con un mensaje exacto de qué caso falló y por qué

Trampas a evitar al cursar este módulo

1. "Este módulo va a llamar a claude-sonnet-5 para juzgar si la respuesta suena bien"

No, en ningún momento. Cada llamada al modelo en este módulo sigue siendo concepto — un guion de turnos escrito a mano, como en toda esta guía. Los chequeos que sí se ejecutan de verdad (check_schema, check_tool_choice, check_cost_threshold, check_latency_threshold) son funciones de Python puro que comparan valores contra otros valores fijos — nunca invocan a ningún modelo para nada.

2. "Si el set de casos es fijo, nunca detecta problemas nuevos"

Es cierto que un set fijo no detecta cualquier problema — pero esa no es su función. Un gate de regresión detecta si un cambio rompió un comportamiento que ya se sabía que debía funcionar. Detectar problemas nuevos, no anticipados por ningún caso existente, es un trabajo distinto (pruebas exploratorias, monitoreo en producción de casos reales) que esta guía no cubre en este módulo.

3. "Un FAIL de este gate significa que el agente 'está mal'"

Un FAIL significa, con precisión, que el comportamiento cambió respecto al esperado — no necesariamente que el nuevo comportamiento sea peor. La lección 07 muestra esto con un caso real: el gate puede fallar por una razón completamente legítima de revisar (alguien cambió el precio base de una sala a propósito), o por una regresión genuina (el prompt empezó a saltarse un paso). El gate señala que algo cambió; decidir si ese cambio es intencional o un error sigue siendo trabajo humano.

4. "check_tool_choice con comparación literal es 'demasiado estricto'"

Es estricto a propósito. La comparación literal (actual == expected, elemento por elemento) es lo que hace que el chequeo sea determinista y confiable — cualquier forma de comparación "aproximada" (¿la tool elegida es "parecida" a la esperada?) requeriría, de nuevo, algún tipo de juicio, y eso es exactamente el territorio que este módulo evita a propósito.

5. "Esto ya es lo mismo que evaluation-frameworks-guide, así que puedo usar sus técnicas aquí"

No. Esa guía resuelve un problema real y distinto —calidad semántica, con datasets dorados y jueces— con un stack propio (OpenAI, RAGAS, TruLens) que ni siquiera comparte convenciones con esta guía. Mezclar sus técnicas aquí rompería la propiedad central de este módulo: que cada chequeo es determinista y reproducible, sin ningún componente probabilístico de por medio.


Cómo trabajar este módulo

  1. Corre cada chequeo por separado antes de juntarlos. Las lecciones 04-06 construyen check_schema, check_tool_choice, check_cost_threshold y check_latency_threshold en aislamiento, con ejemplos que pasan y ejemplos que fallan a propósito — antes de que la lección 07 los junte en un solo gate.
  2. Presta atención a los mensajes de FAIL, no solo al PASS. Un GateReport útil no solo dice "algo falló" — dice exactamente qué caso, qué chequeo, y con qué valores. Cada ejemplo de este módulo que produce un FAIL muestra ese detalle completo.
  3. El mini-proyecto (lección 08) es el gate completo, real. Ahí corres los cinco casos del CASE_SET, ves el PASS del lote limpio, y confirmas el FAIL cuando se simula una regresión — el mismo procedimiento que el Módulo 7 va a reusar para comparar dos versiones del agente.

Tiempo estimado:

Lección 01 (esta)  →  20 min lectura
Lección 02         →  25 min + correr el ejemplo
Lección 03         →  25 min + correr el ejemplo
Lección 04         →  30 min + correr el ejemplo (la frontera central)
Lección 05         →  30 min + correr el ejemplo
Lección 06         →  25 min + correr el ejemplo
Lección 07         →  30 min + correr el gate completo
Lección 08         →  40 min + armar el mini-proyecto completo

Total: ~3.5 horas

Evidencia de éxito

Antes de avanzar al Módulo 6 (Fallos a escala), deberías poder:

  • Explicar, sin dudar, la frontera entre un chequeo de FORMA y un juicio de calidad semántica, con un ejemplo de cada uno.
  • Construir check_schema, check_tool_choice, check_cost_threshold y check_latency_threshold, cada uno con al menos un caso que pasa y uno que falla.
  • Correr run_regression_gate sobre un CASE_SET real y leer un GateReport completo, identificando qué caso falló y por qué.
  • Simular una regresión (un guion "después de un cambio" que rompe la elección de tool) y confirmar que el gate la detecta con un mensaje preciso.
  • Nombrar evaluation-frameworks-guide como la guía correcta cada vez que una pregunta sobre un agente empiece a requerir un juicio de calidad, no solo una comparación de forma.

Resumen

  • Este módulo construye la tercera disciplina de la guía —gatear—: un harness determinista, regression/harness.py, que corre un set fijo de casos, regression/golden_cases.json, contra el agente de Reservo, y produce un veredicto PASS/FAIL.
  • La analogía central es la inspección técnica de un auto: verifica que los frenos respondan y las luces enciendan —la forma no se rompió—, nunca si el auto "es lindo" —eso es una evaluación distinta.
  • La frontera más importante de todo el módulo: este gate verifica FORMA (schema de salida, tool elegida por comparación literal, costo/latencia bajo un umbral fijo) — nunca calidad semántica. Esa evaluación pertenece a evaluation-frameworks-guide, nombrada con precisión en cada lección que roza su territorio.
  • Todo lo que este módulo construye reusa, sin modificar, lo que ya existe: run_reservo_agent (agent-fundamentals M8), traced_run (Módulo 2), cost_for_run (Módulo 3), y el modelo de latencia por tool (Módulo 4).

Siguiente lección: 02 — Qué verifica un eval de regresión. Antes de construir el harness completo, ponemos a prueba, una por una, las tres preguntas exactas que este gate puede contestar — con ejemplos ejecutados de cada una.


Recursos adicionales

  1. Anthropic — Tool use (function calling) overview — El contrato tool_use/tool_result sobre el que corre cada caso de este gate, sin cambios respecto a agent-fundamentals.
  2. Python — jsonjson.load/json.dumps, la base de golden_cases.json y de cada GateReport que este módulo produce.
  3. Python — dataclassesCaseResult y GateReport, las estructuras que este módulo usa para representar el veredicto de cada caso y del lote completo.
  4. Anthropic — Building effective agents — Sobre por qué un sistema agentic confiable necesita un procedimiento repetible para detectar regresiones, no solo pruebas manuales ocasionales.
  5. Python 3.14 — What's New — La versión exacta con la que se ejecuta cada línea de código de este módulo.