作者:来自 Elastic Jeff Vestal
关注博客 ChatGPT 和 Elasticsearch:OpenAI 遇到私人数据。
在此博客中,你将了解如何:
- 创建 Elasticsearch Serverless 项目
- 创建推理端点以使用 ELSER 生成嵌入
- 使用语义文本字段进行自动分块并调用 Inference Endpoint
- 使用 Open Crawler 抓取博客
- 使用 Elastic 的 Playground 连接到 LLM 以测试 RAG 聊天应用程序的提示和上下文设置。
如果你想直接进入代码,可以在此处查看随附的 Jupyter Notebook。
2023 年 4 月
自从我编写最初的《ChatGPT 和 Elasticsearch:OpenAI 遇到了私人数据》 以来,很多事情都发生了变化:。大多数人只是在玩 ChatGPT,如果他们真的尝试过的话。而且每个技术会议的每个展位都没有出现 “AI” 字母(无论它是否有用)。
2024 年 8 月
从那时起,Elastic 就致力于成为一个功能齐全的向量数据库,并投入了大量的工程努力,使其成为任何构建搜索应用程序的人的最佳向量数据库选项。为了避免花几页纸来谈论 Elasticsearch 的所有增强功能,下面是一个不分先后顺序的非详尽列表:
- ELSER - Elastic Learned Sparse Encoder
- Elastic Serverless Service 已构建并处于公开测试阶段
- Elasticsearch 开放推理 API
- Embeddings
- Chat completion
- Semantic rerankers
- Semantic_text 类型 - 简化语义搜索
- 自动分块
- Playground - 在 Elasticsearch 中直观地尝试 RAG 应用程序构建
- Retrievers - 检索器
- Open Web Crawler - 开放网络爬虫
随着所有这些变化以及更多变化,原始博客需要重写。让我们开始吧。
更新流程
此更新流程的计划如下:
- 设置
- 创建一个新的 Elasticsearch Serverless 搜索项目
- 使用 ELSER 创建嵌入推理 API
- 使用 semantic_text 字段配置索引模板
- 创建一个新的 LLM 连接器
- 使用我们的 LLM 连接器配置chat completion 推理服务
- 提取和测试
- 使用 Elastic Open Web Crawler 爬取 Elastic Labs 网站(搜索、可观察性、安全性)。
- 使用 Playground 使用我们索引的 Labs 内容测试提示
- 配置和部署我们的应用程序
- 将生成的代码从 Playground 导出到使用 FastAPI 作为后端并使用 React 作为前端的应用程序。
- 在本地运行
- 可选择将我们的聊天机器人部署到 Google Cloud Run
设置
Elasticsearch Serverless 项目
我们将为我们的聊天机器人使用 Elastic Serverless 项目。Serverless 消除了运行 Elasticsearch 集群的大部分复杂性,让你专注于实际使用和从数据中获取价值。在此处阅读有关 serverless 架构的更多信息。
如果你没有 Elastic Cloud 帐户,你可以在 elastic.co 创建为期两周的免费试用版(Serverless 定价在此处提供)。如果你已经有一个,你只需登录即可。
登录后,你需要创建一个云 API 密钥。
注意:在下面的步骤中,我将展示 Python 代码的相关部分。为了简洁起见,我不会展示导入所需库、等待步骤完成、捕获错误等的完整代码。
如需运行更强大的代码,请参阅随附的 Jypyter 笔记本!
创建 serverless 项目
我们将使用新创建的 API 密钥执行接下来的设置步骤。
首先,创建一个新的 Elasticsearch 项目。
url = "https://api.elastic-cloud.com/api/v1/serverless/projects/elasticsearch"
project_data = {
"name": "The RAG Really Tied the App Together",
"region_id": "aws-us-east-1",
"optimized_for": "vector"
}
auth_header = f"ApiKey {api_key}" # seeing what a comment lokos like with pound
headers = {
"Content-Type": "application/json",
"Authorization": auth_header
}
es_project = requests.post(url, json=project_data, headers=headers) :four:
- url - 这是 Elastic Cloud 的标准 Serverless 端点
- project_data - 你的 Elasticsearch Serverless 项目设置
- name - 我们想要的项目名称
- region_id - 要部署的区域
- optimized_for - 配置类型 - 我们使用的向量对于 ELSER 模型来说并不是严格要求的,但如果你选择密集向量模型(例如 e5),它可能很合适。
创建 Elasticsearch Python 客户端
创建程序化项目的一大好处是,你将获得与其交互所需的连接信息和凭据!
es = Elasticsearch(es_project_keys['endpoints']['elasticsearch'],
basic_auth=(es_project_keys['credentials']['username'],
es_project_keys['credentials']['password']
)
)
ELSER 嵌入 API
项目创建完成后(通常只需几分钟),我们就可以准备处理实验室的数据。
第一步是配置用于嵌入的推理 API。我们将使用 Elastic Learned Sparse Encoder (ELSER)。
- 创建推理端点的命令
- 指定此端点将用于生成稀疏嵌入
model_config = {
"service": "elser",
"service_settings": {
"num_allocations": 8,
"num_threads": 1
}
}
inference_id = "my-elser-model"
create_endpoint = es.inference.put_model(
inference_id=inference_id,
task_type="sparse_embedding",
body=model_config
)
- model_config - 我们要用于部署语义重新排序模型的设置
- service - 使用预定义的 elser 推理服务
- service_settings.num_allocations - 使用 8 个分配部署模型
- service_settings.num_threads - 每个分配使用一个线程部署
- inference_id - 你要赋予推理端点的名称
- task_type- 指定此端点将用于生成稀疏嵌入
此单个命令将触发 Elasticsearch 执行几个任务:
- 它将下载 ELSER 模型。
- 它将部署(启动)具有八个分配和每个分配一个线程的 ELSER 模型。
- 它将创建我们在下一步的字段映射中使用的推理 API。
索引映射
创建 ELSER API 后,我们将创建索引模板。
template_body = {
"index_patterns": ["elastic-labs*"],
"template": {
"mappings": {
"properties": {
"body": {
"type": "text",
"copy_to": "semantic_body"
},
"semantic_body": {
"type": "semantic_text",
"inference_id": "my-elser-model"
},
"headings": {
"type": "text"
},
"id": {
"type": "keyword"
},
"meta_description": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
}
template_resp = es.indices.put_index_template( :eight:
name="labs_template",
body=template_body
)
- index_patterns - 我们希望此模板应用到的索引模式。
- body - 爬虫收集的网页的主要内容将被写入
- type - 它是一个文本字段
- copy_to - 我们需要将该文本复制到我们的语义文本字段进行语义处理
- semantic_body 是我们的 semantic text 字段
- 此字段将自动处理长文本的分块并生成嵌入,我们稍后将使用它进行语义搜索
- inference_id 指定我们上面创建的推理端点的名称,允许我们从我们的 ELSER 模型生成嵌入
- headers - 来自 html 的标题标签
- id - 此文档的抓取 id
- meta_description - 来自 html 的描述元标记的值
- title 是内容来自的网页的标题
其他字段将被索引但自动映射。我们重点在模板中预定义的字段不需要同时是关键字和文本类型,否则会自动定义。
最重要的是,对于本指南,我们必须定义我们的 semantic_text 字段并使用 copy_to 设置要从中复制的源字段。在这种情况下,我们有兴趣对文本正文执行语义搜索,爬虫会将其索引到 body 中。
爬取所有实验室!
我们现在可以安装和配置爬虫程序来爬取 Elastic * 实验室。我们将大致遵循 Open Crawler 发布的优秀指南,该指南发布于技术预览版 Search Labs 博客。
以下步骤将使用 docker 并在 MacBook Pro 上运行。要使用不同的设置运行此程序,请查阅 Open Crawler Github readme。
克隆 repo
打开你选择的命令行工具。我将使用 Iterm2。将 crawler repo 克隆到你的机器上。
~/repos
❯ git clone git@github.com:elastic/crawler.git
Cloning into 'crawler'...
remote: Enumerating objects: 1944, done.
remote: Counting objects: 100% (418/418), done.
remote: Compressing objects: 100% (243/243), done.
remote: Total 1944 (delta 237), reused 238 (delta 170), pack-reused 1526
Receiving objects: 100% (1944/1944), 84.85 MiB | 31.32 MiB/s, done.
Resolving deltas: 100% (727/727), done.
构建爬虫容器
运行以下命令来构建并运行爬虫。
docker build -t crawler-image . && docker run -i -d --name crawler crawler-image
~/repos
❯ cd crawler
~/repos/crawler main
❯ docker build -t crawler-image . && docker run -i -d --name crawler crawler-image
[+] Building 66.9s (6/10) docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 333B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/jruby:9.4.7.0-jdk21 1.7s
=> [auth] library/jruby:pull token for registry-1.docker.io 0.0s
...
...
=> [5/5] RUN make clean install 50.7s
=> exporting to image 0.9s
=> => exporting layers 0.9s
=> => writing image sha256:6b3f4000a121e76aba76fdbbf11b53f53a3fabba61c0b7cf3fdcdb21e244f1d8 0.0s
=> => naming to docker.io/library/crawler-image 0.0s
cc6c16941de04355c050ef5f5fd0041ee7f3505b8cf8448c7223f0d2e80b5498
配置爬虫
在你最喜欢的编辑器(vim)中创建一个新的 YAML:
~/repos/crawler main
❯ vim config/elastic-labs.yml
我们想要抓取这三个实验室网站上的所有文档,但由于这些网站上的博客和教程往往会链接到 elastic.co 的其他部分,因此我们需要设置几次运行来限制范围。我们将允许抓取我们网站的三个路径,然后拒绝其他任何路径。
将以下内容粘贴到文件中并保存
domains:
- url: https://www.elastic.co
seed_urls:
- https://www.elastic.co/search-labs
- https://www.elastic.co/observability-labs
- https://www.elastic.co/security-labs
crawl_rules:
- policy: allow
type: begins
pattern: /search-labs
- policy: allow
type: begins
pattern: /observability-labs
- policy: allow
type: begins
pattern: /security-labs
- policy:deny
type: regex
pattern: .*/author/.*
- policy: deny
type: regex
pattern: .*
output_sink: elasticsearch
output_index: elastic-labs
max_crawl_depth: 2
elasticsearch:
host: "https://<your_serverless_project>.es.<region>.aws.elastic.cloud"
port: "443"
api_key: "<API Key generated above>"
将配置复制到 Docker 容器中:
~/repos/crawler main ⇣
❯ docker cp config/elastic-labs.yml crawler:/app/config/elastic-labs.yml
Successfully copied 2.05kB to crawler:/app/config/elastic-labs.yml
验证域
通过运行以下命令确保配置文件没有问题:
❯ docker exec -it crawler bin/crawler validate config/elastic-labs.yml
Domain https://www.elastic.co is valid
启动爬虫
首次运行爬虫时,处理三个实验室网站上的所有文章可能需要几分钟。
docker exec -it crawler bin/crawler crawl config/elastic-labs.yml
~/repos/crawler/config main ⇣
❯ docker exec -it crawler bin/crawler crawl config/elastic-labs.yml
[crawl:6692c3b584f98612e3a465ce] [primary] Initialized an in-memory URL queue for up to 10000 URLs
[crawl:6692c3b584f98612e3a465ce] [primary] ES connections will be authorized with configured API key
[crawl:6692c3b584f98612e3a465ce] [primary] ES connections will use SSL without ca_fingerprint
[crawl:6692c3b584f98612e3a465ce] [primary] Elasticsearch sink initialized for index [elastic-labs] with pipeline [ent-search-generic-ingestion]
[crawl:6692c3b584f98612e3a465ce] [primary] Starting the crawl with up to 10 parallel thread(s)...
[crawl:6692c3b584f98612e3a465ce] [primary] Crawl status: queue_size=11, pages_visited=1, urls_allowed=12, urls_denied={}, crawl_duration_msec=847, crawling_time_msec=635.0, avg_response_time_msec=635.0, active_threads=1, http_client={:max_connections=>100, :used_connections=>1}, status_codes={"200"=>1}
确认文章已被索引
我们将通过两种方式进行确认。
首先,我们将查看示例文档以确保已生成 ELSER 嵌入。我们只想查看任何文档,这样我们就可以在没有任何参数的情况下进行搜索:
GET elastic-labs/_search
确保你获得结果,然后检查字段 body 是否包含文本以及 semantic_body.inference.chunks.0.embeddings 是否包含标记。
"hits": [
{
"_index": "elastic-labs",
...
"_source": {
"body": "Tutorials Integrations Blog Start Free Trial Contact Sales Open navigation menu Overview ...
"semantic_body": {
"inference": {
"inference_id": "my-elser-model",
"model_settings": {
"task_type": "sparse_embedding"
},
"chunks": [
{
"text": "Tutorials Integrations Blog Start Free Trial Contact Sales Open navigation menu Overview ...
"embeddings": {
"##her": 2.1016746,
"elastic": 2.084594,
"##ai": 1.6336359,
"dock": 1.5765089,
...
我们可以通过 terms 聚合来检查我们是否从三个站点分别收集数据:
GET elastic-labs/_search
{
"size": 0,
"aggs": {
"url_path_dir1": {
"terms": {
"field": "url_path_dir1.keyword"
}
}
}
}
你应该会看到以我们的三个站点路径之一开头的结果。
"buckets": [
{
"key": "security-labs",
"doc_count": 37
},
{
"key": "observability-labs",
"doc_count": 30
},
{
"key": "search-labs",
"doc_count": 6
}
]
前往 Playground!
通过提取、分块和推理数据,我们可以开始编写将与 RAG 应用程序的 LLM 交互的后端应用程序代码。
LLM 连接
我们需要为 Playground 配置一个连接,以便对 LLM 进行 API 调用。截至撰写本文时,Playground 支持与 OpenAI、AWS Bedrock 和 Google Gemini 的聊天完成连接。计划提供更多连接,因此请查看文档以获取最新列表。
首次进入 Playground UI 时,单击 “Connect to an LLM”
由于我在原始博客中使用了 OpenAI,因此我们将坚持使用它。Playground 的优点在于,你可以将连接切换到其他服务,并且 Playground 代码将专门针对该服务的 API 规范生成代码。你只需要选择今天要使用哪一个。
在此步骤中,你必须根据要使用的 LLM 填写字段。如上所述,由于 Playground 将抽象出 API 差异,因此你可以使用任何适合你的受支持的 LLM 服务,本指南中的其余步骤将以相同的方式工作。
如果你没有 Azure OpenAI 帐户或 OpenAI API 帐户,你可以在此处获取一个(OpenAI 现在要求至少 5 美元来资助 API 帐户)。
完成后,点击 “Save”,你将收到连接器已添加的确认信息。之后,你只需选择我们将在应用中使用的索引。你可以选择多个,但由于我们所有的爬虫数据都将进入 elastic-labs,因此你可以选择一个。
单击 “Add data sources”,你就可以开始使用 Playground 了!
选择之前创建的 “restaurant_reviews ”索引。
在 Playground 中尝试
添加数据源后,你将进入 Playground UI。
为了尽可能简化入门过程,我们将坚持使用除提示之外的所有默认设置。但是,有关 Playground 组件及其使用方法的更多详细信息,请查看 Playground:在几分钟内使用 Elasticsearch 尝试 RAG 应用程序博客和 Playground 文档。
试验不同的设置以满足你的特定数据和应用程序需求是设置 RAG 支持的应用程序的重要部分。
我们将使用的默认值是:
- 查询 semantic_body 块
- 使用三个最近的语义块作为上下文传递给 LLM
创建更详细的提示
Playground 中的默认提示只是一个占位符。随着 LLM 功能越来越强大,提示工程也不断发展。探索不断变化的提示工程世界是一个博客,但在创建系统提示时需要记住一些基本概念:
- 在描述 LLM 响应所属的应用程序或服务时要详细说明。这包括将提供哪些数据以及谁将使用响应。
- 提供示例问题和响应。这种称为少发提示的技术可帮助 LLM 构建其响应。
- 清楚地说明 LLM 应如何表现。
- 指定所需的输出格式。
- 测试并迭代提示。
考虑到这一点,我们可以创建一个更详细的系统提示:
You are a helpful and knowledgeable assistant designed to assist users in querying information related to Search, Observability, and Security. Your primary goal is to provide clear, concise, and accurate responses based on semantically relevant documents retrieved using Elasticsearch.
Guidelines:
Audience:
Assume the user could be of any experience level but lean towards a technical slant in your explanations.
Avoid overly complex jargon unless it is common in the context of Elasticsearch, Search, Observability, or Security.
Response Structure:
Clarity: Responses should be clear and concise, avoiding unnecessary verbosity.
Conciseness: Provide information in the most direct way possible, using bullet points when appropriate.
Formatting: Use Markdown formatting for:
Bullet points to organize information
Code blocks for any code snippets, configurations, or commands
Relevance: Ensure the information provided is directly relevant to the user's query, prioritizing accuracy.
Content:
Technical Depth: Offer sufficient technical depth while remaining accessible. Tailor the complexity based on the user's apparent knowledge level inferred from their query.
Examples: Where appropriate, provide examples or scenarios to clarify concepts or illustrate use cases.
Documentation Links: When applicable, suggest additional resources or documentation from Elastic.co that can further assist the user.
Tone and Style:
Maintain a professional yet approachable tone.
Encourage curiosity by being supportive and patient with all user queries, regardless of complexity.
Example Queries:
"How can I optimize my Elasticsearch cluster for large-scale data?"
"What are the best practices for implementing observability in a microservices architecture?"
"How can I secure sensitive data in Elasticsearch?"
你可以随意测试不同的提示和上下文设置,看看你认为最适合特定数据的结果。有关高级技术的更多示例,请查看提示部分两部分博客:高级 RAG 技术中。同样,请参阅 Playground 博客文章,了解有关你可以调整的各种设置的更多详细信息。
导出代码
在后台,Playground 会生成我们执行语义搜索、解析相关上下文字段以及向 LLM 发出聊天完成调用所需的所有后端聊天代码。我们无需进行任何编码工作!
在右上角单击 “View Code” 按钮以展开代码弹出窗口
你将看到生成的 Python 代码,其中包含你配置的所有设置以及对 Elasticsearch 进行语义调用、解析结果、构建完整提示、调用 LLM 和解析这些结果的函数。
单击复制图标以复制代码。
你现在可以将代码合并到你自己的聊天应用程序中!
总结
自一年多前第一次发布此博客以来,发生了很多变化,我们在本博客中涵盖了很多内容。你从云 API 密钥开始,创建了 Elasticsearch Serverless 项目,生成了云 API 密钥,配置了 Open Web Crawler,爬取了三个 Elastic Lab 站点,对长文本进行了分块,生成了嵌入,测试了 RAG 应用程序的最佳聊天设置,并导出了代码!
UI 在哪里,Vestal?
请关注第二部分,我们将在其中将操场代码集成到具有 React 前端的 Python 后端中。我们还将研究部署完整的聊天应用程序。
有关上述所有内容的完整代码集,请参阅随附的 Jypyter 笔记本
准备好自己尝试一下了吗?开始免费试用。
Elasticsearch 集成了 LangChain、Cohere 等工具。加入我们的 Beyond RAG Basics 网络研讨会,构建你的下一个 GenAI 应用程序!
原文:ChatGPT and Elasticsearch integration: RAG enhancements for chatbots — Search Labs