Skip to content
IRC-CodingIRC-Coding
langgraphsistemas-multiagentepythonagent-frameworktutoriallangchain

LangGraph Tutorial: Sistemas Multiagente Paso a Paso

Introducción práctica a LangGraph para sistemas multiagente. Aprende State Graphs, Nodes, Edges y coordinación de agentes con Python.

I

IRC-Coding Team

16 min read
LangGraph Tutorial: Sistemas Multiagente Paso a Paso

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?

PropiedadChain SimpleLangGraph
Secuencia lineal
Ramificaciones condicionales
Bucles (Cycles)
Estado compartidoDifí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:

  1. Actualizaciones parciales: No necesitas devolver todo el state, solo los campos que modifiques. LangGraph fusionará esto con el state existente.
  2. Sin efectos secundarios: Los Nodes deben ser funciones puras idealmente (entrada → salida). Los efectos secundarios (escritura de archivos, llamadas API) deben gestionarse explícitamente.
  3. 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í?

  1. Researcher recopila información sobre el tema
  2. Writer redacta el primer borrador basado en la investigación
  3. Reviewer evalúa el borrador según 4 criterios
  4. Si no se aprueba: volver al Writer con retroalimentación (ciclo)
  5. Después de 3 revisiones fallidas: Human Review (parada de emergencia)
  6. Si se aprueba: Publisher formatea el artículo final
  7. 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

AspectoLangGraphCrewAI
ParadigmaState Graph (Teoría de grafos)Role-Based Agents (Metáfora de equipo)
FlexibilidadMuy alta; controlas cada pasoMedia; la abstracción oculta detalles
Curva de aprendizajePronunciada; hay que entender los conceptos de grafosSuave; define agentes e inicia tareas
ControlGranular; Conditional Edges, State-ManagementAbstrahido; el framework decide mucho
DebuggingVisualización de grafos, Streaming, Tracing con LangSmithLogs, print-statements básicos
Ciclos/BuclesSoporte nativoLimitado (max_iterations)
Memory/StateNativo (Checkpointing, State-Reduction)Limitado (parámetros de Memory)
Human-in-the-LoopNativo (interrupt_before, interrupt_after)Implementado manualmente
Mejor paraWorkflows complejos, sistemas en producciónPrototipos rápidos, equipos de agentes simples
ComunidadEcosistema 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.

Volver al blog
Share:

Nächster Artikel in Programación de IA

Weiterlesen
Orquestación de Agentes: Best Practices

Entradas relacionadas