Condividi tramite


Generare embedding per i fornitori di archivi di vettori

I provider di archivi vettoriali supportano più modi per generare incorporamenti. È possibile generarli manualmente e passarli come parte di un record quando si usa un oggetto VectorStoreCollection<TKey,TRecord>. In alternativa, possono essere generati internamente a VectorStoreCollection<TKey,TRecord>.

Genera tu stesso gli embeddings

L'approccio più diretto consiste nel generare incorporamenti prima di chiamare UpsertAsync o SearchAsynce passarli insieme ai record o alla query di ricerca.

Costruire un generatore di incorporamento

Per informazioni su come costruire Microsoft.Extensions.AI generatori di incorporamento, vedere Incorporamenti in .NET.

Generare incorporamenti in upsert con IEmbeddingGenerator

async Task GenerateEmbeddingsAndUpsertAsync(
    IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator,
    VectorStoreCollection<ulong, Hotel> collection)
{
    // Upsert a record.
    string descriptionText = "A place where everyone can be happy.";
    ulong hotelId = 1;

    // Generate the embedding.
    ReadOnlyMemory<float> embedding =
        (await embeddingGenerator.GenerateAsync(descriptionText)).Vector;

    // Create a record and upsert with the already generated embedding.
    await collection.UpsertAsync(new Hotel
    {
        HotelId = hotelId,
        HotelName = "Hotel Happy",
        Description = descriptionText,
        DescriptionEmbedding = embedding,
        Tags = ["luxury", "pool"]
    });
}

Generare incorporamenti nella ricerca con IEmbeddingGenerator

async Task GenerateEmbeddingsAndSearchAsync(
    IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator,
    VectorStoreCollection<ulong, Hotel> collection)
{
    // Upsert a record.
    string descriptionText = "Find me a hotel with happiness in mind.";

    // Generate the embedding.
    ReadOnlyMemory<float> searchEmbedding =
        (await embeddingGenerator.GenerateAsync(descriptionText)).Vector;

    // Search using the already generated embedding.
    IAsyncEnumerable<VectorSearchResult<Hotel>> searchResult = collection.SearchAsync(searchEmbedding, top: 1);
    List<VectorSearchResult<Hotel>> resultItems = await searchResult.ToListAsync();

    // Print the first search result.
    Console.WriteLine("Score for first result: " + resultItems.FirstOrDefault()?.Score);
    Console.WriteLine("Hotel description for first result: " + resultItems.FirstOrDefault()?.Record.Description);
}

Consentire all'archivio vettoriale di generare incorporamenti

È possibile configurare un generatore di incorporamento nell'archivio vettoriale, che consente la generazione automatica degli incorporamenti durante le operazioni di upsert e di ricerca. Questo approccio elimina la necessità di pre-elaborazione manuale.

Per abilitare la generazione automatica di vettori durante l'upsert, la proprietà vector nel modello di dati viene definita come tipo di origine, ad esempio string, ma è ancora decorata con un attributo VectorStoreVectorAttribute.

[VectorStoreVector(1536)]
public required string Embedding { get; set; }

Prima di upsert, la Embedding proprietà deve contenere la stringa da cui deve essere generato un vettore. Il tipo del vettore archiviato nel database (ad esempio, float32 o float16) verrà derivato dal generatore di incorporamento configurato.

Importante

Queste proprietà vettoriali non supportano il recupero del vettore generato o del testo originale da cui è stato generato il vettore. Inoltre, non archiviano il testo originale. Se il testo originale deve essere archiviato, aggiungere una proprietà di dati separata per archiviarla.

I generatori di incorporamento che implementano le Microsoft.Extensions.AI astrazioni sono supportati e possono essere configurati a vari livelli:

  • Nell'archivio vettoriale:

    È possibile impostare un generatore di incorporamento predefinito per l'intero archivio vettoriale. Questo generatore verrà usato per tutte le raccolte e le proprietà, a meno che non venga sottoposto a override.

    VectorStore vectorStore = new QdrantVectorStore(
        new QdrantClient("localhost"),
        ownsClient: true,
        new QdrantVectorStoreOptions
        {
            EmbeddingGenerator = embeddingGenerator
        });
    
  • In una raccolta:

    È possibile configurare un generatore di incorporamento per una raccolta specifica, sostituendo il generatore a livello di archivio.

    var collectionOptions = new QdrantCollectionOptions
    {
        EmbeddingGenerator = embeddingGenerator
    };
    
    var collection = new QdrantCollection<ulong, MyRecord>(
        new QdrantClient("localhost"),
        "myCollection",
        ownsClient: true,
        collectionOptions);
    
  • In una definizione di record:

    Quando si definiscono proprietà a livello di codice usando VectorStoreCollectionDefinition, è possibile specificare un generatore di incorporamento per tutte le proprietà.

    var definition = new VectorStoreCollectionDefinition
    {
        EmbeddingGenerator = embeddingGenerator,
        Properties =
        [
            new VectorStoreKeyProperty("Key", typeof(ulong)),
            new VectorStoreVectorProperty("DescriptionEmbedding", typeof(string), dimensions: 1536)
        ]
    };
    
    collectionOptions = new QdrantCollectionOptions
    {
        Definition = definition
    };
    
    collection = new QdrantCollection<ulong, MyRecord>(
        new QdrantClient("localhost"),
        "myCollection",
        ownsClient: true,
        collectionOptions);
    
  • In una definizione di proprietà vettoriale:

    Quando si definiscono proprietà a livello di codice, è possibile impostare un generatore di incorporamento direttamente nella proprietà .

    VectorStoreVectorProperty vectorProperty = new(
        "DescriptionEmbedding",
        typeof(string),
        dimensions: 1536)
    {
        EmbeddingGenerator = embeddingGenerator
    };
    

Esempio di utilizzo

Nell'esempio seguente viene illustrato come usare il generatore di incorporamento per generare automaticamente vettori durante le operazioni di upsert e di ricerca. Questo approccio semplifica i flussi di lavoro eliminando la necessità di precompilare manualmente gli incorporamenti.


// The data model.
internal class FinanceInfo
{
    [VectorStoreKey]
    public string Key { get; set; } = string.Empty;

    [VectorStoreData]
    public string Text { get; set; } = string.Empty;

    // Note that the vector property is typed as a string, and
    // its value is derived from the Text property. The string
    // value will however be converted to a vector on upsert and
    // stored in the database as a vector.
    [VectorStoreVector(1536)]
    public string Embedding => Text;
}

public static async Task RunAsync()
{
    // Create an OpenAI embedding generator.
    var embeddingGenerator = new OpenAIClient("your key")
        .GetEmbeddingClient("your chosen model")
        .AsIEmbeddingGenerator();

    // Use the embedding generator with the vector store.
    VectorStore vectorStore = new InMemoryVectorStore(new()
        { EmbeddingGenerator = embeddingGenerator }
        );
    InMemoryCollection<string, FinanceInfo> collection =
        (InMemoryCollection<string, FinanceInfo>)vectorStore.GetCollection<string, FinanceInfo>("finances");
    await collection.EnsureCollectionExistsAsync();

    // Create some test data.
    string[] budgetInfo =
    [
        "The budget for 2020 is EUR 100 000",
        "The budget for 2021 is EUR 120 000",
        "The budget for 2022 is EUR 150 000",
        "The budget for 2023 is EUR 200 000",
        "The budget for 2024 is EUR 364 000"
    ];

    // Embeddings are generated automatically on upsert.
    IEnumerable<FinanceInfo> records = budgetInfo.Select(
        (input, index) => new FinanceInfo { Key = index.ToString(), Text = input }
        );
    await collection.UpsertAsync(records);

    // Embeddings for the search is automatically generated on search.
    IAsyncEnumerable<VectorSearchResult<FinanceInfo>> searchResult =
        collection.SearchAsync("What is my budget for 2024?", top: 1);

    // Output the matching result.
    await foreach (VectorSearchResult<FinanceInfo> result in searchResult)
    {
        Console.WriteLine($"Key: {result.Record.Key}, Text: {result.Record.Text}");
    }
}

Dimensioni di incorporamento

I database vettoriali richiedono in genere di specificare il numero di dimensioni di ogni vettore durante la creazione della raccolta. I diversi modelli di embedding supportano tipicamente la generazione di vettori con dimensioni variabili. Ad esempio, OpenAI text-embedding-ada-002 genera vettori con 1536 dimensioni. Alcuni modelli consentono anche di scegliere il numero di dimensioni desiderate nel vettore di output. Ad esempio, Google text-embedding-004 produce vettori con 768 dimensioni per impostazione predefinita, ma consente di scegliere un numero qualsiasi di dimensioni compreso tra 1 e 768.

È importante assicurarsi che i vettori generati dal modello di incorporamento abbiano lo stesso numero di dimensioni del vettore corrispondente nel database.

Se si crea una raccolta usando le astrazioni dell'archivio vettoriale, è necessario specificare il numero di dimensioni necessarie per ogni proprietà vettoriale tramite annotazioni o tramite la definizione del record. Il codice seguente mostra esempi di impostazione del numero di dimensioni su 1536 usando entrambi i meccanismi.

[VectorStoreVector(Dimensions: 1536)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

new VectorStoreVectorProperty(
    "DescriptionEmbedding",
    typeof(float),
    dimensions: 1536);

Vedere anche