Human in the loop para agentes de IA: aprobaciones reanudables y auditables

Diseña aprobaciones humanas que sobrevivan a reintentos: persiste la intención normalizada, muestra el impacto real, valida sujeto, expiración y hash, y reanuda el mismo run sin convertir aprobar en un booleano del navegador.

Compartir
Human in the loop para agentes de IA: aprobaciones reanudables y auditables

Human in the loop para agentes de IA no significa pintar un botón de aprobar. Significa detener una ejecución antes de una acción sensible, guardar una intención normalizada y reanudarla después con identidad, política, expiración y hash comprobados.

Intención durable y estado

Normaliza antes de presentar: operación, recurso, tenant, importe o alcance, efecto esperado, versión de policy, creador, run_id, idempotency_key y referencias seguras a los datos que el revisor necesita. Excluye secretos y redacta PII; el reviewer debe entender el impacto, no recibir el prompt completo.

Guarda estados explícitos: `proposed`, `pending_review`, `approved`, `rejected`, `expired`, `executing`, `succeeded` y `unknown`. La aprobación incluye `approval_id`, `intent_hash`, `reviewer_id`, `decision_at`, `expires_at`, `policy_version`, `reason` y una versión para compare-and-set.

Calcula el hash sobre representación canónica, no sobre el HTML ni sobre JSON con orden accidental. Aprobar transferir 100 euros a A no autoriza transferir 1.000 a B aunque el modelo reutilice el mismo mensaje.

El flujo es propuesta → intención durable y policy → revisión humana → ejecución, reanudación y auditoría. El worker crea la intención y queda suspendido; la aplicación de revisión lee un resumen derivado; el backend autoriza la decisión y publica una señal para reanudar el mismo `run_id`.
Flujo técnico de aprobación humana para agentes: propuesta, intención durable y policy, revisión, ejecución reanudable y auditoría
La ejecución solo continúa si la decisión corresponde a la misma intención.

¿Te está sirviendo? Hay una dosis cada semana

Te resumo herramientas de IA para devs, agentes, MCP, seguridad y workflows en un email de 5 minutos. En español y sin ruido.

Suscribirme gratis
El navegador nunca llama a la tool ni decide el estado final. Puede enviar `approval_id`, decisión y motivo a un endpoint autenticado; el servidor obtiene el registro, verifica permisos y hace la transición atómica.

TypeScript seguro: hash, expiración y CAS

Este ejemplo muestra los invariantes. El almacenamiento real usa una transacción o CAS; `resumeRun` reanuda el checkpoint existente, no crea una segunda ejecución.

Puntos a revisar

Lo que conviene comprobar

type Decision = "approved" | "rejected";
type Intent = { runId:string; tenantId:string; actorId:string; action:string; resource:string; args:Record<string, unknown>; policyVersion:string; idempotencyKey:string; };
const canonical = (i:Intent) => JSON.stringify(i, Object.keys(i).sort());
const digest = (i:Intent) => createHash("sha256").update(canonical(i)).digest("hex");
async function decide(id:string, reviewerId:string, decision:Decision, expectedHash:string) {
  const a = await db.approvals.get(id);
  if (!a || a.status !== "pending" || a.expiresAt < Date.now()) throw Error("approval_not_active");
  if (!await policy.canReview(reviewerId, a.intent)) throw Error("reviewer_forbidden");
  if (a.intentHash !== expectedHash || digest(a.intent) !== expectedHash) throw Error("intent_changed");
  if (!await db.approvals.compareAndSet(id, a.version, {status:decision, reviewerId, decidedAt:Date.now()})) throw Error("already_consumed");
  await audit({id, runId:a.intent.runId, decision, reviewerId, intentHash:expectedHash});
  if (decision === "approved") await resumeRun(a.intent.runId, {approvalId:id, intentHash:expectedHash});
}

Añade comprobación de tenant y de que el run sigue pausado, una clave de idempotencia para el consumidor y una segunda autorización justo antes de mutar. El hash protege la identidad de la intención; no sustituye ACL, policy ni validación de argumentos.

Timeout, rechazo y cambio de argumentos

Un timeout de revisión termina en `expired`, nunca en aprobación implícita. Un rechazo conserva motivo y deja el run reanudable solo mediante una nueva intención. Si el reviewer pide cambios, genera nueva versión, hash y revisión.

Si el worker pierde conexión después de ejecutar, marca `unknown` y reconcilia por `idempotencyKey` antes de reintentar. Si policy o permiso cambió mientras esperaba, vuelve a evaluar y rechaza la decisión antigua. Para alto impacto, la ventana debe ser corta y visible.

Los tests de UI son insuficientes: el backend debe rechazar aunque la persona pulse aprobar. Evalúa claridad del resumen: destinatario, importe, entorno y efecto irreversible deben poder detectarse.

Antipatrones

  • Enviar `approved=true` al modelo y confiar en que llamará la tool correcta.
  • Guardar aprobación en sesión del navegador o memoria del worker.
  • Aprobar `payments.write` sin ligar recurso, importe y tenant.
  • Modificar argumentos después de aprobar para completar datos faltantes.
  • Reintentar una mutación tras timeout sin reconciliar el resultado.
  • Usar aprobación humana para saltarse autorización del backend.

Preguntas frecuentes

¿Hay que revisar cada llamada?

No. Aplica una matriz de riesgo: lecturas con autorización, reversibles con controles proporcionales y alto impacto con aprobación durable.

¿Puede el modelo aprobar su propuesta?

No es aprobación humana. El modelo prepara el resumen; el sujeto autorizado decide fuera del modelo.

¿Qué pasa si cambia el importe en pantalla?

El hash falla y se requiere una nueva intención.

¿Un framework resuelve la auditoría?

Aporta checkpoint, pero policy, identidad, CAS y autorización pertenecen al dominio.

¿Cuánto dura una aprobación?

Lo mínimo que permita revisar. Para dinero, permisos o producción, una ventana corta y explícita suele ser más segura.

Cómo implementar una aprobación humana reanudable para una tool mutante

  1. Clasificar la acción. separa lectura, reversible y alto impacto por efecto y datos.
  2. Persistir la intención. normaliza argumentos, tenant, recurso, policy, run e idempotency key.
  3. Presentar impacto. muestra destinatario, alcance, cambios, riesgo y expiración.
  4. Autorizar al reviewer. comprueba identidad, tenant, rol y separación de funciones.
  5. Consumir con CAS. valida hash y TTL y permite una sola transición.
  6. Reanudar y reconciliar. continúa el mismo run; ante timeout consulta por idempotency key.
  7. Observar y evaluar. mide estados, concurrencia, inyecciones y evidencia de auditoría.

Cómo encaja con tu runtime

Un checkpoint del orquestador resuelve dónde queda dormido el worker, pero no quién puede aprobar ni qué efecto está autorizado. En LangGraph, `interrupt()` puede pausar y reanudar con un identificador de thread; en otro runtime usarás una cola o una tabla de jobs. En todos los casos, guarda la intención y la decisión en tu propio contrato de dominio para que un cambio de framework no cambie la autoridad.

Separa la bandeja de revisión del ejecutor. La bandeja puede aplicar filtros, accesibilidad y rotación de reviewers; el ejecutor consume una decisión verificable y llama al backend con scopes mínimos. Esa separación permite que el mismo flujo funcione desde una consola, un webhook interno o una revisión por lotes sin convertir un endpoint de UI en un camino privilegiado.

Para operaciones largas, emite eventos de estado y conserva el historial de versiones. El reviewer debe ver si la intención sigue pendiente, si el recurso cambió o si la policy se actualizó. Una notificación tardía no debe reactivar automáticamente una operación cuyo TTL terminó.

Documenta además quién opera la cola, cómo se revoca a un reviewer y qué equipo responde ante estados desconocidos. La continuidad operativa forma parte de la seguridad de la aprobación.

Fuentes y referencias

También te puede interesar

LangGraph: agentes Python con estadoIdempotencia en agentes de IA y reintentos segurosn8n y agentes de IA: workflows fiablesPull requests y gobernanza humana para agentesPrompt injection en agentes de IA: defensas y evals

Recibe una lectura semanal de herramientas IA para devs

Cada semana te resumo herramientas de IA para devs, agentes, MCP, seguridad y workflows en un email de 5 minutos. En español y sin ruido.

Suscribirme gratis

Lo mejor de la IA para desarrolladores, cada martes

Newsletter en español, gratis. Las herramientas, modelos y trucos de IA para devs que de verdad importan — sin ruido.