Megosztás a következőn keresztül:


Vektorkeresés az SQL Server EF Core-szolgáltatóban

Megjegyzés:

A vektortámogatás az EF Core 10.0-ban lett bevezetve, és csak az SQL Server 2025 és újabb verziókban támogatott.

Az SQL Server vektoros adattípusa lehetővé teszi a beágyazások tárolását, amelyek olyan jelentésmegjelenítések, amelyek hatékonyan kereshetők a hasonlóság érdekében, és olyan AI-számítási feladatokat aktiválhatnak, mint a szemantikai keresés és a lekéréses kiterjesztett generáció (RAG).

Vektortulajdonságok beállítása

Az vector adattípus használatához egyszerűen adjon hozzá egy .NET típusú SqlVector<float> tulajdonságot az entitástípushoz, és adja meg a dimenziókat az alábbiak szerint:

public class Blog
{
    // ...

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

Miután hozzáadta a tulajdonságot, és létrehozta a megfelelő oszlopot az adatbázisban, megkezdheti a beágyazások beszúrását. A beágyazási generálás az adatbázison kívül, általában szolgáltatáson keresztül történik, és ennek részletei nem tartoznak a dokumentáció hatókörébe. A .NET Microsoft.Extensions.AI-kódtár azonban tartalmaz IEmbeddingGeneratoregy absztrakciót a fő szolgáltatók számára implementált generátorok beágyazása felett.

Miután kiválasztotta a beágyazási generátort, és beállította, használja beágyazások létrehozására, és szúrja be őket az alábbiak szerint:

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

Miután mentette a beágyazásokat az adatbázisba, készen áll arra, hogy vektoros hasonlósági keresést végezzen rajtuk.

Pontos keresés a VECTOR_DISTANCE() segítségével

A EF.Functions.VectorDistance() függvény kiszámítja a két vektor közötti pontos távolságot. Használatával hasonlósági keresést végezhet egy adott felhasználói lekérdezésben:

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

Ez a függvény kiszámítja a lekérdezésvektor és a tábla minden sora közötti távolságot, majd visszaadja a legközelebbi egyezéseket. Bár ez tökéletesen pontos eredményeket ad, a nagy adathalmazok esetében lassú lehet, mivel az SQL Servernek minden sor és számítási távolság vizsgálatát kell elvégeznie mindegyiknél.

Megjegyzés:

Az EF 10 beépített támogatása lecseréli az előző EFCore.SqlServer.VectorSearch bővítményt, amely lehetővé tette a vektorkeresést az vector adattípus bevezetése előtt. Az EF 10-re való frissítés részeként távolítsa el a bővítményt a projektekből.

Figyelmeztetés

VECTOR_SEARCH() a vektorindexek jelenleg az SQL Server kísérleti funkciói, és változhatnak. Az EF Core api-jait ezen funkciók esetében is módosítani kell.

A nagy adathalmazok esetében a pontos távolságok minden sorra vonatkozó számítása tiltottan lassú lehet. Az SQL Server 2025 támogatja a vektorindexen keresztüli hozzávetőleges keresést, amely sokkal jobb teljesítményt nyújt a lekérdezéshez nagyjából hasonló - nem pedig pontosan hasonló - elemek visszaadása rovására.

Vektorindexek

A használatához VECTOR_SEARCH()létre kell hoznia egy vektorindexet a vektoroszlopon. Használja a HasVectorIndex() módszert a modellkonfigurációban:

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

Ez a következő SQL-migrálást hozza létre:

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

A vektorindexek esetében a következő távolságmetrikák támogatottak:

Metric Leírás
cosine Koszinusz-hasonlóság (szögtávolság)
euclidean Euklideszi távolság (L2 norma)
dot Pont termék (negatív belső termék)

Válassza ki a beágyazási modellnek és a használati esetnek leginkább megfelelő metrikát. A koszin hasonlóságot gyakran használják szövegbeágyazásokhoz, míg az euklideszi távolságot gyakran használják képbeágyazásokhoz.

Ha már rendelkezik vektorindexszel, használja a VectorSearch() bővítménymetódust a következőn 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}");
}

Ez a következő SQL-hez lesz lefordítva:

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

A topN paraméter a visszaadandó eredmények maximális számát adja meg.

VectorSearch() visszaadja VectorSearchResult<TEntity>, amely lehetővé teszi az entitás és a számított távolság elérését:

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

Ez lehetővé teszi a hasonlósági pontszám szűrését, a felhasználók számára való bemutatását stb.

A hibrid keresés kombinálja a vektoros hasonlóság keresését a hagyományos teljes szöveges kereséssel , hogy relevánsabb eredményeket jelenítsen meg. A vektoros keresés kiválóan alkalmas a szemantikailag hasonló tartalmak kereséséhez, míg a teljes szöveges keresés jobb a kulcsszavak pontos egyeztetésében. Ha mindkét megközelítést kombinálja, és a Kölcsönös rangsor fúzió (RRF) használatával egyesíti az eredményeket, intelligensebb keresési élményeket hozhat létre.

Az alábbi példa bemutatja, hogyan valósíthat meg hibrid keresést az EF Core használatával, kombinálva FreeTextTable() és VectorSearch() egyetlen lekérdezésben:

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

Ez a lekérdezés:

  1. Teljes szöveges keresést hajt végre a következőn: Article
  2. Vektorkeresést hajt végre, Article és az eredményeket a teljes szöveges keresési eredményekhez egyesíti egy LEFT JOIN használatával
  3. Kiszámítja az RRF-pontszámot a teljes szöveg és a szemantikai rangsor kombinálásával
  4. A rendelések RRF-pontszám szerinti rendezése történik, majd a kívánt számú találatot kiválasztja, és megjeleníti az eredeti Article entitásokat.

Megjegyzés:

A BAL OLDALI ILLESZTÉS használata helyett a TELJES KÜLSŐ ILLESZTÉS alkalmasabb lenne erre a forgatókönyvre; ez lehetővé tenné, hogy mindkét keresési oldal magas rangú eredményei bekerüljenek a végeredménybe, még akkor is, ha az eredmény egyáltalán nem jelenik meg a másik oldalon. A fenti LEFT JOIN megközelítéssel, ha egy eredmény nagyon magas vektor-hasonlósági pontszámmal rendelkezik, soha nem kerül bele a végeredménybe, ha az eredmény nem tartalmaz magas teljes szöveges pontszámot is. Az EF azonban jelenleg nem támogatja a TELJES KÜLSŐ ÖSSZEKAPCSOLÁST; szavazzon #37633 mellett, ha azt támogatni szeretné.

A lekérdezés a következő SQL-t hozza létre:

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