De un chatbot de un solo nodo a un agente respaldado por MCP en LangGraph
Construya una aplicación LangGraph capa por capa: estado y reductores, aristas, bucles de herramientas, hilos con puntos de control, tres modos de streaming y herramientas proporcionadas a través de MCP.
Fuentes: Building AI Applications with LangGraph: From a Basic Chatbot to Tools, Memory, Streaming and MCP
La mayoría de los prototipos de LLM comienzan con una sola llamada: se envía un prompt y se imprime la respuesta. Esa estructura se desintegra en cuanto la aplicación necesita realizar búsquedas en la web, tomar decisiones sobre qué paso ejecutar a continuación, recordar interacciones anteriores o mostrar un progreso en tiempo real en la interfaz de usuario. LangGraph aborda este problema al convertir el flujo de control en un grafo explícito sobre un estado compartido. Esta guía transforma una aplicación que consiste en un chatbot de un solo nodo en un agente ReAct que utiliza herramientas, cuenta con memoria guardada en puntos de control, soporta transmisiones en tiempo real y accede a herramientas proporcionadas por un servidor MCP separado; además, señala los detalles que suelen causar problemas a lo largo del proceso.
El modelo mental: el estado fluye a través de nodos por medio de aristas
La integración tradicional de un LLM sigue una línea recta desde el prompt hasta el modelo y finalmente la respuesta. LangGraph, en cambio, exige pensar de forma cíclica: un objeto state ingresa al grafo, los nodes lo leen y devuelven los cambios, las edges deciden qué nodo se ejecutará a continuación, y al final sale el estado actualizado. Por lo tanto, los tres componentes básicos son:
- State: los datos que se transfieren entre pasos, como el historial de mensajes.
- Nodes: funciones ordinarias que realizan una tarea específica y devuelven una actualización parcial.
- Edges: las reglas que conectan los nodes, incluyendo el inicio y el final de una ejecución.
El grafo más simple posible cuenta con un nodo entre el punto de entrada y la salida:
┌─────────────┐
│ State │
└──────┬──────┘
│
▼
┌─────────────┐
│ Node │
│ chatbot │
└──────┬──────┘
│
▼
┌─────────────┐
│ END │
└─────────────┘
Separar “qué datos existen” de “qué se ejecuta en qué orden” es la elección fundamental del diseño. Puedes cambiar el flujo sin tocar cómo se almacenan los datos, y puedes inspeccionar el estado en cualquier momento sin tener que realizar ingeniería inversa de una cadena de llamadas a funciones.
Por qué un grafo en lugar de una cadena de llamadas
Un chatbot mínimo es como un conducto directo desde el usuario hasta el modelo y de vuelta:
User
↓
LLM
↓
Response
Ahora añadamos requisitos reales: una búsqueda en la web, varias herramientas, lógica de ramificación, memoria, un paso de aprobación humana, más de un agente, salida en tiempo real y comportamientos diferentes según lo que devuelva el modelo. Cada uno de estos elementos añade una rama o un bucle, y las declaraciones anidadas if alrededor de las llamadas a la API se vuelven rápidamente difíciles de seguir. Incluso el caso más común, “llamar a una herramienta si el modelo lo solicita”, ya contiene un ciclo:
┌─────────────┐
│ LLM │
└──────┬──────┘
│
Need a tool?
/ \
Yes No
↓ ↓
Tool Response
│
↓
LLM
Una vez que el flujo se convierte en un grafo, las ramas y los bucles pasan a ser objetos visibles a los que se puede dar nombre, probar y dibujar, en lugar de ser flujos de control ocultos dentro de funciones auxiliares.
Paso 1: un chatbot de un solo nodo con StateGraph
La primera versión es deliberadamente sencilla, con un único nodo conectado entre los marcadores de inicio y fin:
START
↓
Chatbot Node
↓
END
Definición del estado y su reductor
Para un chatbot, el estado generalmente necesita el historial de la conversación. Un primer intento es utilizar un TypedDict con una lista messages:
from typing import TypedDict
class State(TypedDict):
messages: list
Conceptualmente, esa lista acumula los turnos alternados del usuario y la IA:
State
└── messages
├── HumanMessage
├── AIMessage
├── HumanMessage
└── AIMessage
Hay un detalle muy importante aquí. En LangGraph, cada clave del estado puede tener un reductor que determina cómo el valor devuelto por un nodo se fusiona con el valor existente. Una anotación simple de list no tiene reductor, por lo que un nodo que devuelve {"messages": [reply]} reemplaza toda la lista en lugar de añadirle un elemento. Para el historial de chat, casi siempre se desea utilizar el reductor add_messages, declarado como Annotated[list, add_messages] (o heredado de MessagesState ya preconstruido). Con este reductor, los mensajes devueltos se añaden al final de la lista, y los mensajes con un ID coincidente se actualizan en su lugar. El fragmento anterior sirve como ilustración, pero en el código real se debe utilizar la forma anotada.
Escribiendo el nodo del chatbot
Un nodo es simplemente una función que recibe el estado actual y devuelve las claves que desea modificar. El nodo del chatbot pasa la historia al modelo y devuelve la respuesta:
def chatbot(state: State):
return {
"messages": [
llm.invoke(state["messages"])
]
}
Hay que leerlo de adentro hacia afuera. A la función se le pasa todo el estado:
state
Extrae la conversación hasta ese momento:
state["messages"]
Envía esa historia al modelo:
llm.invoke(state["messages"])
Y envuelve la respuesta del modelo en un diccionario cuyas claves son los campos que actualiza. Cabe señalar que el nodo no modifica directamente el estado; devuelve una actualización parcial y deja que LangGraph la combine:
{
"messages": [
llm.invoke(state["messages"])
]
}
Desde el punto de vista del flujo de datos, el nodo se encuentra entre dos versiones del mismo estado:
State
│
│ messages
▼
┌─────────────┐
│ Chatbot │
│ Node │
└──────┬──────┘
│
│ LLM Response
▼
State
Registro del nodo
La función pasa a formar parte del grafo una vez que se registra en el constructor bajo un nombre específico:
graph_builder.add_node(
"llmchatbot",
chatbot
)
El primer argumento es el identificador del nodo, al cual se referirán las aristas y los registros:
"llmchatbot"
El segundo argumento es la función llamable que se ejecuta cada vez que la ejecución llega a ese nodo:
chatbot
Paso 2: conexión de aristas y compilación
Un nodo por sí solo nunca se ejecuta; las aristas indican a LangGraph cómo llegar hasta allí y a dónde dirigirse después. Este grafo necesita dos aristas. La primera conecta el marcador especial START con el chatbot:
graph_builder.add_edge(
START,
"llmchatbot"
)
En forma de diagrama, la ejecución ingresa al grafo y va directamente al nodo:
START
↓
llmchatbot
La segunda arista envía el control desde el chatbot hasta END, lo que finaliza la ejecución:
graph_builder.add_edge(
"llmchatbot",
END
)
Eso corresponde a:
llmchatbot
↓
END
Juntos forman el grafo completo, aunque mínimo:
START
│
▼
┌────────────────┐
│ llmchatbot │
│ Node │
└────────┬───────┘
│
▼
END
Compilación e invocación
La compilación valida la estructura (por ejemplo, que cada arista apunte a un nodo conocido) y genera un objeto ejecutable:
graph = graph_builder.compile()
Se ejecuta pasando un estado inicial. Se aceptan diccionarios simples de roles/contenido, los cuales se convierten en objetos de mensaje de LangChain:
result = graph.invoke({
"messages": [
{
"role": "user",
"content": "What is LangGraph?"
}
]
})
En el fondo, el estado de entrada llega a START; el nodo del chatbot llama al modelo, la respuesta se integra en el estado y la ejecución finaliza:
Input State
↓
START
↓
llmchatbot
↓
LLM
↓
Updated State
↓
END
El valor de retorno de invoke es el estado final, por lo que la respuesta del modelo es la última entrada en result["messages"].
Paso 3: dibujar el grafo
Un grafo compilado puede renderizarse por sí mismo. En una notebook de Jupyter, los ayudantes de visualización de IPython muestran un PNG generado a partir de una descripción en Mermaid del grafo:
from IPython.display import Image, display
display(
Image(
graph.get_graph().draw_mermaid_png()
)
)
Para este grafo, la imagen es breve:
┌───────┐
│ START │
└───┬───┘
│
▼
┌──────────────┐
│ llmchatbot │
└──────┬───────┘
│
▼
┌───────┐
│ END │
└───────┘
No es evidente el valor con tres cuadros, pero crece rápidamente. Una vez que se incluyen aristas condicionales, nodos de herramienta y bucles, un diagrama renderizado es la forma más rápida de confirmar que el grafo que se construyó es el que se pretendía crear. Tenga en cuenta que draw_mermaid_png() renderiza mediante un servicio externo de Mermaid por defecto, por lo que necesita acceso a la red; si esto no está disponible, draw_mermaid() devuelve el texto de Mermaid, que puede renderizarse en otro lugar.
Paso 4: proporcionar al modelo una herramienta de búsqueda
El chatbot solo puede responder con base en lo que el modelo aprendió durante el entrenamiento. Una pregunta sobre los últimos avances en la IA agente requiere información actualizada, por lo que el siguiente paso consiste en añadir una herramienta de búsqueda web. El ejemplo utiliza Tavily Search, y el flujo esperado es el siguiente:
User
↓
LLM
↓
Search required?
↓
Tavily Search
↓
Search Results
↓
LLM
↓
Final Answer
Vincular herramientas no es lo mismo que ejecutarlas
La primera mitad del trabajo es el vinculamiento de herramientas: indicarle al modelo qué herramientas existen y qué argumentos aceptan.
llm_with_tools = llm.bind_tools(
[tavily_search]
)
Después del vinculamiento, el modelo puede responder con una llamada a herramienta estructurada (“llamar a tavily_search con esta consulta”) en lugar de texto simple. No puede ejecutar esa llamada por sí mismo. Algo en su aplicación debe detectar la llamada a herramienta, ejecutarla, añadir el resultado como un ToolMessage y enviar todo de vuelta al modelo. Darle a un modelo acceso a una herramienta no lo convierte en un agente; lo hace el flujo de ejecución circundante. Ese flujo es exactamente lo que proporciona el grafo.
Paso 5: convertirlo en un bucle ReAct
ReAct combina el razonamiento y la acción: el modelo piensa en la tarea, decide si necesita actuar, realiza la acción a través de una herramienta, observa el resultado y continúa. En términos de gráfico, la línea recta de antes:
START
↓
LLM
↓
END
se convierte en un gráfico con un punto de decisión y una ruta que lleva de vuelta al modelo:
START
│
▼
Agent
│
▼
┌─────────┐
│ LLM │
└────┬────┘
│
Need a tool?
/ \
Yes No
│ │
▼ ▼
Tool END
│
▼
Agent
│
▼
LLM
En el código de LangGraph, esta decisión se manifiesta como un edge condicional. El nodo preconstruido ToolNode ejecuta lo que sea que contenga el último mensaje de IA enviado por la herramienta, y tools_condition redirige la ejecución a dicho nodo cuando existen llamadas de herramientas o a END cuando no las hay. Un edge desde el nodo de la herramienta de vuelta al modelo cierra el bucle. La función auxiliar preconstruida create_react_agent reúne la misma estructura por usted; consulte la documentación actual de LangGraph para conocer su ubicación exacta, ya que las funciones auxiliares de agentes preconstruidas han cambiado de paquete con el tiempo.
Rastreando un paso a través del bucle
Supongamos que el usuario solicita las últimas noticias sobre LangGraph. Una ejecución del bucle genera esta secuencia de mensajes y pasos:
HumanMessage
↓
Agent
↓
LLM
↓
"I need current information."
↓
Tool Call
↓
Tavily Search
↓
Search Results
↓
ToolMessage
↓
Agent
↓
LLM
↓
Final Answer
La diferencia estructural importante es el ciclo. El primer chatbot funcionaba de forma lineal:
START → LLM → END
El grafo de ReAct puede volver a consultar el modelo tantas veces como sea necesario:
START
↓
Agent
↓
LLM
↓
Tool?
┌───────┐
│ │
Yes No
│ │
↓ ↓
Tool END
│
↓
Agent
Dado que, en principio, los bucles pueden ejecutarse indefinidamente, LangGraph impone un límite de recursión en cada ejecución (configurable a través del parámetro recursion_limit). Si un modelo sigue solicitando herramientas, se genera un error en lugar de una factura interminable.
Paso 6: recordar conversaciones con MemorySaver
Hasta ahora, cada llamada a invoke comienza desde cero. Considere dos turnos: primero, el usuario se presenta:
User:
My name is Himanshi.
Luego, en una solicitud separada, hace una pregunta adicional:
User:
What is my name?
La segunda respuesta solo puede darse si el grafo aún contiene la primera. LangGraph resuelve esto con un checkpointer: después de cada paso se guarda una instantánea del estado, y la siguiente ejecución en la misma conversación continúa a partir de esa instantánea. MemorySaver es la implementación más sencilla:
┌─────────────┐
│ Agent │
└──────┬──────┘
│
▼
┌───────────┐
│ State │
└─────┬─────┘
│
▼
┌─────────────┐
│ MemorySaver │
└─────────────┘
Se adjunta en tiempo de compilación con graph_builder.compile(checkpointer=memory). En lugar de que cada llamada sea una ejecución aislada, ahora cada ejecución forma parte de una conversación, y su estado se mantiene entre ejecuciones.
Los hilos mantienen separadas las conversaciones
La clave que une las ejecuciones es un ID de hilo, pasado en la configuración como {"configurable": {"thread_id": "..."}}. Cada hilo tiene su propia historia y su propio estado actual:
Thread 1
├── Message 1
├── Message 2
├── Message 3
└── Current State
Thread 2
├── Message 1
├── Message 2
└── Current State
Así, dos usuarios en dos hilos nunca ven los mensajes del otro:
User A → Thread A → State A
User B → Thread B → State B
Hay dos consideraciones importantes. MemorySaver mantiene todo en la memoria del proceso, por lo que toda la historia desaparece al reiniciar y no se comparte entre instancias del servidor; las implementaciones en producción utilizan un punto de control persistente como los de Postgres o SQLite. En versiones recientes, la clase también se exporta como InMemorySaver. Para conocer mejor qué almacena realmente un punto de control, consulte cómo se integran los puntos de control, las escrituras y los blobs en InMemorySaver.
Paso 7: transmisión en tiempo real del estado mientras funciona el grafo
Todo lo anterior utilizó la llamada bloqueante:
graph.invoke(...)
Espera a que se complete todo el grafo antes de devolver un resultado. En el caso de agentes con múltiples pasos, esto puede significar varios segundos sin actividad en la pantalla, lo cual resulta frustrante para los usuarios. El grafo compilado también expone stream, que genera salida mientras la ejecución aún está en curso.
stream_mode="values": el estado completo después de cada paso
El primer modo que hay que conocer es:
stream_mode="values"
Al usarse en un bucle, proporciona una captura del estado completo después de cada paso:
for chunk in graph.stream(
input_state,
stream_mode="values"
):
print(chunk)
Cada fragmento muestra cómo se ve el estado completo en ese momento, permitiendo observar cómo crece a medida que se ejecutan nodos y herramientas:
START
↓
State Update #1
↓
Node Execution
↓
State Update #2
↓
Tool Execution
↓
State Update #3
↓
END
Esto es conveniente cuando el estado es pequeño y simplemente se desea renderizar “la versión más reciente” en cada ocasión.
stream_mode="updates": solo lo que cambió en cada nodo
El segundo modo es:
stream_mode="updates"
La diferencia radica en lo que contiene cada bloque. En un modo se recibe el estado completo:
values
En el otro se recibe solo la diferencia, identificada por el nombre del nodo que la generó:
updates
Una forma rápida de recordar la distinción:
- values responde a "¿cómo es el estado en este momento?"
- updates responde a "¿qué cambió exactamente este nodo en particular?"
updates suele ser la mejor opción para depurar y registrar, ya que indica qué nodo fue responsable de cada cambio y mantiene los bloques pequeños cuando el estado es grande. Ninguno de los modos transmite tokens individuales del modelo; para una salida token por token existe un modo de flujo separado messages, y se puede pasar una lista de modos para recibir varios a la vez.
Paso 8: visibilidad detallada con astream_events
Las instantáneas de estado y los deltas de nodos indican qué cambió, pero no todo lo que sucedió. astream_events va un nivel más allá y emite un evento para cada momento del ciclo de vida de cada componente dentro de la ejecución:
Graph Execution
│
├── Graph Started
│
├── Node Started
│
├── LLM Started
│
├── LLM Streaming
│
├── LLM Completed
│
├── Tool Started
│
├── Tool Completed
│
├── Node Completed
│
└── Graph Completed
Se consume de forma asíncrona. Tenga en cuenta que la llamada a print en el fragmento a continuación debe estar indentada dentro del cuerpo del bucle; como se muestra, Python la rechazaría con un error de indentación:
async for event in graph.astream_events(
input_state,
version="v2"
):
print(event)
Cada evento lleva un tipo (por ejemplo on_chat_model_stream o on_tool_start), el nombre del componente y sus datos. Eso hace que el flujo de eventos sea útil para:
- depuración y registro estructurado
- observabilidad general de las ejecuciones del agente
- rastreo de cada llamada a LLM y de cada llamada a herramienta
Los ingenieros con experiencia en backend reconocerán este patrón: se trata más bien de rastrear una solicitud distribuida que de leer el valor devuelto por una sola función. La pregunta pasa de “¿funcionó el agente?” a “¿qué hizo exactamente el agente, en qué orden, y cuánto tiempo tomó cada parte?”
Cómo se combinan las partes
Al mirar hacia atrás, cada paso añadió una funcionalidad al mismo esqueleto. Comenzó con una sola llamada al modelo:
START
↓
LLM
↓
END
Las herramientas añadieron un viaje de ida y vuelta al mundo exterior:
START
↓
LLM
↓
Tool
↓
LLM
↓
END
El sistema de puntos de control permitió que el bucle recordara:
START
↓
Agent
↓
LLM ↔ Tools
↓
Memory / Checkpoint
↓
END
La transmisión en tiempo real y los eventos expusieron el proceso a la interfaz de usuario y a las herramientas de monitoreo:
Graph
↓
Events
↓
UI / Logs / Observability
Ninguno de estos pasos requirió reescribir los anteriores; cada uno representa un nodo, arista, opción de compilación o llamada a método nueva en el mismo grafo.
Paso 9: mover las herramientas detrás de un servidor MCP
Hasta ahora, las herramientas estaban dentro de la aplicación. El Model Context Protocol (MCP) adopta un enfoque diferente: un servidor separado expone las herramientas (y recursos), y cualquier cliente MCP puede descubrirlas y utilizarlas. La aplicación ya no necesita contener la implementación de cada herramienta:
┌──────────────┐
│ MCP Client │
└──────┬───────┘
│
│
▼
┌──────────────┐
│ MCP Server │
└──────┬───────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
add() multiply() weather()
Un servidor mínimo con tres herramientas
Basta con un pequeño servidor de aprendizaje para comprender la arquitectura. Este expone una herramienta de suma:
add(a, b)
una herramienta de multiplicación:
multiply(a, b)
y una herramienta meteorológica que acepta el nombre de una ciudad:
weather(city)
La herramienta meteorológica no llama a un servicio meteorológico real. Devuelve una cadena fija, lo que hace que la atención se centre en el protocolo en lugar de en las claves API:
"It's sunny in Mumbai."
Lo importante es el viaje de ida y vuelta, no el pronóstico:
Client
↓
MCP Server
↓
Tool
↓
Result
↓
Client
Las herramientas ya no tienen que estar dentro de la aplicación que las utiliza
La idea general es que las herramientas pueden alojarse de forma independiente de las aplicaciones que las emplean. La aplicación se comunica con un cliente MCP, el cliente utiliza el protocolo para hablar con el servidor, y el servidor se encarga de las implementaciones:
Application
│
▼
MCP Client
│
MCP Protocol
│
▼
MCP Server
/ | \
/ | \
↓ ↓ ↓
Add Multiply Weather
Eso hace posible la existencia de servidores de herramientas reutilizables. Un equipo podría ejecutar un servidor que ofrezca toda una categoría de funcionalidades:
Database Tools
File Tools
Git Tools
Search Tools
Weather Tools
Internal APIs
Y varios agentes diferentes, clientes de chat o integraciones con IDE podrían utilizar el mismo servidor sin duplicar código.
Paso 10: combinar LangGraph y MCP
Las dos tecnologías resuelven diferentes aspectos del problema. LangGraph gestiona el flujo de trabajo: estado, enrutamiento, bucles, memoria y transmisión en tiempo real. El estándar MCP estandariza la forma en que se exponen las herramientas y recursos. En un diseño combinado, el nodo de herramientas del agente se comunica con un cliente MCP en lugar de llamar a funciones locales:
User
│
▼
LangGraph Agent
│
▼
LLM
│
Need a tool?
│
▼
MCP Client
│
▼
MCP Server
/ | \
↓ ↓ ↓
Math Search Weather
El agente no necesita saber cómo se implementa una herramienta. Solo necesita conocer qué capacidades existen y cómo llamarlas, ya que el servidor MCP se encarga del resto. En Python, el paquete langchain-mcp-adapters es una forma común de cargar herramientas MCP como herramientas LangChain para que puedan vincularse a un modelo y utilizarse en un ToolNode. El artículo existente sobre estandarizar la integración de herramientas en LangGraph con MCP aborda dicha integración con más profundidad.
Un modelo mental completo
Al unir todo, un agente LangGraph se ve así: la entrada del usuario se convierte en estado, los nodos operan sobre él, el modelo decide si se necesita una herramienta, los resultados de la herramienta se devuelven al modelo, y la ejecución finaliza con una respuesta:
USER
│
▼
┌────────────┐
│ LangGraph │
│ Agent │
└──────┬─────┘
│
▼
STATE
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
NODE NODE NODE
│ │ │
└─────────────┼─────────────┘
│
▼
LLM
│
Need a tool?
/ \
Yes No
│ │
▼ ▼
TOOL RESPONSE
│
▼
RESULT
│
└──────► LLM
│
▼
END
Alrededor de ese bucle principal se encuentran las funcionalidades que se activan según sea necesario:
┌──────────────────────────────┐
│ Capabilities │
│ │
│ Memory / Checkpointing │
│ Streaming │
│ Event Tracking │
│ MCP │
│ Tools │
└──────────────────────────────┘
Dónde termina LangChain y comienza LangGraph
Dado que LangGraph generalmente se aprende después de LangChain, es útil separar sus funciones. LangChain proporciona los componentes básicos para las aplicaciones basadas en LLM: envoltorios de modelos, mensajes, herramientas, prompts y salida estructurada. LangGraph organiza esos componentes en flujos de trabajo explícitos y con estado. Son capas complementarias, no opciones competitivas, y la mayor parte del código de LangGraph utiliza componentes de LangChain dentro de sus nodos.
Lecciones aplicables a grafos más grandes
Varios principios de esta pequeña implementación se aplican sin cambios a sistemas mucho más grandes.
El estado es la columna vertebral
En lugar de pasar datos a través de argumentos de función, cada nodo lee y escribe en un estado compartido, y los reducers definen cómo se fusionan las actualizaciones:
State
↓
Node
↓
Updated State
↓
Next Node
Los nodos son unidades de trabajo
Cada nodo debe realizar una tarea reconocible, lo que facilita descomponer y probar flujos de trabajo complejos:
chatbot node
search node
tool node
validation node
human approval node
Los bordes codifican el flujo de trabajo
Un borde responde a una pregunta: una vez que este nodo termina, ¿a dónde continúa la ejecución? Los bordes simples generan secuencias; los bordes condicionales crean ramas y bucles.
Los diagramas son mejores que leer código
En un grafo con muchos nodos y rutas condicionales, un diagrama renderizado transmite la arquitectura más rápido que leer el código de construcción:
Node → Decision → Tool → Agent → END
Los puntos de control son más que un historial de chat
Un checkpointer conserva el estado completo del grafo, no solo los mensajes. Eso es lo que permite las conversaciones de varias turnos, y también constituye la base para agentes que operan durante mucho tiempo, pausas con intervención humana y reanudación tras fallos.
La transmisión en tiempo real beneficia tanto a usuarios como a desarrolladores
La transmisión en tiempo real mejora la percepción de rapidez de respuesta, y la misma salida ayuda a depurar y observar los procesos.
El funcionamiento de un agente implica muchas operaciones
Con astream_events queda claro que una llamada a un agente es una secuencia anidada de operaciones en el grafo, nodos, modelos y herramientas:
Graph
↓
Node
↓
LLM
↓
Tool
↓
LLM
↓
Node
↓
Graph
Cuanto más compleja sea la aplicación, mayor es la necesidad de esa visibilidad.
MCP separa las herramientas de las aplicaciones
Las herramientas expuestas a través de MCP pueden compartirse entre aplicaciones y evolucionar independientemente de los agentes que las utilizan.
Siguiente paso
Esta base abre varias direcciones que vale la pena explorar: aristas condicionales y ramas paralelas, interrupciones con participación humana, memoria a largo plazo y puntos de control persistentes, generación mejorada mediante recuperación dentro de un grafo, sistemas multiagente, integraciones más profundas con MCP, observabilidad y evaluación de agentes, despliegue en producción y tolerancia a fallos con intentos repetidos.
El hilo conductor es que una aplicación de IA no es solo un modelo que genera texto. Es un sistema diseñado en torno al modelo, uno que puede recibir una solicitud, decidir, actuar, observar el resultado, recordarlo y continuar:
Understand
↓
Decide
↓
Act
↓
Observe
↓
Remember
↓
Continue
Un grafo es una forma natural de expresar ese bucle.
Conclusión
Todo el proceso comenzó con el grafo más pequeño posible:
START → Chatbot → END
y evolucionó, capa por capa, hasta convertirse en un sistema con herramientas, un bucle de agentes, memoria, transmisión en tiempo real y MCP:
┌─────────────┐
│ LLM │
└──────┬──────┘
│
▼
┌─────────────┐
│ Tools │
└──────┬──────┘
│
▼
┌─────────────┐
│ Agent │
└──────┬──────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
Memory Streaming MCP
│ │ │
└──────────┼──────────┘
↓
AI Application
Considerados juntos, estos componentes dejan de parecer una lista y comienzan a verse como una única arquitectura:
- State almacena datos y, a través de puntos de control, también la memoria; el reductor
add_messagesse encarga de las listas de mensajes. - Nodes realizan el trabajo y devuelven actualizaciones parciales.
- Edges definen el flujo, y los edges condicionales lo convierten en un bucle.
- El modelo proporciona razonamiento y lenguaje; las herramientas le permiten actuar sobre el mundo exterior.
- Streaming y eventos hacen que cada ejecución sea observable, desde actualizaciones de la interfaz hasta registros de trazas.
- MCP estandariza la forma en que se exponen las herramientas, de modo que puedan existir fuera del agente que las utiliza.
Lecturas relacionadas
- De texto bruto a tuberías: analizadores, LCEL, ejecutables y memoria en LangChain — Cómo LangChain convierte el texto bruto del modelo en datos estructurados, compone pasos con LCEL e la interfaz Runnable y gestiona la memoria en las conversaciones actualmente.
- Construyendo un agente de investigación ReAct en LangGraph: cerebro, manos y enrutador — Aprenda cómo implementar el bucle de razonamiento-acción-observación ReAct como un sub-gráfico de LangGraph, con reflexión forzada, presupuestos de iteración e investigación paralela de tipo scatter-gather.