Como usar o conector Elasticsearch Vector Store para o Microsoft Semantic Kernel no desenvolvimento de agentes de IA
O Microsoft Semantic Kernel é um kit de desenvolvimento leve e de código aberto que permite criar facilmente agentes de IA e integrar os modelos de IA mais recentes em seu código C#, Python ou Java. Com o lançamento do Semantic Kernel Elasticsearch Vector Store Connector, os desenvolvedores que usam o Semantic Kernel para criar agentes de IA agora podem integrar o Elasticsearch como um armazenamento vetorial escalável de nível empresarial, continuando a usar as abstrações do Semantic Kernel.
Em colaboração com a equipe do Microsoft Semantic Kernel , estamos anunciando a disponibilidade do Semantic Kernel Elasticsearch Vector Store Connector para usuários do Microsoft Semantic Kernel (.NET). O Semantic Kernel simplifica a criação de agentes de IA de nível empresarial, incluindo a capacidade de aprimorar grandes modelos de linguagem (LLMs) com respostas mais relevantes e orientadas por dados de um Repositório de Vetores. O Semantic Kernel fornece uma camada de abstração perfeita para interagir com armazenamentos vetoriais como o Elasticsearch, oferecendo recursos essenciais como criar, listar e excluir coleções de registros, além de carregar, recuperar e excluir registros individuais.
O conector Semantic Kernel Elasticsearch Vector Store, pronto para uso, oferece suporte às abstrações de armazenamento vetorial do Semantic Kernel, o que facilita muito para os desenvolvedores a integração do Elasticsearch como um armazenamento vetorial durante a criação de agentes de IA.
O Elasticsearch possui uma base sólida na comunidade de código aberto e adotou recentemente a licença AGPL. Em conjunto com o Microsoft Semantic Kernel de código aberto, essas ferramentas oferecem uma solução poderosa e pronta para uso empresarial. Você pode começar localmente executando o Elasticsearch em poucos minutos executando este comando curl -fsSL https://elastic.co/start-local | sh (consulte start-local para obter detalhes) e migrar para versões hospedadas na nuvem ou auto-hospedadas à medida que coloca seus agentes de IA em produção.
Neste blog, veremos como usar o conector Elasticsearch Vector Store do Semantic Kernel ao utilizar o Semantic Kernel. Uma versão em Python do conector será disponibilizada futuramente.
Cenário geral: Construindo um aplicativo RAG com Semantic Kernel e Elasticsearch.
Na seção seguinte, analisaremos um exemplo. Em linhas gerais, estamos construindo um aplicativo RAG (Retrieval Augmented Generation) que recebe uma pergunta do usuário como entrada e retorna uma resposta. Usaremos o Azure OpenAI (também é possível usar um LLM local ) como LLM, o Elasticsearch como repositório de vetores e o Semantic Kernel (.NET) como framework para integrar todos os componentes.
Se você não estiver familiarizado com arquiteturas RAG, pode ter uma breve introdução com este artigo: https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag.
A resposta é gerada pelo LLM, que é alimentado com contexto relevante para a pergunta, obtido do armazenamento de vetores do Elasticsearch. A resposta também inclui a fonte que foi usada como contexto pelo LLM.
Exemplo RAG
Neste exemplo específico, criamos um aplicativo que permite aos usuários fazer perguntas sobre hotéis armazenados em um banco de dados interno de hotéis. O usuário poderia, por exemplo... Pesquise um hotel específico com base em diferentes critérios ou solicite uma lista de hotéis.
Para o banco de dados de exemplo, geramos uma lista de hotéis contendo 100 entradas. O tamanho da amostra é intencionalmente pequeno para permitir que você experimente a demonstração do conector da maneira mais fácil possível. Em uma aplicação do mundo real, o conector Elasticsearch mostraria suas vantagens sobre outras opções, como a implementação de armazenamento vetorial `InMemory`, especialmente ao trabalhar com quantidades extremamente grandes de dados.
A aplicação de demonstração completa pode ser encontrada no repositório do conector de armazenamento vetorial do Elasticsearch.
Vamos começar adicionando os pacotes NuGet necessários e usando as diretivas ao nosso projeto:
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.0using 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;Agora podemos criar nosso modelo de dados e fornecer a ele atributos específicos do Semantic Kernel para definir o esquema do modelo de armazenamento e algumas dicas para a pesquisa de texto:
/// <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; }
}Os atributos do esquema do modelo de armazenamento (`VectorStore*`) são os mais relevantes para o uso prático do conector Elasticsearch Vector Store, a saber:
VectorStoreRecordKeyMarcar uma propriedade em uma classe de registro como a chave sob a qual o registro é armazenado em um armazenamento vetorial.VectorStoreRecordDataMarcar uma propriedade em uma classe de registro como 'dados'.VectorStoreRecordVectorPara marcar uma propriedade em uma classe de registro como um vetor.
Todos esses atributos aceitam vários parâmetros opcionais que podem ser usados para personalizar ainda mais o modelo de armazenamento. No caso de VectorStoreRecordKey , por exemplo, é possível especificar uma função de distância diferente ou um tipo de índice diferente.
Os atributos de pesquisa de texto (TextSearch*) serão importantes na última etapa deste exemplo. Voltaremos a eles mais tarde.
Na próxima etapa, inicializamos o mecanismo do Kernel Semântico e obtemos referências aos serviços principais. Em uma aplicação do mundo real, a injeção de dependência deve ser usada em vez de acessar diretamente a coleção de serviços. O mesmo se aplica à configuração e aos segredos embutidos no código, que devem ser lidos usando um provedor de configuração :
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"));O serviço vectorStoreCollection agora pode ser usado para criar a coleção e para inserir alguns registros de demonstração:
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]
});
}
}Isso demonstra como o Semantic Kernel reduz o uso de um armazenamento vetorial com toda a sua complexidade a algumas chamadas de método simples.
Nos bastidores, um novo índice é criado no Elasticsearch e todos os mapeamentos de propriedades necessários são criados. Nosso conjunto de dados é então mapeado de forma completamente transparente para o modelo de armazenamento e, finalmente, armazenado no índice. Abaixo, você pode ver como os mapeamentos aparecem no Elasticsearch.
{
"mappings": {
"properties": {
"descriptionEmbedding": {
"dims": 1536,
"index": true,
"index_options": {
"type": "hnsw"
},
"similarity": "cosine",
"type": "dense_vector"
},
"hotelName": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
}
}As chamadas embeddings.GenerateEmbeddingsAsync() invocaram de forma transparente o serviço de geração de incorporações de IA do Azure configurado.
Ainda mais magia pode ser observada na última etapa desta demonstração.
Com apenas uma chamada para InvokePromptAsync, todas as seguintes operações são realizadas quando o usuário faz uma pergunta sobre os dados:
1. É gerado um elemento incorporado para a pergunta do usuário.
2. O repositório de vetores é pesquisado em busca de entradas relevantes.
3. Os resultados da consulta são inseridos em um modelo de prompt.
4. A consulta propriamente dita, na forma da mensagem final, é enviada ao serviço de autocompletar do chat por IA.
// 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());Lembra-se dos atributos TextSearch* que definimos anteriormente em nosso modelo de dados? Esses atributos nos permitem usar marcadores correspondentes em nosso modelo de prompt, que são preenchidos automaticamente com as informações de nossas entradas no repositório vetorial.
A resposta final à nossa pergunta "Por favor, mostre-me todos os hotéis que possuem um bar na cobertura" é a seguinte:
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).A resposta correta refere-se à seguinte entrada em nosso arquivo 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/yz567Este exemplo demonstra muito bem como a utilização do Microsoft Semantic Kernel permite uma redução significativa da complexidade através das suas abstrações bem concebidas, além de proporcionar um elevado nível de flexibilidade. Ao alterar uma única linha de código, por exemplo, o armazenamento de vetores ou os serviços de IA utilizados podem ser substituídos sem a necessidade de refatorar qualquer outra parte do código.
Ao mesmo tempo, a estrutura fornece um enorme conjunto de funcionalidades de alto nível, como a função `InvokePrompt`, ou o sistema de plugins de modelo ou de pesquisa.
A aplicação de demonstração completa pode ser encontrada no repositório do conector de armazenamento vetorial do Elasticsearch.
O que mais é possível fazer com o Elasticsearch?
Elasticsearch e Semantic Kernel: O que vem a seguir?
Mostramos como o armazenamento de vetores do Elasticsearch pode ser facilmente integrado ao Semantic Kernel durante a criação de aplicações GenAI em .NET. Fique atento para a integração com Python em breve.
À medida que o Semantic Kernel cria abstrações para recursos de pesquisa avançados, como a pesquisa híbrida, a conexão com o Elasticsearch permitirá que os desenvolvedores .NET os implementem facilmente ao usar o Semantic Kernel.
Perguntas frequentes
O que é o Microsoft Semantic Kernel?
O Microsoft Semantic Kernel é um kit de desenvolvimento leve e de código aberto que permite criar facilmente agentes de IA e integrar os modelos de IA mais recentes em seu código C#, Python ou Java.




