次の方法で共有


SQL Server EF Core プロバイダーでのベクター検索

ベクターのサポートは EF Core 10.0 で導入され、SQL Server 2025 以降でのみサポートされています。

SQL Server ベクター データ型を使用すると、 埋め込みを格納できます。これは、類似性を効率的に検索できる意味を表し、セマンティック検索や取得拡張生成 (RAG) などの AI ワークロードを強化します。

ベクター プロパティの設定

vectorデータ型を使用するには、エンティティ型にSqlVector<float>型の .NET プロパティを追加し、次のようにディメンションを指定します。

public class Blog
{
    // ...

    [Column(TypeName = "vector(1536)")]
    public SqlVector<float> Embedding { get; set; }
}

プロパティが追加され、対応する列がデータベースに作成されたら、埋め込みの挿入を開始できます。 埋め込み生成は、通常はサービスを介してデータベースの外部で行われます。このドキュメントでは、これを行うための詳細は範囲外です。 ただし、 .NET Microsoft.Extensions.AI ライブラリ には IEmbeddingGeneratorが含まれています。これは、主要なプロバイダーの実装を持つジェネレーターを埋め込む抽象化です。

埋め込みジェネレーターを選択して設定したら、それを使用して埋め込みを生成し、次のように挿入します。

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = /* Set up your preferred embedding generator */;

var embedding = await embeddingGenerator.GenerateVectorAsync("Some text to be vectorized");
context.Blogs.Add(new Blog
{
    Name = "Some blog",
    Embedding = new SqlVector<float>(embedding)
});
await context.SaveChangesAsync();

データベースに埋め込みを保存したら、ベクター類似度検索を実行する準備が整います。

VECTOR_DISTANCE() を使用した正確な検索

EF.Functions.VectorDistance()関数は、2 つのベクトル間の正確な距離を計算します。 これを使用して、特定のユーザー クエリの類似性検索を実行します。

var sqlVector = new SqlVector<float>(await embeddingGenerator.GenerateVectorAsync("Some user query to be vectorized"));
var topSimilarBlogs = await context.Blogs
    .OrderBy(b => EF.Functions.VectorDistance("cosine", b.Embedding, sqlVector))
    .Take(3)
    .ToListAsync();

この関数は、クエリ ベクターとテーブル内のすべての行の間の距離を計算し、最も近い一致を返します。 これにより完全に正確な結果が得られますが、SQL Server ではすべての行をスキャンし、それぞれの行の距離を計算する必要があるため、大規模なデータセットでは低速になる可能性があります。

EF 10 の組み込みサポートでは、以前の EFCore.SqlServer.VectorSearch 拡張機能が置き換えられました。この拡張機能では、 vector データ型が導入される前にベクター検索を実行できます。 EF 10 へのアップグレードの一環として、プロジェクトから拡張機能を削除します。

Warnung

VECTOR_SEARCH() およびベクター インデックスは現在、SQL Server の試験的な機能であり、変更される可能性があります。 これらの機能の EF Core の API も変更される可能性があります。

大規模なデータセットの場合、すべての行の正確な距離の計算が非常に遅くなる可能性があります。 SQL Server 2025 では、ベクター インデックスを使用したおおよその検索のサポートが導入されています。これにより、クエリにほぼ似ているのではなく、ほぼ似た項目を返す代わりにパフォーマンスが大幅に向上します。

ベクトル インデックス

VECTOR_SEARCH()を使用するには、ベクター列にベクター インデックスを作成する必要があります。 モデル構成で HasVectorIndex() メソッドを使用します。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasVectorIndex(b => b.Embedding, "cosine");
}

これにより、次の SQL 移行が生成されます。

CREATE VECTOR INDEX [IX_Blogs_Embedding]
    ON [Blogs] ([Embedding])
    WITH (METRIC = COSINE)

ベクター インデックスでは、次の距離メトリックがサポートされています。

メトリクス 説明
cosine コサインの類似性 (角度の距離)
euclidean ユークリッド距離 (L2 ノルム)
dot ドット積 (負の内積)

埋め込みモデルとユース ケースに最適なメトリックを選択します。 コサインの類似性はテキスト埋め込みでよく使用されますが、ユークリッド距離は画像埋め込みでよく使用されます。

ベクター インデックスを作成したら、VectorSearch()DbSet 拡張メソッドを使用します。

var blogs = await context.Blogs
    .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5)
    .ToListAsync();

foreach (var (blog, score) in blogs)
{
    Console.WriteLine($"Blog {blog.Id} with score {score}");
}

これは次の SQL に変換されます。

SELECT [v].[Id], [v].[Embedding], [v].[Name]
FROM VECTOR_SEARCH([Blogs], 'Embedding', @__embedding, 'metric = cosine', @__topN)

topN パラメーターは、返される結果の最大数を指定します。

VectorSearch()VectorSearchResult<TEntity>を返します。これにより、エンティティと計算された距離の両方にアクセスできます。

var searchResults = await context.Blogs
    .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5)
    .Where(r => r.Distance < 0.05)
    .Select(r => new { Blog = r.Value, Distance = r.Distance })
    .ToListAsync();

これにより、類似性スコアをフィルター処理し、ユーザーに提示することができます。

ハイブリッド検索 では、ベクター類似性検索と従来 のフルテキスト検索 を組み合わせて、より関連性の高い結果を提供します。 ベクター検索は意味的に似たコンテンツを見つけるのに優れていますが、フルテキスト検索は正確なキーワードマッチングの方が優れています。 両方のアプローチを組み合わせ、逆ランク 融合 (RRF) を使用して結果をマージすることで、よりインテリジェントな検索エクスペリエンスを構築できます。

次の例では、EF Core を使用してハイブリッド検索を実装し、 FreeTextTable()VectorSearch() を 1 つのクエリで組み合わせる方法を示します。

var k = 20;
string textualQuery = ...;
SqlVector<float> queryEmbedding = ...;

var results = await context.Articles
    // Perform full-text search
    .FreeTextTable<Article, int>(textualQuery, topN: k)
    // Perform vector (semantic) search, joining the results of both searches together
    .LeftJoin(
        context.Articles.VectorSearch(b => b.Embedding, queryEmbedding, "cosine", topN: k),
        fts => fts.Key,
        vs => vs.Value.Id,
        (fts, vs) => new
        {
            Article = vs.Value,
            FullTextRank = fts.Rank,
            VectorDistance = (double?)vs.Distance
        })
    // Apply Reciprocal Rank Fusion (RRF) to combine the results
    .Select(x => new
    {
        x.Article,
        RrfScore = (1.0 / (k + x.FullTextRank)) + (1.0 / (k + x.VectorDistance) ?? 0.0)
    })
    .OrderByDescending(x => x.RrfScore)
    .Take(10)
    .Select(x => x.Article)
    .ToListAsync();

このクエリ:

  1. フルテキスト検索を実行します。 Article
  2. Articleに対してベクター検索を実行し、LEFT JOIN を使用して結果をフルテキスト検索結果に結合します。
  3. フルテキストとセマンティック ランク付けの両方を組み合わせて RRF スコアを計算します
  4. RRF スコアによる注文は、目的の数の結果を取得し、元の Article エンティティを投影します。

LEFT JOIN を使用する代わりに、FULL OUTER JOIN がこのシナリオに適しています。これにより、結果が一方の側にまったく表示されない場合でも、どちらの検索側からの高位の結果も最終的な結果に含められます。 上記の LEFT JOIN アプローチでは、結果にベクターの類似性スコアが非常に高い場合、その結果にフルテキスト スコアも高くない場合、最終的な結果には含まれません。 ただし、EF では現在、FULL OUTER JOIN はサポートされていません。この機能のサポートを希望する場合は、#37633 に投票してください。

このクエリでは、次の SQL が生成されます。

SELECT TOP(@p3) [a0].[Id], [a0].[Content], [a0].[Embedding], [a0].[Title]
FROM FREETEXTTABLE([Articles], *, @p, @p1) AS [f]
LEFT JOIN VECTOR_SEARCH(
    TABLE = [Articles] AS [a0],
    COLUMN = [Embedding],
    SIMILAR_TO = @p2,
    METRIC = 'cosine',
    TOP_N = @p3
) AS [v] ON [f].[KEY] = [a0].[Id]
ORDER BY 1.0E0 / CAST(10 + [f].[RANK] AS float) + ISNULL(1.0E0 / (10.0E0 + [v].[Distance]), 0.0E0) DESC