Compartilhar via


Pesquisa de vetor usando provedores de repositório de vetores

A Microsoft.Extensions.VectorData biblioteca fornece recursos de pesquisa de vetor como parte de suas abstrações de repositório de vetores. Esses recursos incluem filtragem e muitas outras opções.

Dica

Para ver como você pode pesquisar sem gerar inserções por conta própria, consulte Permitir que o repositório de vetores gere inserções.

O SearchAsync método permite pesquisar usando dados que já foram vetorizados. Esse método usa um vetor e uma classe opcional VectorSearchOptions<TRecord> como entrada. SearchAsync está disponível nos seguintes tipos:

Observe que VectorStoreCollection<TKey,TRecord> implementa IVectorSearchable<TRecord>.

Supondo que você tenha uma coleção que já contenha dados, você pode pesquisá-los facilmente. Aqui está um exemplo usando Qdrant.

public async Task SearchAsync()
{
    // 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 GenerateAsync("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.
    IAsyncEnumerable<VectorSearchResult<Hotel>> searchResult =
        collection.SearchAsync(searchVector, top: 1);

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

Dica

Para obter mais informações sobre como gerar inserções, consulte a geração de inserção.

Pesquisar com inserções geradas automaticamente

Se você configurou um IEmbeddingGenerator<TInput,TEmbedding> repositório ou coleção de vetores, poderá passar um string diretamente para SearchAsync em vez de um vetor pré-compilado. O repositório de vetores gera a inserção de pesquisa automaticamente:

public async Task SearchWithAutoEmbeddingAsync()
{
    // If an IEmbeddingGenerator is configured on the vector store or collection,
    // you can pass a string directly to SearchAsync. The store generates
    // the search embedding for you.
    VectorStore vectorStore = new QdrantVectorStore(new QdrantClient("localhost"), ownsClient: true);
    VectorStoreCollection<ulong, Hotel> collection =
        vectorStore.GetCollection<ulong, Hotel>("skhotels");

    // Pass the search text directly — no manual embedding generation required.
    IAsyncEnumerable<VectorSearchResult<Hotel>> searchResult =
        collection.SearchAsync("I'm looking for a hotel where customer happiness is the priority.", top: 1);

    await foreach (VectorSearchResult<Hotel> record in searchResult)
    {
        Console.WriteLine("Found hotel description: " + record.Record.Description);
        Console.WriteLine("Found record score: " + record.Score);
    }
}

Para obter informações sobre como configurar um gerador de inserção em seu repositório de vetores, consulte Permitir que o repositório de vetores gere inserções.

Tipos de vetor com suporte

SearchAsync usa um tipo genérico como o parâmetro de vetor. Os tipos de vetores compatíveis com cada armazenamento de dados variam.

Também é importante que o tipo de vetor de pesquisa corresponda ao vetor de destino que está sendo pesquisado, por exemplo, se você tiver dois vetores no mesmo registro com diferentes tipos de vetor, verifique se o vetor de pesquisa fornecido corresponde ao tipo de vetor específico que você está direcionando. Para obter informações sobre como escolher um vetor de destino se você tiver mais de um por registro, consulte VectorProperty.

Opções de pesquisa de vetor

As opções a seguir podem ser fornecidas usando a VectorSearchOptions<TRecord> classe.

VectorProperty

Use a opção VectorProperty para especificar a propriedade de vetor a ser direcionada durante a pesquisa. Se nenhum for fornecido e o modelo de dados contiver apenas um vetor, esse vetor será usado. Se o modelo de dados não contiver nenhum vetor ou vários vetores e VectorProperty não for fornecido, o método de pesquisa gerará uma exceção.

public async Task VectorPropertySearch()
{
    var vectorStore = new InMemoryVectorStore();
    InMemoryCollection<int, Product> collection =
        vectorStore.GetCollection<int, Product>("skproducts");

    // Create the vector search options and indicate that you 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.
    IAsyncEnumerable<VectorSearchResult<Product>> searchResult =
        collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);
}

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

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

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

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

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

Ignorar resultados ou selecionar os principais resultados

O parâmetro top em SearchAsync<TInput>(TInput, Int32, VectorSearchOptions<TRecord>, CancellationToken) e a opção VectorSearchOptions<TRecord>.Skip permitem limitar o número de resultados. O top parâmetro limita os resultados aos principais n resultados. A Skip opção ignora vários resultados da parte superior do conjunto de resultados. Você pode usar esses controles para executar a paginação se quiser recuperar um grande número de resultados usando chamadas separadas.

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

// This snippet assumes searchVector is already provided,
// having been created using the embedding model of your choice.
// Pass 'top: 20' to indicate that you want to retrieve
// the next 20 results after skipping the first 40.
IAsyncEnumerable<VectorSearchResult<Product>> searchResult =
    collection.SearchAsync(searchVector, top: 20, vectorSearchOptions);

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

O valor padrão de Skip é 0.

Incluir Vetores

A VectorSearchOptions<TRecord>.IncludeVectors opção permite que você especifique se deseja retornar vetores nos resultados da pesquisa. Se false, as propriedades de vetor no modelo retornado serão deixadas nulas. O uso false pode reduzir significativamente a quantidade de dados recuperados do repositório de vetores durante a pesquisa, tornando as pesquisas mais eficientes.

O valor padrão para IncludeVectors é false.

// Create the vector search options and indicate that you 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.
IAsyncEnumerable<VectorSearchResult<Product>> searchResult =
    collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);

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

Filter

Use a opção VectorSearchOptions<TRecord>.Filter para filtrar os registros na coleção escolhida antes de aplicar a pesquisa de vetor. Isso tem vários benefícios:

  • Reduz a latência e o custo de processamento, pois somente os registros restantes após a filtragem precisam ser comparados com o vetor de pesquisa e, portanto, menos comparações de vetor precisam ser feitas.
  • Limita o conjunto de resultados. Por exemplo, você pode implementar o controle de acesso excluindo dados aos quais o usuário não deve ter acesso.

Para que os campos sejam usados para filtragem, muitos repositórios de vetores exigem que esses campos sejam indexados primeiro. Alguns repositórios de vetores permitirão filtragem usando qualquer campo, mas podem opcionalmente permitir que a indexação melhore o desempenho da filtragem.

Se você estiver criando uma coleção através das abstrações do repositório de vetores e quiser habilitar a filtragem em um campo, defina a propriedade IsIndexed para true ao definir seu modelo de dados ou ao criar sua definição de registro.

Dica

Para obter mais informações sobre como habilitar IsIndexed, consulte VectorStoreDataAttribute ou VectorStoreDataProperty.

Os filtros são expressos usando expressões LINQ com base no tipo do modelo de dados. O conjunto de expressões LINQ com suporte variará dependendo da funcionalidade suportada por cada banco de dados, mas todos os bancos de dados dão suporte a uma base ampla de expressões comuns, por exemplo, iguais, não iguais e andor.

public static async Task FilteredSearchAsync()
{
    // Create the vector search options and set the filter on the options.
    VectorSearchOptions<Glossary> vectorSearchOptions = new()
    {
        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.
    IAsyncEnumerable<VectorSearchResult<Glossary>> searchResult =
        collection.SearchAsync(searchVector, top: 3, vectorSearchOptions);

    // Iterate over the search results.
    await foreach (VectorSearchResult<Glossary> result in searchResult)
    {
        Console.WriteLine(result.Record.Definition);

    }
}

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

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

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

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

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