作者:来自 Elastic Paul Oremland
背景介绍
最近,[微软通过 Azure OpenAI 服务 "On Your Data" 将 Elasticsearch 直接集成到 Azure 中](techcommunity.microsoft.com/t5/ai-azure... "微软通过 Azure OpenAI 服务 "On Your Data" 将 Elasticsearch 直接集成到 Azure 中")。"On Your Data" 使组织能够利用强大的 AI 模型(如 GPT-4 和 RAG 模型)构建最先进的对话体验。这样可以让大型语言模型基于你的私有数据,从而确保对话内容的知情性和准确性。
在本文中,我想展示如何轻松启动一个 Elasticsearch 集群,使用 Elasticsearch 连接器来摄取数据,然后通过新的 Azure OpenAI 服务 "On Your Data" 集成来与这些数据进行对话。我们将在本文中使用的一个工具是最近新增的 Connectors API 和 CLI,这使我们能够完全通过编程方式与 Elasticsearch 进行交互,而无需在开发环境和 Kibana 之间来回切换。
先决条件
在本示例中,我将使用 openlibrary.org 中的 OpenLibrary 数据集。 为了使用此数据集,你需要做一些前期工作,让数据准备好摄取到 Elasticsearch 中。 具体来说,你需要:
- 从 openlibrary.org/developers/... 下载最新的数据转储
- 将数据转储导入 PostgreSQL。 我使用了 github.com/LibrariesHa... 中列出的开源工具和步骤
虽然步骤 2 并不是绝对必要的,因为你可以编写自定义摄取脚本,但出于几个原因,我喜欢将数据暂存在 PostgreSQL 中。 首先,Elasticsearch 开箱即用地支持通过连接器从 PostgreSQL 获取数据。 连接器已经针对吞吐量进行了优化,并且内置了一些很好的弹性,我讨厌重新发明轮子。 其次,我可以编写自定义 SQL 查询以与连接器一起使用。 这意味着我有很大的灵活性,可以从数据集中仅提取我想要的数据,并且可以将 "book" 的高度规范化的数据表示为单个文档,而无需编写自定义代码来从原始参考数据转储中执行此操作。 因此,我不必花几天时间编写和调整自定义脚本来创建代表书籍的版本、作者和作品的单个文档,而是只需花费几分钟将数据导入 PostgreSQL 并设置连接器将数据提取到 Elasticsearch 中。
启动 Elasticsearch 集群
现在我已经准备好所有要摄取的数据,我将创建一个 Elasticsearch 部署。我选择使用 Elastic Cloud,因为我不想担心或管理任何基础设施。
注意,我将主要通过命令行和 curl 来完成大部分操作。我会使用一些需要在命令行中导出的 shell 变量,具体包括:
- ES_URL:你的 Elasticsearch 部署的 URL
- API_KEY:我们在后续步骤中创建的密钥
- CONNECTOR_ID:在后续步骤中创建的连接器 ID
第一步是在 . cloud.elastic.co/deployments... 或者通过 API 创建一个部署。因为我知道我会对数百万文档生成嵌入并在查询中执行文本扩展,所以我要将机器学习实例的最小大小增加到 4GB。
展开 "Advanced settings" 部分。
向下滚动到机器学习实例并将 "Minimum size per zone" 更新为 4GB
现在你可以单击 "Create deployment" 按钮。 稍后你的部署就会完成。
接下来,让我们创建一个 API 密钥。 请记住将其保存在安全的地方,因为我们会经常使用它。 请注意,为了使这个示例简单,我使用了过于宽松的访问权限。 在实践中(和在生产中),你可能希望使用角色和/或限制来设置更细粒度的权限。
vbnet
1. curl --user elastic -X PUT "${ES_URL}/_security/api_key?pretty" \
2. -H "Content-Type: application/json" \
3. -d'
4. {
5. "name": "books-api-key",
6. "role_descriptors": {
7. "role-name": {
8. "cluster": ["all"],
9. "index": [ { "names": ["*"], "privileges": ["all"] } ]
10. }
11. }
12. }
13. '
接下来,你需要创建索引并为 title 和 descriptions 字段设置 dense_vector 映射。 此步骤假设你知道文档架构,这会产生一种"先有鸡还是先有蛋"的问题,因为直到摄取数据之后你才知道该架构。 为了简单起见,我使用下面列出的相同步骤设置了一个临时索引,创建了一个连接器,并提取了一个文档(将 SQL 查询的限制设置为 1)。 这使我能够知道文档 schema 是什么样子,这样我就可以在开始时生成我想要的映射。 这是我们团队正在努力在未来版本中解决的已知尖锐问题。
vbnet
1. curl -X PUT "${ES_URL}/books-index?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "mappings": {
7. "properties": {
8. "public_author_works_authors_edition_isbns_editions_works_description": {
9. "type": "text",
10. "copy_to": ["description"]
11. },
12. "public_author_works_authors_edition_isbns_editions_works_title": {
13. "type": "text",
14. "copy_to": ["title"]
15. },
16. "description": { "type": "text" },
17. "title": { "type": "text" },
18. "title_embedding": { "type": "dense_vector" },
19. "description_embedding": { "type": "dense_vector" }
20. }
21. }
22. }
23. '
现在我们已经创建了索引并设置了映射,让我们下载 E5 模型并将输入字段名称设置为我们刚刚创建的密集向量映射。
vbnet
1. curl -X PUT "${ES_URL}/_ml/trained_models/.multilingual-e5-small_linux-x86_64?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "input": {
7. "field_names": ["title_embedding", "description_embedding"]
8. }
9. }
10. '
接下来我们要部署模型。 如果你收到 408,则需要在执行上一个命令后等待一分钟,以便首先完成下载。
bash
curl -X POST "${ES_URL}/_ml/trained_models/.multilingual-e5-small_linux-x86_64/deployment/_start?wait_for=started&deployment_id=for_search&pretty" -H "Authorization: ApiKey "${API_KEY}""
最后,我们需要创建一个推理管道,当我们从连接器获取数据时,该管道将在 title 和 decription 字段上创建嵌入。
vbnet
1. curl -X PUT "${ES_URL}/_ingest/pipeline/e5-small-books?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "processors": [{
7. "inference": {
8. "model_id": "for_search",
9. "input_output": [
10. {
11. "input_field": "public_author_works_authors_edition_isbns_editions_works_title",
12. "output_field": "title_embedding"
13. },
14. {
15. "input_field": "public_author_works_authors_edition_isbns_editions_works_description",
16. "output_field": "description_embedding"
17. }
18. ]
19. }
20. }]
21. }
22. '
摄取数据
现在我们已经创建、配置了集群并准备使用,让我们开始提取数据。 为此,我们将使用 Elasticsearch PostgreSQL 连接器。 我将从源代码中执行此操作,因为为什么不呢,但如果你想要更多开箱即用的东西,你可以从 Docker 安装。
首先,让我们克隆 GitHub 存储库并安装所有依赖项。
bash
git clone git@github.com:elastic/connectors.git
bash
cd connectors && make clean install
接下来,我将使用神奇的连接器 CLI 对我的 Elasticsearch 实例进行身份验证,并让它处理基本的连接器配置。 第一步是通过 CLI 登录 Elasticsearch,并在出现提示时输入你的 Elasticsearch 实例 URL 和 API 密钥。
bash
./bin/connectors login --method apikey
接下来,创建连接器并在出现提示时输入 PostgreSQL 连接信息。
markdown
1. ./bin/connectors connector create \
2. --from-index \
3. --index-name books-index \
4. --service-type postgresql \
5. --index-language en \
6. --update-config
8. ? Connector name: books-postgresql-connector
9. ? Host []: localhost
10. ? Port []: 5432
11. ? Username []: \<USERNAME>
12. ? Password []: \<PASSWORD>
13. ? Database []: openlibrary
14. ? Schema []: public
15. ? Comma-separated list of tables []: *
16. ? Rows fetched per request []:
17. ? Retries per request []:
18. ? Enable SSL verification [False]:
因为我们的数据源有大量标准化数据,所以我们需要创建一个自定义 SQL 语句,我们可以将连接器配置为与其高级同步规则功能一起使用。 我们将使用以下 SQL 语句来帮助我们从高度相关的数据创建单个文档。 请注意,由于此 SQL 将通过 curl 命令发送,因此我们需要使用 ''' 转义 '。
sql
1. SELECT DISTINCT ON (author_works.work_key, editions.data->>'\''publish_date'\'') author_works.author_key,
2. editions.key,
3. author_works.author_key,
4. author_works.work_key,
5. edition_isbns.edition_key,
6. edition_isbns.isbn,
7. authors.key,
8. works.key,
9. works.data->>'\''title'\'' AS title,
10. authors.data->>'\''name'\'' AS author_name,
11. COALESCE(works.data->'\''description'\''->>'\''value'\'', '\'''\'') AS description,
12. edition_isbns.isbn AS isbn,
13. editions.data->>'\''publish_date'\'' as publish_date
14. FROM editions
15. JOIN edition_isbns on edition_isbns.edition_key = editions.key
16. JOIN works on works.key = editions.work_key
17. JOIN author_works on author_works.work_key = works.key
18. JOIN authors on author_works.author_key = authors.key
19. WHERE
20. EXISTS (SELECT 1 FROM jsonb_array_elements(editions.data->'\''languages'\'') AS elem WHERE elem->>'\''key'\'' = '\''/languages/eng'\'')
21. ORDER BY
22. editions.data->>'\''publish_date'\'' desc
起草高级同步规则。 连接器运行后,被创建的同步规则将在通过验证后激活。
注意:此 API 调用当前在 Elasticsearch Serverless 中可用,并将包含在 Elasticsearch 8.14 版本中。
vbnet
1. curl -X PUT "${ES_URL}/_connector/${CONNECTOR_ID}/_filtering?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "advanced_snippet": {
7. "value": [{
8. "tables": [ "editions", "works", "author_works", "authors", "edition_isbns" ],
9. "query": "\<SQL FROM ABOVE>"
10. }]
11. }
12. }
13. '
我们还希望将连接器连接到我们之前创建的推理管道,以便在摄取文档时自动生成嵌入。
vbnet
1. curl -X PUT "${ES_URL}/_connector/${CONNECTOR_ID}/_pipeline?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "pipeline": {
7. "extract_binary_content": true,
8. "name": "e5-small-books",
9. "reduce_whitespace": true,
10. "run_ml_inference": true
11. }
12. }
13. '
接下来,我们将使用 API 启动同步,将数据提取到 Elasticsearch 中。
vbnet
1. curl -X POST "${ES_URL}/_connector/_sync_job?pretty" \
2. -H "Authorization: ApiKey "${API_KEY}"" \
3. -H "Content-Type: application/json" \
4. -d'
5. {
6. "id": $CONNECTOR_ID,
7. "job_type": "full"
8. }
9. '
最后,是时候运行连接器了:
arduino
make run
你可以使用以下命令检查摄取了多少文档:
bash
curl -X POST "${ES_URL}/books-index/_count&pretty" -H "Authorization: ApiKey "${API_KEY}""
将 Azure OpenAI On Your Data 连接到 Elasticsearch
现在我们已经部署了 Elasticsearch 并提取了所有数据,让我们使用 Azure OpenAI 服务的 "On Your Data" 与索引进行对话。 为此,我将使用 Azure OpenAI 控制台。
我们需要做的第一件事是打开 Azure AI Studio 的 Web 界面并:
- 打开 "Chat" 游乐场
- 选择 "Add your data" 选项卡
- 单击 "Add your data source" 按钮
接下来,选择 Elasticsearch 作为你的数据源,然后:
- 输入你的 Elasticsearch 端点 URL(上面的 $ES_URL)
- 输入你的 API 密钥(上面的 $API_KEY)
- 单击 "Verify connection" 按钮
- 选择你的 books-index
- 选中 "Use custom field mapping" 按钮
- 单击 "Next" 按钮
接下来,我们将通过以下方式设置搜索类型和嵌入模型:
- 从 Search type 下拉列表中选择 "Vector"
- 选择 "Elasticsearch - .multilingual-e5-small_linux-x86_64" 作为嵌入模型
- 单击 "Next" 按钮
接下来,我们将通过以下方式配置数据字段:
- 从 "Content data" 下拉列表中选择包含相关内容的所有字段(例如 "author_name" 和 "description")
- 保留 "title" 和 "Vector fields" 的默认值
在下一个屏幕上,检查你的 data source 设置,然后单击 "Save and close" 按钮,你就可以开始聊天了。
现在你已准备好与你的数据聊天。 这就是语义搜索真正发挥作用的地方。 我能够提出一个初始问题,然后使用聊天历史记录的语义和上下文,能够以自然语言询问我的数据的后续问题。
最棒的是我可以看到与弹出窗口中提供的上下文一致的引文。
接下来该怎么做?
与索引对话只是你能做的众多强大功能之一。在 Azure OpenAI On Your Data 集成之外,一旦你将数据导入 Elasticsearch,还有许多其他尝试,例如使用我们支持的众多第三方嵌入模型之一来部署文本嵌入模型。
可以尝试以下内容:
- 使用由 Elastic 训练的稀疏向量检索模型 ELSER。
- 使用 Cohere、HuggingFace 或 OpenAI 的嵌入模型设置语义搜索。
- 一般来说,任何支持架构的训练模型都可以使用 eland 部署到 Elasticsearch 中。
Elasticsearch 还提供了以下功能:
- 使用搜索模板,这是一种存储的搜索,你可以使用不同的变量来运行搜索,而不需要向用户公开 Elasticsearch 的查询语法。搜索模板允许你在不修改应用程序代码的情况下更改搜索。
- 使用混合搜索,将词法(BM25)搜索与近似最近邻(kNN)搜索的强大功能结合起来。
- 使用学习排序(Learn To Rank)或查询重评分阶段重新排序,以及使用 Cohere 的重新排序 API。
并非所有搜索用例都是语义搜索用例。如果词法搜索(BM25)更适合你的搜索用例,Elasticsearch 提供了几个功能来帮助你获得最相关的搜索结果:
- 同义词可以帮助你找到包含类似词语的文档,纠正常见拼写错误,或使特定领域的语言更易于用户理解。你可以使用 synonym API 在内部系统索引中定义和管理同义词,或将相关同义词分组到 "同义词集" 中。
- Query Rules 允许为符合指定条件的查询自定义搜索结果。这可以更好地控制结果,例如确保符合定义条件的推广文档出现在结果列表的顶部。
准备好自己尝试了吗?开始免费试用。 Elasticsearch 还集成了 LangChain、Cohere 等工具。加入我们的高级语义搜索网络研讨会,构建你的下一代 AI 应用程序!
原文:Azure OpenAI On Your Data: How to use Elasticsearch as the Vector Database --- Elastic Search Labs