Partilhar via


O que são Semantic Kernel Vetor Stores? (Pré-visualização)

Aviso

A funcionalidade Semantic Kernel Vector Store está em fase RC, e melhorias que exigem alterações disruptivas ainda podem ocorrer em circunstâncias limitadas antes do lançamento oficial.

Aviso

A funcionalidade Semantic Kernel Vetor Store está em pré-visualização, e melhorias que exigem alterações de quebra ainda podem ocorrer em circunstâncias limitadas antes do lançamento.

Gorjeta

Se você estiver procurando informações sobre os conectores de armazenamento de memória herdados, consulte a página Armazenamentos de memória.

Os bancos de dados vetoriais têm muitos casos de uso em diferentes domínios e aplicativos que envolvem processamento de linguagem natural (NLP), visão computacional (CV), sistemas de recomendação (RS) e outras áreas que exigem compreensão semântica e correspondência de dados.

Um caso de uso para armazenar informações em um banco de dados vetorial é permitir que grandes modelos de linguagem (LLMs) gerem respostas mais relevantes e coerentes. Os modelos linguísticos de grande dimensão enfrentam frequentemente desafios como a geração de informações imprecisas ou irrelevantes; falta de coerência factual ou de bom senso; repetir-se ou contradizer-se; ser tendencioso ou ofensivo. Para ajudar a superar esses desafios, você pode usar um banco de dados vetorial para armazenar informações sobre diferentes tópicos, palavras-chave, fatos, opiniões e/ou fontes relacionadas ao domínio ou gênero desejado. O banco de dados vetorial permite que você encontre eficientemente o subconjunto de informações relacionadas a uma pergunta ou tópico específico. Em seguida, você pode passar informações do banco de dados vetorial com seu prompt para seu modelo de linguagem grande para gerar conteúdo mais preciso e relevante.

Por exemplo, se você quiser escrever uma postagem de blog sobre as últimas tendências em IA, você pode usar um banco de dados vetorial para armazenar as informações mais recentes sobre esse tópico e passar as informações junto com a solicitação para um LLM, a fim de gerar uma postagem de blog que aproveite as informações mais recentes.

O Semantic Kernel e o .net fornecem uma abstração para interagir com Vetor Stores e uma lista de implementações prontas para uso que implementam essas abstrações para vários bancos de dados. Os recursos incluem criar, listar e excluir coleções de registros e carregar, recuperar e excluir registros. A abstração facilita a experimentação com uma loja de vetores gratuita ou hospedada localmente e, em seguida, alternar para um serviço quando precisar aumentar a escalabilidade.

As implementações pré-configuradas podem ser usadas com o Kernel Semântico, mas não dependem da pilha principal do Kernel Semântico e, assim, podem ser usadas completamente de forma independente, caso seja preciso. As implementações fornecidas pelo Kernel Semântico são referidas como 'conectores'.

Geração Aumentada de Recuperação (RAG) com Armazenamento Vetorial

A abstração de armazenamento vetorial é uma api de baixo nível para adicionar e recuperar dados de repositórios vetoriais. O Semantic Kernel tem suporte integrado para usar qualquer uma das implementações do Vetor Store para RAG. Isso é conseguido encapsulando IVectorSearchable<TRecord> e expondo-o como uma implementação de Pesquisa de Texto.

Gorjeta

Para saber mais sobre como usar repositórios vetoriais para RAG, consulte Como usar repositórios vetoriais com pesquisa de texto do kernel semântico.

Gorjeta

Para saber mais sobre a pesquisa de texto, consulte O que é a Pesquisa de Texto do Kernel Semântico?

Gorjeta

Para saber mais sobre como adicionar rapidamente o RAG ao seu agente, consulte Adicionando geração aumentada de recuperação (RAG) aos agentes do kernel semântico.

Abstração de Armazenamento Vetorial

As abstrações do Vetor Store são fornecidas no Microsoft.Extensions.VectorData.Abstractions pacote nuget. A seguir estão as principais classes de base abstratas e interfaces.

Microsoft.Extensions.VectorData.VectorStore

VectorStore contém operações que abrangem todas as coleções no repositório de vetores, por exemplo, ListCollectionNames. Ele fornece também a possibilidade de obter instâncias de VectorStoreCollection<TKey, TRecord>.

Microsoft.Extensions.VectorData.VectorStoreCollection<TKey, TRecord>

VectorStoreCollection<TKey, TRecord> representa uma coleção. Essa coleção pode ou não existir, e a classe base abstrata fornece métodos para verificar se a coleção existe, criá-la ou excluí-la. A classe base abstrata também fornece métodos para inserir ou atualizar, obter e eliminar registos. Finalmente, a classe base abstrata herda de IVectorSearchable<TRecord>, que fornece capacidades de pesquisa vetorial.

Microsoft.Extensions.VectorData.IVectorSearchable<TRecord>

  • SearchAsync<TRecord> pode ser usado para:
    • pesquisas vetoriais tendo alguma entrada que pode ser vetorizada por um gerador de incorporação registrado ou pelo banco de dados vetorial onde o banco de dados suporta isso.
    • pesquisas vetoriais tomando um vetor como entrada.

Geração Aumentada de Recuperação (RAG) com Armazenamento Vetorial

As abstrações de armazenamento vetorial são uma api de baixo nível para adicionar e recuperar dados de repositórios vetoriais. O Semantic Kernel tem suporte integrado para usar qualquer uma das implementações do Vetor Store para RAG. Isso é conseguido quebrando VectorSearchBase[TKey, TModel] com VectorizedSearchMixin[Tmodel], VectorizableTextSearchMixin[TModel] ou VectorTextSearch[TModel] e expondo-o como uma implementação de Pesquisa de Texto.

Gorjeta

Para saber mais sobre como usar repositórios vetoriais para RAG, consulte Como usar repositórios vetoriais com pesquisa de texto do kernel semântico.

Gorjeta

Para saber mais sobre a pesquisa de texto, consulte O que é a Pesquisa de Texto do Kernel Semântico?

Abstração de Armazenamento Vetorial

As principais interfaces na abstração do Vetor Store são as seguintes.

com.microsoft.semantickernel.data.vectorstorage.VectorStore

VectorStore contém operações que abrangem todas as coleções no repositório de vetores, por exemplo, listCollectionNames. Ele fornece também a possibilidade de obter instâncias de VectorStoreRecordCollection<Key, Record>.

com.microsoft.semantickernel.data.vectorstorage.VectorStoreRecordCollection<Key, Registro>

VectorStoreRecordCollection<Key, Record> representa uma coleção. Essa coleção pode ou não existir, e a interface fornece métodos para verificar se a coleção existe, criá-la ou excluí-la. A interface também fornece métodos para inserir ou atualizar, obter e excluir registos. Finalmente, a interface herda de VectorizedSearch<Record>, fornecendo capacidades de pesquisa vetorial.

com.microsoft.semantickernel.data.vectorsearch.VectorizedSearch<Record>

VectorizedSearch<Record> Contém um método para fazer pesquisas vetoriais. VectorStoreRecordCollection<Key, Record> herda de VectorizedSearch<Record>, tornando possível utilizar VectorizedSearch<Record> por si só nos casos em que apenas a pesquisa é necessária e não é necessário qualquer registo ou gestão de coleções.

com.microsoft.semantickernel.data.vectorsearch.VectorizableTextSearch<Record>

VectorizableTextSearch<Record> Contém um método para fazer pesquisas vetoriais onde o banco de dados vetorial tem a capacidade de gerar incorporações automaticamente. Por exemplo, você pode chamar esse método com uma cadeia de texto e o banco de dados gerará a incorporação para você e pesquisará em um campo vetorial. Isso não é suportado por todos os bancos de dados vetoriais e, portanto, só é implementado por conectores selecionados.

Introdução às Vetor Stores

Importe os pacotes nuget necessários

Todas as interfaces de armazenamento vetorial e quaisquer classes relacionadas à Microsoft.Extensions.VectorData.Abstractions abstração estão disponíveis no pacote nuget. Cada implementação de armazenamento vetorial está disponível em seu próprio pacote nuget. Para obter uma lista de implementações conhecidas, consulte a página Conectores prontos para uso.

O pacote de abstrações pode ser adicionado assim.

dotnet add package Microsoft.Extensions.VectorData.Abstractions

Defina seu modelo de dados

As abstrações do Vetor Store usam uma abordagem de modelo inicial para interagir com bancos de dados. Isso significa que a primeira etapa é definir um modelo de dados que corresponda ao esquema de armazenamento. Para ajudar as implementações a criar coleções de registros e mapear para o esquema de armazenamento, o modelo pode ser anotado para indicar a função de cada propriedade.

using Microsoft.Extensions.VectorData;

public class Hotel
{
    [VectorStoreKey]
    public ulong HotelId { get; set; }

    [VectorStoreData(IsIndexed = true)]
    public string HotelName { get; set; }

    [VectorStoreData(IsFullTextIndexed = true)]
    public string Description { get; set; }

    [VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineSimilarity, IndexKind = IndexKind.Hnsw)]
    public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

    [VectorStoreData(IsIndexed = true)]
    public string[] Tags { get; set; }
}
from dataclasses import dataclass, field
from typing import Annotated
from semantic_kernel.data.vector import (
    DistanceFunction,
    IndexKind,
    VectorStoreField,
    vectorstoremodel,
)

@vectorstoremodel
@dataclass
class Hotel:
    hotel_id: Annotated[str, VectorStoreField('key')] = field(default_factory=lambda: str(uuid4()))
    hotel_name: Annotated[str, VectorStoreField('data', is_filterable=True)]
    description: Annotated[str, VectorStoreField('data', is_full_text_searchable=True)]
    description_embedding: Annotated[list[float], VectorStoreField('vector', dimensions=4, distance_function=DistanceFunction.COSINE, index_kind=IndexKind.HNSW)]
    tags: Annotated[list[str], VectorStoreField('data', is_filterable=True)]
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.definition.DistanceFunction;
import com.microsoft.semantickernel.data.vectorstorage.definition.IndexKind;

import java.util.Collections;
import java.util.List;

public class Hotel {
    @VectorStoreRecordKey
    private String hotelId;

    @VectorStoreRecordData(isFilterable = true)
    private String name;

    @VectorStoreRecordData(isFullTextSearchable = true)
    private String description;

    @VectorStoreRecordVector(dimensions = 4, indexKind = IndexKind.HNSW, distanceFunction = DistanceFunction.COSINE_DISTANCE)
    private List<Float> descriptionEmbedding;

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

    public Hotel() { }

    public Hotel(String hotelId, String name, String description, List<Float> descriptionEmbedding, List<String> tags) {
        this.hotelId = hotelId;
        this.name = name;
        this.description = description;
        this.descriptionEmbedding = Collections.unmodifiableList(descriptionEmbedding);
        this.tags = Collections.unmodifiableList(tags);
    }

    public String getHotelId() { return hotelId; }
    public String getName() { return name; }
    public String getDescription() { return description; }
    public List<Float> getDescriptionEmbedding() { return descriptionEmbedding; }
    public List<String> getTags() { return tags; }
}

Gorjeta

Para obter mais informações sobre como anotar seu modelo de dados, consulte Definindo seu modelo de dados.

Gorjeta

Para obter uma alternativa à anotação do seu modelo de dados, consulte definir o seu esquema com uma definição de registo.

Conecte-se ao seu banco de dados e selecione uma coleção

Depois de definir seu modelo de dados, a próxima etapa é criar uma instância VectorStore para o banco de dados de sua escolha e selecionar uma coleção de registros.

Neste exemplo, usaremos o Qdrant. Portanto, você precisará importar o pacote nuget Qdrant.

dotnet add package Microsoft.SemanticKernel.Connectors.Qdrant --prerelease

Se você quiser executar o Qdrant localmente usando o Docker, use o seguinte comando para iniciar o contêiner Qdrant com as configurações usadas neste exemplo.

docker run -d --name qdrant -p 6333:6333 -p 6334:6334 qdrant/qdrant:latest

Para verificar se sua instância do Qdrant está ativada e funcionando corretamente, visite o painel do Qdrant integrado ao contêiner do docker do Qdrant: http://localhost:6333/dashboard

Como os bancos de dados suportam muitos tipos diferentes de chaves e registros, permitimos que você especifique o tipo de chave e registro para sua coleção usando genéricos. No nosso caso, o tipo de registo será a Hotel classe que já definimos, e o tipo de chave será ulong, uma vez que a HotelId propriedade é a ulong e Qdrant apenas suporta Guid ou ulong chaves.

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

// Create a Qdrant VectorStore object
var vectorStore = new QdrantVectorStore(new QdrantClient("localhost"), ownsClient: true);

// Choose a collection from the database and specify the type of key and record stored in it via Generic parameters.
var collection = vectorStore.GetCollection<ulong, Hotel>("skhotels");

Como os bancos de dados suportam muitos tipos diferentes de chaves e registros, permitimos que você especifique o tipo de chave e registro para sua coleção usando genéricos. No nosso caso, o tipo de registo será a Hotel classe que já definimos, e o tipo de chave será str, uma vez que a HotelId propriedade é a str e Qdrant apenas suporta str ou int chaves.

from semantic_kernel.connectors.qdrant import QdrantCollection

# Create a collection specify the type of key and record stored in it via Generic parameters.
collection: QdrantCollection[str, Hotel] = QdrantCollection(
    record_type=Hotel,
    collection_name="skhotels" # this is optional, you can also specify the collection_name in the vectorstoremodel decorator.
)

Como os bancos de dados suportam muitos tipos diferentes de chaves e registros, permitimos que você especifique o tipo de chave e registro para sua coleção usando genéricos. No nosso caso, o tipo de registro será a Hotel classe que já definimos, e o tipo de chave será String, já que a hotelId propriedade é uma String e o armazenamento JDBC só suporta String chaves.

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.mysql.MySQLVectorStoreQueryProvider;
import com.mysql.cj.jdbc.MysqlDataSource;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Create a MySQL data source
        var dataSource = new MysqlDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/sk");
        dataSource.setPassword("root");
        dataSource.setUser("root");

        // Create a JDBC vector store
        var vectorStore = JDBCVectorStore.builder()
            .withDataSource(dataSource)
            .withOptions(
                JDBCVectorStoreOptions.builder()
                    .withQueryProvider(MySQLVectorStoreQueryProvider.builder()
                        .withDataSource(dataSource)
                        .build())
                    .build()
            )
            .build();

        // Get a collection from the vector store
        var collection = vectorStore.getCollection("skhotels",
            JDBCVectorStoreRecordCollectionOptions.<Hotel>builder()
                .withRecordClass(Hotel.class)
                .build()
        );
    }
}

Gorjeta

Para obter mais informações sobre quais tipos de chave e campo cada implementação do Vetor Store suporta, consulte a documentação de cada implementação.

Criar a coleção e adicionar registros

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

// Create the collection if it doesn't exist yet.
await collection.EnsureCollectionExistsAsync();

// Upsert a record.
string descriptionText = "A place where everyone can be happy.";
ulong hotelId = 1;

// Create a record and generate a vector for the description using your chosen embedding generation implementation.
await collection.UpsertAsync(new Hotel
{
    HotelId = hotelId,
    HotelName = "Hotel Happy",
    Description = descriptionText,
    DescriptionEmbedding = await GenerateEmbeddingAsync(descriptionText),
    Tags = new[] { "luxury", "pool" }
});

// Retrieve the upserted record.
Hotel? retrievedHotel = await collection.GetAsync(hotelId);

Criar a coleção e adicionar registros

# Create the collection if it doesn't exist yet.
await collection.ensure_collection_exists()

# Upsert a record.
description = "A place where everyone can be happy."
hotel_id = "1"

await collection.upsert(Hotel(
    hotel_id = hotel_id,
    hotel_name = "Hotel Happy",
    description = description,
    description_embedding = await GenerateEmbeddingAsync(description),
    tags = ["luxury", "pool"]
))

# Retrieve the upserted record.
retrieved_hotel = await collection.get(hotel_id)
// Create the collection if it doesn't exist yet.
collection.createCollectionAsync().block();

// Upsert a record.
var description = "A place where everyone can be happy";
var hotelId = "1";
var hotel = new Hotel(
    hotelId, 
    "Hotel Happy", 
    description, 
    generateEmbeddingsAsync(description).block(), 
    List.of("luxury", "pool")
);

collection.upsertAsync(hotel, null).block();

// Retrieve the upserted record.
var retrievedHotel = collection.getAsync(hotelId, null).block();

Gorjeta

Para obter mais informações sobre como gerar incorporações, consulte Geração de incorporação.

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

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

Faça uma pesquisa vetorial

O método de pesquisa pode ser usado para procurar registros na coleção. Ou aceita uma sequência de caracteres, que é então vetorizada usando a configuração de geração de embeddings no modelo ou coleção, ou um vetor que já está gerado.

# Do a search.
search_result = await collection.search("I'm looking for a hotel where customer happiness is the priority.", vector_property_name="description_embedding", top=3)

# Inspect the returned hotels.
async for result in search_result.results:
    print(f"Found hotel description: {result.record.description}")

Criar uma função de pesquisa

Para criar uma função de pesquisa simples que pode ser usada para pesquisar hotéis, você pode usar o create_search_function método na coleção.

O nome e a descrição, bem como os nomes e descrições dos parâmetros, são usados para gerar uma assinatura de função que é enviada para o LLM quando a chamada de função é usada. Isso significa que ajustar isso pode ser útil para fazer com que o LLM gere a chamada de função correta.

collection.create_search_function(
    function_name="hotel_search",
    description="A hotel search engine, allows searching for hotels in specific cities, "
    "you do not have to specify that you are searching for hotels, for all, use `*`."
)

Há muitos outros parâmetros, por exemplo, é assim que uma versão mais complexa se parece, observe a personalização dos parâmetros e a string_mapper função que é usada para converter o registro em uma cadeia de caracteres.

from semantic_kernel.function import KernelParameterMetadata

collection.create_search_function(
    function_name="hotel_search",
    description="A hotel search engine, allows searching for hotels in specific cities, "
    "you do not have to specify that you are searching for hotels, for all, use `*`.",
    search_type="keyword_hybrid", # default is "vector"
    parameters=[
        KernelParameterMetadata(
            name="query",
            description="The terms you want to search for in the hotel database.",
            type="str",
            is_required=True,
            type_object=str,
        ),
        KernelParameterMetadata(
            name="tags",
            description="The tags you want to search for in the hotel database, use `*` to match all.",
            type="str",
            type_object=str,
            default_value="*",
        ),
        KernelParameterMetadata(
            name="top",
            description="Number of results to return.",
            type="int",
            default_value=5,
            type_object=int,
        ),
    ],
    # finally, we specify the `string_mapper` function that is used to convert the record to a string.
    # This is used to make sure the relevant information from the record is passed to the LLM.
    string_mapper=lambda x: f"Hotel {x.record.hotel_name}: {x.record.description}. Tags: {x.record.tags} (hotel_id: {x.record.hotel_id}) ", 
)

Gorjeta

Para obter mais exemplos, incluindo exemplos de ponta a ponta, consulte o repositório Semantic Kernel Samples.

// 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.
var searchResult = collection.searchAsync(searchVector, VectorSearchOptions.builder()
    .withTop(1).build()
).block();

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

Gorjeta

Para obter mais informações sobre como gerar incorporações, consulte Geração de incorporação.

Próximos passos