Skip to content

English · Español

Lab 01 — Agente tutor sobre 30 frases canónicas

Lee theory/00-motivation.md y theory/01-react-and-planning.md. No consultes solutions/.

Objetivo

Cablea Planner, ScratchpadMemory, LongTermMemory y las herramientas MCP de la Fase 31 en un GrammarTutorAgent. Ejecútalo sobre el conjunto canónico de prueba de 30 frases. Consigue ≥ 90 % de corrección. Captura la traza completa para un happy path, una ambigüedad y un rechazo fuera de alcance.

Setup

Archivos a crear / extender:

  • src/miniagent/agent.pyGrammarTutorAgent
  • src/miniagent/types.pyCorrectionResult, Step, PlannerState
  • tests/test_agent_loop.py — el test de regresión de 30 frases
  • experiments/<date>-phase-32-tutor-demo/ — transcripciones + resumen

Tareas

Tarea 1 — ensambla el conjunto canónico de prueba

Crea experiments/<date>-phase-32-tutor-demo/test_sentences.json con 30 entradas:

  • 10 correcciones happy-path (entrada clara, un solo error). Ejemplos: "He goed to school" → "He went to school", "I has a book" → "I have a book", "She don't like it" → "She doesn't like it".
  • 5 frases ya correctas (no se necesita corrección). Ejemplos: "I work every day". El agente debería devolver corrected = None (ya que la original es correcta).
  • 5 frases ambiguas (múltiples correcciones válidas). Ejemplos: "I will going to the store" — podría ser "I will go" o "I am going". Elige una corrección canónica.
  • 5 frases multi-error (≥ 2 errores). Ejemplo: "He goed and she have went home". El agente debería arreglar un error y reportar el resto como additional_issues.
  • 5 frases fuera de alcance (usan pronombres plurales, verbos fuera de §A13 o tiempos fuera de §A13). Ejemplo: "They are running." El agente debería devolver corrected = None, in_scope = False, rationale = ["plural pronoun out of scope"].

Cada entrada:

{
  "id": 1,
  "original": "He goed to school.",
  "expected": {
    "corrected": "He went to school.",
    "in_scope": true,
    "rationale_keywords": ["irregular", "past", "went"],
    "must_call_tools": ["lookup_irregular_verb", "conjugate"]
  }
}

El campo must_call_tools es el camino dorado por el espacio de herramientas — útil para diagnosticar dónde se desvía el planificador del agente.

Tarea 2 — implementa GrammarTutorAgent

class GrammarTutorAgent:
    def __init__(
        self,
        planner: Planner | MockPlanner,
        tool_dispatcher: MCPClient,
        long_term: LongTermMemory,
        max_steps: int = 8,
    ):
        ...

    def correct(self, sentence: str, learner_id: str = "borja") -> CorrectionResult:
        scratchpad = ScratchpadMemory()  # fresh per call
        for step_index in range(self.max_steps):
            state = PlannerState(
                original=sentence,
                scratchpad=scratchpad,
                long_term_view=self.long_term.view_for(learner_id, sentence),
                step_index=step_index,
            )
            step = self.planner.next_step(state)

            if isinstance(step, FinalAnswer):
                self._update_long_term(learner_id, sentence, step.answer)
                return CorrectionResult(
                    original=sentence,
                    corrected=step.answer.corrected,
                    rationale=step.answer.rationale,
                    spanish_gloss=step.answer.spanish_gloss,
                    in_scope=step.answer.in_scope,
                    tool_trace=scratchpad.tool_calls(),
                )
            elif isinstance(step, ToolCall):
                if self._is_duplicate_action(step, scratchpad):
                    return self._budget_exhausted_result(sentence, scratchpad, reason="loop")
                result = run_under_sandbox(
                    self.tool_dispatcher.get_tool(step.tool),
                    step.args,
                    policy=SandboxPolicy.PERMISSIVE,
                )
                scratchpad.append(thought="", action=step, observation=result)
            else:
                raise TypeError(f"unexpected step type: {type(step)}")

        return self._budget_exhausted_result(sentence, scratchpad, reason="budget")

Restricciones:

  • scratchpad se construye dentro de correct(), no se almacena en self.
  • long_term se comparte entre correcciones (es estado persistente).
  • Todas las herramientas se despachan vía el cliente MCP de la Fase 31 (sin imports directos).
  • Detección de acción duplicada: si la misma (tool, args) aparece dos veces seguidas, detente.

Tarea 3 — cablea primero contra MockPlanner

Antes de enchufar el planificador real (Mini-GPT no entrenado), ejecuta contra MockPlanner del Lab 00. El mock devuelve pasos prefabricados para las 30 frases. Esto verifica que el bucle funciona antes de introducir incertidumbre del modelo.

Ejecuta sobre las 30 frases:

agent = GrammarTutorAgent(planner=MockPlanner(scripts), ...)
results = [agent.correct(s["original"]) for s in test_sentences]

Esperado: ≥ 95 % de corrección contra MockPlanner (con scripts escritos correctamente, esto es cerca del 100 %). Cualquier fallo aquí es un bug del bucle del agente, no del planificador.

Tarea 4 — captura tres transcripciones

Para inclusión en PHASE_32_REPORT.md:

  1. Happy path"He goed to school" — traza completa.
  2. Ambigüedad"I will going home" — traza completa, mostrando cómo el agente elige una corrección canónica.
  3. Fuera de alcance"They went home" — traza completa, mostrando al agente reconociendo y reportando el fuera-de-alcance.

Para cada uno, guarda:

{
  "sentence": "...",
  "trace": [
    {"step": 1, "action": {"tool": "...", "args": {...}}, "observation": "..."},
    {"step": 2, "action": {"tool": "...", "args": {...}}, "observation": "..."},
    {"step": 3, "action": "FINAL_ANSWER", "answer": {...}}
  ]
}

Tarea 5 — mide la corrección

Para el conjunto de prueba de 30 frases, reporta:

  • Precisión (correctas ÷ total) por categoría (happy / ya-correcta / ambigua / multi-error / fuera-de-alcance).
  • Pasos medios por corrección.
  • Herramientas medias invocadas por corrección.
  • Distribución de conteos de pasos (histograma, guardar como experiments/<date>-phase-32-tutor-demo/steps_histogram.png).

Objetivo: ≥ 90 % de precisión global con MockPlanner. Con el planificador real (no entrenado), la precisión probablemente será ~aleatoria; esto se documenta como la brecha entre el "entrenado para corrección forward" de la Fase 17 y el "fine-tuned para seguimiento de instrucciones" de la Fase 28.

Tarea 6 — verificación de actualización de memoria a largo plazo

Tras ejecutar las 30 frases, inspecciona longterm.json. Los contadores de errores para verbos como go, have, do deberían tener ≥ 1 entrada. Las estadísticas de precisión por tiempo deberían estar pobladas.

Una segunda ejecución sobre las mismas 30 frases debería:

  • Producir las mismas correcciones (determinista con semilla fija).
  • Mostrar conteos de errores doblados en longterm.json (ya que cada frase se corrigió dos veces).
  • Posiblemente mostrar racionales diferentes si el agente inyecta contexto long-term en el prompt.

Medidas a capturar

  • Resultados del test de 30 frases (pasa/falla por frase).
  • Tres transcripciones (happy / ambigüedad / fuera-de-alcance).
  • Histograma de distribución de pasos.
  • longterm.json tras una ejecución.
  • Diff entre longterm.json tras la ejecución 1 y la 2.

Aceptación

  • GrammarTutorAgent.correct() funciona sobre las 30 frases sin lanzar.
  • Precisión basada en MockPlanner ≥ 90 %.
  • Tres transcripciones guardadas en el directorio del experimento.
  • Histograma de pasos guardado.
  • longterm.json se actualiza correctamente entre ejecuciones.
  • El test tests/test_agent_loop.py está en verde.

Trampas a esperar

  • Fuga del scratchpad. Si scratchpad se almacena en self, la segunda correct() ve la traza de la primera. Testea esto explícitamente: llama correct("A") luego correct("B"); el scratchpad de la segunda llamada debería tener solo los pasos de B.
  • Actualización de long-term en fuera-de-alcance. ¿Deberíamos incrementar contadores de errores para frases fuera de alcance? Default: no (fuera-de-alcance no es un error de verbo, es un desajuste de corpus). Documenta la elección.
  • Lagunas en el script del MockPlanner. Si scriptaste solo 25 de 30 frases, las 5 que faltan saltarán un KeyError. O scripta las 30, o haz que MockPlanner lance un error claro de "scripted-but-not-found".
  • El agente llama a final_answer inmediatamente. Un planificador con bugs podría emitir final_answer como paso 1 sin invocar ninguna herramienta. El resultado no es fiable pero es estructuralmente válido. Captura esto en el test: asserta conteo de pasos ≥ 1 (al menos un tool call) antes de aceptar una respuesta, excepto para frases "ya correctas".

Siguiente: 02-sandbox-an-evil-tool.md