Agente de IA en bucle infinito: cómo evitarlo

Por qué un agente de IA entra en bucle infinito y las defensas para que el bucle sepa parar: tope de iteraciones, condición de parada, no-progreso y feedback.

Colaboradores: Ivan Garcia Villar

Agente de IA en bucle infinito: cómo evitarlo

Un agente de IA es, por dentro, un bucle agéntico: el modelo mira el contexto, decide una acción, ejecuta una herramienta, lee el resultado y vuelve a empezar. Ese ciclo es lo que convierte un chat en algo que hace tareas por su cuenta. El problema aparece cuando el bucle no sabe cuándo parar. Entonces da vueltas, y vueltas, y sigue dando vueltas. Cada vuelta es una llamada al modelo, y cada llamada gasta tokens.

Un agente de IA en bucle infinito rara vez peta con un error rojo. Se cuelga gastando tu presupuesto mientras parece que está trabajando. Aquí te cuento por qué ocurre y cómo cerrar cada agujero, partiendo de una idea simple: un bucle que no termina es un bucle mal diseñado.

Por qué un agente se queda en bucle

Un agente entra en bucle infinito cuando nada en su diseño le dice, de forma inequívoca, que ya ha terminado o que debe rendirse. El bucle solo se cierra si en algún momento pasa una de dos cosas: el modelo declara que la tarea está hecha, o tu código lo corta. Si ninguna de las dos está bien definida, no hay salida.

Diagrama del ciclo agéntico: el modelo decide una acción, ejecuta una herramienta, recibe el resultado y vuelve a decidir. Cuatro puntos de intercepción muestran dónde actúa cada defensa: el tope duro de iteraciones, la condición de parada, la detección de no-progreso y la señal de feedback.
El ciclo del agente y los cuatro puntos donde las defensas cortan el bucle infinito.

La mecánica es sencilla. En cada vuelta el modelo devuelve una respuesta final o una petición para usar una herramienta. Si pide una herramienta, tu código la ejecuta, le devuelve el resultado y el bucle sigue. En la API de Anthropic esto se ve directo: el modelo responde con stop_reason: "tool_use" cuando quiere llamar a una función, y tu programa itera hasta que responde con "end_turn". Todo el sistema depende de que el modelo, en algún momento, decida que ya está. Y ese “ya está” es justo lo que falla.

Estas son las causas que me he encontrado una y otra vez.

Sin condición de parada clara. Si tu prompt nunca define qué significa “terminado”, el modelo siempre encuentra una cosa más que comprobar. Le pediste que arreglara un test y, ya que estaba, revisa los imports, y de paso el formato, y vuelve a lanzar el test para asegurarse. Sin un límite semántico, la iniciativa se vuelve un bucle.

Feedback que no distingue éxito de fracaso. Esta es la más traicionera. Un agente que tenía que renombrar archivos se quedó pidiendo list_files sin parar, porque la herramienta devolvía la lista tanto si el renombrado había funcionado como si no. El modelo no tenía manera de saber que ya lo había conseguido. Así que lo volvía a intentar.

Reintentos ciegos ante un fallo determinista. Una herramienta que falla siempre igual (una credencial caducada, un 404) va a fallar exactamente igual en el segundo intento. Reintentar sin cambiar nada es, literalmente, la definición de un bucle.

No-progreso: el mismo estado una y otra vez. El agente alterna entre las mismas dos o tres acciones. Cada paso individual es “válido”, pero el conjunto no avanza ni un milímetro. Lee un archivo, decide editarlo, lee el archivo otra vez, decide editarlo. Nunca llega a escribir.

Las cuatro causas se reducen a lo mismo: el bucle no tiene forma de saber si va hacia algún sitio. Las defensas que vienen atacan ese hueco, del ángulo más burdo al más fino.

Defensa 1: pon un tope duro

El tope duro es la defensa más simple y la que nunca debe faltar: un número máximo de vueltas y un presupuesto de tokens que cortan la ejecución pase lo que pase. No hace al agente más listo. Acota el daño cuando algo sale mal.

// El bucle del agente con dos topes duros: número de vueltas y tokens gastados
async function runAgent(task: string) {
  const MAX_TURNS = 20;          // nunca más de 20 vueltas
  const TOKEN_BUDGET = 100_000;  // corta si se pasa de presupuesto
  let tokensUsed = 0;

  for (let turn = 0; turn < MAX_TURNS; turn++) {
    const step = await model.next(task);   // el modelo decide la acción
    tokensUsed += step.usage.input_tokens + step.usage.output_tokens;
    if (step.done) return step.result;     // condición de parada explícita
    if (tokensUsed > TOKEN_BUDGET) break;  // el tope gana si el agente no ha terminado
    await runTool(step.toolCall);          // ejecuta y realimenta el bucle
  }
  throw new Error("El agente no terminó dentro del presupuesto");
}

Piensa en el tope como el fusible del cuadro eléctrico. No arregla el cortocircuito, pero evita que se queme la casa. Este for con límite es lo primero que escribo, siempre, antes de preocuparme por que el agente sea bueno.

Ahora bien, si tus tareas normales chocan a menudo con el MAX_TURNS, el tope no es la solución: es el síntoma. El agente no está convergiendo, y subir el número solo retrasa el problema.

Defensa 2: define qué es “hecho” antes de arrancar

La condición de parada es la respuesta a una pregunta que mucha gente no se hace antes de lanzar el agente: ¿cómo sabré, sin ambigüedad, que la tarea está completa? Si dejas esa decisión únicamente al criterio del modelo, dependes de que “sienta” que ha terminado. A veces lo siente pronto, a veces nunca.

Dale una definición explícita y comprobable. Tienes dos formas de hacerlo. Una es una herramienta que el modelo debe llamar para declarar el final, un submit_result que además obliga a entregar el resultado en el formato que esperas. La otra es una comprobación que ejecuta tu código: los tests pasan, el archivo existe, el JSON valida contra un schema. Cuando la condición es algo que tu programa puede verificar, el “hecho” deja de ser una opinión del modelo y pasa a ser un hecho.

Definir la salida no es un añadido al final. Es parte de diseñar el bucle desde el principio, y es la esencia del loop engineering: programar con IA ya no va tanto de escribir el código como de diseñar bien el ciclo de contexto, acción, verificación y parada que recorre el agente.

Defensa 3: detecta el no-progreso

Si el estado del agente no cambia entre una vuelta y la siguiente, córtalo: está girando, no avanzando. El tope de iteraciones acabaría atrapándolo también, pero veinte vueltas más tarde y veinte llamadas al modelo más caro. La detección de no-progreso lo pilla en la segunda repetición.

La idea es guardar una huella de cada paso (la herramienta y sus argumentos, o un hash del estado) y compararla con la anterior. Si se repite, no reintentes lo mismo. Escala o corta.

// Detecta no-progreso: misma acción con los mismos argumentos dos vueltas seguidas
let lastFingerprint = "";
for (let turn = 0; turn < MAX_TURNS; turn++) {
  const step = await model.next(task);
  if (step.done) return step.result;                         // si terminó, sal antes de comparar
  // huella orden-dependiente: estable en la práctica, no garantizada por la spec
  const fingerprint = JSON.stringify(step.toolCall ?? null); // huella de la acción

  if (fingerprint === lastFingerprint) {
    // el estado no cambia: cambia de estrategia o entrega el control a un humano
    return escalate("El agente está girando sobre la misma acción");
  }
  lastFingerprint = fingerprint;
  await runTool(step.toolCall);
}

La huella no tiene por qué ser exacta: puedes tolerar una repetición antes de cortar, o comparar por similitud en vez de por igualdad estricta, porque a veces un reintento legítimo cambia un parámetro pequeño. Lo importante es que el bucle tenga memoria de su pasado inmediato. Un agente que no recuerda qué hizo la vuelta anterior está condenado a repetirla.

Defensa 4: dale una señal que distinga avanzar de girar

Las tres defensas anteriores cortan el bucle. Esta hace que muchas veces no haga falta cortarlo, porque el agente sabe por sí mismo si va bien. El resultado que cada herramienta devuelve al modelo tiene que decirle, sin ambigüedad, si el paso funcionó o fracasó.

Vuelve al agente que renombraba archivos. El arreglo no era ponerle un límite más bajo ni más reintentos. Era cambiar lo que la herramienta devolvía: en lugar de la lista de archivos siempre igual, un { ok: true, renamed: "..." } o un { ok: false, error: "..." }. Con esa señal, el modelo ya no necesita adivinar. Sabe si avanzó.

Un bucle que mide converge. Uno que no, da vueltas. Esa señal que distingue avanzar de girar es, en el fondo, un feedback loop; de cómo construir la señal de éxito y fracaso va en detalle la guía de cómo evaluar agentes IA en producción, y de cómo aprovechar esas mediciones para que el propio agente vaya mejorando entre ejecuciones, la optimización guiada por experimentos.

Guardarrailes: la red, no el plan

Los guardarrailes son la última línea de defensa: reglas duras que frenan al agente cuando todo lo demás falla. Un tope de coste absoluto, un timeout, una lista de acciones permitidas, una intervención humana obligatoria antes de operaciones peligrosas. No dependen de que el modelo razone bien, porque se aplican siempre.

Son imprescindibles, pero son la red debajo del trapecista, no el número. Si tu única protección contra un bucle infinito es el guardarraíl de coste, el agente seguirá dando vueltas hasta chocar con él cada vez, quemando lo máximo que le permitas antes de parar. El cómo montarlos, paso a paso, lo tienes en la guía de guardarrailes en agentes IA. No confundas la red con el diseño.

Errores comunes

Subir el límite de reintentos en vez de arreglar la señal

Es la reacción instintiva y casi siempre la equivocada. El agente falla, así que le das más intentos. Pero si el feedback no distingue éxito de fracaso, más reintentos solo significan más vueltas idénticas y una factura más alta. Antes de subir ese número, pregúntate por qué el agente no sabe que ha fracasado.

El try/except que reintenta lo idéntico

Envuelves la llamada a una herramienta en un try/except y, en el except, la vuelves a llamar exactamente igual. Si el fallo es determinista, el segundo intento fracasa idéntico al primero, y el tercero, y el cuarto. Un reintento solo tiene sentido si algo cambia entre uno y otro: un backoff, un parámetro distinto, una herramienta alternativa.

No loguear el estado de cada vuelta

Sin un registro por iteración estás depurando a ciegas. Cuando un agente se cuelga, la única forma de entender por qué es ver la secuencia de acciones que lo llevó ahí. Loguea en cada vuelta la acción elegida, sus argumentos y la huella del resultado. Ese log es la diferencia entre “el agente se quedó pillado” y “el agente repitió search_docs catorce veces porque siempre devolvía cero resultados”.

Dejar que solo el modelo decida cuándo parar

Confiar el final de la tarea al criterio del modelo, sin ninguna comprobación externa, funciona hasta que deja de funcionar. El modelo puede darse por satisfecho con un trabajo a medias, o no darse nunca por satisfecho. Una condición de parada que tu código pueda verificar convierte una corazonada en una garantía.

Checklist de implementación

  • El bucle tiene un tope duro de iteraciones y un presupuesto de tokens que cortan pase lo que pase
  • Existe una definición de “tarea completada” que tu código puede comprobar, no solo el criterio del modelo
  • Cada vuelta compara su estado con el anterior y corta o escala si no hay progreso
  • El resultado de cada herramienta distingue éxito de fracaso de forma inequívoca
  • Un fallo determinista (credencial, 404) no se reintenta a ciegas sin cambiar nada
  • Cada iteración se loguea con la acción, los argumentos y el resultado para poder auditarla
  • Los guardarrailes de coste y tiempo están activos como red final, no como única defensa

Preguntas Frecuentes

¿Por qué mi agente de IA repite la misma acción una y otra vez?

Casi siempre porque el resultado de esa acción no le dice si funcionó. Si la herramienta devuelve lo mismo tanto si tuvo éxito como si falló, el modelo no tiene forma de saber que ya lo consiguió, así que lo vuelve a intentar. Arregla la señal de feedback antes de tocar cualquier otra cosa: haz que cada herramienta devuelva un estado claro de éxito o fracaso.

¿Qué límite de iteraciones debería poner para evitar un bucle infinito?

Depende de cuántos pasos necesita tu tarea en su versión más larga y honesta. Ponlo un poco por encima de eso, no como una barrera que se alcanza a menudo. Si tus tareas normales chocan con el límite, el número no es el problema: el agente no está convergiendo, y ahí lo que toca es revisar la condición de parada y el feedback, no subir el tope.

¿No basta con un timeout?

No. Un timeout evita que un agente se cuelgue una noche entera, pero corta por tiempo, no por sentido: no distingue un agente que trabaja despacio de uno que gira en el sitio. Úsalo como red final, junto al tope de iteraciones y la detección de no-progreso, nunca en su lugar.

¿Puede pasar un bucle infinito aunque use un framework como LangChain?

Sí. Los frameworks suelen traer un tope de iteraciones por defecto, pero ese tope solo acota el daño; no hace que el agente sepa cuándo ha terminado. Si defines mal la condición de parada o el feedback es ambiguo, el agente girará hasta chocar con el límite del framework igual que lo haría en código escrito por ti. La defensa de fondo es la misma con framework o sin él.