1. Introducción al curso intensivo de LangGraph y aplicaciones de IA con agentes [1]

Este curso intensivo está dividido en tres partes, cubriendo desde los fundamentos hasta la creación de proyectos completos de IA con agentes utilizando LangGraph.

1.1. Descripción general del curso intensivo [1]

  1. El curso intensivo se divide en tres partes, cada una con una duración aproximada de 2 a 3 horas de video. [2]
  2. La Parte 1 se centra en técnicas fundamentales para construir aplicaciones de IA con agentes. [6]
    1. Los temas incluyen la creación de chatbots, la integración de herramientas (individuales y múltiples), la adición de memoria, la incorporación de retroalimentación humana en el bucle, el uso de técnicas de streaming y la construcción de MCP (Protocolo de Computación Múltiple) desde cero. [7]
    2. También se cubrirán conceptos como estados, grafos, nodos y aristas con la API de Grafos. [9]
  3. La Parte 2 aborda conceptos avanzados de LangGraph, centrándose en flujos de trabajo y agentes. [13]
    1. Se desarrollarán aplicaciones donde los agentes se comunican entre sí para resolver flujos de trabajo complejos. [15]
    2. Otros temas incluyen la gestión de múltiples estados en multiagentes, el uso de la API funcional y la depuración y monitorización en LangGraph Studio. [17]
  4. La Parte 3 se dedica a la construcción de proyectos completos, incluyendo la pipeline de LLM, técnicas de despliegue y evaluación de modelos de lenguaje grandes con herramientas como MLflow y AWS. [24]
    1. El despliegue se realizará utilizando Hugging Face Spaces. [28]

1.2. Preparación del entorno de desarrollo [35]

  1. Para comenzar, se utiliza un espacio de trabajo de proyecto vacío y se abre la línea de comandos. [36]
  2. Se recomienda crear un entorno virtual para el proyecto. [44]
    1. Se utiliza el gestor de paquetes uv, que es rápido y está escrito en Rust. [45]
    2. uv es de 10 a 100 veces más rápido que pip y reemplaza a varias herramientas de gestión de paquetes de Python. [53]
    3. La instalación de uv se realiza mediante comandos específicos para macOS/Linux, Windows (PowerShell) o Raspberry Pi. [57]
  3. Después de la instalación de uv, se inicializa el repositorio del proyecto con uv init. [61]
    1. Esto crea archivos como .gitignore, python-version, main.py y pyproject.toml. [71]
    2. pyproject.toml contiene información del proyecto y las dependencias. [74]

1.3. Configuración del entorno virtual y dependencias [61]

  1. Después de inicializar el proyecto, se crea un entorno virtual con uv venv. [86]
    1. El entorno virtual se activa con un comando específico. [93]
  2. Se crea un archivo requirements.txt para listar las bibliotecas necesarias. [79]
    1. Las bibliotecas incluyen LangGraph, LangChain y Langsmith (para seguimiento y evaluación en la nube de LangGraph). [80]
  3. Las bibliotecas se instalan utilizando uv pip install -r requirements.txt. [101]
  4. Para el desarrollo inicial, se crea una carpeta llamada "one" y un archivo basic_chatbot.ipynb. [109]
    1. Se requiere la biblioteca ipykernel para ejecutar Jupyter Notebooks, la cual se instala si es necesario. [120]

2. Construcción de un chatbot básico con LangGraph [134]

Este apartado explica cómo construir un chatbot básico utilizando la API de Grafos de LangGraph, detallando sus componentes principales: aristas, nodos y estado.

2.1. Componentes fundamentales de LangGraph [134]

  1. Para construir un chatbot básico, se utiliza la API de Grafos de LangGraph, considerada la forma más eficiente de aprender la biblioteca. [137]
  2. Los tres componentes principales de LangGraph son: aristas, nodos y estado. [147]

2.2. Ejemplo de flujo de trabajo: creación de un blog a partir de un video de YouTube [149]

  1. Se presenta un caso de uso para ilustrar los componentes de LangGraph: crear un blog a partir de un video de YouTube. [149]
  2. El flujo de trabajo para este caso de uso consta de tres pasos: [157]
    1. Extraer la transcripción del video de YouTube. [158]
    2. Crear el título del blog utilizando la transcripción. [161]
    3. Generar el contenido del blog a partir del título y la transcripción. [162]
  3. Los LLM (modelos de lenguaje grandes) son muy efectivos para la generación de contenido y pueden usarse para resolver este flujo de trabajo. [168]

2.3. Estructura del grafo para el flujo de trabajo [174]

  1. Para resolver el flujo de trabajo, se crea una estructura de grafo que define el flujo de ejecución. [173]
  2. El grafo incluye los siguientes elementos:
    1. Nodo de inicio: donde se proporciona la entrada, en este caso, la URL de YouTube. [189]
    2. Nodo "Generador de transcripciones": toma la URL de YouTube como entrada y genera la transcripción. [200]
      1. Una arista conecta el nodo de inicio con el nodo "Generador de transcripciones". [208]
    3. Nodo "Generador de títulos": toma la transcripción como entrada y genera el título del blog. [216]
      1. Una arista conecta el nodo "Generador de transcripciones" con el nodo "Generador de títulos". [218]
      2. Este nodo utiliza un LLM y un prompt para generar el título. [230]
    4. Nodo "Generador de contenido": toma el título y la transcripción como entrada y genera el contenido del blog. [224]
      1. Una arista conecta el nodo "Generador de títulos" con el nodo "Generador de contenido". [227]
      2. Este nodo también utiliza un LLM y un prompt para generar el contenido. [232]
    5. Nodo final: donde se obtiene la salida combinada (transcripción, título y contenido). [228]
  3. Las aristas representan el flujo de información de un nodo a otro. [208]
  4. Cada nodo tiene una implementación que define su funcionalidad. [212]

2.4. El concepto de estado en LangGraph [238]

  1. El estado en LangGraph permite definir variables que pueden ser accedidas por cualquier nodo en el grafo. [238]
  2. En el ejemplo del blog, el estado podría incluir variables como: [242]
    1. transcripción: para guardar la transcripción generada. [242]
    2. título: para guardar el título generado. [247]
    3. contenido: para guardar el contenido generado. [249]
  3. La ventaja de guardar valores en el estado es que cualquier nodo del grafo puede acceder a estas variables. [251]
  4. Un grafo que mantiene el contexto del estado en cada nodo se denomina grafo de estado. [253]
  5. El estado no debe confundirse con la memoria externa, que se discutirá más adelante. [255]

3. Implementación de un chatbot básico con LangGraph [260]

Esta sección detalla los pasos para construir un chatbot básico utilizando la API de Grafos de LangGraph, incluyendo la configuración del entorno, la definición del estado, la creación de nodos y aristas, y la ejecución del grafo.

3.1. Preparación e importación de bibliotecas [260]

  1. Se importan bibliotecas esenciales para construir el chatbot: [277]
    1. Annotated de typing para añadir metadatos contextuales a los tipos. [278]
    2. Type de typing_extensions. [282]
    3. StateGraph, START y END de langgraph.graph para definir el grafo de estado. [284]
    4. add_messages de langgraph.graph.message para gestionar los mensajes. [290]
  2. add_messages es un reductor que se utiliza para añadir mensajes a una lista sin reemplazarlos, manteniendo el historial de la conversación. [293]

3.2. Definición del estado del chatbot [297]

  1. Se define una clase State que hereda de TypedDict (o Type[dict]) para representar el estado del grafo. [320]
    1. El estado es un diccionario que contendrá las variables accesibles por los nodos. [321]
  2. Dentro de la clase State, se crea una variable messages de tipo list. [323]
    1. Esta variable se anota con Annotated y la función add_messages. [333]
    2. add_messages asegura que los nuevos mensajes se añadan a la lista messages en lugar de sobrescribir los anteriores, actuando como un reductor. [339]
  3. Se inicializa un graph_builder como una instancia de StateGraph, pasándole la clase State. [349]

3.3. Configuración del modelo de lenguaje (LLM) [360]

  1. Se instalan las bibliotecas necesarias para interactuar con modelos de lenguaje, como langchain-grok. [360]
  2. Se cargan las variables de entorno desde un archivo .env para las claves API (por ejemplo, GROQ_API_KEY). [367]
  3. Se inicializa el LLM utilizando ChatGROQ o init_chat_model de LangChain. [389]
    1. Se puede especificar el nombre del modelo, como "Llama 3 8B 8192". [401]

3.4. Creación del nodo del chatbot y el grafo [414]

  1. Se define la función chatbot que actuará como la implementación del nodo del chatbot. [419]
    1. Esta función toma el state actual como entrada. [421]
    2. Invoca el LLM con los mensajes del estado (llm.invoke(state["messages"])) y devuelve el resultado en la clave messages del estado. [426]
    3. Esto asegura que la respuesta del LLM se añada al historial de mensajes. [432]
  2. Se construye el grafo utilizando el graph_builder: [438]
    1. Se añade un nodo llamado "LLMChatbot" con la función chatbot como su definición. [449]
    2. Se añade una arista desde el START del grafo al nodo "LLMChatbot". [457]
    3. Se añade una arista desde el nodo "LLMChatbot" al END del grafo. [460]
  3. Finalmente, se compila el grafo con graph_builder.compile(). [466]

3.5. Visualización y ejecución del chatbot [473]

  1. Se puede visualizar el grafo utilizando graph.get_graph().draw_mermaid_png(). [473]
    1. El grafo muestra un flujo simple: START -> LLMChatbot -> END. [486]
  2. Para ejecutar el chatbot, se utiliza el método graph.invoke(). [489]
    1. La entrada se proporciona como un diccionario con la clave messages (por ejemplo, {"messages": "Hi"}). [499]
    2. Si la clave API no se carga correctamente, puede ser necesario reiniciar el kernel. [506]
    3. La respuesta del chatbot se guarda en la variable messages del estado, que contiene tanto el mensaje humano como la respuesta de la IA. [516]
    4. El método add_messages convierte automáticamente el mensaje de entrada en un "human message" y la respuesta del LLM en un "AI message", añadiéndolos a la lista. [532]
    5. Se puede acceder al último mensaje de la conversación utilizando response["messages"][-1].content. [536]
  3. Técnicas de streaming:
    1. Se puede utilizar graph.stream() para obtener la salida del chatbot en tiempo real. [542]
    2. Al iterar sobre los eventos del stream, se puede imprimir cada evento o acceder a los valores específicos. [552]
    3. El streaming permite ver cómo se generan los mensajes de la IA a medida que el grafo se ejecuta. [561]

4. Integración de herramientas externas en el chatbot [572]

Esta sección explora cómo integrar herramientas externas en un chatbot de LangGraph para manejar preguntas que requieren información en tiempo real o funcionalidades específicas, introduciendo el concepto de agentes React.

4.1. Necesidad de herramientas externas [572]

  1. Un chatbot básico con un LLM no puede responder preguntas que requieren datos en vivo o información más allá de su entrenamiento. [577]
    1. Por ejemplo, preguntar "Proporcióname las últimas noticias de IA" no puede ser respondido por un LLM sin herramientas externas. [577]
  2. Aquí es donde entran en juego las herramientas externas. [582]

4.2. Flujo de trabajo con herramientas externas [585]

  1. Cuando el chatbot recibe una pregunta que no puede responder, debe realizar una llamada a una herramienta. [584]
  2. Se introduce un nodo de herramienta en el grafo. [592]
    1. Este nodo puede contener APIs de terceros (como un motor de búsqueda de Google) o herramientas específicas como Tavily, un motor de búsqueda web que proporciona una API de búsqueda. [586]
    2. El flujo sería: START -> Chatbot -> Llamada a herramienta -> Nodo de herramienta -> END. [590]
  3. El LLM dentro del chatbot es el "cerebro" que decide cuándo y qué herramienta llamar. [605]
    1. Las herramientas se pueden vincular al LLM. [607]
    2. Cada herramienta debe tener un docstring que describa su función, entradas y argumentos. [612]
    3. Si la entrada del chatbot coincide con los requisitos de una herramienta, el LLM activará una llamada a esa herramienta. [615]
  4. Se pueden combinar múltiples herramientas en un solo nodo de herramienta. [600]
  5. También se introduce una condición de herramienta para gestionar el flujo de ejecución. [618]

4.3. Implementación de un chatbot con herramientas [624]

  1. Se instala la biblioteca langchain-tavily para usar la API de búsqueda de Tavily. [656]
  2. Se obtiene una clave API de Tavily desde console.tavily.com. [663]
  3. Se inicializa el LLM y se carga la clave API de Tavily en el archivo .env. [673]
  4. Se crea una instancia de TavilySearch y se añade a una lista de herramientas. [683]
    1. Se puede probar la herramienta invocándola con una pregunta como "What is LangGraph?". [695]
  5. Se crea una función personalizada (por ejemplo, multiply) para demostrar la integración de herramientas personalizadas. [703]
    1. La función incluye un docstring que describe su propósito y parámetros (a: int, b: int). [707]
    2. Este docstring es crucial para que el LLM entienda la herramienta. [709]
    3. Se añade la función multiply a la lista de herramientas. [712]
  6. Se vinculan las herramientas al LLM utilizando llm.bind_tools(tools). [715]
    1. Esto crea un LLM_with_tools que tiene conocimiento de todas las herramientas disponibles (Tavily Search, Multiply). [718]
  7. Se define la clase State y se inicializa el StateGraph como antes. [749]
  8. Se importan ToolNode y tools_condition de langgraph.prebuilt. [725]
    1. ToolNode convierte una lista de herramientas en un nodo de herramienta. [739]
    2. tools_condition es una función que decide el siguiente paso en el grafo. [740]
  9. Se definen los nodos del grafo: [754]
    1. "Tool Calling LLM": un nodo que utiliza el LLM_with_tools para invocar mensajes. [757]
    2. "Tools": un nodo de tipo ToolNode que encapsula todas las herramientas definidas. [761]
  10. Se añaden las aristas al grafo: [777]
    1. Una arista desde START a "Tool Calling LLM". [782]
    2. Aristas condicionales desde "Tool Calling LLM" utilizando builder.add_conditional_edges(). [787]
      1. La función tools_condition se utiliza para decidir el siguiente nodo. [795]
      2. Si el último mensaje del asistente es una llamada a una herramienta, se dirige al nodo "Tools". [797]
      3. Si no es una llamada a una herramienta, se dirige al END. [800]
    3. Una arista desde el nodo "Tools" al END. [816]
  11. Se compila el grafo y se visualiza. [821]

4.4. Ejecución del chatbot con herramientas [828]

  1. Se invoca el grafo con una pregunta que requiere una herramienta, como "What is the recent AI news?". [843]
    1. El LLM realiza una llamada a la herramienta TavilySearch y devuelve los resultados. [848]
    2. La respuesta incluye el mensaje humano, la llamada a la herramienta y el mensaje de la herramienta con los resultados. [848]
  2. Se invoca el grafo con una pregunta que requiere la herramienta multiply, como "What is 2 * 3?". [876]
    1. El LLM realiza una llamada a la herramienta multiply y devuelve el resultado (por ejemplo, 10 para 5 * 2). [880]
    2. Es importante que la función multiply tenga una sentencia return para que la operación se realice correctamente. [883]
  3. Limitación del flujo actual: Si se hace una pregunta compuesta como "Give me the recent AI news and then multiply 5 by 10", el grafo actual solo procesará la primera parte (noticias de IA) y luego terminará, sin procesar la segunda parte (multiplicación). [910]
    1. Esto se debe a que, después de que el nodo de herramienta devuelve una respuesta, el flujo va directamente al END en lugar de volver al LLM para procesar el resto de la consulta. [918]

4.5. Introducción al agente React [925]

  1. Para resolver la limitación de las consultas compuestas, se utiliza la arquitectura de agente React. [925]
  2. En un agente React, el flujo de ejecución se modifica: después de que el nodo de herramienta devuelve una respuesta, esta se envía de vuelta al LLM (el "cerebro"). [948]
    1. El LLM mantiene el contexto de la consulta original y puede decidir si necesita realizar otra llamada a una herramienta o si puede finalizar. [950]
    2. Si hay más partes en la consulta (como "multiply 5 by 5"), el LLM realiza otra llamada a la herramienta, obtiene la respuesta y la envía de vuelta al LLM. [951]
    3. Una vez que no hay más acciones, el LLM resume y proporciona la salida final. [955]
  3. Los tres términos clave en React son: Actuar, Observar y Razonar. [958]
    1. Actuar: el LLM realiza una llamada a una herramienta cuando recibe una entrada. [958]
    2. Observar: el LLM evalúa la salida de la herramienta y decide si necesita otra acción. [959]
    3. Razonar: el LLM toma decisiones sobre el siguiente paso, ya sea otra llamada a una herramienta o finalizar. [961]
  4. Implementación del agente React:
    1. Se modifica el grafo para que, después del nodo "Tools", la arista regrese al nodo "Tool Calling LLM" en lugar de ir al END. [973]
    2. El diagrama del grafo ahora muestra un bucle: START -> Tool Calling LLM -> Tools -> Tool Calling LLM -> END. [976]
    3. Al ejecutar la consulta compuesta "Give me the recent AI news and then multiply 5 by 10", el agente React procesa ambas partes de la consulta secuencialmente, utilizando las herramientas necesarias y volviendo al LLM para cada paso. [980]
  5. El agente React permite al LLM ser el factor decisivo para elegir qué herramienta usar y cuándo. [990]

5. Adición de memoria y técnicas de streaming en LangGraph [992]

Esta sección aborda cómo añadir memoria a un grafo de agentes para mantener el contexto de las conversaciones y explora diferentes técnicas de streaming para obtener respuestas del chatbot.

5.1. Adición de memoria al grafo de agentes [992]

  1. La memoria en LangGraph resuelve el problema del checkpointing persistente, permitiendo que el chatbot recuerde interacciones anteriores. [993]
    1. Sin memoria, el chatbot no recordará información previamente proporcionada, como el nombre del usuario. [997]
  2. Para añadir memoria, se utiliza MemorySaver de langgraph.checkpointer.memory. [1015]
    1. MemorySaver es un guardador de checkpoints en memoria que almacena la información de cada nodo ejecutado. [1021]
  3. La memoria se añade como un parámetro checkpointer durante la compilación del grafo (graph_builder.compile(checkpointer=memory)). [1029]
  4. Para utilizar la memoria, se necesita un ID de hilo (thread_id) único para cada sesión de conversación. [1034]
    1. Este ID se pasa en la configuración (config) al invocar el grafo. [1040]
  5. Al invocar el grafo con un mensaje y el config que incluye el thread_id, el chatbot puede recordar el contexto de la conversación. [1050]
    1. Por ejemplo, si se le dice "Hi, my name is Krishna" y luego se pregunta "What is my name?", el chatbot responderá "Yes, your name is Kush". [1054]

5.2. Técnicas de streaming en LangGraph [1095]

  1. LangGraph ofrece diferentes técnicas de streaming para obtener respuestas del chatbot, además del método invoke(). [1096]
  2. Se crea un grafo simple con un nodo "SuperBot" para demostrar el streaming. [1100]
  3. Los métodos stream() y astream() (para operaciones asíncronas) tienen dos parámetros principales: value y updates. [1123]
    1. Modo updates: solo muestra el mensaje que se está actualizando actualmente en cada paso de la ejecución del nodo. [1148]
      1. Por ejemplo, si un nodo genera "Hi", solo se mostrará "Hi". Si el siguiente nodo genera "My name is", solo se mostrará "My name is". [1148]
    2. Modo values: muestra todos los mensajes acumulados hasta el momento en cada paso de la ejecución del nodo. [1153]
      1. Por ejemplo, si el primer nodo genera "Hi", se mostrará "Hi". Si el segundo nodo genera "My name is", se mostrará ["Hi", "My name is"]. [1153]
    3. Al usar mode=values con memoria, se puede ver cómo se añade la conversación anterior a cada nueva interacción. [1204]
  4. El método stream_events() proporciona información más detallada sobre los diferentes eventos que ocurren durante la ejecución del grafo, útil para la depuración. [1225]

6. Human in the Loop (Retroalimentación Humana) en LangGraph [1233]

Esta sección explica cómo integrar la retroalimentación humana en un grafo de agentes, permitiendo que un humano intervenga y proporcione información crucial para el flujo de ejecución.

6.1. Concepto de Human in the Loop [1233]

  1. Human in the Loop (también conocido como retroalimentación humana) permite que un humano intervenga en un flujo de trabajo de IA. [1233]
  2. En un grafo de agentes, si un chatbot (vinculado con herramientas) recibe una entrada que requiere asistencia humana, puede activar una llamada a una herramienta de asistencia humana. [1240]
    1. En lugar de llamar a una API externa, se interrumpe el flujo y se espera la retroalimentación del humano. [1244]
    2. Una vez que el humano proporciona la retroalimentación, el chatbot reanuda la ejecución. [1246]
  3. Esto es útil en flujos de trabajo complejos donde se requiere la aprobación humana antes de completar una tarea. [1248]

6.2. Implementación de Human in the Loop [1254]

  1. Se utilizan las bibliotecas Command e Interrupt de LangGraph para gestionar las interrupciones y reanudaciones del flujo. [1260]
  2. Se define una herramienta personalizada llamada HumanAssistance. [1268]
    1. Esta herramienta tiene un docstring que indica "Request assistance from a human". [1272]
    2. La función HumanAssistance utiliza Interrupt.query() para detener el flujo y esperar una entrada humana. [1273]
    3. La respuesta humana se devuelve como human_response_data. [1275]
  3. Se combinan las herramientas (por ejemplo, TavilySearch y HumanAssistance) y se vinculan al LLM. [1277]
  4. Se construye el grafo con un nodo de chatbot y un nodo de herramientas, similar al agente React. [1281]
    1. El grafo incluye START -> Chatbot -> Tools -> Chatbot -> END. [1285]
    2. La interrupción (Interrupt) ocurre en el nodo de herramientas si se activa la herramienta HumanAssistance. [1286]
  5. Ejecución con interrupción:
    1. Se invoca el grafo con una entrada que activa la herramienta HumanAssistance, como "I need some expert guidance for building AI agents. Could you please provide assistance to me?". [1288]
    2. El LLM detecta la necesidad de asistencia y realiza una llamada a la herramienta HumanAssistance. [1306]
    3. El flujo se interrumpe, esperando la entrada humana. [1308]
    4. Se proporciona una respuesta humana (por ejemplo, "We, the experts, are here to help you out. We recommend you check out LangGraph to build your agent."). [1310]
    5. Se utiliza graph.stream(human_command, config=thread_id) para reanudar el flujo con la respuesta humana. [1315]
    6. El chatbot procesa la respuesta humana y continúa la conversación. [1319]
  6. Este mecanismo permite integrar la supervisión y la toma de decisiones humanas en cualquier punto del flujo de trabajo del agente. [1328]

7. Construcción e integración de servidores MCP (Protocolo de Computación Múltiple) [1333]

Esta sección explica cómo construir servidores MCP desde cero, integrarlos con un cliente MCP y utilizar diferentes protocolos de transporte para la comunicación.

7.1. Conceptos básicos de MCP [1333]

  1. Los servidores MCP permiten tener múltiples herramientas (cálculos matemáticos, APIs de terceros) que pueden ser accedidas por un cliente MCP. [1339]
  2. El servidor MCP proporciona herramientas contextuales y prompts al cliente. [1343]
  3. El cliente MCP mantiene una conexión uno a uno con el servidor dentro de la aplicación host. [1344]
  4. La aplicación (cloud, escritorio, etc.) interactúa con el cliente MCP. [1345]
  5. Flujo de comunicación:
    1. El usuario proporciona una entrada a la aplicación (chatbot). [1354]
    2. El LLM de la aplicación decide si necesita una llamada a un servidor MCP. [1356]
    3. El servidor MCP tiene herramientas (por ejemplo, suma, multiplicación, API de clima). [1359]
    4. Si el LLM no puede responder (por ejemplo, "What is the weather in New York?"), realiza una llamada a la herramienta a través del protocolo MCP. [1364]
    5. El cliente MCP interno facilita la comunicación con el servidor. [1366]
    6. La herramienta específica se llama y se obtiene una respuesta. [1367]
  6. Protocolos de transporte: Se utilizan diferentes protocolos para la comunicación entre el cliente y el servidor MCP. [1377]
    1. HTTP/2 y HTTP son ejemplos de protocolos de transporte. [1382]

7.2. Configuración del entorno y dependencias para MCP [1376]

  1. Se utiliza la biblioteca Fast MCP (fastmcp) para construir servidores y clientes MCP de manera eficiente. [1443]
  2. Se crea un entorno virtual con uv venv y se activa. [1399]
  3. Se crea un archivo requirements.txt con las siguientes bibliotecas: [1428]
    1. langchain-grok
    2. langchain-mcp-adapters
    3. fastmcp
  4. Se instalan las bibliotecas con uv pip install -r requirements.txt. [1468]

7.3. Creación de servidores MCP [1478]

  1. Servidor de matemáticas (math_server.py):
    1. Se importa FastAPI de mcp.server.fastapi y se inicializa MCP con el nombre del servidor "math". [1484]
    2. Se definen herramientas como add y multiply utilizando el decorador @mcp.tool. [1492]
      1. Cada función de herramienta tiene un docstring que describe su propósito. [1497]
    3. El servidor se ejecuta con mcp.run(transport="stdio"). [1513]
      1. El transporte stdio utiliza la entrada y salida estándar (línea de comandos) para recibir y responder llamadas a herramientas. [1519]
      2. Esto es útil para pruebas locales, donde el cliente interactúa directamente con el servidor a través de la línea de comandos. [1523]
  2. Servidor de clima (weather_server.py):
    1. Se importa MCP de mcp.server y fastapi y se inicializa MCP con el nombre del servidor "weather". [1543]
    2. Se define una herramienta get_weather que toma una location (str) y devuelve un str. [1554]
      1. Se incluye un docstring para la herramienta. [1561]
      2. Por simplicidad, devuelve un valor constante ("It's always raining in California"), pero en un escenario real interactuaría con una API de clima. [1563]
    3. El servidor se ejecuta con mcp.run(transport="streamable_http"). [1572]
      1. El transporte streamable_http ejecuta el servidor como un servicio API en una URL (por ejemplo, http://127.0.0.1:8000). [1580]
      2. A diferencia de stdio, este servidor se ejecuta como un servicio web accesible a través de HTTP. [1582]
      3. La URL del servidor MCP se encuentra en /mcp (por ejemplo, http://127.0.0.1:8000/mcp). [1593]

7.4. Creación del cliente MCP (client.py) [1606]

  1. Se importa client de langchain_mcp.adapters para crear un cliente MCP multiserver. [1610]
  2. Se importa create_react_agent de langgraph.prebuilt para construir un agente que interactúe con los servidores MCP. [1618]
  3. Se importa ChatGROQ de langchain_grok para el LLM. [1622]
  4. Se cargan las variables de entorno (por ejemplo, GROQ_API_KEY). [1629]
  5. Se define una función asíncrona main para ejecutar el cliente. [1637]
  6. Se crea una instancia de MultiServerMcpClient y se configuran los servidores: [1640]
    1. Servidor de matemáticas: se especifica el comando (python), los argumentos (math_server.py) y el transporte (stdio). [1643]
    2. Servidor de clima: se especifica la URL (http://localhost:8000/mcp) y el transporte (streamable_http). [1668]
  7. Se obtienen las herramientas de los servidores MCP utilizando await client.get_tools(). [1683]
  8. Se inicializa el LLM (ChatGROQ) y se crea un agente React con el LLM y las herramientas. [1687]
  9. Ejecución de consultas:
    1. Se invoca al agente con una pregunta matemática (por ejemplo, "What is 3 + 5 * 12?"). [1698]
      1. El agente utiliza la herramienta add y multiply del servidor de matemáticas (math_server.py) a través del transporte stdio. [1711]
    2. Se invoca al agente con una pregunta sobre el clima (por ejemplo, "What is the weather in NYC or California?"). [1727]
      1. El agente utiliza la herramienta get_weather del servidor de clima (weather_server.py) a través del transporte streamable_http. [1751]
    3. La función main se ejecuta utilizando asyncio.run(main()). [1705]
  10. Este cliente puede comunicarse con múltiples servidores MCP que utilizan diferentes protocolos de transporte, demostrando la flexibilidad del protocolo MCP. [1764]