如何使用 Microsoft Semantic Kernel 的 Elasticsearch Vector Store Connector 进行 AI Agent 开发
Microsoft Semantic Kernel 是一款轻量级开源开发工具包,可让你轻松构建 AI 代理并将最新的 AI 模型集成到你的 C#、Python 或 Java 代码库中。随着 Semantic Kernel Elasticsearch Vector Store Connector 的发布,使用 Semantic Kernel 构建 AI 代理的开发人员现在可以将 Elasticsea
作者:来自 Elastic Florian Bernd 及 Srikanth Manvi
Microsoft Semantic Kernel 是一款轻量级开源开发工具包,可让你轻松构建 AI 代理并将最新的 AI 模型集成到你的 C#、Python 或 Java 代码库中。随着 Semantic Kernel Elasticsearch Vector Store Connector 的发布,使用 Semantic Kernel 构建 AI 代理的开发人员现在可以将 Elasticsearch 插入为可扩展的企业级向量存储,同时继续使用 Semantic Kernel 抽象。
我们与 Microsoft Semantic Kernel 团队合作,宣布为 Microsoft Semantic Kernel (.NET) 用户推出 Semantic Kernel Elasticsearch Vector Store Connector。Semantic Kernel 简化了企业级 AI 代理的构建,包括使用来自 Vector Store 的更相关的数据驱动响应来增强大型语言模型 (large language models - LLMs) 的能力。Semantic Kernel 提供了一个无缝的抽象层,用于与 Elasticsearch 等 Vector Store 交互,提供创建、列出和删除记录集合以及上传、检索和删除单个记录等基本功能。
开箱即用的 Semantic Kernel Elasticsearch Vector Store Connector 支持 Semantic Kernel 向量存储抽象,这使得开发人员在构建 AI 代理时可以非常轻松地将 Elasticsearch 作为向量存储插入。
Elasticsearch 在开源社区中拥有强大的基础,最近采用了 AGPL 许可证。结合开源 Microsoft Semantic Kernel,这些工具提供了强大的企业级解决方案。你可以通过运行以下命令在几分钟内启动 Elasticsearch 来开始本地操作:curl -fsSL https://elastic.co/start-local | sh(有关详细信息,请参阅 start-local),并在生产 AI 代理时迁移到云托管或自托管版本。
在本博客中,我们将介绍如何在使用 Semantic Kernel 时使用 Semantic Kernel Elasticsearch Vector Store Connector。未来将提供 Python 版本的连接器。
高级场景
在下一节中,我们将介绍一个示例。在高级层面上,我们正在构建一个 RAG(Retrieval Augmented Generation - 检索增强生成)应用程序,它将用户的问题作为输入并返回答案。我们将使用 Azure OpenAI(也可以使用本地 LLM)作为 LLM,使用 Elasticsearch 作为向量存储,使用 Semantic Kernel (.net) 作为框架将所有组件绑定在一起。
如果你不熟悉 RAG 架构,可以通过本文快速了解:https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag。
答案由 LLM 生成,该 LLM 以从 Elasticsearch 向量存储中检索的与问题相关的上下文为输入。响应还包括 LLM 用作上下文的源。
RAG 示例
在这个特定示例中,我们构建了一个应用程序,允许用户询问有关存储在内部酒店数据库中的酒店的问题。例如,用户可以根据不同的条件搜索特定酒店,或要求提供酒店列表。
对于示例数据库,我们生成了一个包含 100 个条目的酒店列表。样本大小故意很小,以便你尽可能轻松地试用连接器演示。在实际应用程序中,Elasticsearch 连接器将显示其相对于其他选项(例如 “InMemory” 向量存储实现)的优势,尤其是在处理大量数据时。
完整的演示应用程序可以在 Elasticsearch 向量存储连接器存储库中找到。
让我们从将所需的 NuGet 包和使用指令添加到我们的项目开始:
dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
我们现在可以创建我们的数据模型并为其提供语义内核特定的属性来定义存储模型模式和一些文本搜索的提示:
/// <summary>
/// Data model for storing a "hotel" with a name, a description, a description embedding and an optional reference link.
/// </summary>
public sealed record Hotel
{
[VectorStoreRecordKey]
public required string HotelId { get; set; }
[TextSearchResultName]
[VectorStoreRecordData(IsFilterable = true)]
public required string HotelName { get; set; }
[TextSearchResultValue]
[VectorStoreRecordData(IsFullTextSearchable = true)]
public required string Description { get; set; }
[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
[TextSearchResultLink]
[VectorStoreRecordData]
public string? ReferenceLink { get; set; }
}
存储模型架构属性(`VectorStore*`)与 Elasticsearch Vector Store Connector 的实际使用最为相关,即:
- VectorStoreRecordKey 用于将记录类上的属性标记为记录存储在向量存储中的 key。
- VectorStoreRecordData 用于将记录类上的属性标记为 “data”。
- VectorStoreRecordVector 用于将记录类上的属性标记为 vector。
所有这些属性都接受各种可选参数,可用于进一步自定义存储模型。例如,在 VectorStoreRecordKey 的情况下,可以指定不同的距离函数或不同的索引类型。
文本搜索属性(TextSearch*)在本示例的最后一步中非常重要。我们稍后会回到它们。
在下一步中,我们初始化语义内核引擎并获取对核心服务的引用。在实际应用中,应该使用依赖注入(dependency injection),而不是直接访问服务集合。同样的事情适用于硬编码配置和机密,应该使用配置提供(configuration provider)程序来读取它们:
var builder = Host.CreateApplicationBuilder(args);
// Register AI services.
var kernelBuilder = builder.Services.AddKernel();
kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");
kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");
// Register text search service.
kernelBuilder.AddVectorStoreTextSearch<Hotel>();
// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
.Authentication(new BasicAuthentication("elastic", "my_password"));
kernelBuilder.AddElasticsearchVectorStoreRecordCollection<string, Hotel>("skhotels", elasticsearchClientSettings);
// Build the host.
using var host = builder.Build();
// For demo purposes, we access the services directly without using a DI context.
var kernel = host.Services.GetService<Kernel>()!;
var embeddings = host.Services.GetService<ITextEmbeddingGenerationService>()!;
var vectorStoreCollection = host.Services.GetService<IVectorStoreRecordCollection<string, Hotel>>()!;
// Register search plugin.
var textSearch = host.Services.GetService<VectorStoreTextSearch<Hotel>>()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));
现在可以使用 vectorStoreCollection 服务来创建集合并提取一些演示记录:
await vectorStoreCollection.CreateCollectionIfNotExistsAsync();
// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
.Select(x => x.Split(';'));
foreach (var chunk in hotels.Chunk(25))
{
var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x => x[2]).ToArray());
for (var i = 0; i < chunk.Length; ++i)
{
var hotel = chunk[i];
await vectorStoreCollection.UpsertAsync(new Hotel
{
HotelId = hotel[0],
HotelName = hotel[1],
Description = hotel[2],
DescriptionEmbedding = descriptionEmbeddings[i],
ReferenceLink = hotel[3]
});
}
}
这显示了语义内核如何将向量存储的使用及其所有复杂性简化为几个简单的方法调用。
在底层,在 Elasticsearch 中创建一个新索引,并创建所有必要的属性映射。 然后,我们的数据集完全透明地映射到存储模型中,并最终存储在索引中。 以下是 Elasticsearch 中的映射。
{
"mappings": {
"properties": {
"descriptionEmbedding": {
"dims": 1536,
"index": true,
"index_options": {
"type": "hnsw"
},
"similarity": "cosine",
"type": "dense_vector"
},
"hotelName": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
}
}
embeddings.GenerateEmbeddingsAsync() 透明地调用已配置的 Azure AI 嵌入生成服务。
在本演示的最后一步中,你可以观察到更多神奇之处。
只需一次调用 InvokePromptAsync,当用户询问有关数据的问题时,就会执行以下所有操作:
- 1. 生成用户问题的嵌入
- 2. 在向量存储中搜索相关条目
- 3. 将查询结果插入提示模板
- 4. 将最终提示形式的实际查询发送到 AI 聊天完成服务
// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
promptTemplate: """
Please use this information to answer the question:
{{#with (SearchPlugin-GetTextSearchResults question)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Source: {{Link}}
-----------------
{{/each}}
{{/with}}
Include the source of relevant information in the response.
Question: {{question}}
""",
arguments: new KernelArguments
{
{ "question", "Please show me all hotels that have a rooftop bar." },
},
templateFormat: "handlebars",
promptTemplateFactory: new HandlebarsPromptTemplateFactory());
还记得我们之前在数据模型上定义的 TextSearch* 属性吗?这些属性使我们能够在提示模板中使用相应的占位符,这些占位符会自动填充来自向量存储中的条目的信息。
对我们的问题 “Please show me all hotels that have a rooftop bar.” 的最终答复如下:
Console.WriteLine(response.ToString());
// > The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).
答案正确地引用了我们 hotels.csv 中的以下条目
9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567
此示例很好地展示了如何使用 Microsoft Semantic Kernel 通过其深思熟虑的抽象显著降低复杂性,同时实现极高的灵活性。例如,通过更改一行代码,可以替换使用的向量存储或 AI 服务,而无需重构代码的任何其他部分。
同时,该框架提供了大量高级功能,例如 `InvokePrompt` 函数或模板或搜索插件系统。
完整的演示应用程序可以在 Elasticsearch 向量存储连接器存储库中找到。
ES 还能实现哪些功能
- Elasticsearch 新的 semantic_text 映射:简化语义搜索
- 使用检索器在 Elasticsearch 中进行语义重新排序
- 高级 RAG 技术第 1 部分:数据处理
- 高级 RAG 技术第 2 部分:查询和测试
- 使用 Llama 3 开源和 Elastic 构建 RAG
- 关于从头开始使用 LangGraph、LLaMA3 和 Elasticsearch 向量存储构建本地代理的教程
下一步是什么?
- 我们展示了如何在 .NET 中构建 GenAI 应用程序时将 Elasticsearch 向量存储轻松插入 Semantic Kernel。请继续关注接下来的 Python 集成。
- 随着 Semantic Kernel 为混合搜索(hybrid search)等高级搜索功能构建抽象,Elasticsearch 连接将使 .NET 开发人员能够在使用 Semantic Kernel 时轻松实现它们。
Elasticsearch 与业界领先的 Gen AI 工具和提供商进行了原生集成。查看我们的网络研讨会,了解如何超越 RAG 基础知识,或构建可用于生产的应用程序 Elastic Vector Database。
更多推荐
所有评论(0)