Udostępnij za pośrednictwem


Jak używać magazynów wektorów z semantycznym wyszukiwaniem tekstu jądra

Wszystkie łączniki magazynu wektorów mogą służyć do wyszukiwania tekstu.

  1. Użyj łącznika Vector Store, aby pobrać kolekcję rekordów, którą chcesz wyszukać.
  2. Opakuj kolekcję rekordów za pomocą polecenia VectorStoreTextSearch.
  3. Przekonwertuj na wtyczkę do użycia w scenariuszach wywoływania funkcji RAG i/lub funkcji.

Jest bardzo prawdopodobne, że chcesz dostosować funkcję wyszukiwania wtyczki, aby jej opis odzwierciedlał typ danych dostępnych w kolekcji rekordów. Jeśli na przykład kolekcja rekordów zawiera informacje o hotelach, opis funkcji wyszukiwania wtyczki powinien o tym wspomnieć. Pozwoli to zarejestrować wiele wtyczek, np. jednego do wyszukiwania hoteli, innego dla restauracji i innego, aby robić rzeczy.

Abstrakcje wyszukiwania tekstu obejmują funkcję zwracającą znormalizowany wynik wyszukiwania, tj. wystąpienie klasy TextSearchResult. Ten znormalizowany wynik wyszukiwania zawiera wartość i opcjonalnie nazwę i link. Abstrakcje wyszukiwania tekstu obejmują funkcję zwracającą wartość ciągu, np. jedną z właściwości modelu danych zostanie zwrócona jako wynik wyszukiwania. Aby wyszukiwanie tekstu działało poprawnie, należy zapewnić sposób mapowania z modelu danych magazynu wektorowego TextSearchResultna wystąpienie klasy . W następnej sekcji opisano dwie opcje, których można użyć do wykonania tego mapowania.

Napiwek

Aby uruchomić przykłady wyświetlane na tej stronie, przejdź do pozycji GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.

Mapowanie z modelu danych magazynu wektorowego na element TextSearchResult można deklaratywnie wykonywać przy użyciu atrybutów.

  1. [TextSearchResultValue] — Dodaj ten atrybut do właściwości modelu danych, który będzie wartością TextSearchResult, np. tekstowych danych, których model AI będzie używać do odpowiadania na pytania.
  2. [TextSearchResultName] - Dodaj ten atrybut do właściwości modelu danych, który będzie nazwą TextSearchResult.
  3. [TextSearchResultLink] — Dodaj ten atrybut do właściwości modelu danych, który będzie linkiem do elementu TextSearchResult.

Poniższy przykład przedstawia model danych, który ma zastosowane atrybuty wyników wyszukiwania tekstu.

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; }
}

Mapowanie z modelu danych magazynu wektorowego na string element lub TextSearchResult można również wykonać przez zapewnienie implementacji ITextSearchStringMapper i ITextSearchResultMapper odpowiednio.

Możesz zdecydować się na utworzenie niestandardowych maperów dla następujących scenariuszy:

  1. Aby zapewnić wartość, należy połączyć wiele właściwości z modelu danych.
  2. Dodatkowa logika jest wymagana do wygenerowania jednej z właściwości, np. jeśli właściwość łącza musi zostać obliczona z właściwości modelu danych.

Poniższy przykład przedstawia model danych i dwie przykładowe implementacje mapowania, które mogą być używane z modelem danych.

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.");
    }
}

Implementacje maperów można podać jako parametry podczas tworzenia elementu VectorStoreTextSearch , jak pokazano poniżej:

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);

W poniższym przykładzie pokazano, jak utworzyć wystąpienie kolekcji rekordów magazynu wektorów VectorStoreTextSearch .

Napiwek

Poniższe przykłady wymagają wystąpień IVectorStoreRecordCollection elementów i ITextEmbeddingGenerationService. Aby utworzyć wystąpienie odwołania IVectorStoreRecordCollection do dokumentacji dla każdego łącznika. Aby utworzyć wystąpienie wybranej ITextEmbeddingGenerationService usługi, której chcesz użyć, np. Azure OpenAI, OpenAI, ... lub użyj modelu lokalnego ONNX, Ollama, ... i utwórz wystąpienie odpowiedniej ITextEmbeddingGenerationService implementacji.

Napiwek

Obiekt VectorStoreTextSearch można również skonstruować z wystąpienia IVectorizableTextSearchklasy . W takim przypadku nie ITextEmbeddingGenerationService jest potrzebne.

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}");
}

Tworzenie wtyczki wyszukiwania z magazynu wektorów

W poniższym przykładzie pokazano, jak utworzyć wtyczkę o nazwie SearchPlugin na podstawie wystąpienia programu VectorStoreTextSearch. Użycie polecenia CreateWithGetTextSearchResults tworzy nową wtyczkę z jedną GetTextSearchResults funkcją, która wywołuje podstawową implementację wyszukiwania kolekcji rekordów magazynu wektorów. Element SearchPlugin jest dodawany do Kernel elementu , który udostępnia go do wywołania podczas renderowania monitu. Szablon monitu zawiera wywołanie, do {{SearchPlugin.Search $query}} którego wywoła element SearchPlugin w celu pobrania wyników związanych z bieżącym zapytaniem. Wyniki zostaną następnie wstawione do renderowanego monitu przed wysłaniem ich do modelu.

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
));

Używanie magazynu wektorów z wywoływaniem funkcji

Poniższy przykład tworzy również obiekt SearchPlugin na podstawie wystąpienia VectorStoreTextSearchklasy . Ta wtyczka zostanie anonsowana do modelu do użycia z automatycznym wywołaniem funkcji przy użyciu FunctionChoiceBehavior w ustawieniach wykonywania monitu. Po uruchomieniu tego przykładu model wywoła funkcję wyszukiwania, aby pobrać dodatkowe informacje, aby odpowiedzieć na pytanie. Prawdopodobnie po prostu wyszuka "Jądro semantyczne", a nie całe zapytanie.

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));

Dostosowywanie funkcji wyszukiwania

Poniższy przykładowy sposób dostosowywania opisu funkcji wyszukiwania, która jest dodawana do elementu SearchPlugin. Oto niektóre czynności, które warto wykonać:

  1. Zmień nazwę funkcji wyszukiwania, aby odzwierciedlić, co znajduje się w skojarzonej kolekcji rekordów, np. możesz nazwać funkcję SearchForHotels , jeśli kolekcja rekordów zawiera informacje o hotelu.
  2. Zmień opis funkcji. Dokładny opis funkcji pomaga modelowi sztucznej inteligencji wybrać najlepszą funkcję do wywołania. Jest to szczególnie ważne w przypadku dodawania wielu funkcji wyszukiwania.
  3. Dodaj dodatkowy parametr do funkcji wyszukiwania. Jeśli kolekcja rekordów zawiera informacje o hotelu, a jedną z właściwości jest nazwa miasta, możesz dodać właściwość do funkcji wyszukiwania, aby określić miasto. Filtr zostanie automatycznie dodany i będzie filtrować wyniki wyszukiwania według miasta.

Napiwek

W poniższym przykładzie użyto domyślnej implementacji wyszukiwania. Możesz wybrać własną implementację, która wywołuje podstawową kolekcję rekordów magazynu wektorów z dodatkowymi opcjami w celu dostosowania wyszukiwań.

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));

Wkrótce

Wkrótce pojawi się więcej.

Wkrótce

Wkrótce pojawi się więcej.

Następne kroki