MÓDULO 5 · LANGGRAPH · LECCIÓN 01 · 30 MIN

Anatomía de un grafo de estado

En LangGraph el estado es la única verdad. Los nodos no se pasan datos entre sí como funciones encadenadas: leen del estado y devuelven un parche parcial que LangGraph fusiona. Esa indirección es lo que permite que el grafo tenga ciclos sin volverse ilegible.
01 — Punto de partida
Dónde se acaba la cadena

En el módulo anterior encadenaste con LCEL: prompt | modelo | parser. Ese operador impone una forma —un DAG, un camino sin retorno—. La salida de un eslabón es la entrada del siguiente, y no hay sitio donde escribir «llevo dos intentos» ni forma de volver atrás.

En cuanto necesitas volver —reintentar una validación, dejar que el modelo lea el resultado de una herramienta y decida otra vez— la cadena obliga a montar la lógica de control fuera de ella. LangGraph invierte el reparto: el flujo lo define un grafo explícito y los datos viven en un objeto compartido.

// LangGraph no sustituye a LangChain: los nodos siguen llamando a sus modelos, herramientas y parsers. Lo que cambia es quién decide el orden.
02 — Vocabulario
Las tres piezas
PiezaQué esFirma típica
StateEl esquema de la memoria compartidaclass Estado(TypedDict)
NodeFunción que recibe el estado y devuelve un parchedef nodo(state) -> dict
EdgeEl camino de un nodo al siguienteadd_edge("a", "b")

Y dos constantes: START, el punto de entrada, y END, el nodo virtual que corta la ejecución.

from typing import TypedDict
from langgraph.graph import StateGraph, START, END

class EstadoDelGrafo(TypedDict):
    texto_original: str
    texto_procesado: str
    pasos: list[str]
03 — La regla que lo gobierna todo
El parche, no el estado entero

Un nodo no devuelve el estado completo. Devuelve solo las claves que ha tocado, y LangGraph las fusiona sobre lo que había, clave a clave.

def nodo_mayusculas(state: EstadoDelGrafo):
    return {"texto_procesado": state["texto_original"].upper()}
START→ mayusculas→ reverso→ END

Recorrido del estado por ese grafo, clave a clave:

Momentotexto_originaltexto_procesadoEl nodo devuelve
Entrada"Hola LangGraph"""—
Tras mayusculas"Hola LangGraph""HOLA LANGGRAPH"solo texto_procesado
Tras reverso"Hola LangGraph""HPARGGNAL ALOH"solo texto_procesado

La consecuencia práctica, y la fuente del error más común del módulo: si una clave no aparece en el return, sobrevive intacta; si aparece, se sobrescribe —no se acumula—. Esa asimetría tiene lección propia.

04 — Ejecución
Construir, compilar, invocar
builder = StateGraph(EstadoDelGrafo)
builder.add_node("mayusculas", nodo_mayusculas)
builder.add_node("reverso", nodo_reverso)

builder.add_edge(START, "mayusculas")   # equivale a set_entry_point("mayusculas")
builder.add_edge("mayusculas", "reverso")
builder.add_edge("reverso", END)

app = builder.compile()
resultado = app.invoke({"texto_original": "Hola LangGraph",
                        "texto_procesado": "", "pasos": []})

compile() valida la topología: que todo nodo sea alcanzable y que ninguno se quede sin salida. Después ya no se añaden nodos. invoke() devuelve el estado final completo, no el parche del último nodo.

// la guía usa set_entry_point(); sigue funcionando, pero add_edge(START, ...) deja una sola forma de expresar aristas y el diagrama sale más legible.
Auditoría

En el ejemplo de la guía, nodo_mayusculas devuelve "pasos": ["mayusculas"] con el comentario «se añadirá a la lista existente si el reducer lo permite», y nodo_reverso hace state["pasos"] + ["reverso"] a mano. Pero en ese grafo no hay ningún reducer declarado.

Si el primer nodo no acumula y el segundo sí, ¿qué está pasando de verdad con esa clave? Comprueba qué valor tiene pasos justo después del primer nodo y decide si el resultado final ['mayusculas', 'reverso'] sale por diseño o por la casualidad de que la lista empezara vacía.

05 — Ahora tú
Rompe el estado a propósito

Invoca el grafo omitiendo "pasos": [] del diccionario inicial:

app.invoke({"texto_original": "Hola LangGraph", "texto_procesado": ""})

Anota qué excepción sale, en qué nodo y en qué línea. Después responde: ¿el TypedDict te ha protegido de algo, o solo ha documentado la forma esperada?