Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
O suporte ao vetor foi introduzido no EF Core 10.0 e só tem suporte com SQL Server 2025 e superiores.
O tipo de dados de vetor SQL Server permite armazenar embeddings, que são representações de significado que podem ser pesquisadas com eficiência quanto à similaridade, alimentando cargas de trabalho de IA, como pesquisa semântica e RAG (Geração Aumentada por Recuperação).
Configurando propriedades de vetor
Para usar o tipo de dados vector, basta adicionar uma propriedade .NET do tipo SqlVector<float> ao tipo de entidade, especificando as dimensões da seguinte maneira:
public class Blog
{
// ...
[Column(TypeName = "vector(1536)")]
public SqlVector<float> Embedding { get; set; }
}
Depois que sua propriedade for adicionada e a coluna correspondente criada no banco de dados, você poderá começar a inserir inserções. A geração de inserção é feita fora do banco de dados, geralmente por meio de um serviço, e os detalhes para fazer isso estão fora do escopo desta documentação. No entanto, a biblioteca .NET Microsoft.Extensions.AI contém IEmbeddingGenerator, que é uma abstração sobre geradores de inserção que tem implementações para os principais provedores.
Depois de escolher o gerador de inserção e configurá-lo, use-o para gerar inserções e inseri-las da seguinte maneira:
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();
Depois de salvar as inserções no banco de dados, você estará pronto para executar a pesquisa de similaridade de vetor sobre eles.
Observação
A partir do EF Core 11, as propriedades de vetor não são carregadas por padrão ao consultar entidades, pois os vetores normalmente são grandes e raramente são necessários para serem lidos novamente. Antes do EF Core 11, as propriedades de vetor sempre eram carregadas como qualquer outra propriedade.
Pesquisa exata com VECTOR_DISTANCE()
A EF.Functions.VectorDistance() função calcula a distância exata entre dois vetores. Use-o para executar a pesquisa de similaridade para uma determinada consulta de usuário:
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();
Essa função calcula a distância entre o vetor de consulta e todas as linhas da tabela e retorna as correspondências mais próximas. Embora isso forneça resultados perfeitamente precisos, pode ser lento para grandes conjuntos de dados porque SQL Server deve verificar todas as linhas e distâncias de computação para cada um deles.
Observação
O suporte interno no EF 10 substitui a extensão EFCore.SqlServer.VectorSearch anterior, que permitia a execução da pesquisa de vetor antes da introdução do vector tipo de dados. Como parte da atualização para o EF 10, remova a extensão de seus projetos.
Pesquisando com VECTOR_SEARCH()
Aviso
VECTOR_SEARCH() e índices de vetor são recursos experimentais no momento em SQL Server e estão sujeitos a alterações. As APIs no EF Core para esses recursos também estão sujeitas a alterações.
A função VECTOR_SEARCH() com valor de tabela do SQL Server recupera linhas com base na similaridade do vetor. Ao contrário VECTOR_DISTANCE() — que calcula a distância entre dois vetores específicos — VECTOR_SEARCH() pesquisa uma tabela inteira para obter os vetores mais semelhantes a um determinado vetor de consulta.
Use o método de extensão VectorSearch() no seu DbSet e encadeie OrderBy(), Take() e WithApproximate() para executar uma pesquisa de vizinho mais próximo aproximado (ANN) que usa um índice vetorial:
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}");
}
Isso se traduz no seguinte 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() retorna VectorSearchResult<TEntity>, o que permite acessar a entidade e a distância computada:
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();
Isso permite filtrar a pontuação de similaridade, apresentá-la aos usuários etc.
WithApproximate()
WithApproximate() instrui o SQL Server a usar o índice vetorial para busca aproximada de vizinhos mais próximos (ANN), o que oferece desempenho significativamente melhor em grandes conjuntos de dados. Isso faz com que WITH APPROXIMATE seja adicionado à cláusula SQL TOP.
WithApproximate() deve ser chamado depois Take(), o que especifica o número de resultados a serem retornados.
Sem WithApproximate(), a consulta executa uma pesquisa exata k-nearest neighbor (kNN) que verifica todas as linhas, sem usar o índice de vetor:
// 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();
Índices de vetor
Para usar a busca aproximada com WithApproximate(), você deve criar um índice vetorial na sua coluna vetorial. Use o HasVectorIndex() método na configuração do modelo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasVectorIndex(b => b.Embedding, "cosine");
}
Isso gerará a seguinte migração de SQL:
CREATE VECTOR INDEX [IX_Blogs_Embedding]
ON [Blogs] ([Embedding])
WITH (METRIC = COSINE)
As seguintes métricas de distância têm suporte para índices de vetor:
| Métrica | Descrição |
|---|---|
cosine |
Similaridade cosseno (distância angular) |
euclidean |
Distância euclidiana (norma L2) |
dot |
Produto Dot (produto interno negativo) |
Escolha a métrica que melhor corresponde ao modelo de inserção e ao caso de uso. A similaridade cossina é comumente usada para inserções de texto, enquanto a distância euclidiana é frequentemente usada para inserções de imagem.
Pesquisa híbrida
A pesquisa híbrida combina a pesquisa de similaridade de vetor com a pesquisa de texto completo tradicional para fornecer resultados mais relevantes. A pesquisa de vetor se destaca em encontrar conteúdo semanticamente semelhante, enquanto a pesquisa de texto completo é melhor na correspondência exata da palavra-chave. Combinando abordagens e usando o RRF (Reciprocal Rank Fusion) para mesclar os resultados, você pode criar experiências de pesquisa mais inteligentes.
O exemplo a seguir mostra como implementar a pesquisa híbrida usando o EF Core, combinando FreeTextTable() e VectorSearch() em uma única consulta:
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();
Esta consulta:
- Executa uma pesquisa de texto completo em
Article - Executa uma busca vetorial em
Articlee combina os resultados da busca vetorial com os da busca de texto completo via FULL JOIN - Calcula a pontuação RRF combinando o texto completo e a classificação semântica
- Ordena por pontuação RRF, obtém o número desejado de resultados e extrai as entidades originais
Article.
A consulta produz o seguinte 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