Поделиться через


Использование векторных хранилищ с семантическим поиском текста ядра

Все соединители Vector Store можно использовать для поиска текста.

  1. Используйте соединитель Vector Store для получения коллекции записей, которую требуется выполнить поиск.
  2. Переключите коллекцию записей с VectorStoreTextSearchпомощью .
  3. Преобразуйте подключаемый модуль для использования в сценариях вызова RAG и (или) функций.

Скорее всего, вы хотите настроить функцию поиска подключаемых модулей таким образом, чтобы его описание отражало тип данных, доступных в коллекции записей. Например, если коллекция записей содержит сведения о отелях, описание функции поиска подключаемого модуля должно упомянуть об этом. Это позволит зарегистрировать несколько подключаемых модулей, например, один для поиска отелей, другой для ресторанов и другого для выполнения действий.

Абстракции поиска текста включают функцию для возврата нормализованного результата поиска, т. е. экземпляраTextSearchResult. Этот нормализованный результат поиска содержит значение и при необходимости имя и ссылку. Абстракции поиска текста включают функцию для возврата строкового значения, например одно из свойств модели данных будет возвращено в качестве результата поиска. Чтобы поиск текста работал правильно, необходимо предоставить способ сопоставления из модели данных векторного TextSearchResultхранилища с экземпляром. В следующем разделе описаны два варианта, которые можно использовать для выполнения этого сопоставления.

Совет

Чтобы запустить примеры, показанные на этой странице, перейдите в раздел GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.

Сопоставление из модели данных векторного хранилища с TextSearchResult моделью данных можно сделать декларативно с помощью атрибутов.

  1. [TextSearchResultValue] — Добавьте этот атрибут в свойство модели данных, которая будет значением TextSearchResultтекстовых данных, которые модель ИИ будет использовать для ответа на вопросы.
  2. [TextSearchResultName] — добавьте этот атрибут в свойство модели данных, которое будет именем TextSearchResultмодели данных.
  3. [TextSearchResultLink] — добавьте этот атрибут в свойство модели данных, которая будет ссылкой на объект TextSearchResult.

В следующем примере показана модель данных, которая имеет примененные атрибуты результата поиска текста.

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

Сопоставление из модели данных векторного хранилища с моделью данных или string а TextSearchResult также может быть выполнено путем предоставления реализаций ITextSearchStringMapper и ITextSearchResultMapper соответственно.

Вы можете создать пользовательские схемы для следующих сценариев:

  1. Для предоставления значения необходимо объединить несколько свойств из модели данных, например, если для предоставления значения необходимо объединить несколько свойств.
  2. Для создания одного из свойств модели данных требуется дополнительная логика, например, если свойство ссылки необходимо вычислить из свойств модели данных.

В следующем примере показана модель данных и два примера реализаций mapper, которые можно использовать с моделью данных.

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

Реализации mapper можно указать в качестве параметров при создании, VectorStoreTextSearch как показано ниже:

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

В приведенном ниже примере показано, как создать экземпляр с VectorStoreTextSearch помощью коллекции записей Vector Store.

Совет

Для следующих примеров требуются экземпляры IVectorStoreRecordCollection и ITextEmbeddingGenerationService. Создание экземпляра IVectorStoreRecordCollection см . в документации для каждого соединителя. Чтобы создать экземпляр выбранной ITextEmbeddingGenerationService службы, например Azure OpenAI, OpenAI, ... или используйте локальную модель ONNX, Ollama, ... и создайте экземпляр соответствующей ITextEmbeddingGenerationService реализации.

Совет

Также можно создать объект VectorStoreTextSearch из экземпляра IVectorizableTextSearch. В этом случае нет ITextEmbeddingGenerationService необходимости.

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

Создание подключаемого модуля поиска из векторного хранилища

В приведенном ниже примере показано, как создать подключаемый модуль с именем SearchPlugin из экземпляра VectorStoreTextSearch. При использовании CreateWithGetTextSearchResults создается новый подключаемый модуль с одной GetTextSearchResults функцией, которая вызывает базовую реализацию поиска коллекции записей Vector Store. Добавляется Kernel в SearchPlugin приложение, которое делает его доступным для вызова во время отрисовки запроса. Шаблон запроса включает вызов, к {{SearchPlugin.Search $query}} которому будет вызываться SearchPlugin получение результатов, связанных с текущим запросом. Затем результаты вставляются в отрисованный запрос перед отправкой в модель.

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

Использование векторного хранилища с вызовом функции

В приведенном ниже примере также создается SearchPlugin экземпляр VectorStoreTextSearch. Этот подключаемый модуль будет объявлен в модель для использования с автоматическим вызовом функции с помощью FunctionChoiceBehavior параметров выполнения запроса. При запуске этого примера модель вызовет функцию поиска, чтобы получить дополнительную информацию для ответа на вопрос. Скорее всего, он будет просто искать "Семантический ядро", а не весь запрос.

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

Настройка функции поиска

В приведенном ниже примере описано, как настроить описание функции поиска, добавляемой в .SearchPlugin Ниже приведены некоторые действия.

  1. Измените имя функции поиска, чтобы отразить то, что находится в связанной коллекции записей, например, вы можете присвоить имя функции SearchForHotels , если коллекция записей содержит сведения о отеле.
  2. Измените описание функции. Точное описание функции помогает модели ИИ выбрать лучшую функцию для вызова. Это особенно важно при добавлении нескольких функций поиска.
  3. Добавьте дополнительный параметр в функцию поиска. Если коллекция записей содержит сведения о отеле, а одно из свойств — имя города, которое можно добавить в функцию поиска, чтобы указать город. Фильтр будет автоматически добавлен и будет фильтровать результаты поиска по городу.

Совет

В приведенном ниже примере используется реализация поиска по умолчанию. Вы можете предоставить собственную реализацию, которая вызывает базовую коллекцию записей Vector Store с дополнительными параметрами для точной настройки поисковых запросов.

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

Скоро

В ближайшее время будет больше.

Скоро

В ближайшее время будет больше.

Следующие шаги