Articles de blog

Comment ingérer des données dans Elasticsearch via LlamaIndex

Une étape par étape sur la façon d'ingérer des données et de faire des recherches en utilisant RAG avec LlamaIndex.

Dans cet article, nous allons mettre en place un moteur de recherche pour les FAQ en utilisant LlamaIndex pour indexer les données. Elasticsearch servira de base de données vectorielle, permettant une recherche vectorielle, tandis que RAG (Retrieval-Augmented Generation) enrichira le contexte, fournissant des réponses plus précises.

LlamaIndex & Elasticsearch : Ingestion de documents et construction d'une recherche de FAQ

Qu'est-ce que le LlamaIndex ?

LlamaIndex est un cadre qui facilite la création d'agents et de flux de travail alimentés par de grands modèles linguistiques (LLM) pour interagir avec des données spécifiques ou privées. Il permet l'intégration de données provenant de diverses sources (API, PDF, bases de données) avec les LLM, permettant des tâches telles que la recherche, l'extraction d'informations et la génération de réponses contextualisées.

Concepts clés :

  • Agents : Assistants intelligents qui utilisent des LLM pour effectuer des tâches, allant de simples réponses à des actions complexes.

  • Flux de travail : Processus en plusieurs étapes qui combinent des agents, des connecteurs de données et des outils pour des tâches avancées.

  • Augmentation du contexte : Une technique qui enrichit le LLM avec des données externes, en surmontant ses limites de formation.

Intégration deLlamaIndex avec Elasticsearch :

Elasticsearch peut être utilisé de différentes manières avec LlamaIndex :

  • Source de données : Utilisez le lecteur Elasticsearch pour extraire les documents.

  • Modèle d'encastrement : Encoder les données en vecteurs pour les recherches sémantiques.

  • Stockage vectoriel : Utilisez Elasticsearch comme référentiel pour la recherche de documents vectorisés.

  • Stockage avancé : Configurez des structures telles que des résumés de documents ou des graphes de connaissances.

Utiliser LlamaIndex et Elasticsearch pour construire une recherche de FAQ

Préparation des données

Nous utiliserons la FAQ du service Elasticsearch comme exemple. Chaque question a été extraite du site web et enregistrée dans un fichier texte individuel. Vous pouvez utiliser n'importe quelle approche pour organiser les données ; dans cet exemple, nous avons choisi d'enregistrer les fichiers localement.

Exemple de fichier :

File Name: what-is-elasticsearch-service.txt
Content: Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

Après avoir enregistré toutes les questions, le répertoire ressemblera à ceci :

Installation des dépendances

Nous allons mettre en œuvre l'ingestion et la recherche en utilisant le langage Python, la version que j'ai utilisée étant la 3.9. Comme prérequis, il sera nécessaire d'installer les dépendances suivantes :

llama-index-vector-stores-elasticsearch
llama-index
openai

Elasticsearch et Kibana seront créés avec Docker, configurés via docker-compose.yml pour exécuter la version 8.16.2. Cela facilite la création de l'environnement local.

version: '3.8'
services:

 elasticsearch:
   image: docker.elastic.co/elasticsearch/elasticsearch:8.16.2
   container_name: elasticsearch-8.16.2
   environment:
     - node.name=elasticsearch
     - xpack.security.enabled=false
     - discovery.type=single-node
     - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
   ports:
     - 9200:9200
   networks:
     - shared_network

 kibana:
   image: docker.elastic.co/kibana/kibana:8.16.2
   container_name: kibana-8.16.2
   restart: always
   environment:
     - ELASTICSEARCH_URL=http://elasticsearch:9200
   ports:
     - 5601:5601
   depends_on:
     - elasticsearch
   networks:
     - shared_network

networks:
 shared_network:

L'ingestion de documents à l'aide de LlamaIndex

Les documents seront indexés dans Elasticsearch à l'aide de LlamaIndex. Tout d'abord, nous chargeons les fichiers avec SimpleDirectoryReader, qui permet de charger des fichiers à partir d'un répertoire local. Après avoir chargé les documents, nous les indexons à l'aide du VectorStoreIndex.

documents = SimpleDirectoryReader("./faq").load_data()

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)

Les magasins de vecteurs de LlamaIndex sont responsables du stockage et de la gestion des incorporations de documents. LlamaIndex supporte différents types de Vector Stores, et dans ce cas, nous utiliserons Elasticsearch. Dans le StorageContext, nous configurons l'instance Elasticsearch. Le contexte étant local, aucun paramètre supplémentaire n'a été requis. Pour les configurations dans d'autres environnements, se référer à la documentation pour vérifier les paramètres nécessaires : ElasticsearchStore Configuration.

Par défaut, LlamaIndex utilise le modèle OpenAI text-embedding-ada-002 pour générer des embeddings. Toutefois, dans cet exemple, nous utiliserons le modèle text-embedding-3-small. Il est important de noter qu'une clé API OpenAI sera nécessaire pour utiliser le modèle.

Vous trouverez ci-dessous le code complet pour l'ingestion de documents.

import openai
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore

openai.api_key = os.environ["OPENAI_API_KEY"]

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200"
)

def format_title(filename):
   filename_without_ext = filename.replace('.txt', '')
   text_with_spaces = filename_without_ext.replace('-', ' ')
   formatted_text = text_with_spaces.title()

   return formatted_text


embed_model = OpenAIEmbedding(model="text-embedding-3-small")

documents = SimpleDirectoryReader("./faq").load_data()

for doc in documents:
   doc.metadata['title'] = format_title(doc.metadata['file_name'])

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)

Après l'exécution, les documents seront indexés dans l'index faq comme indiqué ci-dessous :

Recherche avec RAG

Pour effectuer des recherches, nous configurons le client ElasticsearchStore, en définissant les champs index_name et es_url avec l'URL d'Elasticsearch. Dans retrieval_strategy, nous avons défini la stratégie AsyncDenseVectorStrategy pour les recherches vectorielles. D'autres stratégies, telles que AsyncBM25Strategy (recherche par mots-clés) et AsyncSparseVectorStrategy (vecteurs épars), sont également disponibles. Plus de détails peuvent être trouvés dans la documentation officielle.

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)

Ensuite, un objet VectorStoreIndex sera créé, où nous configurerons le vector_store en utilisant l'objet ElasticsearchStore. Avec la méthode as_retriever, nous recherchons les documents les plus pertinents pour une requête, en fixant le nombre de résultats renvoyés à 5 par le biais du paramètre similarity_top_k.

   index = VectorStoreIndex.from_vector_store(vector_store=es)
   retriever = index.as_retriever(similarity_top_k=5)
   results = retriever.retrieve(query)

L'étape suivante est le GCR. Les résultats de la recherche vectorielle sont incorporés dans une invite formatée pour le LLM, permettant une réponse contextualisée basée sur les informations récupérées.

Dans le PromptTemplate, nous définissons le format de l'invite, qui inclut :

  • Contexte ({context_str}) : documents récupérés par le chercheur.

  • Query ({query_str}) : la question de l'utilisateur.

  • Instructions : lignes directrices permettant au modèle de répondre en fonction du contexte, sans s'appuyer sur des connaissances externes.

qa_prompt = PromptTemplate(
   "You are a helpful and knowledgeable assistant."
   "Your task is to answer the user's query based solely on the context provided below."
   "Do not use any prior knowledge or external information.\n"
   "---------------------\n"
   "Context:\n"
   "{context_str}\n"
   "---------------------\n"
   "Query: {query_str}\n"
   "Instructions:\n"
   "1. Carefully read and understand the context provided.\n"
   "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
   "3. Do not make up or guess any information.\n"
   "Answer: "
)

Enfin, le LLM traite l'invite et renvoie une réponse précise et contextuelle.

llm = OpenAI(model="gpt-4o")
context_str = "\n\n".join([n.node.get_content() for n in results])
response = llm.complete(
   qa_prompt.format(context_str=context_str, query_str=query)
)

print("Answer:")
print(response)

Le code complet se trouve ci-dessous :

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)


def print_results(results):
   for rank, result in enumerate(results, start=1):
       title = result.metadata.get("title")
       score = result.get_score()
       text = result.get_text()
       print(f"{rank}. title={title} \nscore={score} \ncontent={text}")


def search(query: str):
   index = VectorStoreIndex.from_vector_store(vector_store=es)

   retriever = index.as_retriever(similarity_top_k=10)
   results = retriever.retrieve(QueryBundle(query_str=query))
   print_results(results)

   qa_prompt = PromptTemplate(
       "You are a helpful and knowledgeable assistant."
       "Your task is to answer the user's query based solely on the context provided below."
       "Do not use any prior knowledge or external information.\n"
       "---------------------\n"
       "Context:\n"
       "{context_str}\n"
       "---------------------\n"
       "Query: {query_str}\n"
       "Instructions:\n"
       "1. Carefully read and understand the context provided.\n"
       "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
       "3. Do not make up or guess any information.\n"
       "Answer: "
   )

   llm = OpenAI(model="gpt-4o")
   context_str = "\n\n".join([n.node.get_content() for n in results])
   response = llm.complete(
       qa_prompt.format(context_str=context_str, query_str=query)
   )

   print("Answer:")
   print(response)


question = "Elastic services are free?"
print(f"Question: {question}")
search(question)

Nous pouvons maintenant effectuer notre recherche, par exemple, "Les services Elastic sont gratuits ?" et obtenir une réponse contextualisée basée sur les données de la FAQ elle-même.

Question: Elastic services are free?
Answer:
Elastic services are not entirely free. However, there is a 14-day free trial available for exploring Elastic solutions. After the trial, access to features and services depends on the subscription level.

Les documents suivants ont été utilisés pour élaborer cette réponse :

1. title=Can I Try Elasticsearch Service For Free 
score=1.0 
content=Yes, sign up for a 14-day free trial. The trial starts the moment a cluster is created.
During the free trial period get access to a deployment to explore Elastic solutions for Enterprise Search, Observability, Security, or the latest version of the Elastic Stack.

2. title=Do You Offer Elastic S Commercial Products 
score=0.9941274512218439 
content=Yes, all Elasticsearch Service customers have access to basic authentication, role-based access control, and monitoring.
Elasticsearch Service Gold, Platinum and Enterprise customers get complete access to all the capabilities in X-Pack: Security, Alerting, Monitoring, Reporting, Graph Analysis & Visualization. Contact us to learn more.

3. title=What Is Elasticsearch Service 
score=0.9896776845746571 
content=Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

4. title=Can I Run The Full Elastic Stack In Elasticsearch Service 
score=0.9880631561979476 
content=Many of the products that are part of the Elastic Stack are readily available in Elasticsearch Service, including Elasticsearch, Kibana, plugins, and features such as monitoring and security. Use other Elastic Stack products directly with Elasticsearch Service. For example, both Logstash and Beats can send their data to Elasticsearch Service. What is run is determined by the subscription level.

5. title=What Is The Difference Between Elasticsearch Service And The Amazon Elasticsearch Service 
score=0.9835054890793161 
content=Elasticsearch Service is the only hosted and managed Elasticsearch service built, managed, and supported by the company behind Elasticsearch, Kibana, Beats, and Logstash. With Elasticsearch Service, you always get the latest versions of the software. Our service is built on best practices and years of experience hosting and managing thousands of Elasticsearch clusters in the Cloud and on premise. For more information, check the following Amazon and Elastic Elasticsearch Service comparison page.
Please note that there is no formal partnership between Elastic and Amazon Web Services (AWS), and Elastic does not provide any support on the AWS Elasticsearch Service.

Conclusion

En utilisant LlamaIndex, nous avons démontré comment créer un système efficace de recherche de FAQ avec le support d'Elasticsearch comme base de données vectorielle. Les documents sont ingérés et indexés à l'aide d'enchâssements, ce qui permet d'effectuer des recherches vectorielles. Grâce à un PromptTemplate, les résultats de la recherche sont intégrés dans le contexte et envoyés au LLM, qui génère des réponses précises et contextualisées sur la base des documents récupérés.

Ce flux de travail intègre la recherche d'informations et la génération de réponses contextualisées afin de fournir des résultats précis et pertinents.

Références

https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html

https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/

https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/

https://docs.llamaindex.ai/en/stable/examples/query_engine/custom_query_engine/

https://www.elastic.co/search-labs/integrations/llama-index

Questions fréquentes

Qu'est-ce que le LlamaIndex ?

LlamaIndex est un cadre qui facilite la création d'agents et de flux de travail alimentés par de grands modèles linguistiques (LLM) pour interagir avec des données spécifiques ou privées.

LlamaIndex peut-il être intégré à Elasticsearch ?

Oui, Elasticsearch peut être utilisé de différentes manières avec LlamaIndex, notamment comme source de données (en utilisant le lecteur Elasticsearch pour extraire des documents), comme modèle d'intégration (en encodant des données en vecteurs pour des recherches sémantiques), comme stockage vectoriel (en utilisant Elasticsearch comme référentiel pour la recherche de documents vectorisés), et pour le stockage avancé (en configurant des structures telles que des résumés de documents ou des graphes de connaissances).

Pour aller plus loin

Affichage des champs dans un index Elasticsearch

Kofi Bartlett

Comment ingérer des données dans Elasticsearch via Kafka ?

Andre Luiz

Scripting Ruby dans Logstash

Dai Sugimori

Modèles d'index dans Elasticsearch : Comment utiliser les modèles composables

Kofi Bartlett

Comment afficher les champs d'un index Elasticsearch ?

JD Armada

Prêt à créer des expériences de recherche d'exception ?

Une recherche suffisamment avancée ne se fait pas avec les efforts d'une seule personne. Elasticsearch est alimenté par des data scientists, des ML ops, des ingénieurs et bien d'autres qui sont tout aussi passionnés par la recherche que vous. Mettons-nous en relation et travaillons ensemble pour construire l'expérience de recherche magique qui vous permettra d'obtenir les résultats que vous souhaitez.

Jugez-en par vous-même