博客

如何通过 LlamaIndex 向 Elasticsearch 采集数据

逐步介绍如何使用 RAG 和 LlamaIndex 采集数据并进行搜索。

在本文中,我们将使用 LlamaIndex 来索引数据,为常见问题实现一个搜索引擎。Elasticsearch 将作为我们的矢量数据库,实现矢量搜索,而 RAG(Retrieval-Augmented Generation,检索增强生成)将丰富上下文,提供更准确的响应。

LlamaIndex& Elasticsearch:输入文档并构建常见问题搜索

什么是 LlamaIndex?

LlamaIndex 是一个框架,可帮助创建由大型语言模型(LLM)驱动的代理和工作流,以便与特定或私人数据进行交互。它可以将各种来源(应用程序接口、PDF、数据库)的数据与 LLM 集成,从而完成研究、信息提取和生成上下文化回复等任务。

关键概念:

  • 代理:使用 LLM 执行任务的智能助手,任务范围从简单的响应到复杂的操作。

  • 工作流程:将代理、数据连接器和高级任务工具结合起来的多步骤流程。

  • 语境增强:利用外部数据丰富 LLM 的技术,克服其训练局限性。

LlamaIndex 与 Elasticsearch 集成:

Elasticsearch 可以通过各种方式与 LlamaIndex 配合使用:

  • 数据源:使用 Elasticsearch 阅读器提取文档。

  • 嵌入模型:将数据编码为向量,用于语义搜索。

  • 矢量存储:将 Elasticsearch 用作搜索矢量化文档的存储库。

  • 高级存储:配置文档摘要或知识图谱等结构。

使用 LlamaIndex 和 Elasticsearch 创建常见问题搜索

数据准备

我们将以Elasticsearch 服务常见问题解答为例进行说明。每个问题都是从网站上提取的,并保存在一个单独的文本文件中。您可以使用任何方法来组织数据;在本例中,我们选择将文件保存在本地。

文件示例:

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.

保存所有问题后,目录将如下所示:

安装依赖项

我们将使用 Python 语言实现摄取和搜索,我使用的版本是 3.9。作为前提条件,有必要安装以下依赖项:

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

Elasticsearch 和 Kibana 将使用 Docker 创建,并通过 docker-compose.yml 配置为运行 8.16.2 版本。这样就能更容易地创建本地环境。

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:

使用 LlamaIndex 进行文件摄取

文件将使用 LlamaIndex 索引到 Elasticsearch 中。首先,我们使用SimpleDirectoryReader 加载文件,它允许从本地目录加载文件。加载文档后,我们将使用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)

LlamaIndex 中的矢量存储负责存储和管理文档嵌入。LlamaIndex 支持不同类型的向量存储,在本例中,我们将使用 Elasticsearch。在 StorageContext 中,我们配置 Elasticsearch 实例。由于上下文是本地的,因此不需要额外的参数。有关其他环境中的配置,请参阅文档检查必要的参数:ElasticsearchStore 配置

默认情况下,LlamaIndex 使用 OpenAItext-embedding-ada-002模型生成嵌入。不过,在本例中,我们将使用文本嵌入-3-小模型。需要注意的是,使用该模型需要一个 OpenAI API 密钥。

以下是完整的文件摄取代码。

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)

执行后,文件将被编入faq索引,如下图所示:

使用 RAG 搜索

要执行搜索,我们需要配置ElasticsearchStore客户端,用 Elasticsearch URL 设置index_namees_url字段。在retrieval_strategy 中,我们为向量搜索定义了AsyncDenseVectorStrategy。还提供其他策略,如AsyncBM25Strategy(关键词搜索)和AsyncSparseVectorStrategy(稀疏向量)。更多详情,请参阅官方文档

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

接下来,我们将创建一个VectorStoreIndex对象,并使用 ElasticsearchStore 对象配置vector_store。通过as_retriever方法,我们可以搜索与查询最相关的文档,并通过similarity_top_k参数设置返回结果的数量为 5。

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

下一步是 RAG。矢量搜索的结果被纳入本地语言管理器的格式化提示中,从而能够根据检索到的信息做出符合具体情况的响应。

在 PromptTemplate 中,我们定义了提示格式,其中包括

  • Context ({context_str}):检索器检索到的文件。

  • Query ({query_str}): 用户的问题。

  • 说明:指导模型根据环境做出反应,而不依赖外部知识。

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 会处理提示,并根据上下文返回准确的回复。

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)

完整代码如下:

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)

现在,我们可以执行搜索,例如"Elastic services are free?" ,并根据常见问题数据本身获得符合上下文的回复。

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.

为了做出这一回应,我们使用了以下文件:

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.

结论

我们使用 LlamaIndex 演示了如何创建一个高效的常见问题搜索系统,并将 Elasticsearch 作为向量数据库提供支持。使用嵌入法对文件进行摄取和索引,从而实现矢量搜索。通过 PromptTemplate,搜索结果被纳入上下文并发送给 LLM,LLM 会根据检索到的文件生成精确且符合上下文的回复。

该工作流程将信息检索与根据上下文生成回复整合在一起,以提供准确、相关的结果。

参考资料

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

常见问题

什么是 LlamaIndex?

LlamaIndex 是一个框架,可帮助创建由大型语言模型(LLM)驱动的代理和工作流,以便与特定或私人数据进行交互。

LlamaIndex 能否与 Elasticsearch 集成?

是的,Elasticsearch 可以多种方式与 LlamaIndex 配合使用,包括作为数据源(使用 Elasticsearch 阅读器提取文档)、作为嵌入模型(将数据编码为向量,用于语义搜索)、作为向量存储(将 Elasticsearch 用作存储库,用于搜索向量化文档),以及用于高级存储(配置文档摘要或知识图谱等结构)。

相关内容

显示 Elasticsearch 索引中的字段

Kofi Bartlett

如何通过 Kafka 向 Elasticsearch 摄取数据

Andre Luiz

在 Logstash 中使用 Ruby 脚本

Dai Sugimori

Elasticsearch 中的索引模板:如何使用可组合模板

Kofi Bartlett

如何显示 Elasticsearch 索引的字段

JD Armada

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用