Skip to content

English · Español

Lab 00 — Tools tipadas

Objetivo: implementar las cuatro tools §A13 como funciones Python planas con descriptores JSON-Schema. Sin MCP (Model Context Protocol) todavía — solo las tools.

Tiempo estimado: 2–3 horas.

Prereq: la tabla de verdad de conjugación §A13 de la Fase 12 disponible como data/processed/conjugation_table.json (o donde sea que la Fase 12 la haya producido).


Qué produces

Un directorio experiments/31-typed-tools/ que contiene:

  • demo.py — instancia cada tool e imprime un pequeño transcript de smoke-test.
  • results.json{tools_registered: 4, smoke_test_pass: true}.
  • manifest.json — según LYNX_CORTEX.md §5.

Más, en src/miniagent/tools/:

  • __init__.py — registro + decorador @register.
  • base.py — dataclass Tool.
  • conjugate.py — implementa conjugate.
  • lookup_irregular_verb.py — implementa lookup_irregular_verb.
  • lookup_spanish.py — implementa lookup_spanish.
  • check_subject_verb_agreement.py — implementa check_subject_verb_agreement.

TODOs

Bloque A — la dataclass Tool

En src/miniagent/tools/base.py:

  • Define una dataclass frozen:
    @dataclass(frozen=True)
    class Tool:
        name: str
        description: str
        input_schema: dict          # JSON-Schema
        fn: Callable[..., Any]
    
  • Define un tipo de excepción ToolError para errores a nivel de tool (inputs fuera de alcance, datos faltantes).
  • Define un decorador register que toma argumentos por palabra clave name, description e input_schema y añade la función envuelta a registered_tools en __init__.py.

Bloque B — conjugate

En src/miniagent/tools/conjugate.py:

  • @register(name="conjugate", description="Return the conjugated form of an English verb.", input_schema={...}) sobre una función:
    def conjugate(verb: str, tense: str, person: str) -> str:
        ...
    
  • Valida los args contra los enums §A13; lanza ToolError si están fuera de alcance.
  • Busca la forma en data/processed/conjugation_table.json (o impórtala como dict Python).
  • Devuelve la cadena conjugada. P. ej. conjugate("eat", "past_simple", "3sg") devuelve "ate".
  • Testea exhaustivamente: 20 verbos × 5 tiempos × 3 personas = 300 combinaciones. Todas deben hacer round-trip.

Bloque C — lookup_irregular_verb

En src/miniagent/tools/lookup_irregular_verb.py:

  • Firma: lookup_irregular_verb(verb: str) -> dict.
  • Devuelve {"infinitive": ..., "past_simple": ..., "past_participle": ..., "is_irregular": true} para los 8 verbos irregulares (be, have, do, go, come, see, eat, write).
  • Para un verbo regular (uno de los 12), devuelve {"is_irregular": false} — útil para que el agente pueda preguntar "¿es este irregular?" antes de comprometerse con una ruta de lookup.
  • Para un verbo fuera de alcance, lanza ToolError.

Bloque D — lookup_spanish

En src/miniagent/tools/lookup_spanish.py:

  • Firma: lookup_spanish(english_form: str) -> str.
  • english_form es la forma conjugada (p. ej., "ate", no "eat").
  • Devuelve la traducción canónica al español (español peninsular, forma tú, pretérito perfecto simple — ver PHASE_31_PLAN.md §5 pitfall 6).
  • Para una forma fuera de alcance, lanza ToolError.

Bloque E — check_subject_verb_agreement

En src/miniagent/tools/check_subject_verb_agreement.py:

  • Firma: check_subject_verb_agreement(subject: str, verb_form: str) -> dict.
  • subject es un pronombre: "I", "you", "he", "she", "it". (Los pronombres en plural están fuera del alcance §A13; devuelve un ToolError.)
  • verb_form es una forma inglesa conjugada.
  • Devuelve {"agrees": bool, "expected_form": str | None}. expected_form se rellena cuando agrees=false, nombrando la forma conjugada que concordaría.

Ejemplo: check_subject_verb_agreement("he", "go") devuelve {"agrees": false, "expected_form": "goes"}. check_subject_verb_agreement("he", "goes") devuelve {"agrees": true, "expected_form": null}.

Bloque F — tests

En tests/test_miniagent_tools.py:

  • test_conjugate_round_trip — 300 casos, todos coinciden con la tabla de verdad.
  • test_conjugate_out_of_scope_raisesconjugate("run", "past_simple", "3sg") lanza ToolError.
  • test_lookup_irregular_verb_returns_principal_parts — los 8 irregulares presentes, con past_simple y past_participle correctos.
  • test_lookup_irregular_verb_marks_regularslookup_irregular_verb("work") devuelve {"is_irregular": false}.
  • test_lookup_spanish_canonicallookup_spanish("ate") devuelve "comió" (o lo que sea que especifique la tabla de verdad para 3sg past).
  • test_check_agreement_pos — casos que concuerdan devuelven agrees=true.
  • test_check_agreement_neg_proposes_fix — casos que no concuerdan devuelven agrees=false con el expected_form correcto.
  • test_check_agreement_plural_pronoun_out_of_scopesubject="we" lanza ToolError.

Bloque G — smoke demo

En experiments/31-typed-tools/demo.py:

  • Importa registered_tools desde src.miniagent.tools.
  • Imprime name y description de cada tool.
  • Llama a cada tool con un input canónico; imprime el resultado.
  • Escribe results.json con {tools_registered, smoke_test_pass}.

Restricciones

  • Sin MCP todavía. Este lab es Python puro. El lab 01 introduce la capa de protocolo.
  • La tabla de verdad es el contrato. La Fase 12 produjo los datos; las tools leen de ahí. No codifiques a mano la rejilla de conjugación dentro de las funciones de tool — eso permitiría que se desviara del corpus.
  • Sin requisito de pydantic. Si Borja quiere añadirlo para validación de argumentos, vale; si no, la validación con dict de stdlib es suficiente.
  • Sin efectos secundarios de I/O. Las tools leen constantes y computan. No escriben ficheros, no van a la red, no llaman a servicios externos.

Condiciones de parada

Hecho cuando:

  1. Todos los tests del Bloque F pasan.
  2. demo.py imprime un transcript limpio y escribe results.json.
  3. El input_schema de cada tool valida con jsonschema.Draft7Validator(schema).check_schema() (sanity-check de que el schema está bien formado).

Pitfalls

  • Codificado a mano vs dirigido por tabla. Es fácil empezar a tipear la rejilla de conjugación dentro de conjugate. Resiste; tira de la tabla de verdad.
  • Variantes de traducción al español. Elige una forma canónica por forma inglesa y documenta la elección. Las variantes (vos vs tú, peninsular vs latinoamericano) son no-features explícitas.
  • Normalización de pronombres. "He" vs "he" vs "He.". Las tools deben pasar a minúsculas + quitar puntuación antes de comparar.
  • is_irregular: false no es un error. lookup_irregular_verb("work") es una llamada exitosa que devuelve una respuesta útil — no una excepción.
  • expected_form igual a null cuando concuerda. No devuelvas string vacío; devuelve None. Distingue "no hace falta arreglo" de "el arreglo es el string vacío".

Cuándo consultar solutions/

Tras que todos los tests del Bloque F pasen. La solución en solutions/00-typed-tools-ref.md (escrita en la apertura de la fase) revisará las formas de los schemas; diferencias menores están bien pero el mapeo test-tabla-de-verdad debe coincidir.


Siguiente lab: lab/01-mcp-server.md.