Cover Image for Gyrus Blog

Agente CRM con LangChain y LangGraph

Cover Image for Agente CRM con LangChain y LangGraph
Álex Hierro
Álex Hierro

1. Introducción

Una de las tecnologías más extendidas para el desarrollo de aplicaciones de inteligencia artificial generativa es el framework LangGraph. Ya hemos desarrollado agentes y chatbots, en su mayoría en la nube utilizando servicios de Azure y AWS. Hemos realizado pruebas de concepto internas y hemos trabajado con diferentes clientes. La experiencia nos ha demostrado que usando paradigmas y herramientas de desarrollo más orientados al cloud de estas soluciones, es habitual que nos podamos abstraer muy fácilmente de qué hacen los servicios internamente. Por ello, es importante profundizar en el funcionamiento interno de estas herramientas para poder aportar soluciones bien diseñadas y eficientes.

En este artículo vamos a repasar los pasos iniciales explorando el ecosistema de tecnologías LangChain y LangGraph, extrayendo conclusiones paso a paso. Para ello hemos desarrollado un ejemplo sencillo, con pocas piezas, pero que resuelve los primeros pasos dentro de este ecosistema. Hemos partido de una idea simple, un agente inteligente que pueda responder cuestiones relativas al CRM de una empresa. Este CRM consta de una base de datos en MySQL y un Blob Storage de Azure con documentos relativos a clientes y proyectos. La idea es compartir un análisis sobre LangChain y LangGraph creando un agente muy básico capaz de lanzar consultas en lenguaje natural contra la base de datos y de responder sobre la documentación existente.

1.1. Objetivo del artículo

Como hemos dicho, la idea principal es analizar LangChain y LangGraph desarrollando un agente básico. Este agente deberá ser capaz de lanzar consultas en lenguaje natural contra una base de datos y responder utilizando la documentación existente. Entre las cuestiones que queremos abordar se encuentran:

  • Monitorización: Evaluar la posibilidad de integrar herramientas de seguimiento y control, explorando soluciones como LangSmith.
  • Conocimiento de modelos, herramientas, flujo...: Comprender en profundidad cómo interactúan y se integran los modelos de lenguaje con las herramientas que ponen a disposición tanto LangChain como LangGraph.
  • Mejorar la definición y el testing de los prompts: Refinar la forma en que se estructuran y evalúan los prompts para obtener respuestas más precisas y coherentes.
  • Control del consumo: Analizar cómo se gestionan los recursos y asegurar que el uso de servicios en la nube sea óptimo. Con estos objetivos buscamos sentar las bases para futuras implementaciones, proporcionando una visión más clara de los retos y las oportunidades en proyectos de inteligencia artificial.

1.2. Breve descripción del Agente

En concreto se han construido dos agentes, uno exclusivamente con herramientas de LangChain y el otro haciendo uso de LangGraph y su potente diseño como grafo del agente. Ambos ejemplos se basan en un agente de tipo ReAct Agent (reasoning and acting agent) con dos herramientas o "tools".

Estas herramientas son el retriever de Azure AiSearch y el agente SQL. Se ha utilizado Azure AiSearch como base de datos vectorial en la que almacenar la indexación de los documentos existentes en el Blob Storage. Se ha tomado esta decisión por sencillez de integración.

1.2.2. Tecnologías principales utilizadas

  • Python
  • Poetry
  • LangChain
  • LangGraph
  • Azure AISearch
  • MySQL
  • Docker

2. Diseño y desarrollo

2.1. LangSmith

Para empezar, necesitamos tener el control de lo que estamos construyendo. Como se describió en la introducción, el objetivo principal es analizar los dos frameworks, pero también monitorizar el comportamiento del LLM en cada paso y poder auditar la calidad de los prompts, etc. Aquí es donde entra en juego LangSmith. Esta herramienta es parte del ecosistema de LangChain y se autodefine como la plataforma todo en uno para cada paso en el ciclo de vida de aplicaciones basadas en LLMs. Su integración es tan sencilla como darse de alta en la plataforma online, generar una API Key e introducirla como variable de entorno segura en nuestro proyecto.

Este primer paso nos permite monitorizar cada acción interna y el flujo de los agentes que vamos a desarrollar, ya sea con LangChain o LangGraph y es esencial para poder sacar conclusiones y mejoras. Además, durante el desarrollo nos daremos cuenta de que también es esencial a la hora de depurar errores.

2.2. Decidir el tipo de agente

Una vez tomado el control, empezamos a diseñar el primer agente con LangChain. La primera decisión a tomar es qué tipo de agente vamos a desarrollar. En la propia documentación de LangChain se encuentra la siguiente tabla que nos ayuda a decidir:

Agent Type Intended Model Type Supports Chat History Supports Multi-Input Tools Supports Parallel Function Calling Required Model Params When to Use API
Tool Calling Chat tools If you are using a tool-calling model Ref
OpenAI Tools Chat tools [Legacy] If you are using a recent OpenAI model (1106 onwards). Generic Tool Calling agent recommended instead. Ref
OpenAI Functions Chat functions [Legacy] If you are using an OpenAI model, or an open-source model that has been finetuned for function calling and exposes the same functions parameters as OpenAI. Generic Tool Calling agent recommended instead. Ref
XML LLM If you are using Anthropic models, or other models good at XML Ref
Structured Chat Chat If you need to support tools with multiple inputs Ref
JSON Chat Chat If you are using a model good at JSON Ref
ReAct LLM If you are using a simple model Ref
Self Ask With Search LLM If you are using a simple model and only have one search tool Ref

Esta documentación ya está en proceso de deprecación y se desaconseja su uso en favor de LangGraph. Esto es importante tenerlo en cuenta de cara a saber qué librerías y funciones en concreto utilizar. Es un ecosistema de herramientas que por su naturaleza es muy cambiante y está en pleno proceso de refactorización. Además, no es nuestro objetivo en este caso crear la mejor solución ni la más moderna, si no desarrollar ejemplos lo más sencillos posibles que nos permitan ampliar los conocimientos durante el proceso. Por ello, vamos a empezar por crear un agente simple de tipo ReAct (Reasoning and Actioning) al que le añadiremos las llamadas a las tools que necesitemos. Las tools en LangChain es la abstracción que permite asociar una función de Python con un esquema con su nombre, descripción y argumentos esperados. Esto permite al LLM devolver respuestas que incluyan llamadas a estas funciones y que el Framework sea capaz de ejecutarlas como parte del flujo del agente.

2.3. Primer agente con LangChain

Investigando como desarrollar el agente hemos dado con una función de LangChain que ya hace todo el trabajo por nosotros y es capaz de integrar un LLM con todas las herramientas internas necesarias para realizar consultas contra diversos dialectos de bases de datos. Esta función es create_sql_agent de la librería langchain_community.agent_toolkits.sql.base. Aunque esta función no está en proceso de deprecación como tal aún, su uso está desaconsejado desde hace algún tiempo (se aconseja crear agentes desde cero con LangGraph y una función más genérica que permite customizar el flujo interno del agente).

Tras hacer diversas preguntas a nuestro primer agente, hemos observado que la fiabilidad de esta solución puede verse comprometida en ciertos casos, por ejemplo:

  1. Con estructuras de bases de datos complejas: Cuando la base de datos cuenta con múltiples relaciones y subestructuras, la generación de consultas precisas se vuelve más complicado.
  2. Nombres de tablas o columnas ambiguos: La ambigüedad en la nomenclatura puede llevar a confusiones al modelo, haciendo difícil que el sistema identifique la tabla correcta para cada consulta o que se realicen mal cruces sencillos si la base de datos no está bien normalizada.
  3. Preguntas que requieren más de una consulta sencilla: Algunas preguntas necesitan una serie de iteraciones o consultas anidadas para obtener la respuesta adecuada, lo que supera la capacidad de una única consulta directa y suele fallar con esta aproximación básica de agente LLM.
  4. El modelo no siempre acierta eligiendo la tool adecuada: Para algunas preguntas del usuario, el modelo no sabe interpretar adecuadamente que tool debe utilizar.

Para tratar de mitigar estos problemas, es fundamental enriquecer el prompt con contexto adicional. Realizando algunas pruebas rápidas, la mejora es significativa siguiendo algunos consejos:

  1. Describir detalladamente la base de datos: Proporcionar una explicación clara y concisa de la estructura de la base de datos, los esquemas y la organización de las tablas y sus relaciones adaptada específicamente al caso de uso ayudará a contextualizar la consulta y a reducir posibles ambigüedades por parte del modelo.
  2. Incorporación de ejemplos prácticos: Incluir ejemplos concretos de preguntas junto con sus consultas SQL correspondientes es esencial. Estos ejemplos actúan como guía y deshacen posibles ambiguedades, mostrando cómo se deben de formular las consultas para obtener resultados más precisos.
  3. Validación y refinamiento iterativo de las consultas: implementar un mecanismo que verifique en tiempo real la sintaxis y eficacia de las consultas generadas. Un enfoque iterativo permite ajustar y perfeccionar la consulta en función del feedback obtenido. Con respecto a la herramienta RAG, su precisión en las respuestas radica principalmente en la configuración del índice en Azure AISearch. Hay diferentes estrategias y parámetros que se pueden afinar durante el indexado de la información y deben estar alineados con la naturaleza de los documentos y su contenido. Ésto se escapa del propósito de este artículo, por lo que no entraremos en profundizar esta parte.

Gracias a este primer ejemplo, conseguimos profundizar en LangChain, su ecosistema y el catálogo de herramientas disponible. Aún así, hemos demostrado que esta estrategia está prácticamente obsoleta. Todo está tendiendo a migrar a LangGraph definiendo los agentes como grafos y evitando las cadenas que en sus inicios hicieron tan potente a LangChain.

2.4. Versión final con LangGraph

Como se ha comentado anteriormente, la comunidad recomienda migrar los proyectos inteligentes a LangGraph. Nuestra aproximación tiene que permitirnos, una vez más, tener el control. Con ésto en mente, comenzamos con el análisis de las opciones que tenemos para construir nuestro agente con LangGraph. Éste es un pequeño resumen de las opciones disponibles:

  1. Sistemas Multi-Agente
  • Network: Permite que dos o más agentes colaboren en una tarea.
  • Supervisor: Utiliza un modelo de lenguaje (LLM) para coordinar y delegar tareas a agentes individuales.
  • Hierarchical Teams: Orquesta equipos anidados de agentes para resolver problemas.
  1. Agentes de Planificación
  • Plan-and-Execute: Implementa un agente básico de planificación y ejecución.
  • Reasoning without Observation: Reduce la necesidad de replanificar guardando las observaciones como variables.
  • LLMCompiler: Transmite y ejecuta de manera proactiva un grafo acíclico dirigido (DAG) de tareas generado por un planificador.
  1. Reflexión y Crítica
  • Basic Reflection: Incita al agente a reflexionar y revisar sus respuestas.
  • Reflexion: Critica los detalles faltantes o superfluos para orientar los pasos siguientes.
  • Tree of Thoughts: Explora soluciones candidatas a un problema mediante un árbol de ideas puntuado.
  • Language Agent Tree Search: Emplea la reflexión y las recompensas para impulsar una búsqueda en árbol Monte Carlo entre agentes.
  • Self-Discover Agent: Analiza un agente que aprende y mejora su comprensión de sus propias capacidades.

La mayoría de las opciones anteriores son demasiado complejas y se escapan del alcance de este artículo. Así que hemos priorizado una vez más que sea muy sencilla. Hemos decidido replicar a mano la arquitectura básica descrita en el primer apartado. Un agente inteligente con tools. A su vez, estas tools las hemos desarrollado como agentes ReAct independientes, permitiendo así hacer un desarrollo incremental, modularizar el código y reutilizar a futuro las piezas construidas. En este caso, haremos uso de la función create_react_agent disponible en la librería langgraph.prebuilt. Esta función sólo necesita recibir una instancia del LLM, un prompt que describa su comportamiento y una lista de funciones de python que usará el agente como tools.

Como hemos hemos podido comprobar con estas pruebas realizadas durante el desarrollo, LangGraph se ha convertido en una de las tecnologías que marcan el estado del arte del desarrollo de aplicaciones inteligentes y sistemas multi-agente. Hemos podido concluir que LangGraph:

  • Aporta más flexibilidad y modularidad: facilita la integración y coordinación de múltiples agentes, ofreciendo una arquitectura modular que se adapta a diversas necesidades y casos de uso. Esta característica permite diseñar soluciones escalables y personalizables, adecuadas para escenarios complejos.
  • Requiere de la buena definición de los prompts: Para obtener respuestas precisas, especialmente en contextos con bases de datos complejas o herramientas múltiples, es esencial enriquecer y afinar la definición de los prompts. La posibilidad de incorporar descripciones detalladas, ejemplos prácticos y contexto adicional es esencial para reducir problemas como la ambigüedad en la nomenclatura o la necesidad de ejecutar múltiples consultas interrelacionadas.
  • Facilita la gestión del estado: permite trabajar con estados representados por cualquier diccionario, ya sea a nivel de ejecución, hilo, aplicación o sesión. Ésto facilita a su vez la incorporación de memoria y persistencia a estas aplicaciones.
  • Permite una implementación flexible: Se pueden utilizar funciones, clases, programación orientada a objetos, etc. para desarrollar nodos y gestionar el estado.
  • Facilita la visualización de la solución creada: al construirla sobre un grafo se puede apreciar de forma clara la estructura de la aplicación, lo que facilita tanto la depuración como el mantenimiento.

En resumen, LangGraph es una herramienta muy prometedora para el desarrollo de soluciones inteligentes. Su capacidad para orquestar agentes y adaptarse a diferentes contextos es ideal. La desventaja a tener en cuenta es que se trata de un ecosistema con muchísima innovación y muy cambiante. Esto complica el mantenimiento de las aplicaciones construidas a lo largo del tiempo, ya que cada nueva versión de LangGraph o alguna de sus librerías tiene altas probabilidades de no ser totalmente retrocompatible.

3. Conclusiones

A partir de las pruebas realizadas y la experiencia adquirida, hemos extraído las siguientes conclusiones:

  • Transición hacia LangGraph

Aunque LangChain y sus funciones preestablecidas han permitido desarrollar agentes de manera rápida, la tendencia actual es migrar a LangGraph. Este framework permite definir agentes como grafos, ofreciendo un control más profundo sobre el flujo interno y evitando el uso de cadenas que, en sus inicios, fueron tan potentes en LangChain.

  • Flexibilidad y modularidad

LangGraph destaca por su capacidad para modelar procesos no lineales y adaptativos, facilitando la integración y coordinación de múltiples agentes. La posibilidad de desarrollar nodos de forma independiente permite crear soluciones escalables y altamente personalizables, ideales para escenarios complejos.

  • Importancia de los prompts bien definidos

La eficacia de toda la solución depende en gran medida de la calidad de los prompts. Es esencial enriquecerlos con descripciones detalladas de la base de datos, ejemplos prácticos y contexto adicional para mitigar problemas derivados de estructuras complejas, nombres ambiguos o consultas que requieran múltiples iteraciones.

  • Monitorización y depuración

LangSmith ha sido crucial para monitorizar cada paso del flujo interno de los agentes, facilitando la detección y corrección de errores. Este seguimiento permite ajustar la solución al momento y mejorar la respuesta del sistema.

  • Funciones y herramientas preestablecidas

El uso de funciones y herramientas preestablecidas por la comunidad de LangChain, como create_react_agent, tools_condition o la clase ToolNode, ha permitido desarrollar agentes de manera muy ágil. Estas utilidades encapsulan complejidades técnicas, ofreciendo soluciones listas para usar que facilitan la integración de funcionalidades avanzadas sin necesidad de partir desde cero. Gracias a este enfoque, se pueden construir arquitecturas modulares y escalables en poco tiempo, lo que abre la puerta a la implementación de agentes con capacidades prácticamente ilimitadas.

  • Ecosistema en evolución

Si bien LangGraph ofrece ventajas significativas, es importante tener en cuenta que se trata de un ecosistema en constante cambio. Las nuevas versiones en ocasiones pueden no ser retrocompatibles con nuestra solución.

En resumen, LangGraph es una herramienta muy prometedora y de vanguardia para desarrollo de nuestros sistemas inteligentes. Sus capacidades de orquestación de agentes, gestión del estado y visualización clara de la estructura de la aplicación gracias a su modelo de grafos, sientan las bases para futuras implementaciones en proyectos de inteligencia artificial.

4. Próximos pasos

De cara a seguir avanzando, se proponen las siguientes líneas de trabajo en futuras iteraciones:

  • Experimentación con el grafo: Profundizar en el manejo del grafo y explorar el uso de las tools como nodos independientes. Esto permitiría diseñar flujos más complejos e iterativos, optimizando la manera en que se procesan y coordinan las tareas.
  • Exploración de nuevas arquitecturas: Probar y evaluar las distintas configuraciones y estructuras descritas para los agentes.
  • Integración de memoria en el agente: Añadir capacidades de memoria que permitan a los agentes recordar información relevante a lo largo del tiempo. Esta mejora contribuirá a la continuidad de la interacción y a la personalización del comportamiento del agente en función del historial de chat.
  • Análisis de modelos y optimización de gastos: Realizar un estudio comparativo de los modelos existentes, identificando oportunidades para optimizar tanto el coste operativo como el rendimiento. Este análisis incluiría la evaluación de parámetros, la optimización del consumo de tokens y la configuración de los modelos para obtener un equilibrio entre precisión y eficiencia.
  • Optimización de prompts: Refinar y optimizar los prompts utilizados en la interacción con los modelos. La optimización de estos mensajes es fundamental para reducir ambigüedades y obtener respuestas más precisas.
  • LangGraph Studio: Investigar y aprovechar las herramientas y funcionalidades que ofrece LangGraph Studio. Esta plataforma puede facilitar la visualización, edición y depuración de los grafos, aportando un entorno más intuitivo y colaborativo para el desarrollo de soluciones inteligentes.
  • Estudio de herramientas y agentes de terceros: Analizar y evaluar el potencial de herramientas y agentes desarrollados por la comunidad, como Tavily y Hugging Face. Este estudio permitiría integrar soluciones complementarias.
  • Integración con CrewAI: Explorar la posibilidad de incorporar frameworks como CrewAI para potenciar la autonomía y capacidades de los agentes. Esta integración puede aportar nuevas perspectivas y mejoras en la orquestación y gestión de tareas.

5. Recursos de referencia