El problema
La solicitud parece simple: encontrar el estado de un pedido y devolver una respuesta breve.
En los logs se ve que el agente repite el mismo ciclo:
plan → call_tool → analyze → plan → call_tool → analyze
Hace una semana este tipo de tarea se cerraba en 3-4 pasos.
Ahora el mismo tipo de solicitud puede girar 20+ pasos y terminar con timeout.
En 15 minutos el agente puede hacer 60+ pasos y gastar cerca de $12 en una tarea que normalmente cuesta ~ $0.08.
El sistema no cae de inmediato.
Simplemente quema tiempo, tokens y dinero de forma lenta.
Analogía: imagina un navegador que en cada cruce dice "date la vuelta", incluso cuando ya lo hiciste. El auto se mueve, pero no te acercas al destino. El bucle infinito en agentes funciona igual: hay acciones, pero no hay progreso.
Por qué pasa
Los agentes LLM son sistemas estocásticos. Incluso un cambio pequeño en prompt, tool output o contexto puede mover el orden de pasos. Si runtime no verifica progreso real, el ciclo se atasca fácilmente.
En producción suele verse así:
- el LLM propone la siguiente acción;
- el agente llama un
tool; - recibe una observación, pero sin señal nueva;
- vuelve al mismo reasoning loop.
El infinite loop no aparece cuando el agente "piensa demasiado", sino cuando runtime no distingue trabajo útil de repetición sin progreso.
Qué fallos aparecen con más frecuencia
Para no complicarlo, en el escenario de infinite loop suelen verse cuatro patrones.
Bucle duro (Hard loop)
El agente llama el mismo tool con los mismos argumentos muchas veces.
Causa típica: no hay dedupe por tool+args o las repeticiones están sin límite.
Bucle suave (Soft loop)
El agente hace la misma acción con cambios mínimos en argumentos: por ejemplo, añade una palabra a la búsqueda y vuelve a probar.
Causa típica: no hay validación de "si apareció algo nuevo".
Tormenta de retries (Retry storm)
La herramienta falla, y al mismo tiempo hacen retries tanto gateway como el agente. Como resultado, la cantidad de llamadas se multiplica.
Causa típica: lógica de retry dispersa en varias capas sin una policy única.
Bucle semántico (Semantic loop)
El agente parece activo, pero no avanza: reformula el plan, re-sintetiza los mismos datos o vuelve a pedir lo que ya se sabe.
Causa típica: no hay criterio claro de progreso en runtime.
Cómo detectar estos problemas
El bucle infinito se ve mejor por combinación de señales, no por una sola métrica.
| Métrica | Señal de bucle | Qué hacer |
|---|---|---|
steps_per_task | crecimiento brusco de pasos sin cierre | añadir max_steps duro y stop reason |
repeated_tool_signature_rate | repeticiones de tool+args dentro de un run | activar dedupe y límite de repeticiones |
no_progress_steps | varios pasos sin hechos/artefactos nuevos | detener run por regla de no-progress window |
stop_reason_distribution | muchos timeout y max_steps_reached | revisar retry policy y runtime gates |
tokens_per_task | sube el gasto y la calidad queda igual | limitar context/tool output y añadir progress check |
Cómo diferenciar un fallo de una tarea realmente difícil
Un run largo no siempre significa loop. La pregunta clave: si aparece una señal útil nueva.
Normal si:
- cada 1-2 pasos añade hechos o artefactos nuevos;
- los
toolcalls cambian de forma sustancial, no cosmética; - el agente se acerca gradualmente a
final_answer.
Peligroso si:
- 3-5 pasos seguidos no añaden nada nuevo;
- se repite el mismo
tool(o la misma intención); - suben los costos y no mejora la calidad de respuesta.
Cómo detener estos fallos
La meta es simple: no continuar el run a cualquier precio, sino cerrarlo de forma controlada.
En práctica:
- pones límites duros de runtime:
max_steps,timeout,max_tool_calls,max_tokens; - añades dedupe por
tool+argsy límite de repeticiones; - detienes run si no hay progreso durante N pasos;
- devuelves un stop reason controlado y resultado parcial, no un fallo "silencioso".
Loop-guard mínimo en runtime:
class LoopGuard:
def __init__(self):
self.max_steps = 12
self.max_repeat = 3
self.max_flat_steps = 4
self.steps = 0
self.flat_steps = 0
self.seen = {}
def on_step(self):
self.steps += 1
if self.steps > self.max_steps:
return "max_steps_reached"
return None
def on_tool_call(self, signature: str):
self.seen[signature] = self.seen.get(signature, 0) + 1
if self.seen[signature] >= self.max_repeat:
return "loop_detected:repeated_tool_signature"
return None
def on_progress(self, has_new_signal: bool):
self.flat_steps = 0 if has_new_signal else self.flat_steps + 1
if self.flat_steps >= self.max_flat_steps:
return "loop_detected:no_progress"
return None
Importante: en cada iteración llama primero on_step(), luego on_tool_call(...), y tras analizar resultado llama on_progress(...).
Este guard no "cura" al agente. Evita que el ciclo se convierta en incidente de producción.
Dónde se implementa en la arquitectura
En sistemas de producción, el control de loop normalmente no vive en el agente, sino en capas arquitectónicas separadas.
Agent Runtime responde por el execution loop del agente: límites (max_steps, timeout, max_tokens), stop reasons y finalización forzada del run. Aquí suele implementarse LoopGuard y la validación de progreso.
Tool Execution Layer responde por ejecución segura de tool_call: dedupe de llamadas, retry policy y normalización de errores. Muchos loops - retry storm, repeated tool calls y tool spam - nacen aquí cuando no hay una retry policy unificada o deduplicación.
Autoevaluación
Verificación rápida antes del release. Marca los puntos y mira el estado abajo.
Este es un sanity-check corto, no una auditoría formal.
Progreso: 0/8
⚠ Hay señales de riesgo
Faltan controles básicos. Cierra los puntos clave del checklist antes del release.
FAQ
Q: ¿Pasar a un modelo más fuerte resuelve infinite loop?
A: A veces ayuda parcialmente, pero no resuelve la raíz. Sin runtime gates, incluso un modelo fuerte puede entrar en bucle.
Q: ¿Cómo elegir max_steps al inicio?
A: Empieza con un límite conservador bajo y súbelo solo donde veas mejora de calidad confirmada.
Q: ¿Siempre hay que hacer retries?
A: No. Para 401/403 y errores de validación estables, retries suele empeorar el loop.
Q: ¿Qué mostrar al usuario cuando se detiene el run?
A: Motivo de parada, qué ya se intentó y resultado parcial. Eso reduce reinicios sin cambios.
Infinite loop casi nunca parece una gran caída. Es una degradación lenta que consume presupuesto y tiempo. Por eso, en producción no basta con un modelo "inteligente": hace falta control estricto de runtime.
Páginas relacionadas
Para cerrar mejor este problema, mira:
- Por qué fallan los agentes de IA - mapa general de fallos en producción.
- Tool spam - cómo limitar llamadas de herramientas duplicadas.
- Budget explosion - cómo un loop se convierte en gasto descontrolado.
- Agent Runtime - dónde implementar loop guards y stop reasons.
- Tool Execution Layer - dónde mantener retries, timeout y validación de llamadas.