Human-in-the-loop en agentes de IA: cuándo pausar y cómo reanudar
Resumen
Guía práctica de HITL: qué acciones de un agente deben esperar a un humano, cómo pausar con interrupt() de LangGraph o needs_approval del Agents SDK, cómo reanudar sin perder estado y qué no conviene aprobar a ciegas. Checklist y anti-patrones.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
Un agente que ejecuta tools sin freno es rápido y, en el peor caso, irreversible. Human-in-the-loop (HITL) no es “un humano revisa todo”: es pausar solo las acciones que no se pueden deshacer y reanudar con el mismo estado. Si pides aprobación para cada búsqueda, nadie usa el agente. Si no pides aprobación para un reembolso, un día lo vas a pagar dos veces.
Esta guía cubre el contrato real de LangGraph (interrupt() + Command(resume=...)) y del OpenAI Agents SDK (needs_approval), más las reglas de producto que ningún SDK te escribe. Fuentes oficiales verificadas el 3 de septiembre de 2026.
HITL encaja con orquestación multi-agente (un supervisor no sustituye a un humano) y con sandboxing de agentes de código (el sandbox limita daño; la aprobación decide si se dispara).
Qué sí merece un humano (y qué no)
Aprueba efecto, no pensamiento. El modelo puede razonar solo; lo que toca dinero, reputación, datos de un tercero o infra no.
| Acción | HITL | Por qué |
|---|---|---|
| Buscar en docs / web | No | Idempotente, barata, reversible |
| Borrador de respuesta al usuario | Opcional | Útil al inicio; quítalo cuando el eval aguante |
| Enviar el mensaje al canal | A veces | Un send no se deshace; un draft sí |
| Crear ticket / issue | A veces | Ruido vs. valor; aprueba si hay SLA o cliente VIP |
| Refund, cargo, cambio de plan | Sí | Dinero |
rm, migrate, deploy, email masivo | Sí | Irreversible o de blast radius alto |
| Escribir memoria durable / PII | Sí si cruza usuarios | Ver memoria y RAG |
La regla corta: si rehacer la acción cuesta más que 10 segundos de un humano, páusala. Si cuesta menos, loguéala y sigue.

LangGraph: interrupt() no es un breakpoint estático
LangGraph documenta interrupts como pausa dinámica en cualquier punto del nodo, no solo “antes/después de un nodo”. Flujo oficial:
- El nodo llama
interrupt(payload). El payload es JSON-serializable y sale al caller (la pregunta al humano). - El grafo se suspende. El checkpointer guarda el estado exacto. Puede esperar indefinidamente.
- Reanudas reinvocando el grafo con
Command(resume=valor). Ese valor es el return deinterrupt()dentro del nodo. - El puntero es
thread_idenconfig={"configurable": {"thread_id": ...}}. Sin thread, no hay resume.
Ejemplo mínimo del patrón de aprobación:
def approval_node(state: State):
approved = interrupt("Do you approve this action?")
return {"approved": approved}
Implicaciones para producción:
- La UI de aprobación es tuya: Slack, mail, un dashboard. LangGraph solo pausa y entrega el payload.
- Si el proceso del servidor muere, el checkpointer tiene que ser durable (Postgres, no memoria). Si no, el “resume” no existe.
- No uses interrupts para timeouts de API. Eso es un job en cola con backoff, como en la arquitectura de webhooks y colas. HITL es espera humana, no retry.
OpenAI Agents SDK: needs_approval en la tool
El SDK documenta HITL como marcar tools que necesitan aprobación, no como un nodo aparte. El flujo oficial (títulos de su guía): marcar tools → cómo corre el approval → mensajes de rechazo custom → decisiones automáticas → streaming y sessions → pause / approve / resume.
Traducción práctica:
- La tool peligrosa lleva
needs_approval(o el equivalente de tu versión del SDK). El runner se detiene antes de ejecutarla. - El humano ve nombre, argumentos y contexto. Aprueba o rechaza. El rechazo puede devolver un mensaje al modelo (“no se envió el mail: política X”) para que reintente otra vía.
- Las sesiones guardan el estado pendiente. Un approve 20 minutos después sigue siendo el mismo run, no un run nuevo.
Si tu agente no usa este SDK, copia el contrato: la tool no corre hasta que llega un token de aprobación atado al id de esa llamada. Aprobar “el agente en general” no es HITL; es un permiso demasiado ancho.
Claude, del lado de tools, distingue client tools (las ejecutas tú) y server tools (las ejecuta Anthropic). HITL solo aplica a las que tú ejecutas: ahí insertas el gate. Una server tool (web search) no pasa por tu aprobación a menos que ni siquiera la habilites.
Cómo no diseñar la cola de aprobaciones
Anti-patrones que he visto romper operaciones:
- Aprobar comandos sueltos sin diff. “¿Corro
migrate?” sin el SQL es un sí por cansancio. Muestra el artefacto. - Aprobar desde el celular a las 2 AM sin contexto. Si no cabe el resumen (quién, qué, blast radius, undo), no es un botón: es una trampa. La noticia de Agent Approve en iPhone no cambia esto: el valor está en la política, no en el reloj.
- Timeout de aprobación = “sí”. El default seguro es no. Si nadie aprueba en N minutos, el job va a dead letter, no a producción.
- Un humano para 200 pings/día. Eso no escala. Sube el umbral (solo refunds > Q500, solo deploys a prod) o el agente no se usa.
- Resume que re-ejecuta tools ya corridas. El estado tiene que saber qué tool calls ya hicieron efecto. Idempotencia, otra vez.

Checklist
- Lista escrita de tools con HITL. El resto corre solo.
- Payload de aprobación: acción, argumentos, usuario afectado, undo posible sí/no.
- Default de timeout = rechazo, no aprobación.
- Estado durable (
thread_id+ checkpointer / session), no RAM del proceso. - Resume no re-dispara tools ya ejecutadas.
- Rechazo vuelve al modelo con motivo, no como error 500.
- Métrica: % de pausas, tiempo medio de espera, % de rechazos. Si el humano aprueba el 99 %, el umbral está mal.
- Log del id de aprobación junto al run, para observabilidad.
FAQ
¿Puedo aprobar el plan y dejar que el agente ejecute todo? Solo si el plan no incluye tools irreversibles nuevas. Un plan “y luego deploy” sigue necesitando gate en el deploy.
¿HITL es lo mismo que un eval? No. El eval mide calidad offline. HITL es un control en caliente. Necesitas ambos.
¿Y si el humano nunca responde? Dead letter + alerta. No reanudes solo. Un cron que “aprueba lo viejo” es un incidente con extra steps.
¿Funciona con streaming? El SDK de OpenAI lo documenta junto a sessions: el stream se corta en el approval y sigue al resume. No mezcles tokens de la respuesta final con una tool que aún no se aprobó.
Empieza con una tool (refund o send_email). Cuando esa cola sea aburrida —pocos pings, contexto claro, timeout a no— recién añades la segunda.
Lecturas relacionadas
Sigue explorando Agentes en Producción y otras piezas para builders.



