Ricerca vettoriale con connettori dell'archivio vettoriale del kernel semantico (anteprima)

Avviso

La funzionalità di store vettoriale del Semantic Kernel è in anteprima e i miglioramenti che richiedono cambiamenti significativi possono verificarsi ancora in circostanze limitate prima del rilascio.

Avviso

La funzionalità di store vettoriale del Semantic Kernel è in anteprima e i miglioramenti che richiedono cambiamenti significativi possono verificarsi ancora in circostanze limitate prima del rilascio.

Il kernel semantico fornisce funzionalità di ricerca vettoriale come parte delle astrazioni di Vector Store. Ciò supporta il filtro e molte altre opzioni, che questo articolo illustra in modo più dettagliato.

Suggerimento

Per informazioni su come eseguire ricerche senza generare incorporamenti manualmente, vedere Consentire all'archivio vettoriale di generare incorporamenti.

Il SearchAsync metodo consente di eseguire ricerche usando dati già vettorializzati. Questo metodo accetta un vettore e una classe facoltativa VectorSearchOptions<TRecord> come input. Questo metodo è disponibile nei tipi seguenti:

  1. IVectorSearchable<TRecord>
  2. VectorStoreCollection<TKey, TRecord>

Si noti che VectorStoreCollection<TKey, TRecord> implementa da IVectorSearchable<TRecord>.

Supponendo di avere una raccolta che contiene già dati, è possibile cercarli facilmente. Di seguito è riportato un esempio di uso di Qdrant.

using Microsoft.SemanticKernel.Connectors.Qdrant;
using Microsoft.Extensions.VectorData;
using Qdrant.Client;

// Placeholder embedding generation method.
async Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string textToVectorize)
{
    // your logic here
}

// Create a Qdrant VectorStore object and choose an existing collection that already contains records.
VectorStore vectorStore = new QdrantVectorStore(new QdrantClient("localhost"), ownsClient: true);
VectorStoreCollection<ulong, Hotel> collection = vectorStore.GetCollection<ulong, Hotel>("skhotels");

// Generate a vector for your search text, using your chosen embedding generation implementation.
ReadOnlyMemory<float> searchVector = await GenerateEmbeddingAsync("I'm looking for a hotel where customer happiness is the priority.");

// Do the search, passing an options object with a Top value to limit results to the single top match.
var searchResult = collection.SearchAsync(searchVector, top: 1);

// Inspect the returned hotel.
await foreach (var record in searchResult)
{
    Console.WriteLine("Found hotel description: " + record.Record.Description);
    Console.WriteLine("Found record score: " + record.Score);
}

Suggerimento

Per altre informazioni su come generare incorporamenti, vedere Generazione di incorporamenti.

Tipi di vettore supportati

SearchAsync accetta un tipo generico come parametro vector. I tipi di vettori supportati da ogni archivio dati variano. Vedere la documentazione per ogni connettore per l'elenco dei tipi di vettore supportati.

È anche importante che il tipo di vettore di ricerca corrisponda al vettore di destinazione in cui viene eseguita la ricerca, ad esempio se si dispone di due vettori nello stesso record con tipi di vettore diversi, assicurarsi che il vettore di ricerca fornito corrisponda al tipo del vettore specifico di destinazione. Vedi VectorProperty per sapere come selezionare un vettore di destinazione se hai più di un vettore per record.

Opzioni di ricerca vettoriale

È possibile specificare le opzioni seguenti usando la VectorSearchOptions<TRecord> classe .

VectorProperty

L'opzione VectorProperty può essere usata per specificare la proprietà vector di destinazione durante la ricerca. Se non viene specificato nessuno e il modello di dati contiene un solo vettore, verrà usato tale vettore. Se il modello di dati non contiene alcun vettore o ne contiene più di uno e non viene fornito VectorProperty, il metodo di ricerca lancerà un'eccezione.

using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;

var vectorStore = new InMemoryVectorStore();
var collection = vectorStore.GetCollection<int, Product>("skproducts");

// Create the vector search options and indicate that we want to search the FeatureListEmbedding property.
var vectorSearchOptions = new VectorSearchOptions<Product>
{
    VectorProperty = r => r.FeatureListEmbedding
};

// This snippet assumes searchVector is already provided, having been created using the embedding model of your choice.
var searchResult = collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);

public sealed class Product
{
    [VectorStoreKey]
    public int Key { get; set; }

    [VectorStoreData]
    public string Description { get; set; }

    [VectorStoreData]
    public List<string> FeatureList { get; set; }

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

    [VectorStoreVector(1536)]
    public ReadOnlyMemory<float> FeatureListEmbedding { get; set; }
}

Inizio e Salta

Le Top opzioni e Skip consentono di limitare il numero di risultati ai primi n risultati e di ignorare un numero di risultati dall'inizio del set di risultati. È possibile usare Top e Skip per eseguire il paging se si desidera recuperare un numero elevato di risultati usando chiamate separate.

// Create the vector search options and indicate that we want to skip the first 40 results.
var vectorSearchOptions = new VectorSearchOptions<Product>
{
    Skip = 40
};

// This snippet assumes searchVector is already provided, having been created using the embedding model of your choice.
// Here we pass top: 20 to indicate that we want to retrieve the next 20 results after skipping
// the first 40
var searchResult = collection.SearchAsync(searchVector, top: 20, vectorSearchOptions);

// Iterate over the search results.
await foreach (var result in searchResult)
{
    Console.WriteLine(result.Record.FeatureList);
}

Il valore Skip predefinito è 0.

IncludiVettori

L'opzione IncludeVectors consente di specificare se si desidera restituire vettori nei risultati della ricerca. Se false, le proprietà vettoriali nel modello restituito verranno lasciate null. L'uso false di può ridurre significativamente la quantità di dati recuperati dall'archivio vettoriale durante la ricerca, rendendo le ricerche più efficienti.

Il valore predefinito per IncludeVectors è false.

// Create the vector search options and indicate that we want to include vectors in the search results.
var vectorSearchOptions = new VectorSearchOptions<Product>
{
    IncludeVectors = true
};

// This snippet assumes searchVector is already provided, having been created using the embedding model of your choice.
var searchResult = collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);

// Iterate over the search results.
await foreach (var result in searchResult)
{
    Console.WriteLine(result.Record.FeatureList);
}

Filtro

L'opzione di filtro di ricerca vettoriale può essere usata per fornire un filtro per filtrare i record nella raccolta scelta prima di applicare la ricerca vettoriale.

Questo offre diversi vantaggi:

  • Ridurre la latenza e i costi di elaborazione, poiché devono essere confrontati solo i record rimanenti dopo il filtro con il vettore di ricerca e quindi è necessario eseguire un minor numero di confronti vettoriali.
  • Limitare il set di risultati per scopi di controllo di accesso, ad esempio, escludendo i dati a cui l'utente non deve avere accesso.

Si noti che affinché i campi vengano usati per filtrare, molti archivi vettoriali richiedono che tali campi vengano indicizzati per primi. Alcuni archivi vettoriali consentiranno di filtrare usando qualsiasi campo, ma possono facoltativamente consentire l'indicizzazione per migliorare le prestazioni di filtro.

Se si crea una raccolta tramite le astrazioni dell'archivio vettoriale del kernel semantico e si vuole abilitare il filtro in un campo, impostare la IsFilterable proprietà su true quando si definisce il modello di dati o quando si crea la definizione del record.

Suggerimento

Per altre informazioni su come impostare la IsFilterable proprietà, fare riferimento ai parametri VectorStoreDataAttribute o alle impostazioni di configurazione VectorStoreDataProperty.

I filtri vengono espressi usando espressioni LINQ in base al tipo del modello di dati. Il set di espressioni LINQ supportate varia a seconda delle funzionalità supportate da ogni database, ma tutti i database supportano un'ampia base di espressioni comuni, ad esempio uguali, non uguali e così via.

// Create the vector search options and set the filter on the options.
var vectorSearchOptions = new VectorSearchOptions<Glossary>
{
    Filter = r => r.Category == "External Definitions" && r.Tags.Contains("memory")
};

// This snippet assumes searchVector is already provided, having been created using the embedding model of your choice.
var searchResult = collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);

// Iterate over the search results.
await foreach (var result in searchResult)
{
    Console.WriteLine(result.Record.Definition);
}

sealed class Glossary
{
    [VectorStoreKey]
    public ulong Key { get; set; }

    // Category is marked as indexed, since we want to filter using this property.
    [VectorStoreData(IsIndexed = true)]
    public string Category { get; set; }

    // Tags is marked as indexed, since we want to filter using this property.
    [VectorStoreData(IsIndexed = true)]
    public List<string> Tags { get; set; }

    [VectorStoreData]
    public string Term { get; set; }

    [VectorStoreData]
    public string Definition { get; set; }

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

Ricerca vettoriale

Esistono due ricerche attualmente supportate nelle astrazioni dell'archivio di vettori del Semantic Kernel.

  1. search (ricerca vettoriale)
    1. Si tratta di una ricerca basata su un valore che può essere vettorializzato, dal campo nel embedding_generator modello di dati o nella definizione di record o dall'archivio vettoriale stesso. Oppure specificando direttamente un vettore.
  2. hybrid_search -> vedere Ricerca ibrida

Tutte le ricerche possono accettare un set facoltativo di parametri:

  • vector: Un vettore usato per la ricerca può essere fornito al posto dei valori o in aggiunta ai valori per una configurazione ibrida.
  • top: numero di risultati da restituire, il valore predefinito è 3.
  • skip: numero di risultati da ignorare, il valore predefinito è 0.
  • include_vectors: indica se includere i vettori nei risultati, per impostazione predefinita è false.
  • filter: filtro da applicare ai risultati prima dell'applicazione della ricerca vettoriale, il valore predefinito è None, sotto forma di espressione lambda: lambda record: record.property == "value".
  • vector_property_name: nome della proprietà vector da usare per la ricerca, per impostazione predefinita viene utilizzata la prima proprietà vettoriale presente nel modello di dati o nella definizione di record.
  • include_total_count: indica se includere il conteggio totale dei risultati nel risultato della ricerca, per impostazione predefinita è false.

Supponendo di avere una raccolta che contiene già dati, è possibile cercarli facilmente. Di seguito è riportato un esempio di uso di Ricerca intelligenza artificiale di Azure.

from semantic_kernel.connectors.azure_ai_search import AzureAISearchCollection, AzureAISearchStore

# Create a Azure AI Search VectorStore object and choose an existing collection that already contains records.
# Hotels is the data model decorated class.
store = AzureAISearchStore()
collection: AzureAISearchCollection[str, Hotels] = store.get_collection(Hotels, collection_name="skhotels")

search_results = await collection.search(
    query, vector_property_name="vector"
)
hotels = [record.record async for record in search_results.results]
print(f"Found hotels: {hotels}")

Suggerimento

Per altre informazioni su come generare incorporamenti, vedere Generazione di incorporamenti.

Filtri

Il filter parametro può essere usato per fornire un filtro per filtrare i record nella raccolta scelta. È definito come espressione lambda o stringa di un'espressione lambda, ad esempio lambda record: record.property == "value".

È importante comprendere che questi non vengono eseguiti direttamente, ma vengono analizzati nella sintassi corrispondente agli archivi vettoriali, l'unica eccezione a questo è che InMemoryCollection esegue direttamente il filtro.

Data questa flessibilità, è importante esaminare la documentazione di un archivio specifico per comprendere quali filtri sono supportati, ad esempio non tutti gli archivi vettoriali supportano filtri negativi (ad lambda x: not x.valueesempio ) e che non diventano evidenti fino a quando non viene eseguita la ricerca.

Ricerca vettoriale

Il searchAsync metodo consente di eseguire ricerche usando dati già vettorializzati. Questo metodo accetta un vettore e una classe facoltativa VectorSearchOptions come input. Questo metodo è disponibile nelle interfacce seguenti:

  1. VectorizedSearch<Record>
  2. VectorStoreRecordCollection<Key, Record>

Si noti che VectorStoreRecordCollection<Key, Record> eredita da VectorizedSearch<Record>.

Supponendo di avere una raccolta che contiene già dati, è possibile cercarli facilmente. Di seguito è riportato un esempio di uso di JDBC con PostgreSQL.

import com.microsoft.semantickernel.data.jdbc.JDBCVectorStore;
import com.microsoft.semantickernel.data.jdbc.JDBCVectorStoreOptions;
import com.microsoft.semantickernel.data.jdbc.JDBCVectorStoreRecordCollectionOptions;
import com.microsoft.semantickernel.data.jdbc.postgres.PostgreSQLVectorStoreQueryProvider;
import com.microsoft.semantickernel.data.vectorstorage.options.VectorSearchOptions;
import org.postgresql.ds.PGSimpleDataSource;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Configure the data source
        PGSimpleDataSource dataSource = new PGSimpleDataSource();
        dataSource.setUrl("jdbc:postgresql://localhost:5432/sk");
        dataSource.setUser("postgres");
        dataSource.setPassword("root");

        // Create a JDBC vector store and choose an existing collection that already contains records.
        var vectorStore = new JDBCVectorStore(dataSource, JDBCVectorStoreOptions.builder()
                .withQueryProvider(PostgreSQLVectorStoreQueryProvider.builder()
                        .withDataSource(dataSource)
                        .build())
                .build());
        var collection = vectorStore.getCollection("skhotels", JDBCVectorStoreRecordCollectionOptions.<Hotel>builder()
                .withRecordClass(Hotel.class)
                .build());

        // Generate a vector for your search text, using your chosen embedding generation implementation.
        // Just showing a placeholder method here for brevity.
        var searchVector = generateEmbeddingsAsync("I'm looking for a hotel where customer happiness is the priority.").block();

        // Do the search, passing an options object with a Top value to limit results to the single top match.
        var searchResult = collection.searchAsync(searchVector, VectorSearchOptions.builder()
                .withTop(1).build()
        ).block();

        // Inspect the returned hotel.
        Hotel hotel = searchResult.getResults().get(0).getRecord();
        System.out.printf("Found hotel description: %s\n", hotel.getDescription());
    }
}

Suggerimento

Per altre informazioni su come generare incorporamenti, vedere Generazione di incorporamenti.

Opzioni di ricerca vettoriale

È possibile specificare le opzioni seguenti usando la VectorSearchOptions classe .

VectorFieldName

L'opzione VectorFieldName può essere usata per specificare il nome del campo vettore di destinazione durante la ricerca. Se non viene specificato alcun valore, verrà usato il primo vettore trovato nel modello di dati o specificato nella definizione del record.

Si noti che quando si specifica il VectorFieldName, usare il nome del campo come definito nel modello di dati o nella definizione del record. Usare questo nome di campo anche se il campo può essere archiviato con un nome diverso nell'archivio vettoriale. Il nome di archiviazione può essere diverso a causa delle impostazioni di serializzazione personalizzate.

import com.microsoft.semantickernel.data.VolatileVectorStore;
import com.microsoft.semantickernel.data.VolatileVectorStoreRecordCollectionOptions;
import com.microsoft.semantickernel.data.vectorstorage.annotations.VectorStoreRecordData;
import com.microsoft.semantickernel.data.vectorstorage.annotations.VectorStoreRecordKey;
import com.microsoft.semantickernel.data.vectorstorage.annotations.VectorStoreRecordVector;
import com.microsoft.semantickernel.data.vectorstorage.options.VectorSearchOptions;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Build a query provider
        var vectorStore = new VolatileVectorStore();
        var collection = vectorStore.getCollection("skproducts", VolatileVectorStoreRecordCollectionOptions.<Product>builder()
                .withRecordClass(Product.class)
                .build());

        // Create the vector search options and indicate that we want to search the FeatureListEmbedding field.
        var searchOptions = VectorSearchOptions.builder()
                .withVectorFieldName("featureListEmbedding")
                .build();

        // Generate a vector for your search text, using the embedding model of your choice
        var searchVector = generateEmbeddingsAsync().block();

        // Do the search
        var searchResult = collection.searchAsync(searchVector, searchOptions).block();
    }

    public static class Product {
        @VectorStoreRecordKey
        private int key;

        @VectorStoreRecordData
        private String description;

        @VectorStoreRecordData
        private List<String> featureList;

        @VectorStoreRecordVector(dimensions = 1536)
        public List<Float> descriptionEmbedding;

        @VectorStoreRecordVector(dimensions = 1536)
        public List<Float> featureListEmbedding;

        public Product() {
        }

        public Product(int key, String description, List<String> featureList, List<Float> descriptionEmbedding, List<Float> featureListEmbedding) {
            this.key = key;
            this.description = description;
            this.featureList = featureList;
            this.descriptionEmbedding = Collections.unmodifiableList(descriptionEmbedding);
            this.featureListEmbedding = Collections.unmodifiableList(featureListEmbedding);
        }

        public int getKey() { return key; }
        public String getDescription() { return description; }
        public List<String> getFeatureList() { return featureList; }
        public List<Float> getDescriptionEmbedding() { return descriptionEmbedding; }
        public List<Float> getFeatureListEmbedding() { return featureListEmbedding; }
    }
}

Inizio e Salta

Le Top opzioni e Skip consentono di limitare il numero di risultati ai primi n risultati e di ignorare un numero di risultati dall'inizio del set di risultati. È possibile usare Top e Skip per eseguire il paging se si desidera recuperare un numero elevato di risultati usando chiamate separate.

// Create the vector search options and indicate that we want to skip the first 40 results and then get the next 20.
var searchOptions = VectorSearchOptions.builder()
        .withTop(20)
        .withSkip(40)
        .build();

// Generate a vector for your search text, using the embedding model of your choice
var searchVector = generateEmbeddingsAsync().block();

// Do the search
var searchResult = collection.searchAsync(searchVector, searchOptions).block();

I valori predefiniti per Top sono 3 e Skip sono 0.

IncludiVettori

L'opzione IncludeVectors consente di specificare se si desidera restituire vettori nei risultati della ricerca. Se false, le proprietà vettoriali nel modello restituito verranno lasciate null. L'uso false di può ridurre significativamente la quantità di dati recuperati dall'archivio vettoriale durante la ricerca, rendendo le ricerche più efficienti.

Il valore predefinito per IncludeVectors è false.

// Create the vector search options and indicate that we want to include vectors in the search results.
var searchOptions = VectorSearchOptions.builder()
        .withIncludeVectors(true)
        .build();

// Generate a vector for your search text, using the embedding model of your choice
var searchVector = generateEmbeddingsAsync().block();

// Do the search
var searchResult = collection.searchAsync(searchVector, searchOptions).block();

VectorSearchFilter

L'opzione VectorSearchFilter può essere usata per fornire un filtro per filtrare i record nella raccolta scelta prima di applicare la ricerca vettoriale.

Questo offre diversi vantaggi:

  • Ridurre la latenza e i costi di elaborazione, poiché devono essere confrontati solo i record rimanenti dopo il filtro con il vettore di ricerca e quindi è necessario eseguire un minor numero di confronti vettoriali.
  • Limitare il set di risultati per scopi di controllo di accesso, ad esempio, escludendo i dati a cui l'utente non deve avere accesso.

Si noti che affinché i campi vengano usati per filtrare, molti archivi vettoriali richiedono che tali campi vengano indicizzati per primi. Alcuni archivi vettoriali consentiranno di filtrare usando qualsiasi campo, ma possono facoltativamente consentire l'indicizzazione per migliorare le prestazioni di filtro.

Se si crea una raccolta tramite le astrazioni dell'archivio vettoriale del kernel semantico e si vuole abilitare il filtro in un campo, impostare il IsFilterable campo su true quando si definisce il modello di dati o quando si crea la definizione del record.

Suggerimento

Per altre informazioni su come impostare il IsFilterable campo, fare riferimento ai parametri VectorStoreRecordData o alle impostazioni di configurazione VectorStoreRecordDataField.

Per creare un filtro, usare la VectorSearchFilter classe . Si possono combinare più clausole di filtro in un unico oggetto VectorSearchFilter. Tutte le clausole di filtro vengono combinate con and. Si noti che quando si specifica un nome di campo quando si costruisce il filtro, usare il nome del campo come definito nel modello di dati o nella definizione del record. Usare questo nome di campo anche se il campo può essere archiviato con un nome diverso nell'archivio vettoriale. Il nome di archiviazione può essere diverso a causa delle impostazioni di serializzazione personalizzate.

// Filter where category == 'External Definitions' and tags contain 'memory'.
var filter = VectorSearchFilter.builder()
        .equalTo("category", "External Definitions")
        .anyTagEqualTo("tags", "memory")
        .build();

// Create the vector search options and indicate that we want to filter the search results by a specific field.
var searchOptions = VectorSearchOptions.builder()
        .withVectorSearchFilter(filter)
        .build();

// Generate a vector for your search text, using the embedding model of your choice
var searchVector = generateEmbeddingsAsync().block();

// Do the search
var searchResult = collection.searchAsync(searchVector, searchOptions).block();


public static class Glossary {
    @VectorStoreRecordKey
    private String key;

    @VectorStoreRecordData(isFilterable = true)
    private String category;

    @VectorStoreRecordData(isFilterable = true)
    private List<String> tags;

    @VectorStoreRecordData
    private String term;

    @VectorStoreRecordData
    private String definition;

    @VectorStoreRecordVector(dimensions = 1536)
    private List<Float> definitionEmbedding;

    public Glossary() {
    }

    public Glossary(String key, String category, List<String> tags, String term, String definition, List<Float> definitionEmbedding) {
        this.key = key;
        this.category = category;
        this.tags = tags;
        this.term = term;
        this.definition = definition;
        this.definitionEmbedding = Collections.unmodifiableList(definitionEmbedding);
    }

    public String getKey() { return key; }
    public String getCategory() { return category; }
    public List<String> getTags() { return tags; }
    public String getTerm() { return term; }
    public String getDefinition() { return definition; }
    public List<Float> getDefinitionEmbedding() { return definitionEmbedding; }
}

Clausola di filtro EqualTo

Usare equalTo per un confronto diretto tra campo e valore.

Clausola di filtro AnyTagEqualTo

Usare anyTagEqualTo per verificare se una delle stringhe, archiviata in un campo tag nell'archivio vettoriale, contiene un valore specificato. Affinché un campo venga considerato un campo tag, deve essere un List<String>.