Condividi tramite


Come usare gli archivi vettoriali con la ricerca di testo del kernel semantico

Tutti i connettori vector store possono essere usati per la ricerca di testo.

  1. Usare il connettore Vector Store per recuperare la raccolta di record da cercare.
  2. Eseguire il wrapping della raccolta di record con VectorStoreTextSearch.
  3. Convertire in un plug-in per l'uso in scenari di chiamata di funzioni e/o RAG.

È molto probabile che si voglia personalizzare la funzione di ricerca del plug-in modo che la relativa descrizione rifletta il tipo di dati disponibili nella raccolta di record. Ad esempio, se la raccolta di record contiene informazioni sugli hotel, la descrizione della funzione di ricerca plug-in dovrebbe menzionare questo. Questo ti permetterà di registrare più plug-in, ad esempio uno per cercare alberghi, un altro per ristoranti e un altro per le cose da fare.

Le astrazioni di ricerca di testo includono una funzione per restituire un risultato di ricerca normalizzato, ad esempio un'istanza di TextSearchResult. Questo risultato della ricerca normalizzato contiene un valore e, facoltativamente, un nome e un collegamento. Le astrazioni di ricerca di testo includono una funzione per restituire un valore stringa, ad esempio una delle proprietà del modello di dati verrà restituita come risultato della ricerca. Per il corretto funzionamento della ricerca di testo, è necessario fornire un modo per eseguire il mapping dal modello di dati Vector Store a un'istanza di TextSearchResult. Nella sezione successiva vengono descritte le due opzioni che è possibile usare per eseguire questo mapping.

Suggerimento

Per eseguire gli esempi visualizzati in questa pagina, passare a GettingStartedWithTextSearch/Step4_Search_With_VectorStore.cs.

Il mapping da un modello di dati Vector Store a un TextSearchResult può essere eseguito in modo dichiarativo usando gli attributi.

  1. [TextSearchResultValue] - Aggiungere questo attributo alla proprietà del modello di dati che sarà il valore di TextSearchResult, ad esempio i dati testuali che il modello di intelligenza artificiale userà per rispondere alle domande.
  2. [TextSearchResultName] - Aggiungere questo attributo alla proprietà del modello di dati che sarà il nome dell'oggetto TextSearchResult.
  3. [TextSearchResultLink] - Aggiungere questo attributo alla proprietà del modello di dati che sarà il collegamento a TextSearchResult.

L'esempio seguente mostra un modello di dati con gli attributi dei risultati della ricerca del testo applicati.

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

Il mapping da un modello di dati vector store a un string oggetto o può TextSearchResult essere eseguito anche fornendo le implementazioni di ITextSearchStringMapper e ITextSearchResultMapper rispettivamente.

È possibile decidere di creare mapper personalizzati per gli scenari seguenti:

  1. È necessario combinare più proprietà del modello di dati, ad esempio se è necessario combinare più proprietà per fornire il valore.
  2. È necessaria logica aggiuntiva per generare una delle proprietà, ad esempio se la proprietà di collegamento deve essere calcolata dalle proprietà del modello di dati.

L'esempio seguente illustra un modello di dati e due implementazioni di mapper di esempio che possono essere usate con il modello di dati.

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

Le implementazioni del mapper possono essere fornite come parametri durante la creazione di VectorStoreTextSearch , come illustrato di seguito:

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

L'esempio seguente illustra come creare un'istanza di usando una raccolta di VectorStoreTextSearch record vector store.

Suggerimento

Gli esempi seguenti richiedono istanze di IVectorStoreRecordCollection e ITextEmbeddingGenerationService. Per creare un'istanza di IVectorStoreRecordCollection fare riferimento alla documentazione per ogni connettore. Per creare un'istanza di ITextEmbeddingGenerationService selezionare il servizio che si vuole usare, ad esempio Azure OpenAI, OpenAI, ... o usare un modello locale ONNX, Ollama, ... e creare un'istanza dell'implementazione corrispondente ITextEmbeddingGenerationService .

Suggerimento

Un VectorStoreTextSearch oggetto può anche essere costruito da un'istanza di IVectorizableTextSearch. In questo caso non ITextEmbeddingGenerationService è necessario.

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

Creazione di un plug-in di ricerca da un archivio vettoriale

L'esempio seguente illustra come creare un plug-in denominato SearchPlugin da un'istanza di VectorStoreTextSearch. Usando CreateWithGetTextSearchResults crea un nuovo plug-in con una singola GetTextSearchResults funzione che chiama l'implementazione di ricerca della raccolta di record vector store sottostante. L'oggetto SearchPlugin viene aggiunto all'oggetto Kernel che lo rende disponibile per essere chiamato durante il rendering della richiesta. Il modello di richiesta include una chiamata a {{SearchPlugin.Search $query}} cui richiamerà l'oggetto SearchPlugin per recuperare i risultati correlati alla query corrente. I risultati vengono quindi inseriti nella richiesta di rendering prima che venga inviata al modello.

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

Uso di un archivio vettoriale con chiamata di funzione

L'esempio seguente crea anche un oggetto SearchPlugin da un'istanza di VectorStoreTextSearch. Questo plug-in verrà annunciato al modello per l'uso con la chiamata automatica delle funzioni usando nelle FunctionChoiceBehavior impostazioni di esecuzione della richiesta. Quando si esegue questo esempio, il modello richiamerà la funzione di ricerca per recuperare informazioni aggiuntive per rispondere alla domanda. È probabile che cerchi semplicemente "Kernel semantico" anziché l'intera query.

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

Personalizzazione della funzione di ricerca

L'esempio seguente illustra come personalizzare la descrizione della funzione di ricerca aggiunta a SearchPlugin. Di seguito sono riportate alcune operazioni da eseguire:

  1. Modificare il nome della funzione di ricerca in modo da riflettere ciò che si trova nella raccolta di record associata, ad esempio se la raccolta SearchForHotels di record contiene informazioni sugli hotel.
  2. Modificare la descrizione della funzione. Una descrizione accurata della funzione consente al modello di intelligenza artificiale di selezionare la funzione migliore da chiamare. Ciò è particolarmente importante se si aggiungono più funzioni di ricerca.
  3. Aggiungere un parametro aggiuntivo alla funzione di ricerca. Se la raccolta record contiene informazioni sull'hotel e una delle proprietà è il nome della città, è possibile aggiungere una proprietà alla funzione di ricerca per specificare la città. Verrà aggiunto automaticamente un filtro che filtra i risultati della ricerca in base alla città.

Suggerimento

L'esempio seguente usa l'implementazione predefinita della ricerca. È possibile scegliere di fornire un'implementazione personalizzata che chiama la raccolta di record vector store sottostante con opzioni aggiuntive per ottimizzare le ricerche.

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

Presto disponibile

Altre informazioni saranno presto disponibili.

Presto disponibile

Altre informazioni saranno presto disponibili.

Passaggi successivi