Compartilhar via


Como usar repositórios vetoriais com pesquisa de texto de kernel semântico

Todos os conectores do Repositório de Vetores podem ser usados para pesquisa de texto.

  1. Use o conector do Repositório de Vetores 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 RAG e/ou cenários de chamada de função.

É muito provável que você queira personalizar a função de pesquisa de plug-in 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 plug-ins, 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 string, por exemplo, uma das propriedades do modelo de dados será retornada como 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 de TextSearchResult. A próxima seção descreve as duas opções que você pode usar para executar esse mapeamento.

Dica

Para executar os exemplos mostrados nesta página, acesse GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.

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

  1. [TextSearchResultValue] - Adicione esse atributo à propriedade do modelo de dados, que será o valor do , por exemplo, os dados textuais que o modelo de TextSearchResultIA usará para responder a perguntas.
  2. [TextSearchResultName] - Adicione esse atributo à propriedade do modelo de dados, que será o nome do TextSearchResult.
  3. [TextSearchResultLink] - Adicione esse 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
{
    [VectorStoreKey]
    [TextSearchResultName]
    public Guid Key { get; init; }

    [VectorStoreData]
    [TextSearchResultValue]
    public string Text { get; init; }

    [VectorStoreData]
    [TextSearchResultLink]
    public string Link { get; init; }

    [VectorStoreData(IsFilterable = true)]
    public required string Tag { get; init; }

    [VectorStoreVector(1536)]
    public ReadOnlyMemory<float> Embedding { get; init; }
}

O mapeamento de um modelo de dados do Repositório de Vetores para a string ou a 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 precisarem ser combinadas para fornecer o valor.
  2. É necessária lógica adicional para gerar uma das propriedades, por exemplo, se a propriedade do 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
{
    [VectorStoreKey]
    public Guid Key { get; init; }

    [VectorStoreData]
    public required string Text { get; init; }

    [VectorStoreData]
    public required string Link { get; init; }

    [VectorStoreData(IsFilterable = true)]
    public required string Tag { get; init; }

    [VectorStoreVector(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 conforme 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 VectorStoreCollection and ITextEmbeddingGenerationService 

// Create a text search instance using the vector store collection.
var result = new VectorStoreTextSearch<DataModel>(vectorStoreCollection, textEmbeddingGeneration, stringMapper, resultMapper);

O exemplo abaixo mostra como criar uma instância de uso de uma coleção de registros do Repositório de VectorStoreTextSearch Vetores.

Dica

Os exemplos a seguir exigem instâncias de VectorStoreCollection e ITextEmbeddingGenerationService. Para criar uma instância de consulte a documentação de VectorStoreCollection cada conector. Para criar uma instância ou ITextEmbeddingGenerationService selecione o serviço que deseja usar, por exemplo, Azure OpenAI, OpenAI, ... ou use um modelo local ONNX, Ollama, ... e crie uma instância da implementação correspondente ITextEmbeddingGenerationService .

Dica

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 VectorStoreCollection and ITextEmbeddingGenerationService 

// Create a text search instance using the vector store collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreCollection, 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 um armazenamento de vetores

O exemplo abaixo mostra como criar um plug-in nomeado SearchPlugin a partir de uma instância do VectorStoreTextSearch. O uso CreateWithGetTextSearchResults cria um novo plug-in com uma única GetTextSearchResults função que chama a implementação de pesquisa de coleção de registros do Repositório de Vetores subjacente. O SearchPlugin é adicionado ao que o torna disponível para ser chamado durante a Kernel renderização imediata. 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 inseridos no prompt renderizado antes de serem enviados ao 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 VectorStoreCollection and ITextEmbeddingGenerationService

// Create a text search instance using the vector store collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreCollection, 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 um SearchPlugin a partir de uma instância de VectorStoreTextSearch. Este plug-in será anunciado ao modelo para uso com chamada automática de função usando o FunctionChoiceBehavior nas configurações de execução de prompt. Quando você executar este exemplo, o modelo invocará a função de pesquisa para recuperar informações adicionais para responder à pergunta. Provavelmente ele apenas pesquisará por "Semantic Kernel" 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 VectorStoreCollection and ITextEmbeddingGenerationService

// Create a text search instance using the vector store collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreCollection, 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));

Personalizando a função de pesquisa

O exemplo abaixo de como personalizar a descrição da função de pesquisa 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 a ser chamada. 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.

Dica

O exemplo abaixo usa a implementação padrão de pesquisa. Você pode optar por fornecer sua própria implementação, que chama a coleção de registros subjacente do Vector 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 VectorStoreCollection and ITextEmbeddingGenerationService

// Create a text search instance using the vector store collection.
var textSearch = new VectorStoreTextSearch<DataModel>(vectorStoreCollection, 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));

Em breve

Mais em breve.

Em breve

Mais em breve.

Próximas etapas