Поделиться через


Что такое хранилища векторов семантического ядра? (Предварительная версия)

Предупреждение

Функциональные возможности хранилища векторов семантического ядра — rc, и улучшения, требующие критических изменений, могут по-прежнему возникать в ограниченных обстоятельствах перед выпуском.

Предупреждение

Функциональность хранилища векторов семантического ядра находится в предварительной версии, и улучшения, требующие необратимых изменений, всё ещё могут возникнуть в ограниченных обстоятельствах перед выпуском.

Совет

Если вы ищете сведения о устаревших соединителях хранилища памяти, перейдите на страницу хранилища памяти.

Векторные базы данных имеют множество вариантов использования в разных доменах и приложениях, которые включают обработку естественного языка (NLP), компьютерное зрение (CV), системы рекомендаций (RS) и другие области, требующие семантического понимания и сопоставления данных.

Одним из вариантов использования для хранения информации в векторной базе данных является включение больших языковых моделей (LLM) для создания более релевантных и согласованных ответов. Крупные языковые модели часто сталкиваются с проблемами, такими как создание неточных или неуместных сведений, отсутствие фактической согласованности или здравого смысла, повторение или противоречие себе, проявление предвзятости или оскорблений. Чтобы устранить эти проблемы, можно использовать векторную базу данных для хранения информации о различных темах, ключевых словах, фактах, мнениях и/или источниках, связанных с нужным доменом или жанром. База данных векторов позволяет эффективно находить подмножество информации, связанной с определенным вопросом или разделом. Затем вы можете передать информацию из векторной базы данных с запросом на большую языковую модель, чтобы создать более точное и соответствующее содержимое.

Например, если вы хотите написать статью блога о последних тенденциях в ИИ, можно использовать векторную базу данных для хранения последних сведений по этой теме и передачи этих сведений вместе с запросом модели LLM, чтобы создать статью блога, которая использует актуальные данные.

Семантический ядро и .net предоставляют абстракцию для взаимодействия с векторными хранилищами и список встроенных реализаций, реализующих эти абстракции для различных баз данных. Функции включают создание, перечисление и удаление коллекций записей, а также отправку, извлечение и удаление записей. Абстракция позволяет легко экспериментировать с бесплатным или локально размещенным векторным хранилищем, а затем переключиться на использование сервиса при необходимости масштабирования.

Встроенные реализации можно использовать с семантическим ядром, но они не зависят от основного стека семантического ядра и, следовательно, их можно использовать полностью независимо, если это необходимо. Семантическое ядро предоставляет реализации, называемые «соединителями».

Извлечение дополненной генерации (RAG) с векторными хранилищами

Абстракция хранилища векторов — это api низкого уровня для добавления и получения данных из векторных хранилищ. Семантическое ядро имеет встроенную поддержку использования любой из реализаций векторного хранилища для RAG. Это достигается путем упаковки IVectorSearchable<TRecord> и представления его в качестве реализации текстового поиска.

Совет

Дополнительные сведения об использовании векторных хранилищ для RAG см. в Использование векторных хранилищ с семантическим поиском текста ядра.

Совет

Дополнительные сведения о поиске текста см. в статье Что такое поиск текста семантического ядра?

Совет

Детальные сведения о том, как быстро добавить RAG в вашего агента, см. в статье "Добавление расширенной генерации с привлечением данных (RAG) в агенты для семантического ядра".

Абстракция хранилища векторов

Абстракции векторного хранилища предоставляются в пакете Microsoft.Extensions.VectorData.Abstractions nuget. Ниже приведены основные абстрактные базовые классы и интерфейсы.

Microsoft.Extensions.VectorData.VectorStore

VectorStore содержит операции, охватывающие все коллекции в хранилище векторов, например ListCollectionNames. Он также предоставляет возможность получения VectorStoreCollection<TKey, TRecord> экземпляров.

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

VectorStoreCollection<TKey, TRecord> представляет коллекцию. Эта коллекция может существовать или не существовать, а абстрактный базовый класс предоставляет методы для проверки наличия коллекции, ее создания или удаления. Абстрактный базовый класс также предоставляет методы для обновления, вставки (upsert), получения и удаления записей. Наконец, абстрактный базовый класс наследует от IVectorSearchable<TRecord>, предоставляя возможности поиска векторов.

Microsoft.Extensions.VectorData.IVectorSearchable<TRecord>

  • SearchAsync<TRecord> можно использовать для выполнения следующих действий:
    • векторный поиск принимает некоторые входные данные, которые могут быть векторизированы зарегистрированным генератором внедрения или базой данных векторов, в которой база данных поддерживает эту функцию.
    • поиск выполняется, используя вектор в качестве входных данных.

Извлечение дополненной генерации (RAG) с векторными хранилищами

Абстракции хранилища векторов — это api низкого уровня для добавления и получения данных из векторных хранилищ. Семантическое ядро имеет встроенную поддержку использования любой из реализаций векторного хранилища для RAG. Это достигается, обернув VectorSearchBase[TKey, TModel] с помощью VectorizedSearchMixin[Tmodel], VectorizableTextSearchMixin[TModel] или VectorTextSearch[TModel] и представляя его в виде реализации поиска текста.

Совет

Дополнительные сведения об использовании векторных хранилищ для RAG см. в Использование векторных хранилищ с семантическим поиском текста ядра.

Совет

Дополнительные сведения о поиске текста см. в статье Что такое поиск текста семантического ядра?

Абстракция хранилища векторов

Ниже приведены основные интерфейсы абстракции векторного хранилища.

com.microsoft.semantickernel.data.vectorstorage.VectorStore

VectorStore содержит операции, охватывающие все коллекции в векторном хранилище, например listCollectionNames. Он также предоставляет возможность получения VectorStoreRecordCollection<Key, Record> экземпляров.

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

VectorStoreRecordCollection<Key, Record> представляет коллекцию. Эта коллекция может существовать или не существовать, и интерфейс предоставляет методы для проверки наличия коллекции, создания или удаления. Интерфейс также предоставляет методы для добавления/обновления, получения и удаления записей. Наконец, интерфейс наследует от VectorizedSearch<Record>, предоставляя возможности векторного поиска.

com.microsoft.semantickernel.data.vectorsearch.VectorizedSearch<Запись>

VectorizedSearch<Record> содержит метод для выполнения векторного поиска. VectorStoreRecordCollection<Key, Record> наследует от VectorizedSearch<Record> того, чтобы можно было использовать VectorizedSearch<Record> самостоятельно в тех случаях, когда требуется только поиск, а управление записями или коллекциями не требуется.

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

VectorizableTextSearch<Record> содержит метод для выполнения векторного поиска, в котором база данных векторов имеет возможность автоматически создавать внедрения. Например, можно вызвать этот метод с текстовой строкой, и база данных создаст встраивание для вас и выполнит поиск по векторному полю. Это не поддерживается всеми векторными базами данных и поэтому реализуется только отдельными соединителями.

Начало работы с векторными хранилищами

Импортируйте необходимые пакеты NuGet

Все интерфейсы хранилища векторов и все связанные с абстракцией классы доступны в пакете Microsoft.Extensions.VectorData.Abstractions nuget. Каждая реализация хранилища векторов доступна в собственном пакете nuget. См. на странице готовых соединителей список известных реализаций.

Пакет абстракций можно добавить следующим образом.

dotnet add package Microsoft.Extensions.VectorData.Abstractions

Определение модели данных

Абстракции векторного хранилища используют подход, ориентированный на модель, для взаимодействия с базами данных. Это означает, что первым шагом является определение модели данных, которая сопоставляется со схемой хранения. Чтобы помочь реализации создавать коллекции записей и сопоставлять с схемой хранения, модель может быть аннотирована, чтобы указать функцию каждого свойства.

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; }
}

Совет

Дополнительные сведения о том, как аннотировать модель данных, см. в определении модели данных.

Совет

Для альтернативы аннотации модели данных см . определение схемы с определением записи.

Подключение к базе данных и выбор коллекции

После определения модели данных необходимо создать экземпляр VectorStore для выбранной базы данных и выбрать коллекцию записей.

В этом примере мы будем использовать Qdrant. Поэтому вам потребуется импортировать пакет NuGet Qdrant.

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

Если вы хотите запустить Qdrant локально с помощью Docker, используйте следующую команду, чтобы запустить контейнер Qdrant с параметрами, используемыми в этом примере.

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

Чтобы убедиться, что экземпляр Qdrant работает правильно, посетите панель мониторинга Qdrant, встроенную в контейнер Docker: http://localhost:6333/dashboard

Так как базы данных поддерживают множество различных типов ключей и записей, мы можем указать тип ключа и записи для коллекции с помощью универсальных шаблонов. В нашем случае тип записи будет классом Hotel , который мы уже определили, и тип ключа будет ulong, так как HotelId свойство является ulong и Qdrant поддерживает Guid только ключ или ulong ключи.

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

Так как базы данных поддерживают множество различных типов ключей и записей, мы можем указать тип ключа и записи для коллекции с помощью универсальных шаблонов. В нашем случае тип записи будет классом Hotel , который мы уже определили, и тип ключа будет str, так как HotelId свойство является str и Qdrant поддерживает str только ключ или int ключи.

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.
)

Так как базы данных поддерживают множество различных типов ключей и записей, мы можем указать тип ключа и записи для коллекции с помощью универсальных шаблонов. В нашем случае тип записи будет классом Hotel , который мы уже определили, а тип ключа будет String, поскольку свойство hotelId является String, а JDBC-хранилище поддерживает только ключи String.

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()
        );
    }
}

Совет

Дополнительные сведения о том, какие ключи и типы полей поддерживает каждая реализация Vector Store, см. в документации по каждой реализации.

Создание коллекции и добавление записей

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

Создание коллекции и добавление записей

# 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();

Совет

Для получения дополнительной информации о создании эмбеддингов см. создание эмбеддингов.

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

Выполнить векторный поиск

Метод поиска можно использовать для поиска записей в коллекции. Она либо принимает строку, которая затем векторируется с помощью установки внедрения поколения в модель или коллекцию, либо вектор, который уже создан.

# 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}")

Создание функции поиска

Чтобы создать простую функцию поиска, которую можно использовать для поиска отелей, можно использовать create_search_function метод в коллекции.

Имя и описание, а также имена и описания параметров используются для генерации сигнатуры функции, отправляемой в LLM, когда осуществляется вызов функции. Это означает, что настройка этого может быть полезна, чтобы LLM сгенерировал корректный вызов функции.

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 `*`."
)

Существует множество других параметров, например, это то, что выглядит более сложная версия, обратите внимание на настройку параметров и string_mapper функцию, используемую для преобразования записи в строку.

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}) ", 
)

Совет

Дополнительные примеры, включая комплексные примеры, см. в репозитории примеров семантического ядра.

// 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());

Совет

Для получения дополнительной информации о создании эмбеддингов см. создание эмбеддингов.

Следующие шаги