Partilhar via


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.

  1. Use o conector Vetor Store para recuperar a coleção de registros que você deseja pesquisar.
  2. Envolva a coleção de registros com VectorStoreTextSearch.
  3. 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.

O mapeamento de um modelo de dados do Vetor Store para um TextSearchResult pode ser feito declarativamente usando atributos.

  1. [TextSearchResultValue] - Adicione este atributo à propriedade do modelo de dados que será o valor do TextSearchResult, por exemplo, os dados textuais que o modelo de IA usará para responder a perguntas.
  2. [TextSearchResultName] - Adicione este atributo à propriedade do modelo de dados que será o nome do TextSearchResult.
  3. [TextSearchResultLink] - Adicione este atributo à propriedade do modelo de dados que será o link para o TextSearchResult.

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:

  1. Várias propriedades do modelo de dados precisam ser combinadas, por exemplo, se várias propriedades precisam ser combinadas para fornecer o valor.
  2. É 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);

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:

  1. 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.
  2. 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.
  3. 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.

Próximos passos