← Todo el material
Curso Intermedio 20 de agosto de 2026

LangGraph — Módulo 5: tools y el patrón ReAct

Quinto módulo del curso de LangGraph: conecta un LLM real, dale herramientas con bind_tools, ejecútalas con ToolNode y arma el ciclo ReAct con tools_condition. Incluye el atajo create_agent.

☰ Índice del contenido

Arranca el bloque intermedio del Curso completo de LangGraph. En los cuatro módulos anteriores construimos grafos con funciones normales de Python: sin LLM, sin API keys. Eso fue a propósito — queríamos que entendieras la mecánica antes de meter magia. Hoy entra el modelo, y con él las tools: las manos del agente.

La tesis del módulo

Un LLM solo puede hablar. Una tool le permite actuar. El patrón ReAct es el ciclo que une ambas cosas: el modelo piensa, pide una herramienta, ve el resultado y vuelve a pensar — hasta que ya no necesita más. En LangGraph ese ciclo es literalmente un ciclo del grafo: exactamente lo que aprendiste en el Módulo 4.

Qué es una tool

Una tool (herramienta) es una función de Python que el modelo puede pedir que se ejecute: buscar en una base de datos, consultar una API, calcular algo, enviar un correo.

Lo importante: el modelo no ejecuta nada. El modelo solo pide. Devuelve un mensaje que dice “quiero llamar a buscar_pedido con el argumento id=4821”. Ejecutar esa petición es trabajo de tu código — y ahí es donde entra LangGraph.

Se declara con el decorador @tool:

from langchain.tools import tool

@tool
def consultar_stock(producto: str) -> str:
    """Consulta el stock disponible de un producto en bodega."""
    inventario = {"teclado": 12, "monitor": 0, "mouse": 47}
    cantidad = inventario.get(producto.lower())
    if cantidad is None:
        return f"No encontré el producto '{producto}'."
    return f"Hay {cantidad} unidades de {producto}."

Tres cosas que el modelo lee de esa función y que por lo tanto importan mucho:

  1. El nombre (consultar_stock) — le dice qué hace.
  2. El docstring ("""Consulta el stock...""") — es la descripción que el modelo usa para decidir cuándo llamarla. Un docstring vago produce un agente que llama tools al azar.
  3. Los tipos (producto: str) — definen el esquema de argumentos que el modelo debe rellenar.

El docstring de una tool no es documentación para ti: es prompt para el modelo. Escríbelo pensando en eso.

Paso 1: darle las tools al modelo

bind_tools le entrega al modelo la lista de herramientas disponibles. Devuelve una versión del modelo que ya sabe que existen:

from langchain.chat_models import init_chat_model

llm = init_chat_model("anthropic:claude-sonnet-4-6")

tools = [consultar_stock]
llm_con_tools = llm.bind_tools(tools)

Cuando invocas llm_con_tools, el mensaje que devuelve puede traer un atributo .tool_calls: la lista de herramientas que el modelo quiere usar, con sus argumentos ya rellenados.

Paso 2: el nodo que llama al modelo

from langgraph.graph import MessagesState
from langchain.messages import SystemMessage

def asistente(state: MessagesState) -> dict:
    respuesta = llm_con_tools.invoke(
        [SystemMessage(content="Eres un asistente de bodega. Sé breve.")]
        + state["messages"]
    )
    # Devolvemos SOLO el mensaje nuevo: add_messages lo acumula (Módulo 3).
    return {"messages": [respuesta]}

Usamos MessagesState, el estado prehecho con el reducer add_messages que vimos en el Módulo 3. Aquí es donde ese módulo empieza a pagar.

Paso 3: el nodo que ejecuta las tools

Podrías escribir a mano el bucle que recorre .tool_calls, ejecuta cada función y arma un ToolMessage con el resultado. Pero LangGraph ya trae ese nodo hecho: ToolNode.

from langgraph.prebuilt import ToolNode

herramientas = ToolNode(tools)

Eso es todo. ToolNode recibe el estado, lee el último mensaje, ejecuta todas las tool calls que encuentre (en paralelo si son varias), captura errores y devuelve los resultados como ToolMessage en messages.

Paso 4: el ciclo ReAct

Ahora la pieza que conecta todo. Después del nodo asistente hay que decidir:

  • ¿El modelo pidió una tool? → ir a tools, ejecutarla, y volver al asistente con el resultado.
  • ¿No pidió nada? → terminar.

Eso es una arista condicional, exactamente como en el Módulo 4. Y LangGraph trae el router ya escrito: tools_condition.

from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition

builder = StateGraph(MessagesState)
builder.add_node("asistente", asistente)
builder.add_node("tools", ToolNode(tools))

builder.add_edge(START, "asistente")
# Si el último mensaje trae tool_calls → "tools". Si no → END.
builder.add_conditional_edges("asistente", tools_condition)
# Y la arista que cierra el ciclo: tras ejecutar, volver a pensar.
builder.add_edge("tools", "asistente")

agente = builder.compile()

El grafo:

                              ┌──────────────┐
                              ▼              │
 START ──► asistente ──► ? ──► tools ────────┘
                         │
                         └──(sin tool_calls)──► END

Esa flecha que vuelve de tools a asistente es el patrón ReAct. Razonar → actuar → observar → razonar. El agente da tantas vueltas como necesite.

respuesta = agente.invoke({
    "messages": [{"role": "user", "content": "¿Queda stock de monitor?"}]
})
print(respuesta["messages"][-1].content)
# → No queda stock de monitor: hay 0 unidades en bodega.

Detrás de esa única línea pasaron cuatro mensajes: tu pregunta, la petición de tool del modelo, el resultado de consultar_stock, y la respuesta final. Puedes verlos todos recorriendo respuesta["messages"].

Ojo con el recursion_limit. Este es un ciclo real, y aplica el tope de 25 pasos del Módulo 4. Un agente que se queda pegado llamando la misma tool una y otra vez casi siempre tiene un docstring confuso o una tool que devuelve algo que el modelo no entiende como respuesta.

El atajo: create_agent

Este patrón es tan universal que existe una función que arma ese grafo completo por ti:

from langchain.agents import create_agent

agente = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[consultar_stock],
)

agente.invoke({"messages": [{"role": "user", "content": "¿Queda stock de mouse?"}]})

Una línea, mismo comportamiento. Y como lo que devuelve es un grafo compilado normal, sigue soportando todo lo que viene en los próximos módulos: checkpointers, interrupts, streaming.

Nota de versión: en LangChain v1 esta función se llama create_agent. Si sigues un tutorial más antiguo verás create_react_agent importado desde langgraph.prebuilt: es la versión previa de la misma idea.

Entonces, ¿por qué aprendimos a armarlo a mano? Porque create_agent es una caja. Sirve mientras tu agente sea un ciclo modelo↔tools estándar. En cuanto necesites meter un paso de validación entre medio, un nodo de aprobación humana o una ruta alternativa, vuelves al grafo explícito — y ahí ya sabes exactamente qué estás modificando.

Errores comunes

  • Docstring vago o ausente. Es la descripción que el modelo usa para elegir. Sin docstring, el agente adivina.
  • Olvidar add_edge("tools", "asistente"). Sin esa arista el agente ejecuta la tool y muere sin usar el resultado. Es el error más común del módulo.
  • Usar llm en vez de llm_con_tools dentro del nodo. Si no invocas el modelo con las tools enlazadas, nunca va a pedir ninguna.
  • Tools que devuelven objetos raros. El resultado va al modelo como texto: devuelve strings claros, no dumps de objetos.
  • Efectos secundarios sin control. Una tool que borra registros o cobra dinero no debería ejecutarse sin supervisión. Eso lo resolvemos en el Módulo 7.

La frase para llevarse

“El modelo no ejecuta tools: las pide. Tu grafo es quien decide si obedecer — y esa distinción es todo el control que tienes sobre un agente.”

Qué viene

En el Módulo 6 resolvemos algo que este agente todavía no puede hacer: recordar. Hoy cada invoke empieza de cero. Con los checkpointers y los threads, el agente mantiene la conversación entre turnos y puede retomar donde quedó.

Material relacionado

LangGraphPythonToolsReActAgentes IA

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.