Problema
En los traces de un run se ve un ciclo de espera:
Agent A -> Agent B -> Agent C -> Agent A.
En 20 minutos, la cantidad de runs en estado waiting puede llegar a 40+.
La cola crece, los workers siguen ocupados y el trabajo útil es casi nulo.
Desde fuera todo parece "silencioso": no hay error explícito ni caída del servicio. Pero el run no termina porque los tres agentes solo se esperan entre sí.
El sistema no se cae.
Simplemente se queda colgado y quema recursos en silencio.
Analogía: imagina a tres personas en una puerta cediéndose el paso con cortesía. Nadie discute y nadie comete un "error", pero nadie pasa. Un deadlock en sistemas multi-agent se ve exactamente así.
Por qué pasa
Un deadlock no aparece porque los agentes "piensen demasiado", sino porque el sistema no tiene claro quién debe mover el estado hacia adelante.
En producción normalmente se ve así:
- los agentes intercambian mensajes y dependen entre sí;
- una espera se retrasa (tool, approval, lock);
- otros agentes también pasan a
waiting; - sin timeout y owner del estado del workflow, el workflow se atasca.
El problema no es un agente aislado. El problema es la coordinación no controlada entre agentes.
Fallos más frecuentes
Para no complicarlo, en deadlocks se repiten cuatro patrones.
Espera circular (Circular wait)
Agent A espera a B, B espera a C, C espera a A. Todos están "ocupados", pero no hay progreso.
Causa típica: el grafo de dependencias contiene un ciclo y no hay un orchestrator único.
Lock sin TTL (Lock without lease)
Un agente toma un lock sobre documento/ticket y crashea. Otros agentes esperan ese lock indefinidamente.
Causa típica: lock sin lease/TTL y sin mecanismo de recovery del owner.
Espera no acotada (Unbounded waiting)
Hay timeout en HTTP, pero no timeout en waits internos entre agentes. El workflow puede esperar "para siempre".
Causa típica: los timeouts están implementados a nivel transport, pero no a nivel de estados de orchestration.
Bucle de retry entre agentes (Cross-agent retry loop)
Los agentes se pasan la tarea con "revísalo otra vez", y eso se vuelve ping-pong infinito.
Causa típica: no hay límite de retries ni stop reason para escenarios de blocked state.
Cómo detectar estos problemas
El deadlock se ve bien con la combinación de métricas de workflow y runtime.
| Métrica | Señal de deadlock | Qué hacer |
|---|---|---|
waiting_runs | la cantidad de runs en waiting crece de forma sostenida | agregar wait-timeout y stop reason para blocked state |
wait_duration_p95 | esperas por encima de lo normal | acotar el tiempo de espera en cada state transition |
blocked_transition_rate | bloqueos frecuentes entre los mismos agentes | revisar el grafo de dependencias para detectar ciclos |
lease_conflict_rate | conflictos frecuentes o leases expiradas | agregar TTL, renew y policy de recovery |
queue_backlog | la cola crece con tráfico de entrada normal | limpiar runs colgados, activar fallback mode |
Cómo distinguir deadlock de una tarea realmente larga
No todo run largo es deadlock. El criterio clave: ¿hay state transitions y progreso útil?
Normal si:
- el estado del workflow cambia como se espera;
- después de esperar aparece un artefacto o paso nuevo;
- hay un owner claro de la transición actual.
Peligroso si:
- el run permanece mucho tiempo en el mismo estado
waiting; - varios agentes "esperan" al mismo tiempo entre sí;
- no hay un stop reason claro de por qué el sistema no avanza.
Cómo detener estos fallos
En la práctica se ve así:
- introduces un owner único de transiciones (orchestrator o leader);
- pones timeout en cada estado
waiting; - usas lease/TTL para shared resources;
- cuando no hay progreso, terminas el run de forma controlada: stop reason + fallback.
Guard mínimo para wait-state:
import time
class WaitGuard:
def __init__(self, wait_timeout_s: int = 30):
self.wait_timeout_s = wait_timeout_s
self.wait_started_at: dict[str, float] = {}
def mark_wait_start(self, run_id: str):
self.wait_started_at[run_id] = time.time()
def check_wait(self, run_id: str):
started = self.wait_started_at.get(run_id)
if started is None:
return None
if time.time() - started > self.wait_timeout_s:
return "deadlock_risk:wait_timeout"
return None
En producción, mark_wait_start(...) suele llamarse al pasar a waiting,
y check_wait(...) se ejecuta en scheduler o heartbeat loop para cerrar a tiempo un run colgado.
Dónde se implementa en la arquitectura
El control de deadlocks en producción normalmente se reparte entre varias capas.
Agent Runtime gestiona el ciclo de vida del run:
timeouts, stop reasons, finalización forzada de estados colgados y transiciones de fallback.
Aquí se suelen definir reglas deadlock_risk:*.
Orchestration Topologies define quién posee las state transitions y cómo interactúan los agentes sin esperas circulares. Si la topología no tiene un owner claro del estado, el deadlock es cuestión de tiempo.
Tool Execution Layer cubre la parte técnica: lease/TTL para shared resources, una retry policy unificada y control de waits a nivel de tools.
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/9
⚠ Hay señales de riesgo
Faltan controles básicos. Cierra los puntos clave del checklist antes del release.
FAQ
Q: ¿Los deadlocks solo pasan en sistemas multi-agent grandes?
A: No. Incluso 2-3 agentes pueden crear un ciclo de espera si no hay owner explícito del estado.
Q: ¿Basta con agregar timeouts?
A: Los timeouts limitan el bloqueo, pero no eliminan la raíz del problema. También necesitas orchestrator y state transitions explícitas.
Q: ¿Las leases resuelven por completo el deadlock?
A: No. Las leases cubren problemas de locks tras crashes, pero no corrigen ciclos lógicos entre agentes.
Q: ¿Qué hago si un run ya está colgado en waiting?
A: Forzar finalización del run con stop reason, liberar lease, pasar workflow a fallback y revisar la cadena de espera en traces.
Un deadlock casi nunca parece una caída ruidosa. Más bien es una parada silenciosa del progreso que consume workers y presupuesto. Por eso los sistemas multi-agent en producción necesitan no solo reparto de roles, sino disciplina estricta de orchestration.
Páginas relacionadas
Para cubrir este problema con más profundidad:
- Por qué fallan los agentes de IA - mapa general de fallos en producción.
- Multi-agent chaos - cómo la interacción no controlada entre agentes destruye estabilidad.
- Partial outage - cómo una degradación parcial de dependencias provoca estados
waiting. - Agent Runtime - dónde gestionar stop reasons, timeouts y ciclo de vida de runs.
- Orchestration Topologies - cómo diseñar coordinación controlada entre agentes.