So verwenden Sie Vektorspeicher mit der Semantic-Kernel-Textsuche

Alle Konnektoren des Vector Store können für die Textsuche verwendet werden.

  1. Verwenden Sie den Vector Store-Connector, um die Datensatzsammlung abzurufen, die Sie durchsuchen möchten.
  2. Umschließen der Datensatzauflistung mit VectorStoreTextSearch.
  3. Konvertieren Sie in ein Plug-In für die Verwendung in RAG- und/oder Funktionsaufrufszenarien.

Es ist sehr wahrscheinlich, dass Sie die Plug-In-Suchfunktion so anpassen möchten, dass die Beschreibung den Typ der in der Datensatzsammlung verfügbaren Daten widerspiegelt. Wenn die Datensatzsammlung beispielsweise Informationen zu Hotels enthält, sollte die Beschreibung der Plug-In-Suchfunktion dies erwähnen. Auf diese Weise können Sie mehrere Plug-Ins registrieren, z. B. eine, um nach Hotels zu suchen, eine für Restaurants und eine andere für Aktivitäten.

Die Textsuchabstraktionen enthalten eine Funktion, um ein normalisiertes Suchergebnis zurückzugeben, d. h. eine Instanz von TextSearchResult. Dieses normalisierte Suchergebnis enthält einen Wert und optional einen Namen und einen Link. Die Textsuchabstraktionen enthalten eine Funktion zum Zurückgeben eines Zeichenfolgenwerts, z. B. eine der Datenmodelleigenschaften, wird als Suchergebnis zurückgegeben. Damit die Textsuche korrekt funktioniert, müssen Sie eine Möglichkeit bereitstellen, das Datenmodell des Vector Store auf eine Instanz von TextSearchResult abzubilden. Im nächsten Abschnitt werden die beiden Optionen beschrieben, die Sie zum Ausführen dieser Zuordnung verwenden können.

Tipp

Um die auf dieser Seite gezeigten Beispiele auszuführen, wechseln Sie zu GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.

Die Zuordnung von einem Vector Store-Datenmodell zu einem TextSearchResult kann deklarativ mithilfe von Attributen erfolgen.

  1. [TextSearchResultValue] - Fügen Sie dieses Attribut der Eigenschaft des Datenmodells hinzu, das den Wert der TextSearchResultDaten darstellt, z. B. die Textdaten, die das KI-Modell verwendet, um Fragen zu beantworten.
  2. [TextSearchResultName] – Fügen Sie dieses Attribut der Eigenschaft im Datenmodell hinzu, die als Name des TextSearchResult verwendet wird.
  3. [TextSearchResultLink] - Fügen Sie dieses Attribut der Eigenschaft im Datenmodell hinzu, die den Link zu TextSearchResult darstellt.

Das folgende Beispiel zeigt ein Datenmodell, auf das die Textsuchergebnisattribute angewendet wurden.

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

Die Zuordnung eines Vector Store-Datenmodells zu einem string oder einem TextSearchResult kann auch durch die Bereitstellung entsprechender Implementierungen von ITextSearchStringMapper bzw. ITextSearchResultMapper erfolgen.

Sie können benutzerdefinierte Mapper für die folgenden Szenarien erstellen:

  1. Mehrere Eigenschaften aus dem Datenmodell müssen kombiniert werden, z. B. wenn mehrere Eigenschaften kombiniert werden müssen, um den Wert bereitzustellen.
  2. Zusätzliche Logik ist erforderlich, um eine der Eigenschaften zu generieren, z. B. wenn die Verknüpfungseigenschaft aus den Datenmodelleigenschaften berechnet werden muss.

Das folgende Beispiel zeigt ein Datenmodell und zwei Beispielzuordnungsimplementierungen, die mit dem Datenmodell verwendet werden können.

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

Die Mapper-Implementierungen können beim Erstellen der VectorStoreTextSearch als Parameter bereitgestellt werden, wie unten gezeigt:

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

Das folgende Beispiel zeigt, wie Sie unter Verwendung einer Vector Store-Datensatzsammlung eine Instanz von VectorStoreTextSearch erstellen.

Tipp

Die folgenden Beispiele erfordern Instanzen von VectorStoreCollection und ITextEmbeddingGenerationService. Um eine Instanz von VectorStoreCollection zu erstellen, lesen Sie in der Dokumentation für jeden Konnektor nach. Um eine Instanz von ITextEmbeddingGenerationService zu erstellen, wählen Sie den Dienst aus, den Sie verwenden möchten, z. B. Azure OpenAI, OpenAI, ... oder verwenden Sie ein lokales Modell wie ONNX, Ollama, ... und erstellen Sie eine Instanz der entsprechenden ITextEmbeddingGenerationService-Implementierung.

Tipp

Ein VectorStoreTextSearch kann auch aus einer Instanz von IVectorizableTextSearch konstruiert werden. In diesem Fall ist keine ITextEmbeddingGenerationService erforderlich.

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

Erstellen eines Such-Plug-Ins aus einem Vektorspeicher

Das folgende Beispiel zeigt, wie Sie ein Plugin mit dem Namen SearchPlugin aus einer Instanz von VectorStoreTextSearch erstellen. Die Verwendung CreateWithGetTextSearchResults erstellt ein neues Plug-In mit einer einzigen GetTextSearchResults Funktion, die die zugrunde liegende Suchimplementierung der Vector Store-Datensatzsammlung aufruft. Das SearchPlugin wird dem Kernel hinzugefügt, wodurch es beim Prompt-Rendering aufgerufen werden kann. Die Promptvorlage enthält einen Aufruf an {{SearchPlugin.Search $query}}, der die SearchPlugin aufruft, um Ergebnisse zur aktuellen Abfrage abzurufen. Die Ergebnisse werden dann in die gerenderte Eingabeaufforderung eingefügt, bevor sie an das Modell gesendet wird.

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

Verwenden eines Vektorspeichers mit Funktionsaufrufen

Das folgende Beispiel erstellt auch eine SearchPlugin aus einer Instanz von VectorStoreTextSearch. Dieses Plugin wird dem Modell für die Verwendung mit automatischem Funktionsaufruf über FunctionChoiceBehavior in den Einstellungen zur Eingabeaufforderungsausführung zur Verfügung gestellt. Wenn Sie dieses Beispiel ausführen, ruft das Modell die Suchfunktion auf, um zusätzliche Informationen abzurufen, um auf die Frage zu antworten. Es wird wahrscheinlich nur nach "Semantischer Kernel" und nicht nach der gesamten Abfrage suchen.

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

Anpassen der Suchfunktion

Im folgenden Beispiel wird beschrieben, wie Sie die Beschreibung der Suchfunktion anpassen, die dem SearchPlugin hinzugefügt wird. Einige Dinge, die Sie möglicherweise tun möchten, sind:

  1. Ändern Sie den Namen der Suchfunktion so, dass sie angibt, was in der zugeordneten Datensatzsammlung enthalten ist, z. B. möchten Sie die Funktion SearchForHotels benennen, wenn die Datensatzsammlung Hotelinformationen enthält.
  2. Ändern Sie die Beschreibung der Funktion. Eine genaue Funktionsbeschreibung hilft dem KI-Modell, die beste Funktion auszuwählen, die aufgerufen werden soll. Dies ist besonders wichtig, wenn Sie mehrere Suchfunktionen hinzufügen.
  3. Fügen Sie der Suchfunktion einen zusätzlichen Parameter hinzu. Wenn die Datensatzsammlung Hotelinformationen enthält und eine der Eigenschaften der Stadtname ist, können Sie der Suchfunktion eine Eigenschaft hinzufügen, um die Stadt festzulegen. Ein Filter wird automatisch hinzugefügt und filtert Suchergebnisse nach Ort.

Tipp

Im folgenden Beispiel wird die Standardimplementierung der Suche verwendet. Sie können ihre eigene Implementierung bereitstellen, die die zugrunde liegende Vector Store-Datensatzsammlung mit zusätzlichen Optionen aufruft, um Ihre Suchvorgänge zu optimieren.

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

In Kürze verfügbar

Mehr in Kürze verfügbar.

In Kürze verfügbar

Mehr in Kürze verfügbar.

Nächste Schritte