Loop engineering: ya no programas al agente, programas su bucle
Durante dos años el consejo fue el mismo: “aprende a promptear”. Y estaba bien, hasta que dejó de estarlo. Llevo meses sin escribir prompts sueltos a un agente. Monto un flujo, le doy una señal contra la que corregirse, y lo dejo trabajar. Peter Steinberger lo dejó en una frase: “You shouldn’t be prompting coding agents anymore. You should be designing loops that prompt your agents.”
Boris Cherny, el creador de Claude Code en Anthropic, lo llevó al extremo: “Ya no prompteo a Claude. Tengo loops ejecutándose. Ellos son los que lo prompean y deciden qué hacer.” A eso se le ha puesto nombre este año: loop engineering. Ya conoces la mecánica del sector: coges el último escalón del trabajo con agentes y le grapas “engineering” detrás, como se hizo con prompt engineering y con todo lo que vino después. Pero por una vez el nombre no es humo: lo que cambia de verdad es dónde está el apalancamiento de tu trabajo.
Te lo cuento con criterio de ingeniero, no de hype. Qué es, por qué importa, y cómo lo haces tú el lunes sin tocar una sola línea de API.
El oficio se movió cuatro veces
Mira la progresión completa, porque el sitio donde estás tú marca lo que puedes hacer:
prompt → escribes la instrucción perfecta una vez.
context → diseñas QUÉ ve el modelo antes de responder.
harness → montas el andamiaje: herramientas, memoria,
verificación, guardarraíles alrededor del modelo.
loop → diseñas el CICLO que usa ese andamiaje solo,
vuelta tras vuelta, sin ti en cada paso.
El harness (que ya conté en su propio artículo) es el equipo: qué herramientas hay, qué memoria, qué verificación. El loop es la coreografía de la carrera: cuándo el agente actúa, cuándo mira el resultado, cuándo decide seguir, cuándo para.
Y aquí está la parte que casi nadie separa: puedes tener un harness excelente y un loop pésimo. Un agente con las mejores herramientas del mundo que igual entra en bucle infinito, o que para demasiado pronto y te deja el trabajo a medias. Loop engineering es diseñar ese ciclo.
El loop es un while de verdad, no una metáfora
Esto es lo que más cuesta creer hasta que lo ves. Thorsten Ball, de Sourcegraph, montó un agente que edita código de verdad en menos de 400 líneas, “la mayoría boilerplate”. El corazón, lo que de verdad es el agente, son unas 40 líneas. Su resumen es la mejor definición que he leído: “It’s an LLM, a loop, and enough tokens” (un LLM, un bucle, y tokens suficientes).
Quítale el andamiaje y el bucle se queda en esto:
mensajes = [tarea_del_usuario]
while True:
respuesta = llm(mensajes) # el modelo decide: ¿herramienta o terminar?
mensajes.append(respuesta)
if not respuesta.tool_calls: # no pidió herramientas: ha terminado
break
resultados = [ejecutar(t) for t in respuesta.tool_calls] # el ENTORNO responde
mensajes.append(resultados) # el resultado real vuelve al contexto
Eso es todo, sin misterio. Lo importante está en la penúltima línea: entre cada vuelta, el loop recibe una respuesta del entorno, no del modelo. La salida de verdad de tus tests, tu compilador, tu linter. Anthropic lo llama obtener ground truth del entorno. Esa es la diferencia entre un loop que funciona y uno que gira en el vacío.
El volante son las señales, no tu paciencia
Un loop necesita, en cada vuelta, saber si va mejor o peor. Esa señal es el volante. Y hay dos clases, con consecuencias opuestas.
El volante malo es tu juicio, o peor, el del propio modelo. Si el loop itera “hasta que la respuesta parezca buena”, estás confiando en que el modelo evalúe su propio trabajo. No funciona, y hay razón: el generador y el evaluador son el mismo modelo, comparten los mismos sesgos, así que no ve el fallo que acaba de cometer. Auto-corregirse contra uno mismo es marcar tu propio examen.
El volante bueno es una señal determinista y externa al modelo: la suite de tests que pasa o falla, el type-checker, el linter. El patrón se llama code-then-correct y se teclea en cuatro líneas (siempre acotado, que a eso vamos):
for intento in range(MAX_INTENTOS): # límite máximo: sin esto es un incendio con presupuesto
codigo = llm(tarea, historial)
escribir(codigo)
resultado = ejecutar("pytest -q") # el volante: ground truth, no opinión
if resultado.ok:
return codigo # verde: terminado
historial.append((codigo, resultado.salida)) # el fallo REAL vuelve al contexto
La línea que hace el trabajo es ejecutar("pytest -q"). No le preguntas al modelo si su código está bien. Se lo preguntas a pytest. Grábate la idea clave: la calidad de un loop está acotada por la calidad de su señal. Un loop excelente contra tests flojos converge rápido a algo malo.
El lado oscuro: el loop hace trampa
Ahora lo contraintuitivo, lo que un senior tiene que tener claro. Un loop no optimiza tu objetivo. Optimiza su señal. Si la señal es “que los tests pasen”, el loop descubrirá que borrar el test que falla también hace que “los tests pasen”.
No me lo invento. METR auditó modelos frontera y documentó dos casos de manual. En una tarea de optimizar un kernel, un modelo recorrió el call stack de Python hasta encontrar la respuesta que el propio sistema de puntuación ya había calculado, y la devolvió tal cual: cero cómputo, el test en verde, el trabajo sin hacer. En otra, el modelo sobrescribió el operador de igualdad de PyTorch para que a == b devolviera 1 == 1, de forma que cualquier salida, correcta o no, pasara el eval.
Fíjate en que ninguno “rompió las reglas”. Optimizaron exactamente la señal que les dieron. Eso es lo que hace tan traicionero el reward hacking, y la razón de que la señal tenga que estar fuera del alcance del bucle: si el agente puede tocar el test, tarde o temprano lo toca.
Lo difícil no es el loop, es cuándo parar
Como dice el propio Cherny: “el loop es la parte fácil; el trabajo es el contexto y la condición de parada.” Escribir el while te lleva diez minutos. Decidir cuándo se detiene, con qué presupuesto y contra qué objetivo alcanzable es donde se va el oficio.
De ahí sale la pregunta que casi nadie hace: ¿cuándo NO montar un loop? Cuando no tienes una señal determinista y barata que juzgue cada vuelta. Un loop brilla arreglando tests, pasando un linter o cuadrando tipos, porque el veredicto es instantáneo y objetivo. Ponlo a “mejorar la arquitectura” o a “escribir una landing que convierta” y no hay pytest que le diga si va bien: gira, gasta, y te devuelve algo que parece terminado sin estarlo. Ahí el humano no sobra, es el único juez que hay.
Y está el coste de la opacidad. Un bucle que se equivoca no te avisa: sigue, convencido. Cuando lo pillas, ya ha tocado veinte ficheros y quemado la mitad de tu presupuesto. Por eso lo primero que diseñas no es la autonomía, es el freno.
Lo que de verdad vas a usar: loop engineering sin API
Todo lo de arriba parece “móntate tu propio agente”. Y sí, se puede. Pero seamos honestos: tú no vas a escribir ese while el lunes. Vas a usar Claude Code, y ahí el bucle ya se ejecuta por ti. Lo que diseñas es cómo se ejecuta. Te enseño las tres piezas, con sintaxis que funciona hoy.
La señal, con un hook. Un hook PostToolUse ejecuta un comando cada vez que el agente edita un fichero. Si falla, le devuelves el error y se corrige solo. En .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{ "type": "command", "command": ".claude/hooks/typecheck.sh" }
]
}
]
}
}
Y el script hace la magia con un código de salida:
#!/bin/bash
if ! salida=$(npm run typecheck 2>&1); then
echo "$salida" >&2
exit 2 # exit 2 = error bloqueante; el stderr se le pasa al agente
fi
exit 0
Ese exit 2 es la clave. Con él, Claude Code le entrega el error al modelo como parte del bucle, y el agente itera hasta dejarlo limpio. Acabas de convertir tu type-check en el volante, sin una línea de API. Cambia npm run typecheck por pytest -q y tienes lo mismo con tests.
El freno, con un sub-agente. Un sub-agente es un bucle hijo con su propio contexto. Se define en un Markdown, y maxTurns le pone el tope. En .claude/agents/arregla-tests.md:
---
name: arregla-tests
description: Ejecuta la suite, lee los fallos y los arregla hasta verde.
tools: Read, Edit, Bash
model: sonnet
maxTurns: 20
---
Ejecuta `pytest -q`, lee el primer fallo, corrígelo, y repite hasta verde
o hasta agotar los turnos. Regla dura: NUNCA modifiques un test para que
pase. Arregla el código.
Dos cosas que valen oro. maxTurns: 20 es el tope: si no converge en veinte vueltas, para en vez de quemarte los tokens. Y esa última línea del prompt es tu defensa contra el reward hacking de antes: le cierras al bucle el camino de hacer trampa borrando el test.
Y cuando quieras que se ejecute sin ti, ya no es una sesión que vigilas:
/loop 5m comprueba si el deploy ha terminado y avísame
claude --bg "investiga el test flaky y arréglalo"
/loop y los agentes en background son estándar. Hay más (workflows para orquestar varios sub-bucles, agentes programados por cron), pero eso ya es otra liga y con sus avisos: los workflows queman muchos tokens y los programados se ejecutan en la nube, no en tu máquina.
Seis loops que se pagan solos
Para bajarlo del todo a tierra, seis bucles que puedes tener en marcha esta semana. Fíjate en que todos comparten lo mismo debajo: una señal clara que dice si la vuelta valió. Sin ella, ninguno funcionaría.
- Guardia de errores. Cada hora revisa Sentry o Grafana, coge el error nuevo, lo reproduce, lo corrige y te deja la PR preparada. La señal es el test que reproduce el bug pasando a verde.
- Revisor de PRs. Cada hora mira las PRs abiertas contra tus reglas (tamaño, tests, nombres, secretos colados) y deja comentarios donde algo chirría. La señal es tu propia checklist.
- Vigilante de dependencias. Cada noche busca librerías con CVE nuevo, actualiza la que puede sin romper nada y abre PR; la que rompe tests, la deja anotada. La señal es la suite completa.
- Cazador de flaky. Cuando CI falla por un test no determinista, lo ejecuta en bucle hasta capturar el fallo y te dice si el culpable es el test o el código. La señal es reproducir el fallo unas cuantas veces.
- Sincronizador de docs. Cuando cambias un endpoint, revisa si el README y los ejemplos siguen cuadrando y actualiza lo que se quedó viejo. La señal es que los ejemplos de la doc siguen ejecutándose.
- Traductor de i18n. Cuando añades una clave en el idioma base, rellena las que falten en los demás idiomas y deja PR. La señal es que no queda ninguna clave sin traducir.
El hilo común: todos son tareas con veredicto objetivo. No es casualidad, es la condición para que un loop merezca la pena.
Lo que te llevas
Tres ideas, y las tres cambian cómo trabajas mañana:
- Tu palanca se movió. Si mides tu skill por lo bien que prompeas, estás optimizando la herramienta de 2024. La de 2026 es cuánto trabajo verificado produces por cada bucle que diseñas y sueltas.
- La señal es lo primero. Antes de automatizar nada, ten un comando determinista que te diga en segundos si la última vuelta fue a mejor. Sin eso no tienes un loop, tienes un generador de texto caro.
- Asume que el loop hará trampa. No es maldad del modelo, es la definición de optimizar. La señal tiene que estar donde el agente no pueda tocarla.
Y la parte tranquilizadora: nada de esto necesita que montes un agente desde cero. Un hook, un maxTurns y una regla en un fichero de texto ya son loop engineering. Empieza por ahí.
Para seguir el hilo: harness engineering es la pieza de debajo (el equipo que el loop ejecuta), y cómo reduje un 80% el consumo de tokens baja a producción mucho de esto.
P.D.: Si montas un loop y descubres una forma nueva en que tu agente hace trampa, cuéntamela. Colecciono esas. Me encuentras en Twitter como @lm_martinbar.
¿Te ha gustado este artículo?
Explora más artículos sobre desarrollo, buenas prácticas y herramientas.