Vektorové vyhledávání ve zprostředkovateli SQL Server EF Core

Poznámka:

Podpora vektorů byla zavedena v EF Core 10.0 a je podporována pouze s SQL Server 2025 a vyšší.

Datový typ vektoru SQL Server umožňuje ukládat embeddings, což je znázornění významu, které se dají efektivně vyhledávat podle podobnosti, což podporuje úlohy umělé inteligence, jako je sémantické vyhledávání a načítání rozšířené generace (RAG).

Nastavení vlastností vektoru

Pokud chcete použít datový typ vector, jednoduše přidejte vlastnost .NET typu SqlVector<float> do typu entity a zadejte dimenze následujícím způsobem:

public class Blog
{
    // ...

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

Po přidání vlastnosti a vytvoření odpovídajícího sloupce v databázi můžete začít vkládat embeddingy. Generování vložení se provádí mimo databázi, obvykle prostřednictvím služby. Podrobnosti k tomu nejsou v rozsahu této dokumentace. Knihovna .NET Microsoft.Extensions.AI obsahuje , což je abstrakce nad generátory vkládání, která má implementace pro hlavní poskytovatele.

Jakmile zvolíte generátor vkládání a nastavíte ho, použijte ho k vygenerování vkládání a jejich vložení následujícím způsobem:

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

Jakmile máte zachycení uložená do databáze, jste připraveni provádět vyhledávání podobností mezi vektory.

Poznámka:

Počínaje EF Core 11 se při dotazování entit ve výchozím nastavení nenačítají vektorové vlastnosti, protože vektory jsou obvykle velké a jen zřídka je potřeba číst zpět. Před EF Core 11 byly vektorové vlastnosti vždy načteny stejně jako všechny ostatní vlastnosti.

Přesné hledání pomocí VECTOR_DISTANCE()

Funkce EF.Functions.VectorDistance() vypočítá přesnou vzdálenost mezi dvěma vektory. Použijte ho k vyhledání podobnosti pro daný uživatelský dotaz:

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

Tato funkce vypočítá vzdálenost mezi vektorem dotazu a každým řádkem v tabulce a vrátí nejbližší shody. I když to poskytuje dokonale přesné výsledky, může to být pomalé u velkých datových sad, protože SQL Server musí prohledat všechny řádky a výpočetní vzdálenosti pro každou z nich.

Poznámka:

Integrovaná podpora v EF 10 nahrazuje předchozí rozšíření EFCore.SqlServer.VectorSearch , které umožňovalo provádět vektorové vyhledávání před zavedením datového vector typu. V rámci upgradu na EF 10 odeberte rozšíření z projektů.

Výstraha

VECTOR_SEARCH() a vektorové indexy jsou v současné době experimentální funkce v SQL Server a můžou se změnit. Rozhraní API v EF Core pro tyto funkce se mohou také změnit.

SQL Server VECTOR_SEARCH() funkce s hodnotou tabulky načte řádky na základě vektorové podobnosti. Na rozdíl od VECTOR_DISTANCE() toho, co vypočítá vzdálenost mezi dvěma specifickými vektory – VECTOR_SEARCH() hledá v celé tabulce nejpodobnější vektory jako daný vektor dotazu.

Použijte metodu rozšíření VectorSearch() pro DbSet a zřetězte OrderBy(), Take() a WithApproximate(), abyste provedli vyhledávání přibližně nejbližšího souseda (ANN), které používá vektorový index:

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

To se přeloží na následující SQL:

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() vrátí VectorSearchResult<TEntity>, což vám umožní přístup jak k entitě, tak k vypočítané vzdálenosti:

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

Díky tomu můžete filtrovat skóre podobnosti, prezentovat ho uživatelům atd.

WithApproximate()

WithApproximate() dává SQL Server pokyn k použití vektorového indexu pro přibližné hledání nejbližšího souseda (ANN), což výrazně zlepšuje výkon velkých datových sad. To způsobí, že se WITH APPROXIMATE přidá do klauzule SQL TOP. WithApproximate() musí být voláno po Take(), které určuje počet vrácených výsledků.

Bez WithApproximate()dotazu provede přesné hledání k-nejbližšího souseda (kNN), které prohledá všechny řádky bez použití vektorového indexu:

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

Vektorové indexy

Chcete-li použít přibližné vyhledávání WithApproximate(), je nutné vytvořit vektorový index ve sloupci vektoru. Použijte metodu HasVectorIndex() v konfiguraci modelu:

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

Tím se vygeneruje následující migrace SQL:

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

Pro indexy vektorů se podporují následující metriky vzdálenosti:

Ukazatel Description
cosine Kosinus podobnost (úhlová vzdálenost)
euclidean Euklidovská vzdálenost (L2 norma)
dot Tečkovaný součin (negativní vnitřní výrobek)

Zvolte metriku, která nejlépe odpovídá vašemu modelu vkládání a případu použití. Kosinus podobnost se běžně používá pro vkládání textu, zatímco euklidová vzdálenost se často používá pro vkládání obrázků.

Hybridní vyhledávání kombinuje hledání vektorové podobnosti s tradičním fulltextovým vyhledáváním , aby poskytovalo relevantnější výsledky. Vektorové vyhledávání exceluje při hledání sémanticky podobného obsahu, zatímco fulltextové vyhledávání je lepší při přesné shodě klíčových slov. Kombinací obou přístupů a použití reciproční Rank Fusion (RRF) ke sloučení výsledků můžete vytvořit inteligentnější vyhledávací prostředí.

Následující příklad ukazuje, jak implementovat hybridní vyhledávání pomocí EF Core, kombinování FreeTextTable() a VectorSearch() v jednom dotazu:

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

Tento dotaz:

  1. , Provede fulltextové vyhledávání na Article
  2. Provádí vektorové vyhledávání na Article a kombinuje výsledky s výsledky fulltextového vyhledávání pomocí operace FULL JOIN.
  3. Vypočítá skóre RRF zkombinováním celého textu i sémantického pořadí.
  4. Řazení podle skóre RRF vezme požadovaný počet výsledků a vyloučí původní Article entity.

Dotaz vytvoří následující SQL:

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