Tutorial de LangGraph: Sistemas Multiagente Paso a Paso
LangGraph es actualmente el framework más potente para desarrollar aplicaciones con estado y múltiples actores usando LLMs. Creado por LangChain, te permite modelar workflows complejos de agentes como grafos dirigidos, con estados, transiciones condicionales, bucles y ejecución paralela.
Si alguna vez intentaste construir un workflow multiagente con LangChain Chains simples, sabes lo rápido que se vuelve caótico. LangGraph resuelve precisamente este problema: en lugar de armar cadenas de prompts desorganizadas, defines un grafo claro con nodos (agentes), aristas (transiciones) y un estado compartido. Esto está más cercano a la arquitectura de software real que al prompt engineering.
En este tutorial te guío desde los conceptos fundamentales hasta un sistema multiagente completo con human-in-the-loop, memoria y manejo de errores. Todos los ejemplos de código son funcionales.
TL;DR — LangGraph en 90 Segundos
LangGraph es un framework para aplicaciones multiagente con estado basado en teoría de grafos: los agentes son nodos, las transiciones son aristas, y un estado compartido fluye entre todos los nodos.
---
Los 4 conceptos centrales: State (estado compartido), Nodes (funciones de agente), Edges (transiciones), Conditional Edges (rutas condicionales).
La mayor ventaja: Puedes construir bucles (cycles). Un agente revisor puede devolver el trabajo al agente escritor si la calidad no es suficiente. Esto no es posible con chains simples.
La curva de aprendizaje: Más pronunciada que CrewAI, pero tienes control granular sobre cada paso. Si construyes workflows complejos, LangGraph es la mejor opción.
Fin de la explicación compacta.
Qué es LangGraph y por qué lo necesitas
El problema con las chains simples
Imagina que quieres construir un pipeline de creación de contenido: un agente investiga, otro escribe, otro revisa. Con LangChain Chains simples se vería así:
# Enfoque ingenuo — funciona, pero no bien
research = llm.invoke("Investiga el tema X")
draft = llm.invoke(f"Escribe un artículo: {research}")
final = llm.invoke(f"Mejora: {draft}")
Funciona para casos simples. Pero qué pasa si:
- El revisor rechaza el artículo y el escritor necesita revisarlo? → Se necesita un bucle
- Quieres compartir el estado entre agentes (por ejemplo, todos los mensajes previos)? → Se necesita gestión de estado
- Necesitas enrutar de manera diferente basándote en el contenido? → Se necesitan transiciones condicionales
- Quieres pausar el workflow y esperar aprobación humana? → Se necesita human-in-the-loop
- Necesitas ejecutar varios agentes en paralelo? → Se necesita ejecución paralela
Exactamente eso es lo que LangGraph resuelve.
La arquitectura de LangGraph
LangGraph modela workflows de agentes como State Graphs, grafos dirigidos donde:
- Nodes (nodos) = funciones de agente que reciben el estado, lo procesan y lo devuelven
- Edges (aristas) = transiciones entre nodos, que pueden ser secuenciales o condicionales
- State = un diccionario compartido (o modelo Pydantic) que fluye entre todos los nodos
- Conditional Edges = funciones que deciden, basadas en el estado, cuál nodo ejecutar a continuación
- Cycles = bucles que permiten que un nodo se ejecute múltiples veces (por ejemplo, Escritor → Revisor → Escritor)
El concepto está inspirado en motores de workflow como Apache Airflow o Temporal, pero optimizado específicamente para agentes basados en LLMs.
Por qué grafos y no simplemente chains?
| Propiedad | Chain Simple | LangGraph |
|---|---|---|
| Secuencia lineal | ✅ | ✅ |
| Ramificaciones condicionales | ❌ | ✅ |
| Bucles (Cycles) | ❌ | ✅ |
| Estado compartido | Difícil | ✅ Nativo |
| Ejecución paralela | ❌ | ✅ |
| Human-in-the-Loop | ❌ | ✅ |
| Checkpointing / Memoria | ❌ | ✅ |
| Visualización de grafos | ❌ | ✅ |
Instalación y Configuración
pip install langgraph langchain-openai langchain-core
Recomendado además para este tutorial:
pip install langchain-anthropic # Para modelos Claude
pip install langgraph-checkpoint-sqlite # Para persistencia SQLite
pip install grandalf # Para visualización de grafos en terminal
Configurar API keys:
export OPENAI_API_KEY="sk-..."
# Opcional para Claude:
export ANTHROPIC_API_KEY="sk-ant-..."
Importante: Nunca incluyas API keys hardcodeadas en el código. Siempre usa variables de entorno o archivos .env con python-dotenv.
Los 4 Conceptos Centrales — En Detalle
1. State — El Corazón del Sistema
El state es un objeto de datos compartido que se pasa entre todos los nodos en el grafo. Cada nodo puede leer y actualizar el state. Esta es la diferencia fundamental respecto a chains simples, donde cada paso solo ve la salida del anterior.
State como TypedDict (recomendado para casos simples):
from typing import TypedDict, List, Optional
class ContentState(TypedDict):
topic: str # El tema que se está procesando
research: str # Resultados de investigación
draft: str # Borrador actual
final_article: str # Artículo final
messages: List[str] # Historial completo de mensajes
revision_count: int # Número de revisiones
approved: bool # ¿Fue aprobado el artículo?
feedback: Optional[str] # Retroalimentación del revisor
Por qué TypedDict? Te proporciona type safety y autocompletado en el IDE sin perder la flexibilidad de un diccionario. Para aplicaciones más complejas, también puedes usar modelos Pydantic:
from pydantic import BaseModel, Field
class ContentState(BaseModel):
topic: str = Field(description="El tema a procesar")
research: str = Field(default="", description="Resultados de investigación")
draft: str = Field(default="", description="Borrador actual")
revision_count: int = Field(default=0)
approved: bool = Field(default=False)
State Reduction — Cómo funcionan las actualizaciones:
Por defecto, el retorno de un nodo sobrescribe el state. Pero puedes definir funciones reductoras para acumular campos del state:
from typing import Annotated
from operator import add
class ContentState(TypedDict):
# messages se añade, no se sobrescribe
messages: Annotated[List[str], add]
# research se sobrescribe (por defecto)
research: str
Si ahora dos nodos devuelven messages, las listas se combinan en lugar de sobrescribirse. Esto es extremadamente útil para historiales de mensajes.
2. Nodes — Los Agentes
Los Nodes son funciones Python que reciben el state, lo procesan y devuelven un state actualizado (o una actualización parcial). Cada Node es un agente o una unidad de procesamiento.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o", temperature=0)
def researcher(state: ContentState) -> dict:
"""Agente de investigación: recopila información sobre el tema."""
topic = state["topic"]
prompt = f"""Eres un experto en investigación. Recopila la información
más importante sobre el tema: {topic}.
Estructura tu respuesta en:
1. Definición y conceptos básicos
2. Desarrollos actuales
3. Ejemplos prácticos
4. Malentendidos frecuentes
Devuelve solo la investigación, sin introducción."""
response = llm.invoke(prompt)
# Actualización parcial: devolver solo los campos modificados
return {
"research": response.content,
"messages": [f"Researcher: {response.content[:200]}..."]
}
Puntos clave sobre los Nodes:
- Actualizaciones parciales: No necesitas devolver todo el state, solo los campos que modifiques. LangGraph fusionará esto con el state existente.
- Sin efectos secundarios: Los Nodes deben ser funciones puras idealmente (entrada → salida). Los efectos secundarios (escritura de archivos, llamadas API) deben gestionarse explícitamente.
- Manejo de errores: Si un Node lanza una excepción, todo el grafo se detiene. Usa try/except para operaciones propensas a fallos.
def researcher(state: ContentState) -> dict:
try:
response = llm.invoke(prompt)
return {"research": response.content}
except Exception as e:
# Escribir el error en el state en lugar de detener el grafo
return {
"research": f"Error en investigación: {str(e)}",
"messages": [f"Researcher ERROR: {str(e)}"]
}
3. Edges — Las Transiciones
Los Edges definen qué Node se ejecuta después de la actual. Hay tres tipos:
Simple Edges (transiciones fijas):
# Después de researcher siempre viene writer
workflow.add_edge("researcher", "writer")
Conditional Edges (transiciones condicionales): Esta es la característica más poderosa de LangGraph. Una función de enrutamiento decide, basándose en el state, qué Node viene después:
def route_after_review(state: ContentState) -> str:
"""Decide qué sucede después de la revisión."""
if state.get("approved"):
return "publish" # Aprobado → publicar
elif state.get("revision_count", 0) >= 3:
return "human_review" # Demasiadas revisiones → revisar manualmente
else:
return "writer" # No aprobado → escritor revisa
# Agregar edge condicional
workflow.add_conditional_edges(
"reviewer", # Node origen
route_after_review, # Función de enrutamiento
{
"publish": "publisher",
"human_review": "human_node",
"writer": "writer"
}
)
Entry Point (Node inicial):
workflow.set_entry_point("researcher")
4. Cycles — Bucles para Mejora Iterativa
Los Cycles son la diferencia más importante entre LangGraph y chains simples. Un cycle permite a un equipo de agentes trabajar de forma iterativa:
researcher → writer → reviewer → ¿no está bien? → writer → reviewer → ¡está bien! → publish
En LangGraph lo logras con Conditional Edges que vuelven a un Node anterior:
# El reviewer puede devolver el writer (¡cycle!)
workflow.add_conditional_edges(
"reviewer",
route_after_review, # Puede devolver "writer"
)
# El writer siempre va al reviewer
workflow.add_edge("writer", "reviewer")
Advertencia: evita bucles infinitos: Implementa siempre un contador o un número máximo de iteraciones:
def route_after_review(state: ContentState) -> str:
revision_count = state.get("revision_count", 0)
if state.get("approved"):
return "publish"
if revision_count >= 5:
return "human_review" # Límite de seguridad
return "writer" # Intentar de nuevo
Un Ejemplo Completo, Paso a Paso
Construiremos ahora un sistema multi-agente completo: Researcher + Writer + Reviewer + Publisher con human-in-the-loop, límite de revisiones y memory.
Paso 1: Definir el State
from typing import TypedDict, List, Optional, Annotated
from operator import add
class ContentState(TypedDict):
topic: str
research: str
draft: str
final_article: str
messages: Annotated[List[str], add] # Se añade, no se sobrescribe
revision_count: int
approved: bool
feedback: Optional[str]
Paso 2: Definir los Nodes (Agentes)
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o", temperature=0.7) # Algo de creatividad para el writer
def researcher(state: ContentState) -> dict:
"""Agente de investigación: recopila información estructurada."""
topic = state["topic"]
prompt = f"""Eres un experto en investigación. Recopila la información
más importante sobre el tema: {topic}.
Considera:
- Definición y conceptos básicos
- Desarrollos actuales (2025-2026)
- Ejemplos prácticos y casos de uso
- Malentendidos frecuentes y trampas
Devuelve solo la investigación estructurada."""
response = llm.invoke(prompt)
return {
"research": response.content,
"messages": [f"Researcher: Investigación completada ({len(response.content)} caracteres)"]
}
def writer(state: ContentState) -> dict:
"""Agente escritor: escribe artículos basados en investigación y feedback."""
research = state["research"]
feedback = state.get("feedback", "")
revision_count = state.get("revision_count", 0)
if feedback and revision_count > 0:
prompt = f"""Eres un autor profesional. Revisa el siguiente artículo
basándote en el feedback del revisor.
Investigación: {research}
Borrador anterior: {state.get('draft', '')}
Feedback del revisor: {feedback}
Escribe el artículo mejorado. Considera todos los puntos
del feedback."""
else:
prompt = f"""Eres un autor profesional. Escribe un artículo completo
y bien estructurado sobre el tema: {state['topic']}
Basándote en esta investigación: {research}
El artículo debe:
- Tener una introducción clara
- Usar subtítulos
- Incluir ejemplos prácticos
- Terminar con un resumen"""
response = llm.invoke(prompt)
return {
"draft": response.content,
"revision_count": revision_count + 1,
"messages": [f"Writer: Borrador v{revision_count + 1} creado"]
}
def reviewer(state: ContentState) -> dict:
"""Agente revisor: verifica la calidad y proporciona feedback."""
draft = state["draft"]
revision_count = state.get("revision_count", 0)
prompt = f"""Eres un revisor exigente. Evalúa el siguiente artículo
según estos criterios:
1. Estructura y legibilidad (1-10)
2. Precisión técnica (1-10)
3. Relevancia práctica (1-10)
4. Completitud (1-10)
Artículo: {draft}
Si todos los criterios alcanzan al menos 7/10, responde con "APPROVED".
Si no, responde con "REJECTED" y proporciona feedback específico para mejorar."""
response = llm.invoke(prompt)
content = response.content
approved = "APPROVED" in content.upper()
return {
"approved": approved,
"feedback": content if not approved else None,
"final_article": draft if approved else "",
"messages": [f"Reviewer: {'Aprobado' if approved else 'Rechazado (Revisión ' + str(revision_count) + ')'}"]
}
def publisher(state: ContentState) -> dict:
"""Agente publicador: formatea el artículo final."""
article = state["final_article"]
prompt = f"""Formatea este artículo como Markdown con:
- Título como H1
- Metadatos (autor, fecha, tiempo de lectura)
- Encabezados de sección limpios
Artículo: {article}"""
response = llm.invoke(prompt)
return {
"final_article": response.content,
"messages": [f"Publisher: Artículo publicado"]
}
def human_review(state: ContentState) -> dict:
"""Human-in-the-Loop: la persona decide en caso de demasiadas revisiones."""
print(f"\n=== REVISIÓN MANUAL ===")
print(f"Tema: {state['topic']}")
print(f"Revisiones: {state.get('revision_count', 0)}")
print(f"Feedback del revisor: {state.get('feedback', 'Ninguno')}")
print(f"\nÚltimo borrador:\n{state.get('draft', '')[:500]}...")
approval = input("\n¿Aprobar artículo? (s/n): ")
return {
"approved": approval.lower() == "s",
"messages": [f"Human: {'Aprobado' if approval.lower() == 's' else 'Rechazado'}"]
}
Paso 3: Definir funciones de enrutamiento
from langgraph.graph import END
def route_after_review(state: ContentState) -> str:
"""Enrutamiento después de la revisión con parada de emergencia."""
if state.get("approved"):
return "publisher"
if state.get("revision_count", 0) >= 3:
return "human_review"
return "writer" # Volver al escritor
def route_after_human(state: ContentState) -> str:
"""Enrutamiento después de la revisión humana."""
if state.get("approved"):
return "publisher"
return END # Abortar si el humano rechaza
Paso 4: Construir el grafo
from langgraph.graph import StateGraph
# Crear el grafo
workflow = StateGraph(ContentState)
# Añadir todos los nodos
workflow.add_node("researcher", researcher)
workflow.add_node("writer", writer)
workflow.add_node("reviewer", reviewer)
workflow.add_node("publisher", publisher)
workflow.add_node("human_review", human_review)
# Definir las aristas
workflow.set_entry_point("researcher")
# researcher → writer (siempre)
workflow.add_edge("researcher", "writer")
# writer → reviewer (siempre)
workflow.add_edge("writer", "reviewer")
# reviewer → condicional (writer, publisher, o human_review)
workflow.add_conditional_edges(
"reviewer",
route_after_review,
{
"writer": "writer",
"publisher": "publisher",
"human_review": "human_review"
}
)
# human_review → condicional (publisher o END)
workflow.add_conditional_edges(
"human_review",
route_after_human,
{
"publisher": "publisher",
END: END
}
)
# publisher → END (siempre)
workflow.add_edge("publisher", END)
# Compilar el grafo
app = workflow.compile()
Paso 5: Ejecutar
# Estado inicial
initial_state = {
"topic": "KI-Programmierung: Best Practices 2026",
"research": "",
"draft": "",
"final_article": "",
"messages": [],
"revision_count": 0,
"approved": False,
"feedback": None
}
# Ejecutar el grafo
result = app.invoke(initial_state)
# Mostrar el resultado
print("\n" + "=" * 60)
print("FINAL ARTICLE:")
print("=" * 60)
print(result["final_article"])
print("\n" + "=" * 60)
print(f"Revisiones: {result['revision_count']}")
print(f"Historial de mensajes:")
for msg in result["messages"]:
print(f" - {msg}")
¿Qué sucede aquí?
- Researcher recopila información sobre el tema
- Writer redacta el primer borrador basado en la investigación
- Reviewer evalúa el borrador según 4 criterios
- Si no se aprueba: volver al Writer con retroalimentación (ciclo)
- Después de 3 revisiones fallidas: Human Review (parada de emergencia)
- Si se aprueba: Publisher formatea el artículo final
- END — el artículo está listo
El flujo completo es un grafo con un ciclo (writer ↔ reviewer) y dos aristas condicionales (reviewer y human_review).
Patrones avanzados
Memoria y persistencia con puntos de control
LangGraph puede guardar el estado después de ejecutar cada nodo (checkpointing). Esto permite:
- Recuperación tras fallos
- Reanudación de flujos pausados
- Historiales de conversación entre múltiples sesiones
from langgraph.checkpoint.memory import MemorySaver
# Para persistencia más allá de reinicios:
# from langgraph.checkpoint.sqlite import SqliteSaver
# Checkpointer en memoria
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
# Ejecutar con thread_id (crucial para la memoria)
result = app.invoke(
initial_state,
config={"configurable": {"thread_id": "article-ki-programmierung"}}
)
# Continuar más tarde (p.ej. después de aprobación humana)
result2 = app.invoke(
{"approved": True}, # Solo enviar la actualización
config={"configurable": {"thread_id": "article-ki-programmierung"}}
)
Cómo funciona: el checkpointer guarda el estado después de cada ejecución de nodo. Con el thread_id puedes reanudar el mismo hilo más adelante. Es especialmente útil en escenarios de human-in-the-loop, donde el flujo se pausa hasta que una persona decide.
Ejecución paralela
Puedes ejecutar varios nodos en paralelo y luego combinar sus resultados:
def researcher_general(state: ContentState) -> dict:
"""Investiga información general."""
response = llm.invoke(f"Información general sobre: {state['topic']}")
return {"research_general": response.content}
def researcher_technical(state: ContentState) -> dict:
"""Investiga detalles técnicos."""
response = llm.invoke(f"Detalles técnicos sobre: {state['topic']}")
return {"research_technical": response.content}
def merge_research(state: ContentState) -> dict:
"""Combina ambas investigaciones."""
combined = f"General:\n{state['research_general']}\n\nTécnico:\n{state['research_technical']}"
return {"research": combined}
# En el grafo:
workflow.add_node("researcher_general", researcher_general)
workflow.add_node("researcher_technical", researcher_technical)
workflow.add_node("merge", merge_research)
# Ambos investigadores en paralelo desde el inicio
workflow.set_entry_point("researcher_general")
workflow.add_edge("researcher_general", "researcher_technical")
# Realmente en paralelo: ambos desde el inicio, ambos → merge
# LangGraph ejecuta automáticamente los nodos paralelos en conjunto
Integración de herramientas: agentes con capacidades
Los agentes se vuelven potentes cuando pueden usar herramientas. LangGraph se integra perfectamente con las herramientas de LangChain:
from langchain.tools import Tool
from langchain_community.tools import DuckDuckGoSearchRun
# Herramienta de búsqueda web
search = DuckDuckGoSearchRun()
def researcher_with_tools(state: ContentState) -> dict:
"""Agente investigador con búsqueda web."""
topic = state["topic"]
# Primero buscar
search_results = search.run(f"{topic} 2026 latest developments")
# Luego alimentar el LLM con los resultados
prompt = f"""Basándote en estos resultados de búsqueda, crea una
investigación estructurada sobre el tema {topic}:
Resultados de búsqueda: {search_results}"""
response = llm.invoke(prompt)
return {"research": response.content}
Sub-grafos: grafos dentro de grafos
Puedes usar un LangGraph como nodo en otro LangGraph. Esto es útil para arquitecturas modulares:
# Sub-grafo para investigación
research_graph = StateGraph(ContentState)
research_graph.add_node("web_search", web_searcher)
research_graph.add_node("summarize", summarizer)
research_graph.set_entry_point("web_search")
research_graph.add_edge("web_search", "summarize")
research_graph.add_edge("summarize", END)
research_app = research_graph.compile()
# El grafo principal usa el sub-grafo como nodo
main_workflow = StateGraph(ContentState)
main_workflow.add_node("research", research_app) # ¡Sub-grafo como nodo!
main_workflow.add_node("write", writer)
main_workflow.set_entry_point("research")
main_workflow.add_edge("research", "write")
main_workflow.add_edge("write", END)
Streaming: resultados en tiempo real
LangGraph soporta streaming para que veas resultados intermedios mientras el grafo se ejecuta:
# Transmitir todas las actualizaciones de nodos
for event in app.stream(initial_state):
for node_name, node_output in event.items():
print(f"[{node_name}] → {list(node_output.keys())} updated")
# Transmitir solo los mensajes
for event in app.stream(
initial_state,
stream_mode="values"
):
messages = event.get("messages", [])
if messages:
print(f"Latest: {messages[-1]}")
Visualizar el grafo
LangGraph puede visualizar el grafo, algo extremadamente útil para debugging:
# En Jupyter Notebook:
from IPython.display import Image, display
# Renderizar el grafo como imagen
display(Image(app.get_graph().draw_mermaid_png()))
# En terminal:
app.get_graph().print_ascii()
Esto te proporciona una representación visual de todos los nodos, edges y rutas condicionales, imprescindible cuando trabajas con grafos complejos.
LangGraph vs. CrewAI: qué framework usar en cada caso
| Aspecto | LangGraph | CrewAI |
|---|---|---|
| Paradigma | State Graph (Teoría de grafos) | Role-Based Agents (Metáfora de equipo) |
| Flexibilidad | Muy alta; controlas cada paso | Media; la abstracción oculta detalles |
| Curva de aprendizaje | Pronunciada; hay que entender los conceptos de grafos | Suave; define agentes e inicia tareas |
| Control | Granular; Conditional Edges, State-Management | Abstrahido; el framework decide mucho |
| Debugging | Visualización de grafos, Streaming, Tracing con LangSmith | Logs, print-statements básicos |
| Ciclos/Bucles | Soporte nativo | Limitado (max_iterations) |
| Memory/State | Nativo (Checkpointing, State-Reduction) | Limitado (parámetros de Memory) |
| Human-in-the-Loop | Nativo (interrupt_before, interrupt_after) | Implementado manualmente |
| Mejor para | Workflows complejos, sistemas en producción | Prototipos rápidos, equipos de agentes simples |
| Comunidad | Ecosistema LangChain (muy grande) | En crecimiento, pero más pequeña |
Mi recomendación:
- CrewAI para prototipos y equipos de agentes simples (2-3 agentes, flujos lineales)
- LangGraph para sistemas en producción, workflows complejos con bucles, condiciones e interacción humana
- Ambos en proyectos diferentes; no son excluyentes entre sí
Mejores prácticas desde la experiencia
1. Mantener el state lo más pequeño posible
No toda información debe estar en el state. Las variables locales dentro de los nodos suelen ser suficientes. El state debe contener solo datos que realmente necesitan compartirse entre nodos.
2. Establecer siempre límites de revisión
Sin límite, un ciclo puede ejecutarse indefinidamente. Implementa siempre un contador y un mecanismo de parada:
MAX_REVISIONS = 5
def route_after_review(state: ContentState) -> str:
if state.get("revision_count", 0) >= MAX_REVISIONS:
return "human_review" # o END
# ...
3. Activar tracing con LangSmith
LangSmith (de LangChain) te da visibilidad detallada en cada paso:
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-..."
Verás cada llamada a LLM, cada transición de state y cada cálculo de tokens en el dashboard de LangSmith.
4. Vigilar los costos
Cada nodo realiza al menos una llamada a LLM. Con 3 agentes y 3 revisiones, eso son 9+ llamadas. Con GPT-4o esto puede costar 5-10$ por ejecución. Usa modelos más económicos para tareas simples (por ejemplo, GPT-4o-mini para el revisor).
5. Modelos locales para desarrollo
Durante el desarrollo y pruebas, usa modelos locales (Ollama) para ahorrar costos:
from langchain_community.chat_models import ChatOllama
llm = ChatOllama(model="llama3.1:8b", temperature=0)
Problemas frecuentes y soluciones
Problema: “Graph doesn’t have an entry point”
Solución: Debes llamar a workflow.set_entry_point("node_name") antes de compilar.
Problema: “Node ‘xyz’ not found”
Solución: Todo nodo que se referencie en edges debe haberse añadido con add_node antes de definir los edges.
Problema: Bucle infinito
Solución: El Conditional Edge siempre devuelve el mismo nodo. Revisa la función de enrutamiento; debe retornar valores diferentes según el state.
Problema: El state no se actualiza
Solución: El nodo devuelve una actualización parcial, pero el campo utiliza un Annotated-Reducer. Verifica que el reducer esté definido correctamente. Con Annotated[List[str], add], el nodo debe devolver una lista que se añadirá.
Puntos relevantes para evaluación
- LangGraph: Framework para aplicaciones multi-agente con estado, basado en teoría de grafos
- 4 conceptos centrales: State (estado compartido), Nodes (funciones de agentes), Edges (transiciones), Conditional Edges (rutas condicionales)
- Ciclos: Permiten mejora iterativa (Writer ↔ Reviewer)
- State-Reduction: Annotated-Reducer (p.ej.
add) para campos de state acumulativos - Human-in-the-Loop: Pausar el workflow para decisiones humanas
- Checkpointing: Guardar el state después de cada nodo, recuperación tras fallos
- Ejecución paralela: Múltiples nodos simultáneamente, fusión automática
- Sub-Graphs: Un grafo como nodo dentro de otro grafo, arquitectura modular
- Streaming: Actualizaciones en tiempo real durante la ejecución del grafo
- LangSmith: Tracing y debugging para workflows de LangGraph
- Comparación: LangGraph (flexible, complejo, listo para producción) vs. CrewAI (simple, rápido, prototipos)
Preguntas frecuentes
¿Necesito conocimientos de LangChain para usar LangGraph? Sí, conocimientos básicos de LangChain son útiles, ya que LangGraph se construye sobre LangChain (misma integración de LLM, mismas herramientas, mismos Runnables). Si aún no conoces LangChain, deberías aprender primero los fundamentos.
¿Es LangGraph gratuito? LangGraph en sí es Open Source (licencia MIT). Los costos de LLM (OpenAI, Anthropic) se suman aparte. LangSmith (Tracing) tiene un nivel gratuito, pero es de pago para uso intensivo.
¿Puede LangGraph trabajar con modelos locales? Sí, mediante Ollama, llama.cpp o LM Studio. Esto es especialmente recomendable para desarrollo y pruebas para ahorrar costos de API. Para producción, los modelos en la nube suelen tener mejor calidad.
¿Cómo debuggeo un workflow de LangGraph?
Tres herramientas: 1) app.get_graph().draw_mermaid_png() para visualización del grafo. 2) Streaming con app.stream() para actualizaciones en tiempo real. 3) LangSmith para tracing detallado de cada llamada a LLM.
¿Cuál es la diferencia entre LangGraph y LangChain? LangChain es un framework general para aplicaciones con LLM (Chains, Agents, Tools). LangGraph está especializado en workflows multi-actor con estado y estructura de grafo. LangGraph se construye sobre LangChain, pero no lo reemplaza.
¿Puedo usar LangGraph en producción? Sí, LangGraph está listo para producción. Con Checkpointing (persistencia), manejo de errores y tracing con LangSmith, tienes todo lo necesario para sistemas confiables en producción. Además, LangGraph Cloud ofrece hosting gestionado para aplicaciones LangGraph.
Lecturas recomendadas
Keine Bücher für Kategorie "ki-agenten" gefunden.


