De tu primer agente con create_agent, hasta un supervisor multi-agente en LangGraph con balanceo de carga entre workers.
0 / 10 completado
Nivel 1 · Fundamentos
Tu primer agente con create_agent
Objetivo: instalar LangChain y crear tu primer agente con la función create_agent.
create_agent (del paquete langchain) es hoy la forma recomendada de crear un agente ReAct: te da un modelo con capacidad de usar herramientas y razonar en varios pasos, con un sistema de middleware flexible para extenderlo. Por debajo corre sobre LangGraph.
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4o",
tools=[],
)
resultado = agent.invoke({
"messages": [{"role": "user", "content": "¿Qué es un agente de IA, en una frase?"}]
})
print(resultado["messages"][-1].content)
Todo agente creado con create_agent trabaja con un estado de mensajes (messages) — el mismo formato que usás en LangGraph, lo que hace que subir de nivel hacia grafos más complejos sea un paso natural, no una reescritura.
Reto
Cambiá model por otro proveedor (por ejemplo un modelo de Anthropic vía langchain-anthropic) sin cambiar nada más del código.
Nivel 2 · Personalidad
System prompt e instrucciones
Objetivo: moldear el comportamiento del agente con un prompt de sistema y parámetros del modelo.
agente.py
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o", temperature=0.3, max_tokens=400)
agent = create_agent(
model=model,
tools=[],
system_prompt=(
"Sos un asistente técnico de soporte. Respondé en español, "
"breve y con pasos numerados. Si no sabés algo, decilo."
),
)
resultado = agent.invoke({"messages": [{"role": "user", "content": "La app se cierra al abrir la cámara"}]})
print(resultado["messages"][-1].content)
Pasar un objeto ChatOpenAI en vez de un string te da control fino sobre temperature, max_tokens y otros parámetros del proveedor.
Reto
Escribí un system prompt que rechace responder cualquier pregunta fuera de un dominio específico que elijas.
Nivel 3 · Herramientas
Herramientas con @tool
Objetivo: crear una herramienta custom con el decorador @tool de langchain_core.
agente.py
from langchain_core.tools import tool
from langchain.agents import create_agent
@tool
def clima_actual(ciudad: str) -> str:
"""Devuelve el clima actual reportado para una ciudad dada."""
datos = {"Ciudad de Guatemala": "22°C, parcialmente nublado"}
return datos.get(ciudad, "No tengo datos para esa ciudad")
agent = create_agent(model="gpt-4o", tools=[clima_actual])
resultado = agent.invoke({
"messages": [{"role": "user", "content": "¿Cómo está el clima en Ciudad de Guatemala?"}]
})
print(resultado["messages"][-1].content)
El agente decide solo cuándo llamar clima_actual — el docstring de la función es la descripción que el modelo usa para decidirlo, igual que en la mayoría de frameworks de agentes modernos.
Reto
Agregá una segunda herramienta buscar_producto(nombre: str) y verificá que el agente combine ambas en una sola respuesta cuando se le pregunten las dos cosas a la vez.
Nivel 4 · Memoria
Memoria con checkpointer
Objetivo: persistir el historial de conversación entre llamadas usando un checkpointer y un thread_id.
Como create_agent corre sobre LangGraph, la memoria se maneja con un checkpointer: guarda el estado completo del grafo (incluyendo mensajes) asociado a un thread_id, para que puedas retomar la misma conversación después.
agente_memoria.py
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4o",
tools=[],
checkpointer=InMemorySaver(),
)
config = {"configurable": {"thread_id": "cliente-42"}}
agent.invoke({"messages": [{"role": "user", "content": "Busco una laptop para diseño gráfico"}]}, config)
r = agent.invoke({"messages": [{"role": "user", "content": "¿Cuál es la más barata de las que mencionaste?"}]}, config)
print(r["messages"][-1].content)
InMemorySaver es ideal para prototipos — en producción se usa PostgresSaver o SqliteSaver para que el historial sobreviva a un reinicio del proceso.
Reto
Cambiá el thread_id a mitad de la conversación y confirmá que el agente "olvida" el contexto anterior — así comprobás que la memoria está atada al hilo, no al proceso.
Nivel 5 · Salida estructurada
response_format con Pydantic
Objetivo: forzar que el agente devuelva un objeto validado en vez de texto libre.
agente.py
from pydantic import BaseModel
from langchain.agents import create_agent
class TicketSoporte(BaseModel):
categoria: str
urgencia: str
resumen: str
agent = create_agent(model="gpt-4o", tools=[], response_format=TicketSoporte)
resultado = agent.invoke({
"messages": [{"role": "user", "content": "Se cierra la app al subir foto de perfil, es urgente"}]
})
ticket: TicketSoporte = resultado["structured_response"]
print(ticket.categoria, ticket.urgencia, ticket.resumen)
LangChain elige automáticamente la estrategia correcta: si el modelo soporta salida estructurada nativa (como los JSON schemas de OpenAI) la usa directamente; si no, envuelve tu schema como una tool interna que el modelo debe invocar.
Reto
Definí un modelo Pydantic para extraer rating, sentimiento y si menciona un problema específico de una reseña de producto.
Nivel 6 · Streaming
Streaming con .stream()
Objetivo: mostrar la respuesta del agente en tiempo real, token por token.
stream.py
for evento, metadata in agent.stream(
{"messages": [{"role": "user", "content": "Explicame qué es RAG en 3 pasos"}]},
stream_mode="messages",
):
if evento.content:
print(evento.content, end="", flush=True)
stream_mode="messages" te da los tokens del modelo a medida que se generan. Otros modos como "updates" te muestran cada paso del grafo (útil para depurar qué herramienta se ejecutó y cuándo). Para async, usá agent.astream() dentro de una función async def.
Reto
Envolvé el streaming en un endpoint de FastAPI con StreamingResponse, usando astream() en vez de stream().
Nivel 7 · Multi-agente
Handoffs entre agentes
Objetivo: transferir el control de una conversación de un agente especializado a otro usando el patrón de "handoff".
En LangGraph, un handoff es una tool especial que, al ejecutarse, no solo devuelve un resultado sino que le dice al grafo "de ahora en adelante, que siga otro agente". Se implementa devolviendo un objeto Command con goto (a quién transferir) y update (qué agregar al estado compartido).
handoff.py
from langchain_core.tools import tool
from langgraph.types import Command
from langgraph.prebuilt import InjectedState
@tool
def transferir_a_soporte(state: InjectedState) -> Command:
"""Transferir la conversación al especialista de soporte técnico."""
return Command(
goto="agente_soporte",
update={"messages": state["messages"]},
graph=Command.PARENT,
)
# El agente principal recibe esta tool; cuando decide usarla,
# el control pasa directo al nodo "agente_soporte" del grafo
A diferencia de "agente como herramienta" (donde el agente A llama al agente B y espera su respuesta), un handoff transfiere el control completo — el agente B sigue la conversación directamente con el usuario.
Reto
Creá un segundo handoff transferir_a_ventas y probá una conversación donde el agente principal decida a cuál de los dos transferir.
Nivel 8 · Orquestador
Supervisor con StateGraph
Objetivo: construir un orquestador explícito que enruta cada mensaje al especialista correcto usando un grafo de estados.
Cuando necesitás ver y controlar cada decisión de enrutamiento explícitamente (útil para depurar y trazar en producción), construís el supervisor directamente con StateGraph en vez de dejar que un agente "decida solo".
orquestador.py
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END
class Estado(TypedDict):
mensaje: str
respuesta: str
siguiente: str
def nodo_supervisor(estado: Estado) -> Estado:
# El supervisor usa el modelo con salida estructurada para decidir el ruteo
decision = supervisor_agent.invoke({"messages": [{"role": "user", "content": estado["mensaje"]}]})
estado["siguiente"] = decision["structured_response"].agente # "ventas" | "soporte" | "facturacion"
return estado
def ruta(estado: Estado) -> Literal["ventas", "soporte", "facturacion"]:
return estado["siguiente"]
grafo = StateGraph(Estado)
grafo.add_node("supervisor", nodo_supervisor)
grafo.add_node("ventas", nodo_ventas)
grafo.add_node("soporte", nodo_soporte)
grafo.add_node("facturacion", nodo_facturacion)
grafo.set_entry_point("supervisor")
grafo.add_conditional_edges("supervisor", ruta, {"ventas": "ventas", "soporte": "soporte", "facturacion": "facturacion"})
grafo.add_edge("ventas", END)
grafo.add_edge("soporte", END)
grafo.add_edge("facturacion", END)
app = grafo.compile()
print(app.invoke({"mensaje": "Me cobraron dos veces la suscripción"}))
Para el mismo problema, también existe el paquete langgraph-supervisor, que ya trae este patrón empaquetado si no necesitás personalizar la lógica de ruteo.
Reto
Agregá una arista de vuelta de cada especialista al supervisor (en vez de END) para soportar conversaciones multi-turno con varios especialistas en la misma sesión.
Nivel 9 · Producción
Trazas, errores y guardrails
Objetivo: instrumentar el grafo con LangSmith y manejar errores de forma robusta.
TERMINAL
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="tu-api-key"
export LANGSMITH_PROJECT="agente-soporte-prod"
python orquestador.py
# Cada nodo del grafo, cada tool call y cada tokens generado
# queda trazado y visible en smith.langchain.com
Usar un checkpointer compartido (Postgres) en vez de InMemorySaver es lo que permite correr varias réplicas del mismo servicio sin que cada una tenga su propia memoria aislada e inconsistente.
2. Load balancer entre réplicas
nginx.conf
upstream agentes_soporte {
least_conn;
server soporte-1:8001;
server soporte-2:8001;
server soporte-3:8001;
}
server {
listen 80;
location /soporte/ { proxy_pass http://agentes_soporte/; }
}
3. Arquitectura completa
arquitectura final
Cliente
│
▼
Supervisor (StateGraph, servicio propio)
│
▼
Cola de trabajos (Redis / SQS)
│
├──▶ Load Balancer ──▶ [Worker Ventas x3]
├──▶ Load Balancer ──▶ [Worker Soporte x3]
└──▶ Load Balancer ──▶ [Worker Facturación x3]
│
▼
PostgresSaver compartido (memoria)
│
▼
LangSmith (trazas de todo el flujo)
Con esto tenés el camino completo: de un create_agent() de una línea en el Nivel 1, a un supervisor multi-agente en producción, con memoria persistente, escalado horizontal y trazas de punta a punta.
Reto final
Separá los 3 especialistas del Nivel 8 en servicios FastAPI independientes con checkpointer Postgres compartido, y montá 2 réplicas de uno de ellos detrás de nginx.