RAG (Generación aumentada por recuperación) con LlamaIndex, Elasticsearch y Mistral

Aprende a implementar un sistema RAG (Generación Aumentada por Recuperación) usando LlamaIndex, Elasticsearch y Mistral ejecutando localmente.

Elasticsearch tiene integraciones nativas con las herramientas y proveedores líderes en la industria de IA generativa. Echa un vistazo a nuestros webinars sobre cómo ir más allá de los conceptos básicos de RAG o crear apps listas para la producción con la base de datos vectorial de Elastic.

Para crear las mejores soluciones de búsqueda para tu caso de uso, inicia una prueba gratuita en el cloud o prueba Elastic en tu máquina local ahora mismo.

En este blog discutiremos cómo implementar la experiencia de preguntas frecuentes empleando una técnica RAG (Generación Aumentada de Recuperación) con Elasticsearch como base de datos vectorial. Emplearemos LlamaIndex y un LLM Mistral que se ejecuta localmente.

Antes de empezar, veremos algo de terminología.

Terminología

LlamaIndex es un marco de datos líder para construir aplicaciones LLM (Grandes Modelos de Lenguaje). LlamaIndex proporciona abstracciones para las distintas etapas de la construcción de una aplicación RAG (Generación Aumentada por Recuperación). Frameworks como LlamaIndex y LangChain proporcionan abstracciones para que las aplicaciones no se acoplen estrechamente a las APIs de ningún LLM específico.

Elasticsearch es ofrecido por Elastic. Elastic es líder en el sector detrás de Elasticsearch, un motor de búsqueda y análisis que soporta búsqueda en texto completo para mayor precisión, búsqueda vectorial para comprensión semántica y búsqueda híbrida para lo mejor de ambos mundos. Elasticsearch es un almacén de datos escalable y una base de datos vectorial. Las capacidades de elasticsearch que empleamos en este blog están disponibles en la versión gratis y abierta de Elasticsearch.

La Generación de Aumentos por Recuperación (RAG) es una técnica/patrón de IA en la que los LLMs reciben conocimientos externos para generar respuestas a las consultas de los usuarios. Esto permite adaptar las respuestas de los LLM a un contexto específico y que las respuestas sean más específicas.

Mistral ofrece tanto modelos LLM de código abierto como optimizados de nivel empresarial. En este tutorial, usaremos su modelo de código abierto mistral-7b que funciona en tu portátil. Si no quieres ejecutar el modelo en tu portátil, alternativamente podrías usar su versión en la nube, en cuyo caso tendrás que modificar el código de este blog para usar las claves y paquetes de API adecuados.

Ollama ayuda a ejecutar LLMs localmente en tu portátil. Usaremos Ollama para ejecutar localmente el modelo de código abierto Mistral-7b.

Las incrustaciones son representaciones numéricas del significado de texto/medios. Son representaciones de información de alta dimensión en dimensiones inferiores.

Construcción de una aplicación RAG con LlamaIndex, Elasticsearch y Mistral: Resumen del escenario

Escenario:

Disponemos de un conjunto de datos de ejemplo (como archivo JSON) de conversaciones en centros de llamadas entre agentes y clientes de una compañía de seguros de hogar ficticia. Construiremos una aplicación RAG sencilla que podrá responder preguntas como

Give me summary of water related issues.

Caudal de alto nivel

Tenemos Mistral LLM funcionando localmente usando Ollama.

A continuación, cargamos las conversaciones del archivo JSON como Documents en ElasticsearchStore (que es un VectorStore respaldado por Elasticsearch). Al cargar los documentos creamos incrustaciones usando el modelo Mistral que se ejecuta localmente. Almacenamos estos embeddings junto con las conversaciones en LlamaIndex Elasticsearch vector Store (ElasticsearchStore).

Configuramos un LlamaIndex IngestionPipeline y lo suministramos con el LLM local que usamos, en este caso Mistral que funciona a través de Ollama.

Cuando hacemos una pregunta como "Dame un resumen de los problemas relacionados con el agua.", Elasticsearch realiza una búsqueda semántica y devuelve conversaciones relacionadas con problemas relacionados con el agua. Estas conversaciones , junto con la pregunta original, se envían al LLM que se ejecuta localmente para generar una respuesta.

Pasos para construir la aplicación RAG

Ejecutar Mistral localmente

Descarga e instala Ollama. Luego de instalar Ollama, ejecuta este comando para descargar y ejecutar mistral

Puede que tardes unos minutos en descargar y ejecutar el modelo localmente por primera vez. Verifica si el mistral está funcionando haciendo una pregunta como la siguiente: "Escribe un poema sobre nubes" y verifica si el poema te gusta. Mantén a Ollama funcionando, ya que tendremos que interactuar con el modelo mistral más adelante mediante código.

Instalar Elasticsearch

Pon en marcha Elasticsearch ya sea creando un despliegue en la nube (instrucciones aquí) o ejecutándolo en docker (instrucciones aquí). También puedes crear un despliegue autoalojado de Elasticsearch de calidad producción empezando por aquí.

Suponiendo que usas el despliegue en la nube, descarga la clave API y el ID de la nube para el despliegue tal y como se indica en las instrucciones. Los usaremos más tarde.

Aplicación RAG

Como referencia, se puede encontrar el código completo en este repositorio de Github. Clonar el repositorio es opcional, ya que repasaremos el código a continuación.

En tu IDE favorito, crea una nueva aplicación en Python con los siguientes 3 archivos.

  • index.py donde va el código relacionado con la indexación de datos.
  • query.py donde va el código relacionado con la consulta y la interacción con LLM.
  • .env donde van propiedades de configuración como las claves de API.

Necesitamos instalar algunos paquetes. Comenzamos creando un nuevo entorno virtual de Python en la carpeta raíz de tu aplicación.

Activa el entorno virtual e instala los paquetes requeridos a continuación.

Datos de indexación

Descarga el archivo conversations.json que contiene conversaciones entre clientes y agentes de centros de llamadas de nuestra compañía de seguros de hogar ficticia. Coloca el archivo en el directorio raíz de la aplicación junto a los 2 archivos python y el archivo .env Archivo que creaste antes. A continuación se muestra un ejemplo del contenido del archivo.

Definimos una función llamada get_documents_from_file en index.py que lee el archivo json y crea una lista de Documentos. Los objetos documento son la unidad básica de información con la que trabaja LlamaIndex.

Create IngestPipeline

En primer lugar, agrega las claves de CloudID y API de Elasticsearch que obtuviste en la sección Install Elasticsearch al archivo .env . Tu archivo .env debería parecer a lo siguiente (con valores reales).

LlamaIndex IngestionPipeline te permite componer una tubería usando múltiples componentes. Agrega el código de abajo al archivo index.py .

Como se mencionó anteriormente, el LlamaIndex IngestPipeline puede estar compuesto por múltiples componentes. Estamos agregando 3 componentes a la tubería en la línea pipeline = IngestionPipeline(....

  • Fragmentador de Sentences: Como se puede ver en la definición de get_documents_from_file(), cada documento tiene un campo de texto que contiene la conversación encontrada en el archivo json. Este campo de texto es un texto largo. Para que la búsqueda semántica funcione bien, debe dividir en fragmentos de textos más pequeños. La clase SentenceSplitter hace esto por nosotros. Estos bloques se denominan nodos en la terminología de LlamaIndex. Hay metadatos en los nodos que apuntan al documento al que pertenecen. Alternativamente, podrías usar Elasticsearch Ingestpipeline para el chunking, como se muestra en este blog.
  • OllamaEmbedding: Los modelos de incrustación convierten un fragmento de texto en números (también llamados vectores). Tener representación numérica nos permite realizar búsquedas semánticas donde los resultados coinciden con el significado de la palabra en lugar de simplemente hacer una búsqueda de texto. Suministramos a IngestionPipeline OllamaEmbedding("mistral"). Los chunks que dividimos usando SentenceSplitter se envían al modelo Mistral que se ejecuta en tu máquina local mediante Ollama, luego Mistral crea embeddings para los chunks.
  • ElasticsearchStore: El almacén vectorial LlamaIndex ElasticsearchStore respalda las incrustaciones que se están creando en un Índice Elasticsearch. ElasticsearchStore se encarga de crear y poblar el contenido del índice Elasticsearch especificado. Al crear el ElasticsearchStore (referenciado por es_vector_store) proporcionamos el nombre del índice Elasticsearch que queremos crear (calls en nuestro caso), el campo en el índice donde queremos almacenar las incrustaciones (conversation_vector en nuestro caso) y el campo donde queremos almacenar el texto (conversation en nuestro caso). En resumen, basar en nuestra configuración ElasticsearchStore crea un nuevo índice en Elasticsearch con conversation_vector y conversation como campos (entre otros campos creados automáticamente).

Para unirlo todo, gestionamos la tubería llamando a pipeline.run(documents=documents).

Ejecuta el script index.py para ejecutar la tubería de ingest:

Una vez completada la ejecución de la pipeline, deberíamos ver un nuevo índice en Elasticsearch llamado calls. Ejecutando una consulta sencilla de elasticsearch usando la Dev Console deberías poder ver los datos cargados junto con las incrustaciones.

Para resumir lo que hicimos hasta ahora, creamos Documents a partir de un archivo JSON, los dividimos en bloques, creamos incrustaciones para esos fragmentos y almacenamos las incrustaciones (y la conversación de texto) en un almacén vectorial (ElasticsearchStore).

Consulta

El llamaIndex VectorStoreIndex te permite recuperar documentos relevantes y datos de consulta. Por defecto, VectorStoreIndex almacena incrustaciones en memoria en un SimpleVectorStore. Sin embargo, se pueden usar almacenes vectoriales externos (como ElasticsearchStore) para hacer que las incrustaciones sean persistentes.

Abre el query.py y pega el código de abajo

Definimos un LLM local (local_llm) para apuntar al modelo Mistral que se ejecuta en Ollama. A continuación, creamos un VectorStoreIndex (index) a partir de la tienda vectorial ElasticssearchStore que creamos antes y luego obtenemos un motor de consulta desde el índice. Al crear el motor de consultas, hacemos referencia al LLM local que debe usar para responder, también proporcionamos (similarity_top_k=10) para configurar el número de documentos que deben recuperar del almacén vectorial y enviar al LLM para obtener una respuesta.

Ejecuta el script query.py para ejecutar el flujo RAG:

Enviamos el Give me summary of water related issues de consulta (no dudes en personalizar el query) y la respuesta del LLM, que se proporciona con los documentos relacionados, debería ser algo así como el siguiente.

En el contexto proporcionado, vemos varios casos en los que los clientes consultaron por la cobertura por daños relacionados con el agua. En dos casos, las inundaciones causaron daños en los sótanos y las filtraciones en el tejado fueron el problema en otro caso. Los agentes confirmaron que ambos tipos de daños por agua están cubiertos por sus respectivas pólizas. Por lo tanto, los problemas relacionados con el agua, como inundaciones y filtraciones en el tejado, suelen estar cubiertos por las pólizas de seguro de hogar.

Algunas advertencias:

Esta entrada de blog es una introducción para principiantes a la técnica RAG con Elasticsearch y, por tanto, omite la configuración de las características que te permitirán llevar este punto de partida a producción. Al construir para casos de uso en producción, querrás considerar aspectos más sofisticados como poder proteger tus datos con Seguridad a Nivel de Documento, fragmentar tus datos como parte de una pipeline de Ingest de Elasticsearch o incluso ejecutar otros trabajos de ML con los mismos datos que se usan para casos de uso de GenAI/Chat/Q&A.

También podrías considerar buscar datos y crear incrustaciones desde diversas fuentes externas (por ejemplo, Azure Blob Storage, Dropbox, Gmail, etc.) usando Elastic Connectors.

Elastic hace posible todo lo anterior y más posible, y ofrece una solución integral de nivel empresarial para casos de uso de GenAI y más allá.

¿Qué sigue?

  • Quizá notaste que estamos enviando 10 conversaciones relacionadas junto con la pregunta del usuario al LLM para formular una respuesta. Estas conversaciones pueden contener información personal identificable (PII) como nombre, fecha de nacimiento, dirección, etc. En nuestro caso, el LLM es local, así que la fuga de datos no es un problema. Sin embargo, cuando quieres usar un LLM que se ejecuta en la nube (por ejemplo, OpenAI) no es deseable enviar textos que contengan información de PII. En un blog de seguimiento veremos cómo lograr información de PII de Enmascaramiento antes de enviarla a LLMs externos en el flujo RAG.
  • En esta publicación empleamos un LLM local; en la próxima publicación sobre Enmascarar datos de PII en RAG, veremos cómo podemos cambiar fácilmente de un LLM local a uno público.

Preguntas frecuentes

¿Qué es LlamaIndex?

LlamaIndex es un marco de datos líder para construir aplicaciones LLM (Grandes Modelos de Lenguaje).

¿Qué es Mistral?

Mistral es una compañía que ofrece tanto modelos LLM de código abierto como modelos optimizados de nivel empresarial.

Contenido relacionado

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo