什么是语义内核矢量存储连接器? (预览版)
警告
语义内核向量存储功能处于预览状态,需要中断性变更的改进可能仍发生在发布前的有限情况下。
提示
如果要查找有关旧版内存存储连接器的信息,请参阅 “内存存储”页。
矢量数据库在不同的域和应用程序中有许多用例,这些用例涉及自然语言处理(NLP)、计算机视觉(CV)、建议系统(RS)以及需要语义理解和匹配数据的其他领域。
在向量数据库中存储信息的一个用例是使大型语言模型(LLM)能够生成更相关且连贯的响应。 大型语言模型经常面临诸如生成不准确或无关信息等挑战;缺乏事实一致性或常识;重复或矛盾自己:有偏见或冒犯性。 为了帮助克服这些挑战,可以使用矢量数据库来存储与所需域或流派相关的不同主题、关键字、事实、观点和/或源的信息。 矢量数据库允许你有效地查找与特定问题或主题相关的信息的子集。 然后,可以使用提示将来自矢量数据库的信息传递给大型语言模型,以生成更准确且更相关的内容。
例如,如果要编写有关 AI 中最新趋势的博客文章,则可以使用矢量数据库来存储有关该主题的最新信息,并将信息连同请求一起传递给 LLM,以便生成利用最新信息的博客文章。
语义内核和 .net 提供了一个抽象,用于与矢量存储进行交互,以及实现这些抽象的现用连接器列表。 功能包括创建、列出和删除记录集合,以及上传、检索和删除记录。 利用抽象,可以轻松地试验免费或本地托管的向量存储,然后在需要纵向扩展时切换到服务。
矢量存储抽象
矢量存储抽象中的主要接口如下。
Microsoft.Extensions.VectorData.IVectorStore
IVectorStore
包含跨向量存储中的所有集合的操作,例如 ListCollectionNames。
它还提供获取 IVectorStoreRecordCollection<TKey, TRecord>
实例的功能。
Microsoft.Extensions.VectorData.IVectorStoreRecordCollection<TKey, TRecord>
IVectorStoreRecordCollection<TKey, TRecord>
表示集合。
此集合可能或可能不存在,并且该接口提供用于检查集合是否存在、创建或删除该集合的方法。
该接口还提供更新插入、获取和删除记录的方法。
最后,接口继承自 IVectorizedSearch<TRecord>
提供矢量搜索功能。
Microsoft.Extensions.VectorData.IVectorizedSearch<TRecord>
IVectorizedSearch<TRecord>
包含用于执行矢量搜索的方法。
IVectorStoreRecordCollection<TKey, TRecord>
继承自 IVectorizedSearch<TRecord>
在仅在需要搜索且不需要记录或集合管理的情况下自行使用 IVectorizedSearch<TRecord>
。
IVectorizableTextSearch<TRecord>
IVectorizableTextSearch<TRecord>
包含用于执行矢量搜索的方法,其中矢量数据库能够自动生成嵌入内容。 例如,可以使用文本字符串调用此方法,数据库将为你生成嵌入内容,并针对矢量字段进行搜索。 这不受所有向量数据库的支持,因此仅由选择连接器实现。
矢量存储抽象
矢量存储抽象中的主要接口如下。
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>
包含用于执行矢量搜索的方法,其中矢量数据库能够自动生成嵌入内容。 例如,可以使用文本字符串调用此方法,数据库将为你生成嵌入内容,并针对矢量字段进行搜索。 这不受所有向量数据库的支持,因此仅由选择连接器实现。
Vector Store 连接器入门
导入必要的 nuget 包
nuget 包中 Microsoft.Extensions.VectorData.Abstractions
提供了所有矢量存储接口和任何抽象相关类。
每个向量存储实现都在其自己的 nuget 包中可用。 有关已知实现的列表,请参阅 现用连接器页。
可以像这样添加抽象包。
dotnet add package Microsoft.Extensions.VectorData.Abstractions --prerelease
警告
从语义内核版本 1.23.0 中,矢量存储抽象已从中删除 Microsoft.SemanticKernel.Abstractions
,并可在新的专用 Microsoft.Extensions.VectorData.Abstractions
包中使用。
请注意,从版本 1.23.0 开始, Microsoft.SemanticKernel.Abstractions
依赖于 Microsoft.Extensions.VectorData.Abstractions
该版本,因此无需引用其他包。
但是,抽象现在将位于新 Microsoft.Extensions.VectorData
命名空间中。
从 1.22.0 或更高版本升级到 1.23.0 或更高版本时,需要在使用任何向量存储抽象类型的文件中添加一个附加using Microsoft.Extensions.VectorData;
子句,例如IVectorStore
,IVectorStoreRecordCollection
、、VectorStoreRecordDataAttribute
VectorStoreRecordKeyProperty
等。
创建自己的实现时,已进行此更改以支持向量存储提供程序。 提供程序只能引用 Microsoft.Extensions.VectorData.Abstractions
包。 这减少了潜在的版本冲突,并允许语义内核在不影响矢量存储提供程序的情况下继续快速发展。
定义数据模型
语义内核向量存储连接器使用模型第一种方法与数据库交互。 这意味着第一步是定义映射到存储架构的数据模型。 为了帮助连接器创建记录集合并映射到存储架构,可以批注模型以指示每个属性的函数。
using Microsoft.Extensions.VectorData;
public class Hotel
{
[VectorStoreRecordKey]
public ulong HotelId { get; set; }
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[VectorStoreRecordVector(Dimensions: 4, DistanceFunction.CosineDistance, IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
[VectorStoreRecordData(IsFilterable = true)]
public string[] Tags { get; set; }
}
from dataclasses import dataclass, field
from typing import Annotated
from semantic_kernel.data import (
DistanceFunction,
IndexKind,
VectorStoreRecordDataField,
VectorStoreRecordDefinition,
VectorStoreRecordKeyField,
VectorStoreRecordVectorField,
vectorstoremodel,
)
@vectorstoremodel
@dataclass
class Hotel:
hotel_id: Annotated[str, VectorStoreRecordKeyField()] = field(default_factory=lambda: str(uuid4()))
hotel_name: Annotated[str, VectorStoreRecordDataField(is_filterable=True)]
description: Annotated[str, VectorStoreRecordDataField(is_full_text_searchable=True)]
description_embedding: Annotated[list[float], VectorStoreRecordVectorField(dimensions=4, distance_function=DistanceFunction.COSINE, index_kind=IndexKind.HNSW)]
tags: Annotated[list[str], VectorStoreRecordDataField(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
由于数据库支持许多不同类型的键和记录,因此可以使用泛型指定集合的键和记录的类型。
在本例中,记录的类型将是 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"));
// 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.memory.qdrant import QdrantStore
# Create a Qdrant VectorStore object, this will look in the environment for Qdrant related settings, and will fall back to the default, which is to run in-memory.
vector_store = QdrantStore()
# Choose a collection from the database and specify the type of key and record stored in it via Generic parameters.
collection = vector_store.get_collection(
collection_name="skhotels",
data_model_type=Hotel
)
由于数据库支持许多不同类型的键和记录,因此可以使用泛型指定集合的键和记录的类型。
在本例中,记录的类型将是 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 连接器支持的键和字段类型的详细信息,请参阅 每个连接器的文档。
创建集合并添加记录
// Create the collection if it doesn't exist yet.
await collection.CreateCollectionIfNotExistsAsync();
// 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.
// Just showing a placeholder embedding generation method here for brevity.
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.create_collection_if_not_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();
提示
有关如何生成嵌入内容的详细信息,请参阅 嵌入生成。
执行矢量搜索
// Generate a vector for your search text, using your chosen embedding generation implementation.
// Just showing a placeholder method here for brevity.
var searchVector = await GenerateEmbeddingAsync("I'm looking for a hotel where customer happiness is the priority.");
// Do the search.
var searchResult = await collection.VectorizedSearchAsync(searchVector, new() { Top = 1 }).Results.ToListAsync()
// Inspect the returned hotels.
Hotel hotel = searchResult.First().Record;
Console.WriteLine("Found hotel description: " + hotel.Description);
// 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());
提示
有关如何生成嵌入内容的详细信息,请参阅 嵌入生成。