Agentes Loops: ReAct, Ralph y el arte de no perder el contexto
Tienes un agente que funciona de maravilla en tareas cortas. Le pides que arregle un bug, lo hace. Le pides que escriba un test, lo escribe. Pero cuando intentas que refactorice un módulo completo, que migre una base de datos o que implemente varias features seguidas, algo se rompe a mitad del camino. El agente empieza fuerte y acaba cometiendo errores que no cometería al principio.
El problema no es el modelo. El problema es cómo está estructurado el bucle.
Qué es un bucle de agente
Un agente es un modelo de lenguaje al que le das acceso a herramientas: leer archivos, ejecutar comandos, buscar en la web, editar código. Pero lo que convierte un LLM en un agente útil no son las herramientas en sí, sino el bucle que controla cómo las usa.
El bucle es el mecanismo que decide qué hacer con la respuesta del modelo. ¿Llamó a una herramienta? Ejecuta la herramienta y devuelve el resultado. ¿No llamó a ninguna? El trabajo está hecho. Repite hasta terminar.
Así de simple en teoría. Así de crítico en la práctica.
El bucle ReAct
ReAct (Reasoning + Acting) es el patrón estándar para construir agentes. El nombre describe exactamente lo que hace: el modelo razona sobre el estado actual y después actúa usando herramientas.
El ciclo completo tiene cuatro pasos que se repiten:
- Receive — Claude recibe el prompt, el historial de la conversación y las definiciones de las herramientas disponibles.
- Evaluate — El modelo evalúa el estado y decide qué hacer: responder con texto, llamar a una o más herramientas, o ambas.
- Execute — El SDK ejecuta las herramientas solicitadas y devuelve los resultados a Claude.
- Repeat — Claude procesa los resultados y vuelve al paso 2. El bucle termina cuando el modelo produce una respuesta sin llamadas a herramientas.
En código, con el Claude Agent SDK, esto se ve así:
import { query, ResultMessage } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Encuentra y arregla el bug que hace fallar los tests en auth.ts",
options: {
allowedTools: ["Read", "Edit", "Bash", "Glob", "Grep"],
maxTurns: 30,
},
})) {
if (message.type === "result") {
console.log(message.result);
console.log(`Costo: $${message.total_cost_usd?.toFixed(4)}`);
}
}
El equivalente en Python es igual de limpio:
from claude_agent_sdk import query, ResultMessage, ClaudeAgentOptions
async for message in query(
prompt="Encuentra y arregla el bug que hace fallar los tests en auth.ts",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Bash", "Glob", "Grep"],
max_turns=30,
),
):
if isinstance(message, ResultMessage):
print(message.result)
LangChain también implementa este patrón con su API de agentes. La idea es la misma: un bucle que conecta las llamadas al modelo con la ejecución de herramientas.
Lo que ReAct hace bien
- Tareas cortas y bien definidas: arreglar un bug, escribir un test, analizar un archivo.
- Adaptación en tiempo real: Claude ajusta su plan basándose en el output de cada herramienta.
- Sin complejidad extra: un único proceso, un único estado, resultado directo.
El problema: el contexto que nunca se reinicia
Aquí está el talón de Aquiles de ReAct.
La ventana de contexto es toda la información que tiene Claude en un momento dado: el prompt inicial, el historial de mensajes, las definiciones de herramientas, los resultados de las herramientas anteriores. Y en ReAct, todo se acumula en la misma sesión.
Cada llamada a una herramienta consume tokens. Leer un archivo grande: miles de tokens. Ejecutar un comando con output verbose: más tokens. Una sesión larga de refactorización puede consumir decenas de miles de tokens antes de llegar a la tarea importante.
El efecto es predecible:
- Las instrucciones del prompt inicial quedan enterradas bajo capas de historial.
- El modelo empieza a contradecirse con decisiones que tomó hace 20 turnos.
- La calidad del output se degrada de forma progresiva.
- En el peor caso, el agente entra en un bucle infinito intentando arreglar algo que él mismo rompió.
# Lo que parece al principio
Turn 1: Lee auth.ts ✅
Turn 2: Edita la función tokenize() ✅
Turn 3: Ejecuta los tests ✅
# Lo que pasa en turno 45
Turn 45: Edita auth.ts... sobreescribiendo la corrección del turno 12 ❌
Turn 46: Ejecuta tests → fallan → intenta arreglarlos ❌
Turn 47: Sobreescribe auth.ts de nuevo... ❌
El contexto acumulado no es gratis. Cada token que el modelo tiene que “digerir” es un token que no puede dedicar a razonar sobre la tarea en sí.
El bucle Ralph
Geoffrey Huntley publicó en 2025 lo que llamó el Ralph Loop: una técnica radical en su simplicidad para resolver exactamente este problema.
El núcleo de Ralph cabe en una línea de bash:
while :; do cat PROMPT.md | claude-code; done
Eso es todo. Un bucle infinito que lanza una instancia fresca de claude-code en cada iteración. Cada vez que el agente termina (con éxito o con error), el proceso muere y nace uno nuevo. Contexto siempre a cero.
Las reglas del Ralph Loop
Ralph tiene tres restricciones que no son opcionales:
1. Una tarea por iteración. El agente no intenta hacer todo de una vez. Lee la lista de tareas pendientes, elige la más prioritaria, la implementa, la testea, hace commit y termina. La siguiente iteración se ocupa de la siguiente tarea.
2. Contexto en archivos, no en memoria. El estado del proyecto no vive en la conversación del agente. Vive en archivos: SPEC.md (qué hay que construir), PLAN.md (lista de tareas priorizadas), tests (qué define “correcto”). El agente los lee al empezar cada iteración.
3. Backpressure por tests. Si los tests fallan, el agente no puede hacer commit. La calidad se garantiza por el criterio de salida, no por confiar en que el agente lo haga bien.
📋 SPEC.md → "Qué hay que construir"
📝 PLAN.md → "Lista de tareas priorizadas"
🧪 tests/ → "Qué significa estar terminado"
🔄 ralph.sh → "El bucle que lo ejecuta todo"
Por qué funciona
La clave es que el contexto fresco no es un bug, es el diseño. En lugar de un agente que recuerda todo, tienes un agente que lee el estado actual del proyecto en cada iteración. El estado persiste en el código y los archivos. El agente es stateless.
Huntley documentó un caso real: entregó un MVP completo por $297 USD en un contrato de $50k. El coste bajo no es por usar un modelo peor, sino por no desperdiciar tokens en historial que el modelo no necesita.
El Ralph Loop no compite con ReAct. Resuelve un problema diferente: proyectos largos donde la consistencia a lo largo de muchas tareas importa más que la flexibilidad dentro de una sola tarea.
Simulador interactivo
Observa el Ralph Loop en acción. Fíjate en dos cosas: cómo crece la barra de contexto durante la iteración y cómo se reinicia a 0 cuando el agente termina y arranca el siguiente.
Simulador del Ralph Loop
Observa cómo el Ralph Loop ejecuta una tarea por iteración y reinicia el agente con contexto limpio en cada ciclo. Selecciona un escenario y pulsa Iniciar.
Prueba el escenario “Proyecto complejo” a velocidad 4x para ver varias iteraciones seguidas. El patrón es siempre el mismo: crece, crece, crece… y reset. Con ReAct puro, ese contexto nunca se reiniciaría.
Cómo implementar el Ralph Loop
Versión bash (la original)
Crea tres archivos y un script:
# ralph.sh
#!/usr/bin/env bash
set -euo pipefail
while :; do
# El agente lee PROMPT.md para saber qué hacer
result=$(cat PROMPT.md | claude-code --dangerously-skip-permissions 2>&1)
# Si el agente indica que no hay más tareas, terminamos
if echo "$result" | grep -q "NO_TASKS_REMAINING"; then
echo "✅ Proyecto completado"
break
fi
echo "🔄 Iteración completada. Reiniciando con contexto limpio..."
done
# PROMPT.md
Lee SPEC.md y PLAN.md. Elige la tarea más prioritaria no completada.
Impleméntala, asegúrate de que los tests pasen, haz commit con un mensaje descriptivo.
Actualiza PLAN.md marcando la tarea como completada.
Si no quedan tareas, escribe exactamente: NO_TASKS_REMAINING
Versión TypeScript con el Agent SDK
Si quieres más control sobre el bucle (logs, límites de coste, manejo de errores), puedes implementarlo con el SDK:
import { query, ResultMessage } from "@anthropic-ai/claude-agent-sdk";
import { readFileSync, writeFileSync } from "fs";
async function ralphLoop() {
let iteration = 0;
const MAX_ITERATIONS = 50; // límite de seguridad
while (iteration < MAX_ITERATIONS) {
iteration++;
console.log(`\n━━━ Iteración #${iteration} — contexto limpio ━━━`);
const prompt = readFileSync("PROMPT.md", "utf-8");
let finalResult = "";
let cost = 0;
// Nueva instancia del agente en cada iteración
for await (const message of query({
prompt,
options: {
allowedTools: ["Read", "Edit", "Write", "Bash", "Glob", "Grep"],
maxTurns: 25,
maxBudgetUsd: 0.5, // tope por iteración
effort: "medium",
},
})) {
if (message.type === "result") {
finalResult = message.result ?? "";
cost = message.total_cost_usd ?? 0;
}
}
console.log(`💰 Costo iteración: $${cost.toFixed(4)}`);
if (finalResult.includes("NO_TASKS_REMAINING")) {
console.log("🎉 Proyecto completado");
break;
}
}
}
ralphLoop();
El SPEC.md que marca la diferencia
El éxito del Ralph Loop depende casi completamente de un buen SPEC.md. Cuanto más preciso, más autónomo puede ser el bucle:
# SPEC.md — API de autenticación
## Objetivo
API REST con JWT para registro e inicio de sesión.
## Criterios de aceptación
- POST /auth/register → crea usuario, devuelve 201
- POST /auth/login → valida credenciales, devuelve token JWT
- GET /auth/me → devuelve perfil (requiere token válido)
- Contraseñas hasheadas con bcrypt (cost factor 12)
- Tokens expiran en 24h
- Todos los endpoints tienen tests de integración
## Stack
- Node.js + TypeScript + Express
- PostgreSQL + Prisma
- Vitest para tests
## Estándares
- Errores en formato { error: string, code: string }
- Logs estructurados con pino
ReAct vs Ralph: cuándo usar cada uno
| Criterio | ReAct | Ralph Loop |
|---|---|---|
| Tipo de tarea | Una tarea bien definida | Múltiples tareas relacionadas |
| Duración | Corta (< 20 turnos) | Larga (muchas iteraciones) |
| Estado | En la conversación | En archivos del proyecto |
| Consistencia | Puede degradarse | Constante por iteración |
| Coste | Más alto en sesiones largas | Predecible por tarea |
| Control humano | Supervisión continua | Pausable entre iteraciones |
| Setup | Mínimo | Requiere SPEC.md + PLAN.md |
Usa ReAct cuando:
- La tarea tiene un alcance claro y acotado.
- Necesitas adaptación dinámica dentro de la tarea.
- No quieres configurar archivos de especificación.
- Estás explorando o prototipando.
Usa Ralph cuando:
- Tienes un proyecto con muchas tareas bien definidas.
- La consistencia importa más que la flexibilidad.
- Quieres que el agente trabaje de forma autónoma durante horas.
- Necesitas un coste predecible y controlable.
Conclusión
El ReAct loop es el estándar porque es simple y funciona bien para el caso común: una tarea, un contexto, un resultado. El SDK de Claude, LangChain y la mayoría de frameworks de agentes lo implementan por defecto.
El Ralph Loop es la respuesta pragmática a un problema real: los agentes se vuelven poco fiables cuando el contexto crece sin control. La solución no es un modelo más grande ni un contexto más largo. La solución es reiniciar el contexto con intención.
Para mí, lo más valioso de Ralph no es el bash one-liner, sino el cambio de mentalidad que propone: el agente es stateless, el estado vive en el código. Es la misma filosofía que hace que los microservicios escalen mejor que los monolitos con estado. Y es sorprendentemente efectivo.
Si estás construyendo algo más ambicioso que una tarea puntual, vale la pena experimentar con el bucle Ralph. El coste de configurar un SPEC.md es mucho menor que el coste de depurar por qué tu agente sobreescribió el trabajo correcto en el turno 47.
Preguntas frecuentes
¿El Ralph Loop funciona solo con Claude Code? No. Funciona con cualquier agente que pueda leer un prompt de un archivo y hacer commit. El repo de ralph-wiggum.ai tiene ejemplos con Cursor y OpenAI Codex.
¿Cuánto contexto consume una iteración típica? Depende de la tarea. Una tarea media (leer 3-4 archivos, editar 1-2, ejecutar tests) suele consumir entre el 30% y el 70% de la ventana de contexto. Ralph resuelve esto haciendo que ese consumo no importe: siempre empiezas fresco.
¿Puedo combinar ReAct y Ralph? Sí. Puedes usar ReAct para las subtareas dentro de cada iteración de Ralph. El bucle externo (Ralph) garantiza contexto limpio entre tareas. El bucle interno (ReAct) resuelve cada tarea con toda la flexibilidad del agente.
¿Qué pasa si el agente falla en mitad de una iteración? El commit no se produce, así que el código queda sin cambios. El PLAN.md tampoco se actualiza. En la siguiente iteración, el agente intenta la misma tarea de nuevo. Es una forma rudimentaria pero efectiva de idempotencia.
¿El --dangerously-skip-permissions en el script bash es seguro?
Solo en entornos aislados: containers, VMs, CI. En tu máquina local, usa el modo interactivo de Claude Code para aprobar operaciones sensibles, o configura permisos más granulares con allowedTools en el SDK.
Artículos relacionados
25 jul 2026
Graphify: dale a tu agente de IA un mapa de tu proyecto en vez de un buscador
Qué es Graphify y qué aporta frente al grep. Ejemplo paso a paso sobre un proyecto público en GitHub, con el grafo interactivo y métricas A/B reales.
14 jun 2026
Mejora las respuestas de tu IA: deja de pedir texto, pide HTML
La IA genera respuestas mucho más útiles cuando le pides HTML en lugar de texto. Aprende a usar artefactos HTML autocontenidos y a mantener consistencia con design.md.
12 jun 2026
Feature Flags: despliega sin miedo y controla quién ve qué
Qué son los feature flags, cómo implementarlos desde cero y cómo usarlos para lanzar funcionalidades de forma segura y controlada. Con simulador interactivo.
Newsletter
Aprende algo útil cada semana.
Ideas sobre programación, arquitectura e IA para mejorar tu código en menos de 5 minutos de lectura.
Sin spam · Baja cuando quieras