共用方式為


什麼是語意核心向量存放區? (預覽)

警告

語意核心向量儲存庫功能目前處於發行候選階段,且在正式發行前,可能仍需在某些有限情況下進行需要重大變更的改善。

警告

語意核心向量存放功能目前處於預覽階段,需要進行重大更改的改進可能仍會在發行前的有限情況下發生。

提示

如果您要尋找舊版記憶體存放區連接器的相關信息,請參閱 記憶體存放區頁面

向量資料庫在涉及自然語言處理 (NLP)、計算機視覺 (CV)、建議系統 (RS) 和其他需要語意理解和比對數據的其他領域,有許多使用案例。

將資訊儲存在向量資料庫中的一個使用案例是讓大型語言模型 (LLM) 產生更相關且一致的回應。 大型語言模型通常面臨挑戰,例如產生不準確或無關的資訊;缺乏事實一致性或常識;重複或矛盾自己:有偏見或冒犯性。 為了協助克服這些挑戰,您可以使用向量資料庫來儲存與所需領域或內容類型相關的不同主題、關鍵詞、事實、意見和/或來源的相關信息。 向量資料庫可讓您有效率地尋找與特定問題或主題相關的資訊子集。 然後,您可以使用提示將向量資料庫的資訊傳遞至大型語言模型,以產生更精確且相關的內容。

例如,如果您想要撰寫 AI 中最新趨勢的部落格文章,您可以使用向量資料庫來儲存該主題的最新資訊,並將資訊連同要求一起傳遞給 LLM,以產生利用最新資訊的部落格文章。

語意核心與 .NET 提供了一種抽象方法來與向量存儲庫互動,以及針對多種資料庫實作此抽象方法的現成實作清單。 功能包括建立、列出和刪除記錄集合,以及上傳、擷取和刪除記錄。 抽象層可讓您輕鬆地試驗免費或本地托管的向量存放區,然後在需要擴展時切換至服務型方案。

現成的實作可以搭配 Semantic Kernel 使用,但不依賴核心 Semantic Kernel 堆疊,因此也可以根據需要完全獨立使用。 「語意核心」提供的實作稱為「連接器」。

向量資料庫的檢索增強生成(RAG)

向量存放區抽象概念是低階 API,可用於從向量存放區新增和擷取數據。 Semantic Kernel 內建支援使用任何一個適用於 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> 表示集合。 這個集合可能或可能不存在,而抽象基類會提供方法來檢查集合是否存在、建立或刪除集合。 抽象基類也提供插入更新、獲取和刪除記錄的方法。 最後,抽象基類繼承自 IVectorSearchable<TRecord> 提供向量搜尋功能。

Microsoft.Extensions.VectorData.IVectorSearchable<TRecord>

  • SearchAsync<TRecord> 可以用來執行下列其中一項動作:
    • 向量搜尋會擷取一些輸入,這些輸入可由已註冊的內嵌產生器或資料庫支援的向量資料庫進行向量化。
    • 向量搜尋以向量作為輸入。

向量資料庫的檢索增強生成(RAG)

向量存放區抽象概念是低階 API,可用於從向量存放區新增和擷取數據。 Semantic Kernel 內建支援使用任何一個適用於 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<Record>

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。 因此,您必須匯入 Qdrant nuget 套件。

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

如果您想要使用 Docker 在本機執行 Qdrant,請使用下列命令來啟動 Qdrant 容器,並搭配此範例中使用的設定。

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

若要確認 Qdrant 實例已正常啟動並正常執行,請流覽 Qdrant Docker 容器內建的 Qdrant 儀錶板:http://localhost:6333/dashboard

由於資料庫支援許多不同類型的索引鍵和記錄,因此我們可讓您使用泛型來指定集合的索引鍵和記錄類型。 在我們的案例中,記錄類型會是 Hotel 我們已經定義的類別,而索引鍵的類型將會 ulong是 ,因為 HotelId 屬性是 ulong 和 Qdrant 僅支援 Guidulong 索引鍵。

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 僅支援 strint 索引鍵。

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

提示

如需每個向量存放區實作所支援之索引鍵和字段類型的詳細資訊,請參閱 每個實作的檔

建立集合並新增記錄

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

提示

如需如何產生內嵌的詳細資訊,請參閱 內嵌產生

下一步