Partager via


Modifications du magasin de vecteurs - Préversion 2 - avril 2025

Prise en charge intégrée pour la génération d'embeddings dans le stockage vectoriel

La mise à jour d'avril 2025 introduit la prise en charge intégrée de la génération d'intégrations directement dans le stockage vectoriel. En configurant un générateur d’incorporation, vous pouvez désormais générer automatiquement des incorporations pour les propriétés vectorielles sans avoir à les précomputer en externe. Cette fonctionnalité simplifie les flux de travail et réduit le besoin d’étapes de prétraitement supplémentaires.

Configuration d’un générateur d’incorporation

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

  1. Dans le magasin 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 en cas de substitution.

    using Microsoft.Extensions.AI;
    using Microsoft.SemanticKernel.Connectors.Qdrant;
    using OpenAI;
    using Qdrant.Client;
    
    var embeddingGenerator = new OpenAIClient("your key")
        .GetEmbeddingClient("your chosen model")
        .AsIEmbeddingGenerator();
    
    var vectorStore = new QdrantVectorStore(new QdrantClient("localhost"), new QdrantVectorStoreOptions
    {
         EmbeddingGenerator = embeddingGenerator
    });
    
  2. Dans une collection : vous pouvez configurer un générateur d'intégration pour une collection spécifique, en remplaçant le générateur au niveau du magasin.

    using Microsoft.Extensions.AI;
    using Microsoft.SemanticKernel.Connectors.Qdrant;
    using OpenAI;
    using Qdrant.Client;
    
    var embeddingGenerator = new OpenAIClient("your key")
        .GetEmbeddingClient("your chosen model")
        .AsIEmbeddingGenerator();
    
    var collectionOptions = new QdrantVectorStoreRecordCollectionOptions<MyRecord>
    {
        EmbeddingGenerator = embeddingGenerator
    };
    var collection = new QdrantVectorStoreRecordCollection<ulong, MyRecord>(new QdrantClient("localhost"), "myCollection", collectionOptions);
    
  3. Sur une définition d’enregistrement : lors de la définition des propriétés par programmation VectorStoreRecordDefinition, vous pouvez spécifier un générateur d’incorporation pour toutes les propriétés.

    using Microsoft.Extensions.AI;
    using Microsoft.Extensions.VectorData;
    using Microsoft.SemanticKernel.Connectors.Qdrant;
    using OpenAI;
    using Qdrant.Client;
    
    var embeddingGenerator = new OpenAIClient("your key")
        .GetEmbeddingClient("your chosen model")
        .AsIEmbeddingGenerator();
    
    var recordDefinition = new VectorStoreRecordDefinition
    {
        EmbeddingGenerator = embeddingGenerator,
        Properties = new List<VectorStoreRecordProperty>
        {
            new VectorStoreRecordKeyProperty("Key", typeof(ulong)),
            new VectorStoreRecordVectorProperty("DescriptionEmbedding", typeof(string), dimensions: 1536)
        }
    };
    
    var collectionOptions = new QdrantVectorStoreRecordCollectionOptions<MyRecord>
    {
        VectorStoreRecordDefinition = recordDefinition
    };
    var collection = new QdrantVectorStoreRecordCollection<ulong, MyRecord>(new QdrantClient("localhost"), "myCollection", collectionOptions);
    
  4. 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é.

    using Microsoft.Extensions.AI;
    using Microsoft.Extensions.VectorData;
    using OpenAI;
    
    var embeddingGenerator = new OpenAIClient("your key")
        .GetEmbeddingClient("your chosen model")
        .AsIEmbeddingGenerator();
    
    var vectorProperty = new VectorStoreRecordVectorProperty("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
{
    [VectorStoreRecordKey]
    public string Key { get; set; } = string.Empty;

    [VectorStoreRecordData]
    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.
    [VectorStoreRecordVector(1536)]
    public string Embedding => this.Text;
}

// Create an OpenAI embedding generator.
var embeddingGenerator = new OpenAIClient("your key")
    .GetEmbeddingClient("your chosen model")
    .AsIEmbeddingGenerator();

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

// 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.
var 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.
var searchResult = collection.SearchAsync(
    "What is my budget for 2024?",
    top: 1);

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

Transition depuis IVectorizableTextSearch et IVectorizedSearch vers IVectorSearch

Les interfaces IVectorizableTextSearch et IVectorizedSearch ont été déclarées obsolètes et remplacées par l’interface plus unifiée et flexible IVectorSearch. Cette modification simplifie la surface de l’API et offre une approche plus cohérente des opérations de recherche vectorielle.

Modifications clés

  1. Interface unifiée :

    • L’interface IVectorSearch consolide les fonctionnalités des deux IVectorizableTextSearch et IVectorizedSearch dans une seule interface.
  2. Renommage de la méthode :

    • VectorizableTextSearchAsync de IVectorizableTextSearch a été remplacé par SearchAsync in IVectorSearch.
    • VectorizedSearchAsync de IVectorizedSearch a été remplacé par SearchEmbeddingAsync in IVectorSearch.
  3. Flexibilité améliorée :

    • Méthode SearchAsync dans IVectorSearch gère la génération d’incorporations, en prenant en charge soit l’incorporation locale, si un générateur d’incorporation est configuré, soit l’incorporation côté serveur.
    • La méthode SearchEmbeddingAsync dans IVectorSearch permet des recherches basées sur les embeddings, fournissant une API de bas niveau pour des cas d'utilisation avancés.

Modification du type de retour pour les méthodes de recherche

Outre le changement de nommage des méthodes de recherche, le type de retour de toutes les méthodes de recherche a été modifié pour simplifier l’utilisation. Le type de résultats des méthodes de recherche est maintenant IAsyncEnumerable<VectorSearchResult<TRecord>>, ce qui permet de parcourir directement les résultats. Auparavant, l’objet retourné contenait une propriété IAsyncEnumerable.

Prise en charge de la recherche sans vecteurs / récupération filtrée

La mise à jour d’avril 2025 introduit la prise en charge de la recherche d’enregistrements à l’aide d’un filtre et le retour des résultats avec un ordre de tri configurable. Cela permet d’énumérer des enregistrements dans un ordre prévisible, ce qui est particulièrement utile lors de la synchronisation du magasin vectoriel avec une source de données externe.

Exemple : utilisation de filtres GetAsync

L’exemple suivant montre comment utiliser la GetAsync méthode avec un filtre et des options pour récupérer des enregistrements d’une collection de magasins vectoriels. Cette approche vous permet d’appliquer des critères de filtrage et de trier les résultats dans un ordre prévisible.

// Define a filter to retrieve products priced above $600
Expression<Func<ProductInfo, bool>> filter = product => product.Price > 600;

// Define the options with a sort order
var options = new GetFilteredRecordOptions<ProductInfo>();
options.OrderBy.Descending(product => product.Price);

// Use GetAsync with the filter and sort order
var filteredProducts = await collection.GetAsync(filter, top: 10, options)
    .ToListAsync();

Cet exemple montre comment utiliser la GetAsync méthode pour récupérer des enregistrements filtrés et les trier en fonction de critères spécifiques, tels que le prix.

Nouvelles méthodes sur IVectorStore

Certaines nouvelles méthodes sont disponibles sur l’interface IVectorStore qui vous permettent d’effectuer plus d’opérations directement sans avoir besoin d’un objet VectorStoreRecordCollection.

Vérifier si une collection existe

Vous pouvez maintenant vérifier si une collection existe dans le magasin de vecteurs sans avoir à créer d’objet VectorStoreRecordCollection.

// Example: Check if a collection exists
bool exists = await vectorStore.CollectionExistsAsync("myCollection", cancellationToken);
if (exists)
{
    Console.WriteLine("The collection exists.");
}
else
{
    Console.WriteLine("The collection does not exist.");
}

Supprimer une collection

Une nouvelle méthode vous permet de supprimer une collection du magasin vectoriel sans avoir à créer d’objet VectorStoreRecordCollection.

// Example: Delete a collection
await vectorStore.DeleteCollectionAsync("myCollection", cancellationToken);
Console.WriteLine("The collection has been deleted.");

Remplacement de VectorStoreGenericDataModel<TKey> par Dictionary<string, object?>

Les abstractions de données vectorielles prennent en charge l’utilisation de bases de données où le schéma d’une collection n’est pas connu au moment de la génération. Auparavant, cela a été pris en charge via le VectorStoreGenericDataModel<TKey> type, où ce modèle peut être utilisé à la place d’un modèle de données personnalisé.

Dans cette version, le VectorStoreGenericDataModel<TKey> est devenu obsolète, et l'approche recommandée consiste à utiliser un Dictionary<string, object?> à la place.

Comme précédemment, une définition d’enregistrement doit être fournie pour déterminer le schéma de la collection. Notez également que le type de clé requis lors de l’obtention de l’instance de collection est object, tandis que dans le schéma, il est fixe à string.

var recordDefinition = new VectorStoreRecordDefinition
{
    Properties = new List<VectorStoreRecordProperty>
    {
        new VectorStoreRecordKeyProperty("Key", typeof(string)),
        new VectorStoreRecordDataProperty("Text", typeof(string)),
        new VectorStoreRecordVectorProperty("Embedding", typeof(ReadOnlyMemory<float>), 1536)
    }
};
var collection = vectorStore.GetCollection<object, Dictionary<string, object?>>("finances", recordDefinition);
var record = new Dictionary<string, object?>
{
    { "Key", "1" },
    { "Text", "The budget for 2024 is EUR 364 000" },
    { "Embedding", vector }
};
await collection.UpsertAsync(record);
var retrievedRecord = await collection.GetAsync("1");
Console.WriteLine(retrievedRecord["Text"]);

Modification de la convention d’affectation de noms de méthode Batch

L’interface IVectorStoreRecordCollection a été mise à jour pour améliorer la cohérence dans les conventions d’affectation de noms de méthodes. Plus précisément, les méthodes de traitement par lots ont été renommées pour supprimer la partie « Batch » de leurs noms. Cette modification s’aligne sur une convention d’affectation de noms plus concise.

Renommer les exemples

  • Ancienne méthode :GetBatchAsync(IEnumerable<TKey> keys, ...)
    Nouvelle méthode :GetAsync(IEnumerable<TKey> keys, ...)

  • Ancienne méthode :DeleteBatchAsync(IEnumerable<TKey> keys, ...)
    Nouvelle méthode :DeleteAsync(IEnumerable<TKey> keys, ...)

  • Ancienne méthode :UpsertBatchAsync(IEnumerable<TRecord> records, ...)
    Nouvelle méthode :UpsertAsync(IEnumerable<TRecord> records, ...)

Modification du type de retour pour la méthode Upsert batch

Le type de retour de la méthode batch upsert a été remplacé de IAsyncEnumerable<TKey> à Task<IReadOnlyList<TKey>>. Cette modification a un impact sur la façon dont la méthode est consommée. Vous pouvez maintenant simplement attendre le résultat et récupérer une liste de clés. Auparavant, pour s’assurer que tous les upserts étaient terminés, l’IAsyncEnumerable devait être entièrement énuméré. Cela simplifie l’expérience du développeur lors de l’utilisation de la méthode batch upsert.

La propriété CollectionName a été remplacée par Name

La propriété CollectionName de l’interface IVectorStoreRecordCollection a été renommée en Name.

IsFilterable et IsFullTextSearchable ont été renommés en IsIndexed et IsFullTextIndexed

Les propriétés IsFilterable et IsFullTextSearchable des classes VectorStoreRecordDataAttribute et VectorStoreRecordDataProperty ont été renommées respectivement IsIndexed et IsFullTextIndexed.

Les dimensions sont désormais requises pour les attributs et définitions de vecteurs

Dans la mise à jour d’avril 2025, la spécification du nombre de dimensions est devenue obligatoire lors de l’utilisation d’attributs vectoriels ou de définitions de propriétés vectorielles. Cela garantit que le magasin de vecteurs dispose toujours des informations nécessaires pour gérer correctement les incorporations.

Modifications apportées à VectorStoreRecordVectorAttribute

Précédemment, le VectorStoreRecordVectorAttribute vous permettait d'omettre le paramètre Dimensions. Cela n’est plus autorisé, et le Dimensions paramètre doit maintenant être fourni explicitement.

Avant :

[VectorStoreRecordVector]
public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }

Après :

[VectorStoreRecordVector(Dimensions: 1536)]
public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }

Modifications apportées à VectorStoreRecordVectorProperty

De même, lors de la définition d’une propriété vectorielle de manière programmatique en utilisant VectorStoreRecordVectorProperty, le paramètre dimensions est désormais requis.

Avant :

var vectorProperty = new VectorStoreRecordVectorProperty("DefinitionEmbedding", typeof(ReadOnlyMemory<float>));

Après :

var vectorProperty = new VectorStoreRecordVectorProperty("DefinitionEmbedding", typeof(ReadOnlyMemory<float>), dimensions: 1536);

Toutes les collections nécessitent que le type de clé soit transmis en tant que paramètre de type générique

Lors de la construction directe d’une collection, il est maintenant nécessaire de fournir le TKey paramètre de type générique. Auparavant, où certaines bases de données n’autorisaient qu’un seul type de clé, il s’agissait maintenant d’un paramètre obligatoire, mais pour autoriser l’utilisation de collections avec un Dictionary<string, object?> type et un object type de clé, TKey doivent maintenant toujours être fournies.

Sans objet

Ces modifications sont actuellement applicables uniquement en C#

Sans objet

Ces modifications sont actuellement applicables uniquement en C#