📦 Microsoft.Extensions.VectorData.Abstractions (MEVD) パッケージは、.NET のベクター ストアを操作するための統合 API を提供します。 同じコードを使用して、異なるベクター データベース プロバイダー間で埋め込みを格納および検索できます。
この記事では、ライブラリの主な機能を使用する方法について説明します。
前提条件
- .NET 9 SDK 以降
- 埋め込みとベクター データベースの理解
パッケージのインストール
ベクター データベースのプロバイダー パッケージをインストールします。
Microsoft.Extensions.VectorData.Abstractions パッケージは推移的な依存関係として自動的に取り込まれます。 次の例では、開発とテストにメモリ内プロバイダーを使用します。
dotnet package add Microsoft.SemanticKernel.Connectors.InMemory --prerelease
運用環境のシナリオでは、 Microsoft.SemanticKernel.Connectors.InMemory をデータベースのプロバイダーに置き換えます。 使用可能なプロバイダーについては、「すぐに使用できる ベクター ストア プロバイダー」を参照してください。 (プロバイダー パッケージ名に "SemanticKernel" が含まれているにもかかわらず、これらのプロバイダーはセマンティック カーネルとは関係なく、エージェント フレームワークを含む .NET のどこでも使用できます)。
データ モデルを定義する
ベクター ストアに格納するレコードを表す .NET クラスを定義します。 属性を使用して、主キー、一般データ、ベクター データのいずれを表すかに応じて、クラス内のプロパティに注釈を付けます。 簡単な例を次に示します。
public class Hotel
{
[VectorStoreKey]
public ulong HotelId { get; set; }
[VectorStoreData(IsIndexed = true)]
public required string HotelName { get; set; }
[VectorStoreData(IsFullTextIndexed = true)]
public required string Description { get; set; }
[VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineSimilarity, IndexKind = IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
[VectorStoreData(IsIndexed = true)]
public required string[] Tags { get; set; }
}
属性を使用する代わりに、 VectorStoreCollectionDefinitionを使用してプログラムでスキーマを定義できます。 この方法は、異なる構成で同じデータ モデルを使用する場合や、データ モデル クラスに属性を追加できない場合に便利です。
詳細については、「 データ モデルの定義」を参照してください。
ベクター ストアを作成する
選択したデータベースの VectorStore 実装のインスタンスを作成します。 次の例では、インメモリ ベクター ストアを作成します。
// Create an in-memory vector store (no external service required).
// For production, replace this with a connector for your preferred database.
var vectorStore = new InMemoryVectorStore();
コレクションを取得する
GetCollectionのVectorStoreを呼び出して、型指定されたVectorStoreCollection<TKey,TRecord>参照を取得します。 次に、 EnsureCollectionExistsAsync を呼び出して、コレクションがまだ存在しない場合は作成します。
// Get a reference to a collection named "hotels".
VectorStoreCollection<int, Hotel> collection =
vectorStore.GetCollection<int, Hotel>("hotels");
// Ensure the collection exists in the database.
await collection.EnsureCollectionExistsAsync();
コレクション名は、データベースの基になるストレージの概念 (SQL Server のテーブル、Azure AI Search のインデックス、Cosmos DB のコンテナーなど) にマップされます。
レコードの挿入・更新
UpsertAsyncを使用して、コレクション内のレコードを挿入または更新します。 同じキーを持つレコードが既に存在する場合は、更新されます。
// Upsert records into the collection.
// In a real app, generate embeddings using an IEmbeddingGenerator.
// The CreateFakeEmbedding helper at the bottom of this file generates
// placeholder vectors for demonstration purposes only.
var hotels = new List<Hotel>
{
new()
{
HotelId = 1,
HotelName = "Seaside Retreat",
Description = "A peaceful hotel on the coast with stunning ocean views.",
DescriptionEmbedding = CreateFakeEmbedding(1),
Tags = ["beach", "ocean", "relaxation"]
},
new()
{
HotelId = 2,
HotelName = "Mountain Lodge",
Description = "A cozy lodge in the mountains with hiking trails nearby.",
DescriptionEmbedding = CreateFakeEmbedding(2),
Tags = ["mountain", "hiking", "nature"]
},
new()
{
HotelId = 3,
HotelName = "City Centre Hotel",
Description = "A modern hotel in the heart of the city, close to attractions.",
DescriptionEmbedding = CreateFakeEmbedding(3),
Tags = ["city", "business", "urban"]
}
};
foreach (Hotel h in hotels)
{
await collection.UpsertAsync(h);
}
Important
実際のアプリでは、レコードを格納する前に MEVD で埋め込みを生成することをお 勧めします。
レコードを取得する
GetAsyncを使用して、キーによって 1 つのレコードを取得します。 複数のレコードを取得するには、 IEnumerable<TKey> を GetAsyncに渡します。
// Get a specific record by its key.
Hotel? hotel = await collection.GetAsync(1);
if (hotel is not null)
{
Console.WriteLine($"Hotel: {hotel.HotelName}");
Console.WriteLine($"Description: {hotel.Description}");
}
複数のレコードを一度に取得するには:
// Get multiple records by their keys.
IAsyncEnumerable<Hotel> hotelBatch = collection.GetAsync([1, 2, 3]);
await foreach (Hotel h in hotelBatch)
{
Console.WriteLine($"Batch hotel: {h.HotelName}");
}
ベクター検索を実行する
SearchAsyncを使用して、意味的にクエリに似たレコードを検索します。 クエリの埋め込みベクターと返される結果の数を渡します。
// Search for the top 2 hotels most similar to the query embedding.
IAsyncEnumerable<VectorSearchResult<Hotel>> searchResults =
collection.SearchAsync(queryEmbedding, top: 2);
await foreach (VectorSearchResult<Hotel> result in searchResults)
{
Console.WriteLine($"Found: {result.Record.HotelName} (score: {result.Score:F4})");
}
各 VectorSearchResult<TRecord> には、一致するレコードと類似性スコアが含まれます。 スコアが高いほど、セマンティック一致が近いことを示します。
検索結果をフィルター処理する
VectorSearchOptions<TRecord>を使用して、ベクター比較の前に検索結果をフィルター処理します。
IsIndexed = trueでマークされた任意のプロパティでフィルター処理できます。
// Filter results before the vector comparison.
// Only properties marked with IsIndexed = true can be used in filters.
var searchOptions = new VectorSearchOptions<Hotel>
{
Filter = h => h.HotelName == "Seaside Retreat"
};
IAsyncEnumerable<VectorSearchResult<Hotel>> filteredResults =
collection.SearchAsync(queryEmbedding, top: 2, searchOptions);
await foreach (VectorSearchResult<Hotel> result in filteredResults)
{
Console.WriteLine($"Filtered: {result.Record.HotelName} (score: {result.Score:F4})");
}
フィルターは LINQ 式として表されます。 サポートされる操作はプロバイダーによって異なりますが、すべてのプロバイダーは、等値、不等値、論理 && や ||などの一般的な比較をサポートします。
VectorSearchOptions を使用して検索動作を制御する
VectorSearchOptions<TRecord>を使用して、ベクター検索動作のさまざまな側面を制御します。
// Use VectorSearchOptions to control paging and vector inclusion.
var pagedOptions = new VectorSearchOptions<Hotel>
{
Skip = 1, // Skip the first result (useful for paging).
IncludeVectors = false // Don't include vector data in results (default).
};
IAsyncEnumerable<VectorSearchResult<Hotel>> pagedResults =
collection.SearchAsync(queryEmbedding, top: 2, pagedOptions);
await foreach (VectorSearchResult<Hotel> result in pagedResults)
{
Console.WriteLine($"Paged: {result.Record.HotelName}");
}
次の表では、使用可能なオプションについて説明します。
| オプション | 説明 |
|---|---|
Filter |
ベクター比較の前にレコードをフィルター処理する LINQ 式。 |
VectorProperty |
検索対象のベクター プロパティ。 データ モデルに複数のベクター プロパティがある場合に必要です。 |
Skip |
結果が返される前にスキップする数。 ページングに便利です。 既定値は 0 です。 |
IncludeVectors |
返されたレコードにベクター データを含めるかどうか。 ベクターを省略すると、データ転送が減ります。 既定値は false です。 |
詳細については、「 ベクター検索オプション」を参照してください。
組み込みの埋め込み生成を使用する
各アップサートの前に埋め込みを手動で生成する代わりに、ベクター ストアまたはコレクションで IEmbeddingGenerator を構成できます。 その場合は、ベクター プロパティを string 型 (ソース テキスト) として宣言すると、ストアによって埋め込みが自動的に生成されます。
詳細については、「 ベクター ストアで埋め込みを生成できるようにする」を参照してください。
ハイブリッド検索
一部のベクター ストアでは、ベクトルの類似性とキーワードマッチングを組み合わせた ハイブリッド検索がサポートされています。 この方法では、ベクターのみの検索と比較して結果の関連性を向上させることができます。
ハイブリッド検索を使用するには、コレクションが IKeywordHybridSearchable<TRecord>を実装しているかどうかを確認します。 この機能をサポートするデータベースのプロバイダーのみが、このインターフェイスを実装します。
詳細については、「 ベクター ストア プロバイダーを使用したハイブリッド検索」を参照してください。
レコードを削除する
キーで 1 つのレコードを削除するには、 DeleteAsyncを使用します。
// Delete a record by its key.
await collection.DeleteAsync(3);
コレクションを削除する
ベクター ストアからコレクション全体を削除するには、 EnsureCollectionDeletedAsyncを使用します。
// Delete the entire collection from the vector store.
await collection.EnsureCollectionDeletedAsync();
ベクター ストア プロバイダーの切り替え
すべてのプロバイダーは同じ VectorStore 抽象クラスを実装するため、起動時に具象型を変更することで、それらを切り替えることができます。 ほとんどの場合、コレクションと検索コードは変わりません。 ただし、一部の調整は通常必要です。たとえば、データベースによってサポートされるデータ型が異なるためです。
関連するコンテンツ
.NET