Pencarian vektor di Penyedia Inti SQL Server EF

Nota

Dukungan vektor diperkenalkan di EF Core 10.0, dan hanya didukung dengan SQL Server 2025 ke atas.

Jenis data vektor SQL Server memungkinkan penyimpanan embeddings, yaitu representasi dari makna yang dapat dicari secara efisien untuk kesamaan, mendukung beban kerja AI seperti pencarian semantik dan pembuatan-pengambilan yang ditingkatkan (RAG).

Menyiapkan properti vektor

Untuk menggunakan jenis data vector, cukup tambahkan properti .NET jenis SqlVector<float> ke jenis entitas Anda, menentukan dimensi sebagai berikut:

public class Blog
{
    // ...

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

Setelah properti Anda ditambahkan dan kolom yang sesuai dibuat di database, Anda bisa mulai menyisipkan embedding. Pembuatan embedding dilakukan di luar database, biasanya melalui layanan, dan rincian tentang cara melakukannya tidak termasuk dalam cakupan dokumentasi ini. Namun, pustaka .NET Microsoft.Extensions.AI berisi IEmbeddingGenerator, yang merupakan abstraksi atas generator penyematan yang memiliki implementasi untuk penyedia utama.

Setelah Anda memilih generator embedding dan menyiapkannya, gunakan untuk menghasilkan embedding dan menyisipkannya sebagai berikut:

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

Setelah penyematan disimpan ke database, Anda siap untuk melakukan pencarian kesamaan vektor di atasnya.

Nota

Dimulai dengan EF Core 11, properti vektor tidak dimuat secara default saat mengkueri entitas, karena vektor biasanya besar dan jarang perlu dibaca kembali. Sebelum EF Core 11, properti vektor selalu dimuat seperti properti lainnya.

Pencarian yang tepat dengan VECTOR_DISTANCE()

Fungsi ini EF.Functions.VectorDistance() menghitung jarak yang tepat antara dua vektor. Gunakan untuk melakukan pencarian kesamaan untuk kueri pengguna tertentu:

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

Fungsi ini menghitung jarak antara vektor kueri dan setiap baris dalam tabel, lalu mengembalikan kecocokan terdekat. Meskipun ini memberikan hasil yang sangat akurat, itu bisa lambat untuk himpunan data besar karena SQL Server harus memindai semua baris dan jarak komputasi untuk masing-masing baris.

Nota

Dukungan bawaan di EF 10 menggantikan ekstensi EFCore.SqlServer.VectorSearch sebelumnya, yang memungkinkan melakukan pencarian vektor sebelum vector jenis data diperkenalkan. Sebagai bagian dari peningkatan ke EF 10, hapus ekstensi dari proyek Anda.

Peringatan

VECTOR_SEARCH() dan indeks vektor saat ini adalah fitur eksperimental dalam SQL Server dan dapat berubah. API di EF Core untuk fitur-fitur ini juga dapat berubah.

Fungsi bernilai tabel VECTOR_SEARCH() di SQL Server mengembalikan baris berdasarkan kemiripan vektor. Tidak seperti VECTOR_DISTANCE() — yang menghitung jarak antara dua vektor tertentu — VECTOR_SEARCH() mencari seluruh tabel untuk vektor yang paling mirip dengan vektor kueri tertentu.

Gunakan metode ekstensi VectorSearch() pada DbSet, lalu rangkai OrderBy(), Take(), dan WithApproximate() untuk melakukan pencarian tetangga terdekat aproksimatif (ANN) yang menggunakan indeks vektor:

var results = await context.Blogs
    .VectorSearch(b => b.Embedding, embedding, "cosine")
    .OrderBy(r => r.Distance)
    .Take(5)
    .WithApproximate()
    .ToListAsync();

foreach (var result in results)
{
    Console.WriteLine($"Blog {result.Value.Id} with distance {result.Distance}");
}

Ini diterjemahkan ke SQL berikut:

SELECT TOP(@__p_1) WITH APPROXIMATE [b].[Id], [b].[Name], [v].[Distance]
FROM VECTOR_SEARCH(
    TABLE = [Blogs] AS [b],
    COLUMN = [Embedding],
    SIMILAR_TO = @__embedding_0,
    METRIC = 'cosine'
) AS [v]
ORDER BY [v].[Distance]

VectorSearch() mengembalikan VectorSearchResult<TEntity>, yang memungkinkan Anda mengakses entitas dan jarak yang dihitung.

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

Ini memungkinkan Anda untuk memfilter skor kesamaan, menyajikannya kepada pengguna, dll.

WithApproximate()

WithApproximate() menginstruksikan SQL Server untuk menggunakan indeks vektor untuk pencarian tetangga terdekat (ANN), yang memberikan performa yang jauh lebih baik untuk himpunan data besar. Ini menyebabkan WITH APPROXIMATE ditambahkan ke klausa SQL TOP. WithApproximate() harus dipanggil setelah Take(), yang menentukan jumlah hasil yang akan dikembalikan.

Tanpa WithApproximate(), kueri melakukan pencarian k-nearest neighbor (kNN) secara eksak yang memindai semua baris, tanpa menggunakan indeks vektor:

// Exact kNN search (no vector index used)
var blogs = await context.Blogs
    .VectorSearch(b => b.Embedding, embedding, "cosine")
    .OrderBy(r => r.Distance)
    .Take(5)
    .ToListAsync();

Indeks vektor

Untuk menggunakan perkiraan pencarian dengan WithApproximate(), Anda harus membuat indeks vektor pada kolom vektor Anda. HasVectorIndex() Gunakan metode dalam konfigurasi model Anda:

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

Ini akan menghasilkan migrasi SQL berikut:

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

Metrik jarak berikut didukung untuk indeks vektor:

Ukuran Deskripsi
cosine Kesamaan kosinus (jarak sudut)
euclidean Jarak euclidean (norma L2)
dot Produk titik (produk dalam negatif)

Pilih metrik yang paling cocok dengan model penyematan dan kasus penggunaan Anda. Kesamaan kosinus umumnya digunakan untuk penyematan teks, sementara jarak euclidean sering digunakan untuk penyematan gambar.

Pencarian hibrid menggabungkan pencarian kesamaan vektor dengan pencarian teks lengkap tradisional untuk memberikan hasil yang lebih relevan. Pencarian vektor unggul dalam menemukan konten semantik yang mirip, sementara pencarian teks lengkap lebih baik pada pencocokan kata kunci yang tepat. Dengan menggabungkan kedua pendekatan dan menggunakan Reciprocal Rank Fusion (RRF) untuk menggabungkan hasilnya, Anda dapat membangun pengalaman pencarian yang lebih cerdas.

Contoh berikut menunjukkan cara menerapkan pencarian hibrid menggunakan EF Core, menggabungkan FreeTextTable() , dan VectorSearch() dalam satu kueri:

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

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

Kueri ini:

  1. Melakukan pencarian teks lengkap pada Article
  2. Melakukan pencarian vektor pada Article dan menggabungkan hasilnya dengan hasil pencarian teks penuh melalui FULL JOIN
  3. Menghitung skor RRF dengan menggabungkan teks lengkap dan peringkat semantik
  4. Mengurutkan berdasarkan skor RRF, mengambil hasil sejumlah yang diinginkan dan mengeluarkan entitas asli Article.

Kueri menghasilkan SQL berikut:

SELECT TOP(@__p_4) COALESCE([a].[Id], [t].[Id]) AS [Id], COALESCE([a].[Content], [t].[Content]) AS [Content], COALESCE([a].[Title], [t].[Title]) AS [Title]
FROM FREETEXTTABLE([Articles], *, @__textualQuery_0, @__k_1) AS [f]
INNER JOIN [Articles] AS [a] ON [f].[KEY] = [a].[Id]
FULL JOIN (
    SELECT TOP(@__k_1) WITH APPROXIMATE [a].[Id], [a].[Content], [a].[Title], [v].[Distance]
    FROM VECTOR_SEARCH(
        TABLE = [Articles] AS [a],
        COLUMN = [Embedding],
        SIMILAR_TO = @__queryEmbedding_2,
        METRIC = 'cosine'
    ) AS [v]
    ORDER BY [v].[Distance]
) AS [t] ON [a].[Id] = [t].[Id]
ORDER BY ISNULL(1.0E0 / CAST(@__k_1 + [f].[RANK] AS float), 0.0E0) + ISNULL(1.0E0 / (CAST(@__k_1 AS float) + [t].[Distance]), 0.0E0) DESC