在本博客中,您将了解 Playground 及其如何用于使用 Elasticsearch 试验检索增强生成 (RAG) 应用。
更新:在 Elastic 演示库中试用新的 Playground 应用。
什么是 Playground?
Elastic 的 Playground 体验为开发者提供了一个低代码界面,可在数分钟内使用他们自己的私有数据探索他们选择的落地式大型语言模型 (LLM)。
在原型设计对话式搜索时,能够快速迭代和试验 RAG 工作流程的关键组件(例如:混合搜索或添加 重新排序)非常重要——以便从大型语言模型 (LLM) 获取准确且无幻觉的响应。
Elasticsearch 向量数据库和 Search AI 平台为开发者提供了广泛的功能,例如全面的混合搜索,以及使用来自不断增长的 LLM 提供商列表的创新。我们在 Playground 体验中的方法使您可以利用这些功能的强大功能,而无需增加复杂性。
A/B 测试大型语言模型 (LLM) 并选择不同的推理提供商
Playground 的直观界面允许您对来自模型提供商(如 OpenAI 和 Anthropic)的不同大型语言模型 (LLM) 进行 A/B 测试,并改进您的检索机制,以便使用索引到一个或多个 Elasticsearch 索引中的您自己的数据来定位答案。Playground 体验可以直接在 Elasticsearch 中利用 转换器模型,但也可以通过 Elasticsearch 开放推理 API 加强,该 API 与越来越多的推理提供商集成,包括 Cohere 和 Azure AI Studio。
使用检索器和混合搜索获得最佳上下文窗口
正如 Elasticsearch 开发者所知,最佳上下文窗口是使用 混合搜索 构建的。构建此结果的策略需要访问多种形式的向量化和纯文本数据,这些数据可以被分割并分散在多个索引中。
我们正在帮助您简化查询构建,方法是将新引入的 查询检索器 用于搜索所有内容!使用三个关键检索器(现已在 8.14 和 Elastic Cloud Serverless 中提供),使用 RRF 标准化分数的混合搜索只需一个统一查询即可完成。使用检索器,Playground 会理解所选数据的形状,并自动代表您生成统一查询。存储向量化数据并探索 kNN 检索器,或添加元数据和上下文以通过选择您的数据生成混合搜索查询。即将推出,语义重新排序 可以轻松地集成到您的生成查询中,以获得更高质量的召回率。
一旦您将语义搜索调整和配置到生产标准,您就可以导出代码,并使用您的 Python Elasticsearch 语言客户端或 LangChain Python 集成在您的应用程序中完成体验。
Playground 现已在 Elastic Cloud Serverless 上提供,并且现在可在 8.14 的 Elastic Cloud 上使用。
使用 Playground
可以通过在侧边导航中导航到“Playground”来从 Kibana(Elasticsearch UI)中访问 Playground。
连接到您的大型语言模型 (LLM)
Playground 支持诸如来自 OpenAI 的 GPT-4o、Azure OpenAI 或通过 Amazon Bedrock 的 Anthropic 等聊天完成模型。首先,您需要连接到这些模型提供商中的一个,以引入您选择的 LLM。
与您的数据聊天
可以使用任何数据,甚至基于 BM25 的索引。您的数据字段可以选择使用文本嵌入模型(如我们的零样本语义搜索模型 ELSER)进行转换,但这并非必需。入门非常简单 - 只需选择要用来定位答案的索引,然后开始提问即可。在此示例中,我们将使用 PDF,并从使用 BM25 开始,其中每个文档代表 PDF 的一页。
使用 Python 对 PDF 文档进行 BM25 索引
首先,我们安装依赖项。我们使用 pypdf 库来读取 PDF 并请求检索它们。
!pip install -qU pypdf requests elasticsearch
然后我们读取文件,创建一个包含文本的页面数组。
import PyPDF2
import requests
from io import BytesIO
def download_pdf(url):
response = requests.get(url)
if response.status_code == 200:
return BytesIO(response.content)
else:
print("Failed to download PDF")
return None
def get_pdf_pages(pdf_file):
pages = []
pdf_reader = PyPDF2.PdfReader(pdf_file)
for page in pdf_reader.pages:
text = page.extract_text()
pages.append(text)
return pages
pdf_file = download_pdf("https://arxiv.org/pdf/2103.15348.pdf")
if pdf_file:
pages = get_pdf_pages(pdf_file)
然后我们将它导入到 elasticsearch 中,位于 my_pdf_index_bm25 索引下。
from elasticsearch import helpers, Elasticsearch
client = Elasticsearch(
"<my-cloud-url>",
api_key=ELASTIC_API_KEY,
)
helpers.bulk(
client,
[
{
"_index": "my_pdf_index_bm25",
"_source": {
"text": page,
"page_number": i,
},
}
for i, page in enumerate(pages)
],
request_timeout=60,
)
使用 Playground 与您的数据聊天
一旦我们使用连接器连接了我们的 LLM 并选择了索引,我们就可以开始询问有关 PDF 的问题了。LLM 现在可以轻松地回答您的数据问题。
幕后发生了什么?
当我们选择索引时,我们会自动确定最佳检索方法。在这种情况下,只有 BM25 关键字搜索可用,因此我们生成多匹配类型查询来执行检索。
由于我们只有一个字段,因此我们默认为搜索此字段。如果您有多个字段,您可以选择要搜索的字段以改进相关文档的检索。
提问
当您提问时,Playground 将使用查询执行检索以查找与您的问题匹配的相关文档。然后,它将以此作为上下文并提供提示,从而定位从您选择的 LLM 模型返回的答案。
我们使用文档中的特定字段作为上下文。在此示例中,Playground 选择名为“text”的字段,但这可以在“编辑上下文”操作中更改。
默认情况下,我们最多检索 3 个文档作为上下文,但您也可以在编辑上下文弹出窗口中调整数量。
提出后续问题
通常,后续问题与之前的对话相关。考虑到这一点,我们要求 LLM 使用对话将后续问题改写成一个独立的问题,然后将其用于检索。这使我们可以检索更好的文档作为上下文,以帮助回答问题。
上下文
当根据您的问题找到文档时,我们将这些文档作为上下文提供给 LLM,以在回答问题时定位 LLM 的知识。我们自动选择我们认为最佳的单个索引字段,但您可以通过转到编辑上下文弹出窗口来更改此字段。
使用语义搜索和分块改进检索
由于我们的查询采用问题的形式,因此检索能够基于语义含义进行匹配非常重要。使用 BM25,我们只能匹配词汇上与我们的问题匹配的文档,因此我们需要添加语义搜索。
使用 ELSER 进行稀疏向量语义搜索
开始使用语义搜索的一种简单方法是将 Elastic 的 ELSER 稀疏嵌入模型与我们的数据一起使用。与许多这种大小和架构的模型一样,ELSER 具有典型的 512 个令牌限制,并且需要针对合适的分割策略进行设计选择以适应它。在即将发布的 Elasticsearch 版本中,我们将默认在向量化过程中进行分割,但在本版本中,我们将采用按段落进行分割的策略作为起点。您的数据的形状可能会从 其他 分割策略中受益,我们鼓励您进行实验以改进检索。
使用pyPDF和LangChain对PDF进行分块和摄取
为了简化示例,我们将使用LangChain工具加载页面并将页面拆分为段落。LangChain是RAG开发中一个流行的工具,可以与Elasticsearch向量数据库集成并使用,并结合我们更新的集成使用语义重新排序功能。
创建ELSER推理端点
可以执行以下REST API调用来下载、部署和检查模型的运行状态。您可以使用Kibana中的Dev Tools来执行这些操作。
# Starts ELSER Service into Elasticsearch ML node
# This may take a while, depending on ML node autoscaling
PUT _inference/sparse_embedding/my-elser-model
{
"service": "elser",
"service_settings": {
"num_allocations": 1,
"num_threads": 1
}
}
# Check to see if trained model status. Look at nodes.routing_state is "started"
GET _ml/trained_models/my-elser-model/_stats
摄取到Elasticsearch
接下来,我们将设置一个索引并附加一个管道来为我们处理推理。
# Setup an elser pipeline to embed the contents in text field
# using ELSER into the text_embedding field
PUT /_ingest/pipeline/my-elser-pipeline
{
"processors": [
{
"inference": {
"model_id": "my-elser-model",
"input_output": [
{
"input_field": "text",
"output_field": "text_embedding"
}
]
}
}
]
}
# Setup an index which uses the embedding pipeline
# ready for our documents to be stored in
PUT /elser_index
{
"mappings": {
"properties": {
"text": {
"type": "text"
},
"text_embedding": {
"type": "sparse_vector"
}
}
},
"settings": {
"index": {
"default_pipeline": "my-elser-pipeline"
}
}
}
将页面拆分为段落并摄取到Elasticsearch
现在ELSER模型已经部署,我们可以开始将PDF页面拆分为段落并将其摄取到Elasticsearch。
# pip install pypdf langchain_community langchain elasticsearch tiktoken langchain-elasticsearch
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from elasticsearch import helpers, Elasticsearch
loader = PyPDFLoader("https://arxiv.org/pdf/2103.15348.pdf")
client = Elasticsearch(
"<my-cloud-url>",
api_key=ELASTIC_API_KEY,
)
data = loader.load()
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=256
)
docs = loader.load_and_split(text_splitter=text_splitter)
helpers.bulk(
client,
[
{
"_index": "elser_index",
"_source": {
"text": doc.page_content,
"page_number": i,
},
}
for i, doc in enumerate(docs)
],
request_timeout=60,
)
就是这样!我们应该已经将使用ELSER嵌入的段落摄取到Elasticsearch中了。
在Playground上查看实际效果
现在选择索引时,我们将使用deployment_id生成基于ELSER的查询,用于嵌入查询字符串。
提问时,我们现在可以使用语义搜索查询来检索与问题语义含义匹配的文档。
简化混合搜索
启用文本字段还可以启用混合搜索。检索文档时,我们现在将搜索关键字匹配和语义含义,并使用RRF算法对这两个结果集进行排名。
改进LLM的答案
使用Playground,您可以调整提示、微调检索并创建多个索引(分块策略和嵌入模型)以改进和比较您的响应。
将来,我们将提供有关如何充分利用索引的提示,并建议优化检索策略的方法。
系统提示
默认情况下,我们提供一个简单的系统提示,您可以在模型设置中更改它。这与更广泛的系统提示结合使用。您可以通过编辑来更改简单的系统提示。
优化上下文
良好的响应依赖于良好的上下文。使用诸如对内容进行分块并针对您的数据优化分块策略之类的的方法非常重要。除了对数据进行分块外,您还可以尝试不同的文本嵌入模型来查看哪种模型可以为您提供最佳结果。在上面的示例中,我们使用了Elastic自己的ELSER模型,但推理服务支持许多可能更适合您需求的嵌入模型。
优化上下文的其他好处包括更好的成本效率和速度:成本是根据标记(输入和输出)计算的。借助分块和Elasticsearch强大的检索功能,我们可以提供的相关文档越多,用户的成本就越低,延迟就越快。
如果您注意到,我们在BM25示例中使用的输入标记比ELSER示例中的输入标记更大。这是因为我们有效地对文档进行了分块,并且只向LLM提供了页面上最相关的段落。
最后一步!将RAG集成到您的应用程序中
如果您对响应感到满意,则可以将此体验集成到您的应用程序中。“查看代码”提供了如何在您自己的API中执行此操作的示例应用程序代码。
目前,我们提供OpenAI或LangChain的示例,但是Elasticsearch查询、系统提示以及模型和Elasticsearch之间的一般交互相对易于适应您自己的用途。
结论
可以考虑使用多种方法构建对话式搜索体验,而这些选择可能会让人不知所措,尤其是在新的重新排序和检索技术快速发展的背景下,这两种技术都适用于RAG应用程序。
借助我们的Playground,即使开发人员可以使用大量的功能,这些选择也变得简单直观。我们的方法独树一帜,因为它立即将混合搜索作为构建的主要支柱,并对所选和分块数据的形状有直观的理解,并可以访问多个外部LLM提供商。
使用Playground构建、测试和玩乐
尝试Playground 演示或访问Playground 文档立即开始!在GitHub上探索Search Labs,了解Cohere、Anthropic、Azure OpenAI等提供商的新教程和集成。