Como usar repositórios vetoriais com pesquisa de texto do kernel semântico
Todos os conectores do Vetor Store podem ser usados para pesquisa de texto.
- Use o conector Vetor Store para recuperar a coleção de registros que você deseja pesquisar.
- Envolva a coleção de registros com
VectorStoreTextSearch
. - Converta em um plug-in para uso em cenários de RAG e/ou chamada de função.
É muito provável que você queira personalizar a função de pesquisa do plugin para que sua descrição reflita o tipo de dados disponíveis na coleção de registros. Por exemplo, se a coleção de registros contiver informações sobre hotéis, a descrição da função de pesquisa do plug-in deve mencionar isso. Isso permitirá que você registre vários plugins, por exemplo, um para pesquisar hotéis, outro para restaurantes e outro para coisas para fazer.
As abstrações de pesquisa de texto incluem uma função para retornar um resultado de pesquisa normalizado, ou seja, uma instância de TextSearchResult
.
Esse resultado de pesquisa normalizado contém um valor e, opcionalmente, um nome e um link.
As abstrações de pesquisa de texto incluem uma função para retornar um valor de cadeia de caracteres, por exemplo, uma das propriedades do modelo de dados será retornada como o resultado da pesquisa.
Para que a pesquisa de texto funcione corretamente, você precisa fornecer uma maneira de mapear do modelo de dados do Repositório de Vetores para uma instância do TextSearchResult
.
A próxima seção descreve as duas opções que você pode usar para executar esse mapeamento.
Gorjeta
Para executar os exemplos mostrados nesta página, vá para GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.
Usando um modelo de armazenamento vetorial com pesquisa de texto
O mapeamento de um modelo de dados do Vetor Store para um TextSearchResult
pode ser feito declarativamente usando atributos.
[TextSearchResultValue]
- Adicione este atributo à propriedade do modelo de dados que será o valor doTextSearchResult
, por exemplo, os dados textuais que o modelo de IA usará para responder a perguntas.[TextSearchResultName]
- Adicione este atributo à propriedade do modelo de dados que será o nome doTextSearchResult
.[TextSearchResultLink]
- Adicione este atributo à propriedade do modelo de dados que será o link para oTextSearchResult
.
O exemplo a seguir mostra um modelo de dados que tem os atributos de resultado da pesquisa de texto aplicados.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
public sealed class DataModel
{
[VectorStoreRecordKey]
[TextSearchResultName]
public Guid Key { get; init; }
[VectorStoreRecordData]
[TextSearchResultValue]
public string Text { get; init; }
[VectorStoreRecordData]
[TextSearchResultLink]
public string Link { get; init; }
[VectorStoreRecordData(IsFilterable = true)]
public required string Tag { get; init; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> Embedding { get; init; }
}
O mapeamento de um modelo de dados do Vetor Store para um string
ou um TextSearchResult
também pode ser feito fornecendo implementações de ITextSearchStringMapper
e ITextSearchResultMapper
respectivamente.
Você pode decidir criar mapeadores personalizados para os seguintes cenários:
- Várias propriedades do modelo de dados precisam ser combinadas, por exemplo, se várias propriedades precisam ser combinadas para fornecer o valor.
- É necessária lógica adicional para gerar uma das propriedades, por exemplo, se a propriedade link precisar ser calculada a partir das propriedades do modelo de dados.
O exemplo a seguir mostra um modelo de dados e duas implementações de mapeador de exemplo que podem ser usadas com o modelo de dados.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
protected sealed class DataModel
{
[VectorStoreRecordKey]
public Guid Key { get; init; }
[VectorStoreRecordData]
public required string Text { get; init; }
[VectorStoreRecordData]
public required string Link { get; init; }
[VectorStoreRecordData(IsFilterable = true)]
public required string Tag { get; init; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> Embedding { get; init; }
}
/// <summary>
/// String mapper which converts a DataModel to a string.
/// </summary>
protected sealed class DataModelTextSearchStringMapper : ITextSearchStringMapper
{
/// <inheritdoc />
public string MapFromResultToString(object result)
{
if (result is DataModel dataModel)
{
return dataModel.Text;
}
throw new ArgumentException("Invalid result type.");
}
}
/// <summary>
/// Result mapper which converts a DataModel to a TextSearchResult.
/// </summary>
protected sealed class DataModelTextSearchResultMapper : ITextSearchResultMapper
{
/// <inheritdoc />
public TextSearchResult MapFromResultToTextSearchResult(object result)
{
if (result is DataModel dataModel)
{
return new TextSearchResult(value: dataModel.Text) { Name = dataModel.Key.ToString(), Link = dataModel.Link };
}
throw new ArgumentException("Invalid result type.");
}
}
As implementações do mapeador podem ser fornecidas como parâmetros ao criar o VectorStoreTextSearch
como mostrado abaixo:
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
// Create custom mapper to map a <see cref="DataModel"/> to a <see cref="string"/>
var stringMapper = new DataModelTextSearchStringMapper();
// Create custom mapper to map a <see cref="DataModel"/> to a <see cref="TextSearchResult"/>
var resultMapper = new DataModelTextSearchResultMapper();
// Add code to create instances of IVectorStoreRecordCollection and ITextEmbeddingGenerationService
// Create a text search instance using the vector store record collection.
var result = new VectorStoreTextSearch<DataModel>(vectorStoreRecordCollection, textEmbeddingGeneration, stringMapper, resultMapper);
Usando um repositório vetorial com pesquisa de texto
O exemplo abaixo mostra como criar uma instância de uso de uma coleção de registros Vetor VectorStoreTextSearch
Store.
Gorjeta
Os exemplos a seguir exigem instâncias de IVectorStoreRecordCollection
e ITextEmbeddingGenerationService
.
Para criar uma instância de, consulte a documentação de IVectorStoreRecordCollection
cada conector.
Para criar uma instância de ITextEmbeddingGenerationService
selecionar o serviço que você deseja usar, por exemplo, Azure OpenAI, OpenAI, ... ou usar um modelo local ONNX, Ollama, ... e criar uma instância da implementação correspondente ITextEmbeddingGenerationService
.
Gorjeta
A VectorStoreTextSearch
também pode ser construído a partir de uma instância de IVectorizableTextSearch
. Neste caso, não ITextEmbeddingGenerationService
é necessário.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Add code to create instances of IVectorStoreRecordCollection and ITextEmbeddingGenerationService
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreRecordCollection, textEmbeddingGeneration);
// Search and return results as TextSearchResult items
var query = "What is the Semantic Kernel?";
KernelSearchResults<TextSearchResult> textResults = await textSearch.GetTextSearchResultsAsync(query, new() { Top = 2, Skip = 0 });
Console.WriteLine("\n--- Text Search Results ---\n");
await foreach (TextSearchResult result in textResults.Results)
{
Console.WriteLine($"Name: {result.Name}");
Console.WriteLine($"Value: {result.Value}");
Console.WriteLine($"Link: {result.Link}");
}
Criando um plug-in de pesquisa a partir de uma loja de vetores
O exemplo abaixo mostra como criar um plug-in nomeado SearchPlugin
a partir de uma instância do VectorStoreTextSearch
.
Usar CreateWithGetTextSearchResults
cria um novo plug-in com uma única GetTextSearchResults
função que chama a implementação de pesquisa de coleção de registros subjacente do Vetor Store.
O SearchPlugin
é adicionado ao que o torna disponível para ser chamado durante a Kernel
renderização de prompt.
O modelo de prompt inclui uma chamada para {{SearchPlugin.Search $query}}
a qual invocará o SearchPlugin
para recuperar resultados relacionados à consulta atual.
Os resultados são então inseridos no prompt renderizado antes de serem enviados para o modelo.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a kernel with OpenAI chat completion
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: TestConfiguration.OpenAI.ChatModelId,
apiKey: TestConfiguration.OpenAI.ApiKey);
Kernel kernel = kernelBuilder.Build();
// Add code to create instances of IVectorStoreRecordCollection and ITextEmbeddingGenerationService
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreRecordCollection, textEmbeddingGeneration);
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin = textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
string promptTemplate = """
{{#with (SearchPlugin-GetTextSearchResults query)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Link: {{Link}}
-----------------
{{/each}}
{{/with}}
{{query}}
Include citations to the relevant information where it is referenced in the response.
""";
KernelArguments arguments = new() { { "query", query } };
HandlebarsPromptTemplateFactory promptTemplateFactory = new();
Console.WriteLine(await kernel.InvokePromptAsync(
promptTemplate,
arguments,
templateFormat: HandlebarsPromptTemplateFactory.HandlebarsTemplateFormat,
promptTemplateFactory: promptTemplateFactory
));
Usando um repositório vetorial com chamada de função
O exemplo abaixo também cria uma SearchPlugin
instância de VectorStoreTextSearch
.
Este plugin será anunciado para o modelo para uso com chamada de função automática usando o FunctionChoiceBehavior
nas configurações de execução de prompt.
Quando você executa este exemplo, o modelo invocará a função de pesquisa para recuperar informações adicionais para responder à pergunta.
Provavelmente irá apenas procurar por "Kernel Semântico" em vez de toda a consulta.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a kernel with OpenAI chat completion
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: TestConfiguration.OpenAI.ChatModelId,
apiKey: TestConfiguration.OpenAI.ApiKey);
Kernel kernel = kernelBuilder.Build();
// Add code to create instances of IVectorStoreRecordCollection and ITextEmbeddingGenerationService
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreRecordCollection, textEmbeddingGeneration);
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin = textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel?", arguments));
Personalizar a função de pesquisa
O exemplo abaixo de como personalizar a descrição da função de pesquisa que é adicionada ao SearchPlugin
.
Algumas coisas que você pode querer fazer são:
- Altere o nome da função de pesquisa para refletir o que está na coleção de registros associada, por exemplo, você pode querer nomear a função
SearchForHotels
se a coleção de registros contiver informações do hotel. - Altere a descrição da função. Uma descrição precisa da função ajuda o modelo de IA a selecionar a melhor função para chamar. Isso é especialmente importante se você estiver adicionando várias funções de pesquisa.
- Adicione um parâmetro adicional à função de pesquisa. Se a coleção de registros contiver informações do hotel e uma das propriedades for o nome da cidade, você poderá adicionar uma propriedade à função de pesquisa para especificar a cidade. Um filtro será adicionado automaticamente e filtrará os resultados da pesquisa por cidade.
Gorjeta
O exemplo abaixo usa a implementação padrão da pesquisa. Você pode optar por fornecer sua própria implementação, que chama a coleção de registros subjacente do Vetor Store com opções adicionais para ajustar suas pesquisas.
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a kernel with OpenAI chat completion
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: TestConfiguration.OpenAI.ChatModelId,
apiKey: TestConfiguration.OpenAI.ApiKey);
Kernel kernel = kernelBuilder.Build();
// Add code to create instances of IVectorStoreRecordCollection and ITextEmbeddingGenerationService
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreRecordCollection, textEmbeddingGeneration);
// Create options to describe the function I want to register.
var options = new KernelFunctionFromMethodOptions()
{
FunctionName = "Search",
Description = "Perform a search for content related to the specified query from a record collection.",
Parameters =
[
new KernelParameterMetadata("query") { Description = "What to search for", IsRequired = true },
new KernelParameterMetadata("top") { Description = "Number of results", IsRequired = false, DefaultValue = 2 },
new KernelParameterMetadata("skip") { Description = "Number of results to skip", IsRequired = false, DefaultValue = 0 },
],
ReturnParameter = new() { ParameterType = typeof(KernelSearchResults<string>) },
};
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin = textSearch.CreateWithGetTextSearchResults("SearchPlugin", "Search a record collection", [textSearch.CreateSearch(options)]);
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel?", arguments));
Brevemente
Mais em breve.
Brevemente
Mais em breve.