English · Español
01 — ReAct y la división planificador-ejecutor¶
🇪🇸 Hay dos arquitecturas dominantes para agentes: ReAct (decide → actúa → observa → decide) y plan-and-execute (planifica todo de una vez, luego ejecuta). Para nuestro tutor, ReAct gana — porque las decisiones dependen del resultado de la herramienta anterior y no sabemos a priori qué herramientas hacen falta.
ReAct: pensar + actuar, entrelazados¶
ReAct viene de Yao et al. 2022 ("ReAct: Synergizing Reasoning and Acting in Language Models"). La forma:
loop:
thought ← planner(state)
if thought.terminates:
return thought.answer
action ← thought.tool_call
observation ← execute(action)
state ← append(state, thought, action, observation)
El entrelazado es la clave. El modelo piensa después de ver cada observación, así que cada nueva acción está condicionada al resultado acumulado de las acciones anteriores. Esto cubre el caso en que el planificador no sabe qué herramienta llamar a continuación hasta haber visto la salida de la herramienta previa — que es el caso común en la corrección gramatical.
Traza de ejemplo para "He goed to school":
state_0: "He goed to school"
thought_1: subject seems to be "he"; verb seems to be "goed". Let me check if "go" is regular.
action_1: lookup_irregular_verb(verb="go")
observation_1: {"is_irregular": true, "past_simple": "went", ...}
state_1: {... above ...}
thought_2: irregular → "goed" might be wrong. Let me look up past simple of "go" for 3rd person sg.
action_2: conjugate(verb="go", tense="past_simple", person="3sg")
observation_2: "went"
state_2: {... above ...}
thought_3: confirmed: "goed" should be "went". Compose final answer.
action_3: FINAL_ANSWER
answer: CorrectionResult(corrected="He went to school", rationale=["go is irregular; past simple 3sg is 'went' not 'goed'"], spanish_gloss="Él fue a la escuela")
Tres pasos. Cada action estuvo condicionada por la observation previa. Un enfoque plan-and-execute habría tenido que adivinar las herramientas correctas por adelantado; ReAct las descubre sobre la marcha.
Plan-and-execute: compilar, después ejecutar¶
La alternativa (Wang et al. 2023 "Plan-and-Solve Prompting"; Sun et al. 2023 "AdaPlanner"): el modelo produce primero un plan completo — una secuencia (o DAG) de tool calls previstos — y después ejecuta el plan.
plan ← planner(state_0) # ["lookup_irregular_verb", "conjugate", ...]
for step in plan:
observation ← execute(step)
state ← append(state, step, observation)
answer ← responder(state)
Pros:
- Una invocación del modelo para planificar, \(n\) para ejecutar — menos invocaciones del modelo en total.
- El plan es introspeccionable: un humano puede revisar el plan antes de ejecutar acciones destructivas.
Contras:
- El plan no puede adaptarse a las observaciones. Si
lookup_irregular_verb("go").is_irregular = truedevuelve algo inesperado, el plan ya está comprometido. - Difícil planificar cuando el espacio de acción depende de resultados previos. ("Si el verbo es regular, haz X; si no, haz Y" requiere un plan condicional, que es más difícil de generar.)
Para la Fase 32, ReAct gana porque:
- El árbol de decisión se ramifica según los resultados de la herramienta (regular/irregular, concordancia OK/no, dentro de alcance/fuera).
- El corpus §A13 es lo bastante pequeño como para que las llamadas al LM por paso de ReAct sean baratas.
- El número de pasos suele ser 2–4 — pequeño incluso para estándares de ReAct.
Plan-and-execute es la elección correcta cuando los pasos son caros (p. ej., llamadas a API remotas con rate limits) y el plan puede comprobarse estáticamente.
El planificador como decoding restringido¶
El trabajo del planificador: dado el estado actual, producir el siguiente paso. Restringimos la salida del planificador a uno de dos esquemas:
// Schema for ToolCall
{
"next": "tool_call",
"tool": "<enum: conjugate | lookup_irregular_verb | check_subject_verb_agreement | lookup_spanish>",
"args": { ... } // schema depends on tool
}
// Schema for FinalAnswer
{
"next": "final_answer",
"answer": {
"corrected": "<string | null>",
"rationale": ["<string>", "..."],
"spanish_gloss": "<string | null>",
"in_scope": "<boolean>"
}
}
El planificador es literalmente un JSONSchemaMask sobre la unión de estos dos esquemas. En cada paso de generación, solo se permiten tokens que mantengan válida la salida parcial. Esta es la técnica de la Fase 30 aplicada al flujo de control del agente.
Una consecuencia sutil pero importante: el planificador no puede alucinar un nombre de herramienta inexistente. El campo tool es un enum sobre los nombres de las herramientas registradas. Los tokens fuera de ese enum quedan enmascarados. Esto elimina el modo de fallo más común de los agentes en texto libre.
Estado y observaciones: qué ve el planificador¶
El planificador ve un prompt ensamblado a partir de:
- La instrucción del sistema (qué hace el agente, qué herramientas existen, qué esquema seguir).
- La entrada del usuario (la frase a corregir).
- La traza hasta ahora: cada triple
(thought, action, observation)de pasos anteriores del loop. - El prompt que pide al planificador emitir el siguiente paso.
La traza es la "ventana de contexto" del agente — es el único estado que el planificador puede leer. La memoria a largo plazo (entre correcciones) se trae al prompt a mano cuando es relevante. Cubriremos la memoria en el siguiente archivo.
Un prompt canónico:
You are a grammar tutor for English verb conjugations (5 tenses, 3 singular persons).
You have these tools: <enumerate with arg schemas>.
Output JSON matching the planner schema.
SENTENCE: He goed to school.
TRACE:
step 1:
action: lookup_irregular_verb(verb="go")
observation: {"is_irregular": true, "past_simple": "went", "past_participle": "gone"}
What's the next step?
El modelo completa con una salida enmascarada por JSON. La parseamos. Despachamos. Repetimos.
Terminación, presupuestos y dedup¶
Tres señales de terminación:
FinalAnsweremitido. El happy path.- Presupuesto de pasos agotado. Cap duro en
K = 8. El agente devuelve un resultado estructurado "no pude converger" con la traza parcial. - Bucle de acción duplicada. Si la misma
(tool, args)aparece dos veces seguidas, el agente se detiene y reporta el bucle. Esto es un bug del planificador; queremos verlo, no iterar para siempre.
Estas tres dan al agente una ejecución acotada. Cualquier agente que no las tenga las tres es inseguro para producción.
La Bitter Lesson, edición agente¶
La "Bitter Lesson" de Rich Sutton (2019) dice: a largo plazo, los métodos generales que escalan con cómputo baten a los métodos ingeniosos que hardcodean conocimiento humano. Para agentes, esto significa:
- Un modelo más grande con un loop más simple generalmente bate a un modelo más pequeño con un sistema de planificación más elaborado.
- Tool calling entrenado vía RLHF / DPO bate al tool calling vía prompt, dado suficiente cómputo.
La Fase 32 está en el lado del método ingenioso de esta división — modelo pequeño, mucha estructura (la máscara JSON). Esa es la decisión correcta para un proyecto pequeño; en producción también tendrías los datos de entrenamiento para hacer la versión bitter-lesson.
Lo que este archivo NO cubre¶
- Ejecutores de plan-DAG. Mencionados por completitud; no implementados.
- Las matemáticas de las distribuciones de logit con máscara. Fase 30.
- MCTS / tree-of-thought / reflection. Bucles de agente de mayor potencia que son caros y no encajan en el presupuesto de Mini-GPT. Citados solo por vocabulario.
Siguiente: 02-memory.md