Detección y ruptura de bucles infinitos
Límites de iteraciones máximas, detección de acciones repetidas y disyuntores para bucles.
Detección y ruptura de bucles infinitos es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
La amenaza de los bucles infinitos
Un agente atrapado en un bucle infinito consume tokens, bloquea recursos y no produce ninguna salida útil. En producción, esto se traduce directamente en dinero desperdiciado y frustración para los usuarios.
Tres mecanismos funcionan conjuntamente para evitar los bucles infinitos: límites máximos de iteraciones, detección de acciones repetidas y tiempos de espera.
Protección 1: Límite máximo de iteraciones
La protección más sencilla e importante es un límite estricto de pasos. Todo bucle de agente debe tener uno. Cuando se alcanza el límite, el agente devuelve su mejor respuesta actual o un mensaje de fallo explícito.
MAX_ITERATIONS = 20
def run_agent(query: str) -> dict:
history = []
for step in range(1, MAX_ITERATIONS + 1):
action = decide_action(query, history)
if action['type'] == 'final_answer':
return {'status': 'ok', 'answer': action['answer'], 'steps': step}
result = execute_tool(action['tool'], action['args'])
history.append({'step': step, 'tool': action['tool'], 'result': result})
# Hard stop — max iterations reached
return {
'status': 'max_iterations_reached',
'answer': None,
'steps': MAX_ITERATIONS
}Protección 2: Detección de acciones repetidas
Las acciones repetidas son el principal indicio de un bucle infinito. Realice un seguimiento de un historial de pares (tool_name, arguments). Si el mismo par aparece más de N veces, el agente está atascado: detenga el bucle e inyecte un mensaje de error.
import hashlib
import json
def action_hash(tool_name: str, args: dict) -> str:
payload = json.dumps({'tool': tool_name, 'args': args}, sort_keys=True)
return hashlib.md5(payload.encode()).hexdigest()
def run_agent_with_repeat_detection(query: str) -> dict:
history = []
action_counts = {}
for step in range(1, 21):
action = decide_action(query, history)
if action['type'] == 'final_answer':
return {'status': 'ok', 'answer': action['answer']}
key = action_hash(action['tool'], action['args'])
action_counts[key] = action_counts.get(key, 0) + 1
if action_counts[key] > 2: # seen this exact action more than twice
history.append({
'role': 'system',
'content': f'You have called {action["tool"]} with the same arguments {action_counts[key]} times. '
f'This approach is not working. Try a completely different strategy or state what you know so far.'
})
continue
result = execute_tool(action['tool'], action['args'])
history.append({'tool': action['tool'], 'result': result})
return {'status': 'loop_detected', 'answer': None}Seguimiento de acciones repetidas en una ventana
En lugar de realizar un seguimiento de los recuentos de todos los pasos, detecte repeticiones en una ventana deslizante que contenga los últimos N pasos. Esto permite detectar bucles que varían ligeramente, pero se repiten en un ciclo breve.
from collections import deque
def is_cycling(recent_actions: deque, window: int = 6) -> bool:
if len(recent_actions) < window:
return False
# Check if the last window/2 actions repeat the first window/2
half = window // 2
first_half = list(recent_actions)[:half]
second_half = list(recent_actions)[half:window]
return first_half == second_half
# In the agent loop:
# recent_actions = deque(maxlen=6)
# recent_actions.append(action_hash(tool, args))
# if is_cycling(recent_actions):
# print('Cycling detected — breaking loop')
# break
recent = deque(['a', 'b', 'a', 'b'], maxlen=6)
print(is_cycling(recent, window=4)) # True — cycling detectedProtección 3: Tiempo de espera en tiempo real con signal.alarm
En sistemas Unix, signal.alarm() genera un SIGALRM después del número de segundos especificado. Esto proporciona un tiempo de espera estricto incluso si el bucle del agente está bloqueado en una llamada lenta a una herramienta.
import signal
class AgentTimeout(Exception):
pass
def timeout_handler(signum, frame):
raise AgentTimeout('Agent exceeded time limit')
def run_agent_with_signal_timeout(query: str, timeout_seconds: int = 60) -> dict:
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds) # set the alarm
try:
result = run_core_agent_loop(query)
signal.alarm(0) # cancel the alarm on success
return result
except AgentTimeout:
signal.alarm(0)
return {'status': 'timeout', 'answer': None}
except Exception as e:
signal.alarm(0)
raise
# Note: signal.alarm is Unix-only (Linux/Mac)Tiempo de espera con threading.Timer (multiplataforma)
threading.Timer funciona en todas las plataformas, incluido Windows. Establezca un indicador cuando transcurra el tiempo de espera; el bucle del agente comprueba el indicador y sale si está activado.
import threading
def run_agent_with_timer_timeout(query: str, timeout_seconds: int = 60) -> dict:
timed_out = threading.Event()
def set_timeout():
timed_out.set()
timer = threading.Timer(timeout_seconds, set_timeout)
timer.start()
history = []
try:
for step in range(1, 21):
if timed_out.is_set():
return {'status': 'timeout', 'answer': None, 'steps': step}
action = decide_action(query, history)
if action['type'] == 'final_answer':
return {'status': 'ok', 'answer': action['answer']}
result = execute_tool(action['tool'], action['args'])
history.append({'tool': action['tool'], 'result': result})
finally:
timer.cancel() # always cancel if done before timeout
return {'status': 'max_steps', 'answer': None}Tiempo de espera asíncrono con asyncio.wait_for
En arquitecturas de agentes asíncronos, use asyncio.wait_for(coroutine, timeout=N). Genera asyncio.TimeoutError si la corrutina no se completa dentro del número de segundos especificado.
import asyncio
async def run_async_agent(query: str) -> dict:
history = []
for step in range(1, 21):
action = await async_decide_action(query, history)
if action['type'] == 'final_answer':
return {'status': 'ok', 'answer': action['answer']}
result = await async_execute_tool(action['tool'], action['args'])
history.append({'tool': action['tool'], 'result': result})
return {'status': 'max_steps', 'answer': None}
async def run_with_timeout(query: str, timeout: float = 60.0) -> dict:
try:
return await asyncio.wait_for(run_async_agent(query), timeout=timeout)
except asyncio.TimeoutError:
return {'status': 'timeout', 'answer': None}
# asyncio.run(run_with_timeout('What is Python?', timeout=30.0))Inyección de instrucciones de salida en el historial
Cuando se detecte un bucle, no se limite a detenerlo silenciosamente. Inyecte un mensaje del sistema en el historial de la conversación que explique lo sucedido y pida al agente que concluya. Esto le da al LLM la oportunidad de producir una respuesta final antes de que se interrumpa su ejecución.
def inject_loop_escape(history: list, step: int, reason: str):
message = (
f'[SYSTEM] You have been running for {step} steps. Reason: {reason}. '
f'You MUST now provide a FINAL_ANSWER based on what you have found so far, '
f'even if the information is incomplete. Do not call any more tools.'
)
history.append({'role': 'system', 'content': message})
# In the agent loop, when approaching the limit:
# if step >= MAX_ITERATIONS - 2:
# inject_loop_escape(history, step, 'approaching max iteration limit')
# Or when a repeat is detected:
# if action_counts[key] > 2:
# inject_loop_escape(history, step, 'repeated action detected')
if __name__ == '__main__':
demo_history = []
inject_loop_escape(demo_history, step=18, reason='approaching max iteration limit')
print(demo_history[-1]['content'])
Registro de la interrupción de un bucle
Registre siempre cuándo se activa una protección del bucle. Esto crea un registro de la frecuencia y los motivos por los que los agentes se quedan atascados, un dato muy valioso para mejorar las instrucciones y las implementaciones de las herramientas.
import logging
logger = logging.getLogger('agent_guard')
def check_and_break_loop(step: int, action_counts: dict, current_key: str) -> bool:
count = action_counts.get(current_key, 0)
if count > 2:
logger.warning(
f'Infinite loop detected at step {step}. '
f'Action hash {current_key[:8]} seen {count} times. '
f'Breaking loop.'
)
return True # signal to break
if step >= 18: # approaching limit
logger.warning(
f'Approaching max iterations at step {step}. '
f'Injecting escape prompt.'
)
return False
if __name__ == '__main__':
import sys
logging.basicConfig(level=logging.WARNING, format='%(message)s', stream=sys.stdout)
demo_counts = {'search:{"q": "weather"}': 3}
check_and_break_loop(step=10, action_counts=demo_counts, current_key='search:{"q": "weather"}')
Combinación de las tres protecciones
Los agentes de producción deben combinar las tres protecciones: máximo de iteraciones (obligatorio), detección de repeticiones (detecta ciclos) y tiempo de espera (detecta llamadas bloqueadas a herramientas). Juntas hacen que el bucle sea prácticamente infalible.
import threading
import hashlib
import json
def run_production_agent(query: str) -> dict:
MAX_STEPS = 20
TIMEOUT_SEC = 120
timed_out = threading.Event()
timer = threading.Timer(TIMEOUT_SEC, timed_out.set)
timer.start()
history = []
action_counts = {}
try:
for step in range(1, MAX_STEPS + 1):
if timed_out.is_set():
return {'status': 'timeout'}
action = decide_action(query, history)
if action['type'] == 'final_answer':
return {'status': 'ok', 'answer': action['answer']}
key = hashlib.md5(json.dumps(action, sort_keys=True).encode()).hexdigest()
action_counts[key] = action_counts.get(key, 0) + 1
if action_counts[key] > 2:
inject_loop_escape(history, step, 'repeat detected')
continue
result = execute_tool(action['tool'], action['args'])
history.append({'tool': action['tool'], 'result': result})
finally:
timer.cancel()
return {'status': 'max_steps'}Prueba de las protecciones del bucle con pruebas unitarias
Escriba pruebas unitarias específicas para las protecciones del bucle. Cree un agente simulado que llame siempre a la misma herramienta y compruebe que la protección lo detecta dentro del número de pasos esperado.
from unittest.mock import MagicMock
def test_repeat_detection_breaks_loop():
# Create a mock that always returns the same action
always_same_action = MagicMock(return_value={
'type': 'tool',
'tool': 'search_web',
'args': {'query': 'same query'}
})
always_success = MagicMock(return_value='some result')
result = run_agent_with_repeat_detection(
query='test',
decide_action=always_same_action,
execute_tool=always_success
)
# Should stop due to loop detection, not run all 20 steps
assert result['status'] in ('loop_detected', 'max_iterations_reached')
# Should not have run all 20 steps (loop should be detected by step 6-7)
print('Loop guard test passed')Comprobación de conocimientos: Detección de bucles infinitos
Compruebe sus conocimientos sobre las técnicas para detectar y detener bucles infinitos.
Resumen: Detección y detención de bucles infinitos
Ahora puede proteger sus agentes frente a los bucles infinitos con tres protecciones complementarias:
- Máximo de iteraciones: límite estricto de pasos, obligatorio en todo bucle de agente
- Detección de repeticiones: aplique un hash a los pares (tool, args) y detenga el bucle cuando aparezcan más de N veces
- Tiempo de espera:
signal.alarm()en Unix,threading.Timerpara varias plataformas yasyncio.wait_for()para código asíncrono - Inyección de instrucciones de salida: dé al agente la oportunidad de concluir antes de detenerlo de forma forzosa
- Registre siempre cuándo se activa una protección; son datos valiosos para mejorar las instrucciones
Aprende AI Agents con un tutor de IA — gratis
Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.
- Cursos
- 60
- Lecciones
- 239
Preguntas frecuentes
¿La lección «Detección y ruptura de bucles infinitos» es gratis?
Sí — el texto completo de «Detección y ruptura de bucles infinitos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Detección y ruptura de bucles infinitos»?
Límites de iteraciones máximas, detección de acciones repetidas y disyuntores para bucles. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Detección y ruptura de bucles infinitos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Fallos habituales en los bucles de agentes
- Registro de trazas para los pasos del agente
- Detección y ruptura de bucles infinitos
- Técnicas de depuración paso a paso