LangGraph: máquinas de estado para agentes complejos

By Carlos Montiel | Especialista en IA Empresarial
Publicado: 2026-07-28 | Por: Carlos Montiel | Lectura: ~4 minutos

Los agentes reales no son secuencias lineales de pasos: se ramifican, repiten acciones y necesitan recordar contexto. LangGraph modela esto explícitamente como una máquina de estados.

El modelo mental: grafo de estado, no cadena

LangGraph representa un agente como un grafo dirigido donde los nodos son funciones (o cadenas LCEL) que reciben y modifican un estado compartido, y las aristas determinan qué nodo se ejecuta después. A diferencia de una cadena LCEL, las aristas pueden ser condicionales y pueden formar ciclos: el grafo puede volver a un nodo ya visitado tantas veces como la lógica lo requiera.

El estado es el elemento central. Se define típicamente con `TypedDict` y cada nodo recibe el estado completo, devolviendo solo las claves que modifica; LangGraph se encarga de fusionar esa actualización con el estado existente.

Definiendo el esquema de estado

from typing import TypedDict, Annotated, Sequence from langchain_core.messages import BaseMessage import operator class EstadoConversacion(TypedDict): mensajes: Annotated[Sequence[BaseMessage], operator.add] pasos_ejecutados: int finalizado: bool

El uso de `Annotated[..., operator.add]` es la parte menos intuitiva para quien viene de programación imperativa clásica: le indica a LangGraph que, cuando un nodo devuelva `{"mensajes": [nuevo_mensaje]}`, debe concatenar esa lista al estado existente en lugar de sobreescribirla. Sin ese reductor, cada actualización de nodo reemplazaría el historial completo.

Construyendo el grafo

from langgraph.graph import StateGraph, START, END def nodo_analizar(estado: EstadoConversacion): ultimo = estado["mensajes"][-1] return {"pasos_ejecutados": estado["pasos_ejecutados"] + 1} def nodo_responder(estado: EstadoConversacion): respuesta = llm.invoke(estado["mensajes"]) return {"mensajes": [respuesta]} def enrutar(estado: EstadoConversacion): if estado["pasos_ejecutados"] > 5: return "fin" return "responder" builder = StateGraph(EstadoConversacion) builder.add_node("analizar", nodo_analizar) builder.add_node("responder", nodo_responder) builder.add_edge(START, "analizar") builder.add_conditional_edges("analizar", enrutar, {"responder": "responder", "fin": END}) builder.add_edge("responder", "analizar") grafo_compilado = builder.compile()

Nótese el ciclo explícito: `responder` vuelve a `analizar`, que puede volver a enrutar hacia `responder` de nuevo. Esto es exactamente lo que un `AgentExecutor` clásico hacía de forma opaca internamente; LangGraph lo hace visible y editable.

Ejecutando e inspeccionando el grafo

resultado = grafo_compilado.invoke({ "mensajes": [HumanMessage(content="¿Cómo renuevo mi licencia de software?")], "pasos_ejecutados": 0, "finalizado": False, }) for evento in grafo_compilado.stream({"mensajes": [...], "pasos_ejecutados": 0}): print(evento) # imprime el estado tras cada nodo ejecutado

El método `.stream()` es particularmente útil para debugging: muestra el estado completo después de cada nodo, lo que hace visible exactamente en qué punto del ciclo el agente tomó una decisión incorrecta, algo que en una implementación manual con un bucle `while` y banderas booleanas es mucho más difícil de rastrear.

Checkpointers: persistencia de estado entre invocaciones

Un `StateGraph` compilado con un checkpointer conserva el estado entre llamadas, identificado por un `thread_id`, lo cual es la base de la memoria conversacional (cubierto en detalle en el siguiente artículo de esta serie):

from langgraph.checkpoint.memory import MemorySaver memoria = MemorySaver() app = builder.compile(checkpointer=memoria) config = {"configurable": {"thread_id": "conversacion-usuario-882"}} app.invoke({"mensajes": [HumanMessage(content="Hola")]}, config=config) app.invoke({"mensajes": [HumanMessage(content="¿Y sobre lo que te pregunté antes?")]}, config=config)

Cuándo esta complejidad se justifica

Un grafo de estado es la herramienta correcta cuando el comportamiento del agente depende de historia acumulada, no solo del último mensaje: un agente de soporte que debe recordar cuántas veces intentó resolver un problema antes de escalar a un humano, un pipeline de investigación que itera sobre búsquedas hasta reunir suficiente evidencia, o cualquier flujo donde "cuántas veces ya pasamos por aquí" cambia la decisión siguiente. Si tu flujo no tiene esta característica, una cadena LCEL simple sigue siendo la opción más mantenible.

Carlos Montiel
Arquitecto de Soluciones IA Empresarial
Especialista en LLMs, Agentes y Orquestación
guatemalia.com/#contacto · info@guatemalia.com

¿Necesitas implementar IA en tu empresa?

Carlos Montiel es arquitecto de soluciones IA empresarial. Implementa LLMs, Agentes, RAG y orquestadores en empresas de Guatemala y Latinoamérica. Contáctalo para una consultoría.

Contactar a Carlos Montiel

info@guatemalia.com