Partager via


Générer des représentations vectorielles pour les fournisseurs de stockage de vecteurs

Les fournisseurs de stockage de vecteurs prennent en charge plusieurs façons de générer des embeddings. Vous pouvez les générer vous-même et les transmettre dans le cadre d’un enregistrement lors de l’utilisation d’un VectorStoreCollection<TKey,TRecord>. Ou ils peuvent être générés à l'intérieur du VectorStoreCollection<TKey,TRecord>.

Générez des embeddings vous-même

L’approche la plus directe consiste à générer des embeddings avant d’appeler UpsertAsync ou SearchAsync, puis de les transmettre avec vos enregistrements ou vos requêtes de recherche.

Construire un générateur d’incorporation

Pour plus d’informations sur la Microsoft.Extensions.AI construction de générateurs d’incorporation, consultez Incorporations dans .NET.

Générer des incorporations sur upsert avec 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"]
    });
}

Générer des embeddings pour la recherche avec 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);
}

Laissez le réservoir de vecteurs générer des embeddings

Vous pouvez configurer un générateur d’incorporation sur votre magasin de vecteurs, ce qui permet aux incorporations d’être générées automatiquement pendant les opérations upsert et de recherche. Cette approche élimine le besoin de prétraitement manuel.

Pour activer la génération automatique de vecteurs sur upsert, la propriété vector sur votre modèle de données est définie comme type source, par exemple, stringmais toujours décorée d’un VectorStoreVectorAttribute.

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

Avant upsert, la Embedding propriété doit contenir la chaîne à partir de laquelle un vecteur doit être généré. Le type du vecteur stocké dans la base de données (par exemple, float32 ou float16) est dérivé du générateur d’incorporation configuré.

Important

Ces propriétés vectorielles ne prennent pas en charge la récupération du vecteur généré ou du texte d’origine à partir duquel le vecteur a été généré. Ils ne stockent pas non plus le texte d’origine. Si le texte d’origine doit être stocké, ajoutez une propriété de données distincte pour la stocker.

Les générateurs d’incorporation qui implémentent les Microsoft.Extensions.AI abstractions sont pris en charge et peuvent être configurés à différents niveaux :

  • Sur le stockage de vecteurs :

    Vous pouvez définir un générateur d’incorporation par défaut pour l’intégralité du magasin vectoriel. Ce générateur sera utilisé pour toutes les collections et propriétés, sauf si remplacé.

    VectorStore vectorStore = new QdrantVectorStore(
        new QdrantClient("localhost"),
        ownsClient: true,
        new QdrantVectorStoreOptions
        {
            EmbeddingGenerator = embeddingGenerator
        });
    
  • Sur une collection :

    Vous pouvez configurer un générateur d’incorporation pour une collection spécifique, en remplaçant le générateur au niveau du magasin.

    var collectionOptions = new QdrantCollectionOptions
    {
        EmbeddingGenerator = embeddingGenerator
    };
    
    var collection = new QdrantCollection<ulong, MyRecord>(
        new QdrantClient("localhost"),
        "myCollection",
        ownsClient: true,
        collectionOptions);
    
  • Sur une définition d’enregistrement :

    Lorsque vous définissez des propriétés par programmation VectorStoreCollectionDefinition, vous pouvez spécifier un générateur d’incorporation pour toutes les propriétés.

    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);
    
  • Sur une définition de propriété vectorielle :

    Lorsque vous définissez des propriétés par programmation, vous pouvez définir un générateur d’incorporation directement sur la propriété.

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

Exemple d’utilisation

L’exemple suivant montre comment utiliser le générateur d’incorporation pour générer automatiquement des vecteurs pendant les opérations upsert et de recherche. Cette approche simplifie les flux de travail en éliminant la nécessité de précomputer manuellement les incorporations.


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

Incorporation des dimensions

Les bases de données vectorielles nécessitent généralement que vous spécifiiez le nombre de dimensions dont chaque vecteur a lors de la création de la collection. Différents modèles d’incorporation prennent généralement en charge la génération de vecteurs avec différentes tailles de dimension. Par exemple, OpenAI text-embedding-ada-002 génère des vecteurs avec 1536 dimensions. Certains modèles vous permettent également de choisir le nombre de dimensions souhaitées dans le vecteur de sortie. Par exemple, Google text-embedding-004 produit des vecteurs avec 768 dimensions par défaut, mais vous permet de choisir un nombre quelconque de dimensions comprises entre 1 et 768.

Il est important de s’assurer que les vecteurs générés par le modèle d’incorporation ont le même nombre de dimensions que le vecteur correspondant dans la base de données.

Si vous créez une collection à l’aide des abstractions de magasin de vecteurs, vous devez spécifier le nombre de dimensions requises pour chaque propriété vectorielle via des annotations ou via la définition d’enregistrement. Le code suivant montre des exemples de définition du nombre de dimensions sur 1536 à l’aide des deux mécanismes.

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

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

Voir également