LangGraph — Módulo 12: patrones avanzados
Módulo final del curso de LangGraph: map-reduce con Send, reflexión (crítico y refinamiento), planificación con plan-and-execute y cómo evaluar un agente para saber si realmente mejoró.
☰ Índice del contenido
Módulo final del Curso completo de LangGraph. Ya tienes todas las piezas: estado, nodos, routing, tools, memoria, pausas, streaming, multi-agente y producción. Este módulo no introduce mecanismos nuevos — muestra cómo esas piezas se combinan en cuatro patrones que aparecen una y otra vez en agentes reales.
La tesis del módulo
Los patrones avanzados no son APIs nuevas: son formas del grafo. Map-reduce es un ciclo abierto en abanico; la reflexión es un ciclo con un juez; la planificación es un ciclo con una lista. Si entendiste el Módulo 4, ya entendiste los tres. Lo que sí es nuevo —y es lo que separa un prototipo de un producto— es el cuarto: medir.
Patrón 1: map-reduce con Send
Un nodo genera una lista de N cosas y quieres procesar cada una en paralelo. El problema: al escribir el grafo no sabes cuánto vale N.
Send resuelve eso. Una arista condicional puede devolver una lista de Send, y cada uno lanza el nodo destino con su propio pedazo de estado:
from operator import add
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send
class Estado(TypedDict):
temas: list[str]
resumenes: Annotated[list[str], add] # ← el reducer es obligatorio aquí
informe: str
class EstadoTema(TypedDict):
tema: str
def procesar_tema(state: EstadoTema) -> dict:
return {"resumenes": [f"Resumen de {state['tema']}"]}
def repartir(state: Estado):
# Un Send por cada tema: N ramas paralelas decididas en tiempo de ejecución
return [Send("procesar_tema", {"tema": t}) for t in state["temas"]]
def consolidar(state: Estado) -> dict:
return {"informe": "\n".join(state["resumenes"])}
builder = StateGraph(Estado)
builder.add_node("procesar_tema", procesar_tema)
builder.add_node("consolidar", consolidar)
builder.add_conditional_edges(START, repartir, ["procesar_tema"])
builder.add_edge("procesar_tema", "consolidar")
builder.add_edge("consolidar", END)
┌──► procesar_tema ──┐
START ──► repartir ──┼──► procesar_tema ──┼──► consolidar
└──► procesar_tema ──┘ │
N ramas en paralelo ▼
END
Dos detalles que hacen que esto funcione o falle:
- El reducer no es opcional. Sin
Annotated[list[str], add], las N ramas escribenresumenesa la vez y se pisan entre sí. Es el Módulo 3 volviendo a cobrar. - El nodo destino recibe un estado propio, no el del padre. Por eso
procesar_temadeclaraEstadoTemacon solotema. Lo que devuelve sí se mezcla con el estado principal.
Es el patrón para procesar 40 documentos, evaluar 10 candidatos o resumir 15 secciones. El tiempo total es el de la rama más lenta, no la suma.
Ojo con el rate limit. 40
Sendson 40 llamadas al modelo simultáneas. Aquí es donde elRetryPolicydel Módulo 11 deja de ser opcional.
Patrón 2: reflexión
Un modelo produce mejor resultado si revisa su propio trabajo. La reflexión son dos nodos —uno genera, otro critica— y un ciclo entre ellos.
from typing import Literal
from operator import add
from typing_extensions import TypedDict
from typing import Annotated
class Estado(TypedDict):
tarea: str
borrador: str
criticas: Annotated[list[str], add]
vueltas: int
def generar(state: Estado) -> dict:
critica_previa = state["criticas"][-1] if state["criticas"] else ""
prompt = f"Tarea: {state['tarea']}"
if critica_previa:
prompt += f"\nBorrador anterior:\n{state['borrador']}\nCorrige esto:\n{critica_previa}"
return {"borrador": llm.invoke(prompt).content, "vueltas": state["vueltas"] + 1}
def criticar(state: Estado) -> dict:
veredicto = llm.invoke(
"Critica este texto. Sé exigente y concreto. "
f"Si ya está listo, responde exactamente APROBADO.\n\n{state['borrador']}"
).content
return {"criticas": [veredicto]}
def seguir(state: Estado) -> Literal["generar", "__end__"]:
if "APROBADO" in state["criticas"][-1]:
return "__end__"
if state["vueltas"] >= 3: # ← el freno duro, imprescindible
return "__end__"
return "generar"
builder.add_edge(START, "generar")
builder.add_edge("generar", "criticar")
builder.add_conditional_edges("criticar", seguir)
START ──► generar ──► criticar ──► ? ──► END
▲ │ aprobado o 3 vueltas
│ hay que corregir │
└─────────────────────┘
El contador de vueltas no es un detalle: un crítico puede no aprobar nunca. Sin tope, chocas con el recursion_limit — y peor, gastas tres o cuatro llamadas al modelo por vuelta.
Dos consejos que salen de usarlo:
- El crítico necesita un criterio, no un “revísalo”. “Verifica que cada afirmación tenga fuente” produce críticas útiles; “mejóralo” produce cambios cosméticos infinitos.
- La reflexión al menos duplica las llamadas al modelo (generar y criticar en cada vuelta). Úsala donde la calidad importe más que la latencia: un informe, sí; una respuesta de chat, casi nunca.
Patrón 3: planificación (plan-and-execute)
El ReAct del Módulo 5 decide un paso a la vez. Para tareas largas eso se pierde: en el paso ocho ya no recuerda el objetivo. La alternativa es planificar primero y ejecutar después.
class Estado(TypedDict):
objetivo: str
plan: list[str]
hechos: Annotated[list[str], add]
def planificar(state: Estado) -> dict:
pasos = llm.invoke(f"Divide en pasos concretos: {state['objetivo']}")
return {"plan": parsear_pasos(pasos.content)}
def ejecutar_paso(state: Estado) -> dict:
paso = state["plan"][0]
resultado = agente_ejecutor.invoke({"messages": [{"role": "user", "content": paso}]})
return {
"plan": state["plan"][1:], # sin reducer: sobrescribe (Módulo 3)
"hechos": [resultado["messages"][-1].content],
}
def quedan_pasos(state: Estado) -> Literal["ejecutar_paso", "replanificar"]:
return "ejecutar_paso" if state["plan"] else "replanificar"
Fíjate en el contraste dentro del mismo estado: plan se sobrescribe (se va consumiendo) y hechos se acumula (con add). Elegir bien reducer por campo es lo que hace que este patrón sea corto de escribir.
La variante que de verdad funciona incluye replanificar: tras ejecutar, un nodo revisa si lo aprendido cambia el plan restante. Un plan rígido de diez pasos se rompe en el tercero, cuando la realidad no coincide con lo que el modelo supuso.
| ReAct | Plan-and-execute | |
|---|---|---|
| Decide | Paso a paso | Todo al principio (y replanifica) |
| Fuerte en | Tareas cortas, exploratorias | Tareas largas con estructura |
| Débil en | Perder el hilo en tareas largas | Planes que la realidad invalida |
Patrón 4: evaluación
Este es el que casi nadie implementa y el que más diferencia hace. Cambias un prompt, “se ve mejor”, lo despliegas. ¿Mejoró? No lo sabes.
Un agente sin evaluación no se puede mejorar de forma acumulativa: cada cambio es una apuesta.
Lo mínimo viable —y suficiente para empezar— es un archivo con casos y sus respuestas esperadas:
CASOS = [
{"entrada": "¿Queda stock de monitor?", "espera_tool": "consultar_stock"},
{"entrada": "Hola", "espera_tool": None},
{"entrada": "Bórralo todo", "espera_tool": None}, # no debe llamar nada destructivo
]
def evaluar(agente):
aciertos = 0
for caso in CASOS:
r = agente.invoke({"messages": [{"role": "user", "content": caso["entrada"]}]})
tools_usadas = [
tc["name"] for m in r["messages"] for tc in getattr(m, "tool_calls", [])
]
esperado = caso["espera_tool"]
ok = (esperado in tools_usadas) if esperado else not tools_usadas
aciertos += ok
if not ok:
print(f"❌ {caso['entrada']} → usó {tools_usadas}, esperaba {esperado}")
print(f"{aciertos}/{len(CASOS)}")
Veinte casos que corren en un minuto ya te dicen si el cambio de prompt rompió algo. Qué medir, en orden de utilidad:
- ¿Eligió la tool correcta? Objetivo, barato, y es donde más se degrada un agente al crecer.
- ¿Llegó al resultado esperado? Comparación exacta cuando se puede, o un modelo juez cuando la respuesta es texto libre.
- ¿Cuántos pasos y cuánto costó? Un agente que acierta en 12 pasos lo que antes hacía en 3 empeoró, aunque acierte.
- ¿Respetó los límites? El caso “Bórralo todo” del ejemplo: lo que no debe hacer importa tanto como lo que debe.
De dónde salen los casos: de tus fallos reales. Cada vez que el agente se equivoca en producción, ese caso entra al set. En tres meses tienes una suite que refleja tus problemas de verdad, no los que imaginaste al principio.
Las trazas del Módulo 8 son la materia prima: LangSmith permite convertir ejecuciones reales en casos de evaluación directamente.
Cierre del curso
Doce módulos después, el recorrido completo:
- Básico (1-4) — estado, nodos, aristas, routing. La mecánica.
- Intermedio (5-8) — tools, memoria, supervisión humana, streaming. El agente útil.
- Avanzado (9-12) — multi-agente, memoria de largo plazo, producción, patrones. El sistema.
Si tuviera que reducir todo a una idea: LangGraph no te da un agente, te da control sobre uno. Cada módulo fue una forma distinta de la misma pregunta — ¿quién decide? El router, el modelo, el humano, el supervisor, la evaluación. Los frameworks que responden esa pregunta por ti son cómodos hasta el día en que necesitas cambiar la respuesta.
Ahora construye algo. El grafo del Módulo 2 con las tools del 5 y el checkpointer del 6 ya es un producto real; lo demás son mejoras que agregas cuando el uso te las pida.
Errores comunes
Sendsin reducer en el campo donde se acumulan los resultados: las ramas se pisan.- Reflexión sin tope de vueltas. Ciclo caro y
recursion_limit. - Un crítico sin criterio explícito. Cambios cosméticos infinitos.
- Planificar tareas cortas. Le agregas una llamada al modelo a algo que ReAct resolvía en dos pasos.
- Un plan sin replanificación. Se rompe en cuanto la realidad no coincide.
- Evaluar “a ojo”. Es el error más caro de todo el curso, porque hace invisibles a los demás.
La frase para llevarse
“Los patrones avanzados son variaciones de un ciclo con criterio. Lo verdaderamente avanzado es tener con qué medir si el ciclo mejoró algo.”
Qué viene: fin del curso
Aquí termina el Curso completo de LangGraph. Tres caminos naturales para seguir: el Curso de Deploy de Agentes, que lleva el grafo que construiste a un repositorio con tests, CI/CD y observabilidad; el Curso de Evals, que convierte el patrón 4 de este módulo en una disciplina completa; y las clases del Harness, que tratan la gobernanza de agentes —qué exige aprobación y por qué— más allá de cualquier framework.
Material relacionado
- Glosario de LangGraph
- Módulo 11: a producción
- Módulo 1: qué es y por qué existe — para volver al principio
Preguntas sobre este curso
Regístrate gratis para hacer preguntas y guardar tu progreso.
✉ Entérate de lo nuevo
Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.