Diseño de máquinas de estado para agentes de IA: por qué un workflow complejo no puede depender solo del prompt

"La documentación de LangGraph Persistence describe los checkpoints como graph state snapshots con alcance de thread y explica que soportan conversation continuity, human-in-the-loop, time travel y fault tolerance."
Un agente de reportes falló justo antes de enviar el e-mail en el paso 5. Operaciones relanzó la tarea. El agente volvió al paso 1, generó un reporte nuevo y sobrescribió la versión que ya había sido aprobada. Se perdió el estado de aprobación. El registro firmado por la persona aprobadora fue reemplazado por el nuevo resultado, y no quedó ningún log que probara que la primera versión había sido aprobada.
No era un rollback de base de datos ni un retry de cola de mensajes. En el prompt solo quedaba la frase “continuar el procesamiento”. El modelo volvió a inferir todo el flujo sin saber que los pasos 1 a 4 ya habían producido efectos externos: llamada a la API de aprobación, generación del reporte y escritura de un archivo temporal. El fallo ocurrió en el paso 5, pero los efectos empezaron en el paso 2.
El problema real no era si el modelo tenía suficiente capacidad. El progreso de la tarea estaba escondido en lenguaje natural dentro del prompt, sin un state snapshot recuperable. Los messages del prompt son contexto para el modelo, no hechos de ejecución.
Para corregir un incidente así, no alcanza con agregar al prompt una frase como “revisar el progreso antes de continuar”. Lo más robusto es escribir el nodo actual, los efectos ya producidos, la siguiente acción y la compensación de fallos en una tabla de estados recuperable.
Puntos clave del incidente
Flujo de ejecución del agente de reportes:
| Paso | Operación | Efecto externo | Idempotencia |
|---|---|---|---|
| Paso 1 | Consulta de datos | Llama a la base de datos y consulta datos de usuarios | Idempotente (lectura) |
| Paso 2 | Generación de reporte | Llama a la herramienta de reportes y genera un PDF | No idempotente (sobrescribe archivo) |
| Paso 3 | Espera de aprobación | Envía solicitud de aprobación y espera a una persona | Idempotente (la API lo soporta) |
| Paso 4 | Aprobación recibida | Recibe el event approve | Idempotente (consulta de estado) |
| Paso 5 | Enviar e-mail | Llama a la API de e-mail y envía el reporte | Fallo (timeout) |
Causa del fallo: el envío de e-mail del paso 5 hizo timeout por rate limiting de la API externa, y la tarea quedó marcada como FAILED.
Lógica de relanzamiento: leer el “progreso actual” desde el prompt. El prompt solo decía “aprobado, continuar”. Ejecución real: empezar otra vez desde el paso 1 -> regenerar el reporte en el paso 2 (sobrescribiendo la versión aprobada) -> pedir aprobación otra vez en el paso 3 -> enviar con éxito en el paso 5.
Impacto de negocio: el reporte aprobado fue reemplazado, el registro de aprobación ya no coincidía con el reporte entregado, el usuario reclamó que el reporte que aprobó no era el que recibió, y el flujo de aprobación se desperdició: se aprobaron dos versiones, pero solo una se envió.
Tabla para detectar anti-patrones
Revisa si tu agente cae en estos anti-patrones:
| Anti-patrón | Cómo se ve | Riesgo oculto | Corrección |
|---|---|---|---|
| Progreso escrito en el Prompt | Resumen natural como “está en el paso 3” | Se pierde tras reinicio, no se recupera | Registrar el nodo actual en un campo State |
| Trace tratado como State | Tener trace completo se confunde con tener estado | Trace no decide el siguiente paso | State registra qué debe pasar después |
| Retry sin idempotencia | Fallo implica volver al inicio | Los efectos externos se ejecutan dos veces | Clave de idempotencia + verificación de ya ejecutado |
| Resume después de approval sin validar | Continuar directamente | No vuelve al punto correcto | checkpoint + thread_id |
1. Fundamentos de la máquina de estados: State, Event, Transition, Guard, Action
Una máquina de estados no es necesaria para todos los agentes. Un Q&A simple de soporte puede funcionar con un array de messages. Pero una tarea compleja, con varios pasos, approval, llamadas a sistemas externos y recuperación tras fallos, debe hacer explícito el progreso.
1.1 Tabla de términos centrales
Los términos básicos vienen de la documentación de Stately:
| Término | Definición | Ejemplo en Agent | Fuente |
|---|---|---|---|
| State | Modo en el que está la máquina, con una intención semántica única | INIT, PLAN_READY, TOOL_RUNNING, APPROVAL_PENDING, FAILED, COMPLETED | Stately state machines |
| Event | Señal externa que dispara un cambio de estado | timeout, approve, reject, retry, resume, task_received | Stately state machines |
| Transition | Ruta permitida entre estados, con mapeo determinista | INIT -> PLAN_READY (event: task_received) | Stately state machines |
| Guard/Condition | Condición previa para entrar en un estado | Solo entrar en TOOL_RUNNING si el presupuesto alcanza | Stately state machines |
| Action | Operación ejecutada durante una transición | Llamar una herramienta al entrar en TOOL_RUNNING | Stately state machines |
| Checkpoint | Snapshot de estado usado para recuperar | LangGraph checkpointer guarda graph state | LangGraph Persistence |
Principio de determinismo: la misma combinación State + Event debería apuntar a un único next state, sin ambigüedad. Conjunto finito de estados: una máquina de estados no es un flowchart infinito, sino un conjunto finito de estados alcanzables más reglas explícitas de transición.
1.2 Comparación Trace vs State vs Audit
Trace, Audit Log y State Snapshot resuelven tres problemas distintos:
| Concepto | Qué problema resuelve | ¿Es estado de negocio? | ¿Decide el siguiente paso? | Ejemplo en Agent |
|---|---|---|---|---|
| Trace | Observabilidad y diagnóstico | No | No | OpenAI Agents SDK trace (workflow_name, trace_id) |
| Audit Log | Registro de cumplimiento y auditoría | No | No | Campos de auditoría del modelo de permisos (actor, traceId, action, result) |
| State Snapshot | Estado actual que decide el siguiente paso | Sí | Sí | LangGraph checkpoint (nodo actual, pasos ejecutados, siguiente acción) |
La diferencia importa: un trace ayuda a observar qué pasó, pero no es el estado de negocio. Un audit log deja historial para auditoría. Un state snapshot decide qué debe pasar después y por eso es el núcleo de la recuperación. No son intercambiables: tener trace no significa tener state; tener audit no significa poder recuperar.
2. Cómo LangGraph hace persistencia de estado
Un checkpoint no es un resumen en lenguaje natural dentro del prompt. Es un state snapshot recuperable, inspeccionable y reproducible. La documentación de LangGraph persistence define checkpoint como graph state snapshot e incluye el estado completo y los siguientes nodos a ejecutar.
2.1 Checkpointer y Thread State
Mecanismos centrales (según LangGraph Persistence):
- Checkpointer: guarda snapshots de estado con alcance de thread (graph state snapshots)
- Store: guarda datos de largo plazo entre threads (application-defined store)
- Thread_id: entrada única para recuperar el estado de un thread concreto
- Cuatro usos: conversation continuity, human-in-the-loop, time travel, fault tolerance
LangGraph persistence pone el estado corto con alcance de thread en checkpointers y los datos de largo plazo entre threads en stores. Un checkpoint incluye state snapshot y application-defined store. Thread_id es la entrada de recuperación: con la misma thread_id se puede continuar desde el punto de pausa.
Un checkpoint de LangGraph contiene graph state, lista de nodos siguientes, checkpoint_id, timestamp y versión. Los datos sensibles no deberían entrar ciegamente en el checkpoint: algunos campos de graph state pueden contener información sensible y requieren configuración explícita para no persistirse.
2.2 Interrupts y mecanismo de recuperación
Mecanismos centrales (según LangGraph Interrupts):
- interrupt(): pausa dinámicamente la ejecución dentro de un nodo del grafo, guarda graph state y espera input externo
- Método de recuperación: usar la misma thread_id y Command(resume=…)
- Patrones comunes: approval, review/edit, tool call review, human input validation
- Advertencia sobre efectos idempotentes: los efectos antes de interrupt deben ser idempotentes porque, al reanudar, el nodo se ejecuta desde el comienzo del nodo que llamó a interrupt
Una pausa de aprobación debe ser un estado de pausa de la máquina de estados, no una esperanza de que el modelo “recuerde esperar aprobación”. Recuperar requiere el mismo thread cursor.
La recuperación usa la misma thread_id y Command(resume=…). La idempotencia de los efectos es condición previa. Si hay un efecto antes de la aprobación, como una llamada a una API externa, debe ser idempotente; de lo contrario, al reanudar el nodo volverá a llamar la API.
3. Analogía de ingeniería: Temporal Durable Execution
La fiabilidad de tareas largas no es un problema nuevo. Temporal durable execution ofrece una referencia madura.
3.1 Definición de Durable Execution
Conceptos centrales (según Temporal Durable Execution):
- Durable Execution: workflow execution conserva state/progress ante fallos, caídas o interrupciones de servicio
- Event History: registra el estado de cada paso para recuperar desde el último evento registrado tras un fallo
- Tres propiedades: Resumable, Recoverable, Reactive
La fiabilidad de tareas largas viene del event history y de una ejecución recuperable, no de la memoria de un único proceso ni del contexto del prompt. Una máquina de estados para agentes necesita algo parecido: checkpoint/event log + estado de negocio, no solo inferencia nueva del modelo.
El Event History de Temporal y el checkpoint de LangGraph son similares conceptualmente: registran historial de ejecución y permiten recuperar desde el punto de fallo. La diferencia es que Temporal es un motor completo de workflow, mientras LangGraph es un framework de gestión de estado para agentes. La lección es clara: durable execution necesita historial de estado estructurado, no memoria de proceso ni contexto del modelo.
4. Plantilla de tabla de estados: una Agent State Table reutilizable
Los conceptos de máquina de estados son abstractos. Para aterrizarlos, necesitas un modelo de estado concreto. Aquí van tres plantillas: tabla de estados, tabla de eventos y ejemplo derivado del incidente.
4.1 Plantilla de tabla de estados (bloque ejecutable)
Estructura:
| State | Event | Guard | Action obligatoria | Next |
|---|---|---|---|---|
| INIT | task_received | Ninguna | Inicializar contexto y registrar hora de inicio | PLAN_READY |
| PLAN_READY | plan_generated | plan_valid | Generar plan de ejecución y registrar secuencia de herramientas | TOOL_RUNNING |
| TOOL_RUNNING | tool_completed | budget_sufficient | Llamar herramienta, registrar resultado y actualizar presupuesto | APPROVAL_PENDING o COMPLETED |
| APPROVAL_PENDING | approve | approval_required | Enviar solicitud de aprobación y registrar aprobador | COMPLETED |
| APPROVAL_PENDING | reject | Ninguna | Registrar motivo de rechazo y notificar al usuario | FAILED |
| FAILED | retry | retry_count < max | Revisar idempotencia y volver al checkpoint previo | TOOL_RUNNING o APPROVAL_PENDING |
| COMPLETED | Ninguna | Ninguna | Registrar hora de finalización y limpiar recursos | Terminal |
Notas: la columna State define los estados alcanzables (INIT, PLAN_READY, TOOL_RUNNING, APPROVAL_PENDING, FAILED, COMPLETED). Event define los eventos que disparan transitions (task_received, approve, reject, retry). Guard define precondiciones (budget_sufficient, retry_count < max). Action define la operación obligatoria durante la transición. Next define el estado siguiente de forma determinista.
4.2 Plantilla de tabla de eventos (complemento de la tabla de estados)
Estructura:
| Event | Condición de disparo | Estado previo requerido | Estado posterior | ¿Produce efectos externos? |
|---|---|---|---|---|
| task_received | El usuario envía una tarea | INIT | PLAN_READY | No |
| plan_generated | El LLM genera un plan de ejecución | PLAN_READY | TOOL_RUNNING | No |
| tool_completed | La herramienta termina | TOOL_RUNNING | APPROVAL_PENDING o COMPLETED | Sí (llamada a API externa) |
| approve | La persona aprobadora acepta | APPROVAL_PENDING | COMPLETED | Sí (envía e-mail, descuenta presupuesto) |
| reject | La persona aprobadora rechaza | APPROVAL_PENDING | FAILED | No |
| retry | Solicitud de retry tras fallo | FAILED | TOOL_RUNNING o APPROVAL_PENDING | Requiere revisión de idempotencia |
| timeout | Timeout de ejecución | TOOL_RUNNING | FAILED | No |
Notas: el estado previo requerido deja claro en qué estados puede recibirse cada event. La columna de efectos externos marca qué events necesitan idempotencia o compensación.
4.3 Ejemplo de tabla de estados derivado del incidente de reporte sobrescrito
Ejemplo completo: tabla de estados del agente de reportes derivada del incidente inicial
| State | Event | Guard | Action | Next | Revisión de idempotencia/compensación |
|---|---|---|---|---|---|
| INIT | task_received | Ninguna | Inicializar thread_id y registrar hora de inicio | QUERY_RUNNING | No hace falta |
| QUERY_RUNNING | query_completed | Ninguna | Consultar datos y guardar resultado en state | REPORT_GENERATING | No hace falta |
| REPORT_GENERATING | report_generated | Ninguna | Generar reporte y guardar report ID en state | APPROVAL_PENDING | Idempotencia: si el reporte ya existe, saltar generación |
| APPROVAL_PENDING | approve | Ninguna | Registrar aprobador y hora de aprobación | EMAIL_SENDING | No hace falta |
| APPROVAL_PENDING | reject | Ninguna | Registrar motivo de rechazo | FAILED | No hace falta |
| EMAIL_SENDING | email_sent | Ninguna | Enviar e-mail y registrar email ID | COMPLETED | Idempotencia: si el e-mail ya fue enviado, saltar |
| EMAIL_SENDING | timeout | retry_count < 3 | Registrar fallo y revisar idempotencia | EMAIL_SENDING (retry) o FAILED | Clave de idempotencia: email_id + thread_id |
| FAILED | retry | retry_count < max | Revisar idempotencia y recuperar desde el checkpoint anterior | QUERY_RUNNING o REPORT_GENERATING o EMAIL_SENDING | Decidir punto de recuperación según checkpoint |
| COMPLETED | Ninguna | Ninguna | Registrar hora de finalización y limpiar recursos | Terminal | No hace falta |
Corrección del incidente: si falla el paso 5 (EMAIL_SENDING -> timeout), se debe recuperar desde EMAIL_SENDING, no desde QUERY_RUNNING. El checkpoint debe registrar el nodo actual (EMAIL_SENDING), los pasos ejecutados (QUERY, REPORT_GENERATED, APPROVAL_APPROVED) y lo siguiente que debe ocurrir (EMAIL_SENDING). La generación de reportes y el envío de e-mails necesitan claves de idempotencia para evitar duplicados.
5. Idempotencia y compensación: recuperar no es solo checkpoint
Tener un checkpoint no significa que todos los efectos externos se recuperen de forma segura. La recuperación también necesita idempotencia, transacciones, compensación y revisión del estado del sistema externo.
5.1 Conceptos de idempotencia y compensación
Definiciones:
- Idempotente: varias ejecuciones producen el mismo resultado y no crean efectos externos duplicados
- Compensación: deshacer un efecto externo ya producido y restaurar consistencia
- Rollback de transacción: operación atómica que se revierte automáticamente al fallar
- Revisión de estado externo: revisar el sistema externo antes de recuperar para evitar operaciones duplicadas
Tres pilares de consistencia de estado: identidad de idempotencia (action_id + schema_hash), cadena de state snapshots (snapshot + prev_hash + delta) y acción de compensación registrada (undo_op).
5.2 Checklist de idempotencia y compensación
Cómo decidir qué operaciones necesitan idempotencia y cuáles compensación:
| Tipo de operación | ¿Necesita idempotencia? | ¿Necesita compensación? | Diseño de clave de idempotencia | Plan de compensación |
|---|---|---|---|---|
| Consulta de datos (sin efecto externo) | No | No | - | - |
| Generación de reporte (sobrescribe archivo) | Sí | Sí | report_id + thread_id | Borrar el reporte nuevo y restaurar la versión aprobada |
| Envío de e-mail (API externa) | Sí | Difícil | email_id + thread_id | Enviar e-mail de corrección o cancelación en algunos casos |
| Descuento de inventario (base de datos) | Sí | Sí | inventory_id + order_id | Reponer inventario |
| Creación de ticket (sistema externo) | Sí | Sí | ticket_id + thread_id | Cerrar ticket |
| Descuento de presupuesto (estado interno) | Sí | Sí | budget_id + thread_id | Reponer presupuesto |
| Envío de solicitud de aprobación (sin efecto duradero) | No | No | - | - |
Lógica: si la operación produce un efecto externo, necesita idempotencia. Si el efecto puede revertirse, necesita compensación. En llamadas entre sistemas, la clave de idempotencia debería incluir un identificador del sistema externo. Las operaciones atómicas pueden apoyarse en rollback de transacción.
La recuperación no es solo checkpoint. También necesita idempotencia, transacciones, compensación y revisión del estado externo. Decir que un checkpoint permite recuperar todos los efectos de forma segura no es exacto.
6. Checklist de estados de tarea Agent: recuperable vs no recuperable
No todo checkpoint permite recuperar. Un terminal state es el estado final de una workflow execution: completado, fallido, con timeout o cancelado. Un terminal state no se reanuda; solo puede volver a ejecutarse o compensarse.
6.1 Tabla de clasificación de estados
| Tipo de estado | ¿Recuperable? | Condición de recuperación | Método de recuperación | Ejemplo |
|---|---|---|---|---|
| Failed | Sí | retry_count < max | Recuperar desde el checkpoint previo | Timeout de llamada a herramienta |
| Retry | Sí | Revisión de idempotencia aprobada | Reejecutar desde el nodo fallido | Fallo al enviar e-mail |
| Compensation | Parcialmente | Existe plan de compensación | Ejecutar undo_op | Fallo al descontar inventario |
| Approval Pause | Sí | event approve/reject | Command(resume=…) | Espera de aprobación |
| Terminal | No | Ninguna | Sin ruta de recuperación | COMPLETED, FAILED (retry_count = max) |
Notas: un estado Failed puede recuperarse con retry si retry_count < max. Un estado Retry necesita revisión de idempotencia y reejecuta desde el nodo fallido. Un estado Compensation es parcialmente recuperable si existe un plan. Approval Pause se recupera con event approve/reject. Terminal State no es recuperable, como COMPLETED o FAILED tras alcanzar el máximo de retries.
7. Lecturas relacionadas
El diseño de máquinas de estado es solo el punto de partida. El modelado de estado debe ajustarse al escenario de negocio; cada tarea necesita distinta granularidad y estrategia de recuperación.
Navegación de la serie
| Artículo | Relación | Enlace |
|---|---|---|
| Diseño Human-in-the-loop Agent: qué pasos necesitan aprobación humana | Detalles de pausa de aprobación | /blog/es/posts/ai/20260707-human-in-the-loop-agent-approval-design/ |
| Control de costos Agent: model routing, presupuesto de herramientas y retry de fallos | Estrategia de presupuesto y retry | /blog/es/posts/ai/20260707-agent-cost-control-model-routing-tool-budget-cache-retry/ |
| Gestión de estado con LangGraph en la práctica: mejores prácticas de arquitectura Agent 2026 | Gestión de estado en LangGraph | /blog/es/posts/ai/20260424-langgraph-agent-architecture/ |
| Monitoreo, alertas y recuperación de fallos para AI Agent: de logs a máquinas de estado | Monitoreo y recuperación | /blog/es/posts/ai/20260527-ai-agent-monitoring-recovery/ |
| LangGraph vs AutoGen State Tracking | Comparación de frameworks | /blog/es/posts/ai/20260526-langgraph-autogen-state-tracking/ |
| Datasets de evaluación Agent y pruebas de regresión: cómo evitar romper todo con un cambio | Evaluación y pruebas de regresión | Próximo artículo de la serie |
Referencias externas
Fuentes de alta confianza:
| Fuente | Confianza | Tema | Enlace |
|---|---|---|---|
| Documentación LangGraph Persistence | high | Checkpointer, Store, Thread State, Checkpoint | https://docs.langchain.com/oss/python/langgraph/persistence |
| Documentación LangGraph Interrupts | high | interrupt(), Command(resume=…), thread_id | https://docs.langchain.com/oss/python/langgraph/interrupts |
| Documentación Temporal Durable Execution | high | Event History, Durable Execution, Resumable/Recoverable | https://docs.temporal.io/temporal |
| Documentación OpenAI Agents SDK Tracing | high | Trace, Span, workflow_name, trace_id | https://openai.github.io/openai-agents-python/tracing/ |
| Documentación AWS Step Functions State Machines | high | State Machine, Flow State, Task State, StartAt, Next | https://docs.aws.amazon.com/step-functions/latest/dg/concepts-statemachines.html |
| Stately: State machines and statecharts | medium | State, Event, Transition, Guard, Action, Hierarchy | https://stately.ai/docs/state-machines-and-statecharts |
Una máquina de estados no es necesaria para todos los agentes, pero las tareas complejas deben hacer explícito su progreso. El siguiente paso no es sumar más frameworks. Es diseñar State, Event, Transition, Guard y Action adecuados para tu escenario de negocio, y mover el progreso de la tarea desde lenguaje natural en el prompt hacia estado estructurado.
Diseñar una máquina de estados para un agente de IA complejo
Divide una tarea compleja de agente de IA en state, event, guard, action, checkpoint, retry, compensation y terminal state para que el progreso no quede oculto solo en el prompt.
⏱️ Estimated time: 45 min
- 1
Step 1: Lista los puntos de riesgo
Lista los efectos externos, puntos de pausa humana, puntos de fallo y condiciones terminales de la tarea. - 2
Step 2: Define el conjunto mínimo de estados
Define un conjunto mínimo de estados, como pending, running, waiting_approval, retrying, compensating, succeeded, failed y cancelled. - 3
Step 3: Conecta events con next states
Para cada state, escribe qué events puede recibir y a qué next state conduce cada event. - 4
Step 4: Agrega condiciones de guard
Agrega guards a las transitions peligrosas: permisos, presupuesto, approval, clave de idempotencia y estado de recursos externos. - 5
Step 5: Aísla las acciones de herramientas
Pon las llamadas a herramientas en la capa action y registra resumen de input, resumen de output, traceId y resultado del efecto externo. - 6
Step 6: Define políticas de fallo
Define retry policy, terminal state y compensation policy para cada ruta de fallo. - 7
Step 7: Persiste la base de recuperación
Define un checkpoint o event log para recuperar, y trata el prompt como contexto temporal, no como la única fuente de verdad.
FAQ
Si el agente falla en el paso 5, ¿debo volver al paso 1 o continuar desde un checkpoint?
¿El estado de la tarea debe vivir en el prompt, en una base de datos, en un checkpoint de LangGraph o en un job de cola?
¿Cuál es la diferencia entre una máquina de estados y un diagrama de workflow?
¿Cómo garantizo que el agente vuelva al mismo punto de ejecución después de approval?
¿Retry y compensation van en el prompt o en las reglas de transición de estado?
¿Un agente simple de atención al cliente necesita una máquina de estados?
14 min de lectura · Publicado el: 17 sep 2026
Guía de ingeniería de AI Agents
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Modelo de permisos para agentes de IA: identidad de usuario, permisos de herramientas, auditoría y aislamiento de secretos
Antes de conectar un agente de IA a herramientas reales, diseña su modelo de permisos: mapeo de identidad, service accounts, per-tool permission, scopes, secret vault, rotación de claves, approval policy, data boundary y audit log.
Parte 21 de 22
Siguiente
Este es el artículo más reciente de la serie por ahora.



Comentarios
Inicia sesión con GitHub para dejar un comentario