Skip to content

English · Español

Lab 04 — Arnés evaluador del tutor de gramática

🇪🇸 Construye un arnés evaluador (~30 prompts §A13) que ejecuta el agente del tutor de gramática contra ground-truth conocido, calcula precisión, fidelidad y coste medio de turnos/llamadas a herramientas, y emite un reporte machine-readable. Mismo formato que el A4 capstone-tracker para que ambos converjan al cierre de Fase 32.

Lee theory/05-agent-loop-architecture.md y lab/01-tutor-end-to-end.md antes de empezar. No consultes solutions/.

Objetivo

Cablea un pipeline de evaluación determinista y reproducible para el agente tutor de gramática. El arnés lee un YAML de ~30 prompts §A13 con respuestas doradas, ejecuta cada prompt a través del agente y produce un reporte estructurado. El reporte dirige el DoD de la Fase 32.

Alcance (solo §A13)

El conjunto de eval cubre:

  • 20 verbos (12 regulares + 8 irregulares).
  • 5 tiempos (infinitive, present simple, past simple, past participle, simple future).
  • 3 personas (1ª sg, 2ª sg, 3ª sg).
  • Prompts en inglés y español (política bilingüe, §A2).

Fuera de alcance:

  • Personas plurales (1ª/2ª/3ª plural) — diferido según §A13.
  • Verbos fuera de los 20 listados en §A13.
  • Tiempos fuera de los 5 listados.

El arnés impone estas restricciones vía la máscara JSON-Schema sobre los tool calls del agente (referencia cruzada Fase 30 → 32).

Tareas

Tarea 1 — autora el conjunto de eval

Crea data/eval/phase-32-grammar-tutor.yaml con ~30 entradas. Esquema (por entrada):

- id: tut-01-eat-pst-3s
  category: irregular_verb_correction
  prompt_en: "Correct this sentence: 'He eated the apple.'"
  prompt_es: "Corrige esta oración: 'Él comió la manzana.'"
  gold_answer: |
    {"original": "He eated the apple.",
     "verb": "eat", "tense": "past simple", "person": "3rd singular",
     "correct_form": "ate", "spanish": "comió"}
  expected_tool_calls: ["conjugate"]
  max_turns: 3

Apunta a cobertura, no a volumen:

  • 8 entradas: correcciones de verbos irregulares (una por verbo irregular).
  • 6 entradas: conjugaciones de verbos regulares (muestrea entre los 12).
  • 5 entradas: identificación de tiempo ("¿en qué tiempo está wrote?").
  • 5 entradas: pares de traducción inglés ↔ español.
  • 4 entradas: adversarias / fuera de alcance ("conjuga swim en past simple" → esperado: el agente declina, ya que swim ∉ §A13).
  • 2 entradas: entradas ambiguas / infraespecificadas (el agente debería pedir aclaración o rendirse con elegancia).

Tarea 2 — script del arnés

Crea scripts/eval_grammar_tutor.py:

def run_eval(eval_path: Path, model_ckpt: Path) -> EvalReport:
    """Load eval set, instantiate agent, run each prompt, accumulate metrics."""

El arnés debe:

  1. Usar el fixture autouse de semilla para que dos ejecuciones produzcan salidas idénticas (determinista).
  2. Cronometrar cada prompt; registrar reloj-de-pared y conteo de turnos.
  3. Validar la salida del agente contra gold_answer usando un matcher estructural (no solo ==): para el campo "correct_form", coincidencia exacta; para el campo "spanish", permitir sinónimos de una pequeña whitelist (p. ej., comió y se comió ambos aceptables para "ate").
  4. Emitir un EvalReport:
@dataclass
class EvalReport:
    n_prompts: int
    correct: int                 # exact-match on correct_form
    faithful: int                # gold supported by retrieved chunks / tool outputs
    declined: int                # agent gave up; gold also "declined"
    failed: int                  # agent gave up; gold expected an answer
    mean_turns: float
    mean_tool_calls: float
    p95_latency_ms: float
    by_category: dict[str, dict[str, float]]

Tarea 3 — umbrales DoD

Los criterios de cierre de la Fase 32, codificados en el código de salida del arnés (no-cero si algún umbral falla):

Métrica Umbral
correct / n_prompts ≥ 0.85 (tras LoRA fine-tune de la Fase 28)
faithful / correct ≥ 0.95 (sin citas alucinadas)
mean_turns ≤ 3.5
mean_tool_calls ≤ 2.5
p95_latency_ms ≤ 1500 en el i5-8250U de Borja
declined en entradas fuera de alcance = 4 (coincide con los 4 prompts fuera de alcance)
failed ≤ 2 (permite que 2 entradas fallen sin bloquear el cierre)

Si correct < 0.85, el reporte debería imprimir los 5 peores prompts con la respuesta real del agente y la dorada para diff visual. Esta es la entrada diagnóstica para la reflexión de la Fase 32.

Tarea 4 — log de traza JSON-line

Para cada prompt, emite una línea JSON a experiments/<date>-32-tutor-eval/traces.jsonl conteniendo:

{
  "id": "tut-01-eat-pst-3s",
  "prompt_en": "Correct this sentence: 'He eated the apple.'",
  "scratchpad": [{"role": "user", "text": "..."}, {"role": "model", "action": "tool_call", ...}, ...],
  "agent_output": {"correct_form": "ate", "spanish": "comió"},
  "gold": {...},
  "metrics": {"correct": true, "faithful": true, "turns": 2, "tool_calls": 1, "latency_ms": 437}
}

Esta traza es el log de auditoría que un profesor usa para diagnosticar fallos individuales. El subagente journal-summarizer puede digerirla más tarde.

Tarea 5 — convergencia con el capstone tracker (A4)

El modo examen del portal de la Fase 41 lee el mismo data/eval/phase-32-grammar-tutor.yaml. El arnés aquí y el ejecutor de examen del portal deben coincidir en las métricas — ambos consumen el mismo YAML, ambos producen formas comparables de EvalReport. Esta convergencia es el gancho §A4: el arnés es la eval offline; el portal es la eval online; ambos llegan a la misma conclusión sobre si el tutor es lo bastante bueno.

El contrato del portal está documentado en src/miniportal/BLUEPRINT.md §"Exam scoring". La salida de tu arnés debe satisfacer:

  • Mismos nombres de métrica (correct, faithful, etc.).
  • Mismos valores de umbral (≥ 0.85 de corrección, etc.).
  • Mismo esquema JSON del log de traza.

Medidas a capturar

  • Todos los campos del EvalReport de arriba.
  • Desglose por categoría: precisión por categoría de verbo irregular, precisión de verbo regular, precisión de OOS-decline.
  • Un histograma de turns y tool_calls sobre los 30 prompts.
  • Un scatter de latencia por prompt.

Aceptación

  • data/eval/phase-32-grammar-tutor.yaml tiene ~30 entradas abarcando las 6 categorías anteriores.
  • scripts/eval_grammar_tutor.py se ejecuta hasta completar en la CPU de Borja en ≤ 90 s.
  • Re-ejecutar el arnés con la misma semilla produce un traces.jsonl byte-idéntico.
  • Los siete umbrales DoD pasan (o el arnés sale con código no-cero con un resumen claro del fallo).
  • El esquema JSON-line de la traza coincide con el formato esperado por el portal.
  • El conjunto de eval se publica bajo data/eval/ y es trackeado por DVC (dvc add data/eval/phase-32-grammar-tutor.yaml) para que la versión sea reproducible.

Trampas a esperar

  • Olvidar la semilla: dos ejecuciones discrepan en un par de prompts porque el sampler de Mini-GPT no es determinista. Fija temperature=0.0 para ejecuciones de eval; si un prompt falla a T=0, esa es la señal real.
  • Contar OOS-declines como fallos: un prompt fuera de alcance ("conjugate swim") que el agente correctamente declina es un ÉXITO, no un fallo. La métrica declined está separada de failed. La mayoría de evaluadores las confunden — ten cuidado aquí.
  • Whitelist creep en sinónimos en español: tentación de meter en whitelist corrió, corría, correría todos para corrió. Resiste. Añade sinónimos uno a uno conforme te encuentres falsos negativos; documenta cada adición en el YAML con un comentario.
  • Tamaño del log de traza: ~30 trazas × ~5 KiB cada una = 150 KiB por ejecución. No comitees las trazas; son artefactos de experimento (.gitignore experiments/).

Referencias cruzadas

  • docs/phase-20-evaluation-harness/ — el patrón del arnés de eval reusa la estructura de la Fase 20 para tareas de gramática.
  • src/miniportal/BLUEPRINT.md §"Exam scoring" — el contrato con el que este arnés debe converger.
  • docs/extension-track/X3-rlhf-dpo/lab/01-dpo-on-grammar-tutor.md — DPO usa este arnés como la señal de recompensa; las métricas aquí se convierten en el objetivo DPO.

Siguiente: con el arnés en verde, escribe PHASE_32_REPORT.md resumiendo la puntuación del agente sobre los 30 prompts y reflexiona sobre qué categorías necesitaron más qué componentes de las Fases 26-31.