Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
O suporte a vector foi introduzido no EF Core 10.0 e é suportado apenas com o SQL Server 2025 e versões superiores.
O tipo de dado vetorial SQL Server permite armazenar embeddings, que são representações de significado que podem ser pesquisadas de forma eficiente quanto à semelhança, alimentando cargas de trabalho de IA como a geração aumentada de pesquisa e recuperação semântica (RAG).
Configuração das propriedades vetoriais
Para usar o tipo de dado vector, basta adicionar uma propriedade .NET do tipo SqlVector<float> ao seu tipo de entidade, especificando as dimensões da seguinte forma:
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 incorporações. A geração de incorporaçã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 embedding que tem implementações para os principais fornecedores.
Depois de escolheres o teu gerador de embedding e o configurares, usa-o para gerar embeddings e insere-os da seguinte forma:
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 ter os embeddings guardados na sua base de dados, está pronto para realizar uma pesquisa vetorial por similaridade sobre eles.
Observação
A partir do EF Core 11, as propriedades vetoriais não são carregadas por padrão ao consultar entidades, uma vez que os vetores são tipicamente grandes e raramente necessitam de ser lidos. Antes do EF Core 11, as propriedades vetoriais eram sempre 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 realizar uma pesquisa de similaridade para uma determinada consulta de utilizador:
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();
Esta função calcula a distância entre o vetor de consulta e cada linha da tabela, depois devolve as correspondências mais próximas. Embora isto forneça resultados perfeitamente precisos, pode ser lento para conjuntos de dados grandes porque o SQL Server tem de analisar todas as linhas e calcular distâncias para cada uma.
Observação
O suporte interno no EF 10 substitui a extensão EFCore.SqlServer.VectorSearch anterior, que permitia executar a pesquisa vetorial antes que o vector tipo de dados fosse introduzido. Como parte da atualização para o EF 10, remova a extensão dos seus projetos.
Pesquisa com VECTOR_SEARCH()
Advertência
Os índices VECTOR_SEARCH() e vetoriais são atualmente características experimentais em SQL Server e estão sujeitos a alterações. As APIs do EF Core para estas funcionalidades também estão sujeitas a alterações.
A função com valor de tabela VECTOR_SEARCH() do SQL Server obtém linhas com base na similaridade vetorial. Diferente VECTOR_DISTANCE() — que calcula a distância entre dois vetores específicos — VECTOR_SEARCH() procura numa tabela inteira os vetores mais semelhantes a um dado vetor de consulta.
Utilize o método de extensão VectorSearch() no seu DbSet e encadeie OrderBy(), Take() e WithApproximate() para realizar uma pesquisa aproximada do vizinho mais próximo (ANN) que utiliza 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() devolve VectorSearchResult<TEntity>, que permite aceder tanto à entidade como à distância calculada:
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();
Isto permite-te filtrar pela pontuação de similaridade, apresentá-la aos utilizadores, etc.
WithApproximate()
WithApproximate() instrui SQL Server a usar o índice vetorial para pesquisa aproximada de vizinho mais próximo (ANN), o que proporciona um desempenho significativamente melhor para conjuntos de dados grandes. Faz com que WITH APPROXIMATE seja adicionada à cláusula SQL TOP.
WithApproximate() deve ser chamado após Take(), que especifica o número de resultados a devolver.
Sem WithApproximate(), a consulta realiza uma pesquisa exata dos k vizinhos mais próximos (kNN) que varre todas as linhas, sem usar o índice vetorial:
// 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 vetoriais
Para usar pesquisa aproximada com WithApproximate(), deve criar um índice vetorial na sua coluna vetorial. Use o HasVectorIndex() método na configuração do seu modelo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasVectorIndex(b => b.Embedding, "cosine");
}
Isto irá gerar a seguinte migração SQL:
CREATE VECTOR INDEX [IX_Blogs_Embedding]
ON [Blogs] ([Embedding])
WITH (METRIC = COSINE)
As seguintes métricas de distância são suportadas para índices vetoriais:
| Métrico | Descrição |
|---|---|
cosine |
Similaridade de cosseno (distância angular) |
euclidean |
Distância euclidiana (norma L2) |
dot |
Produto escalar (produto interno negativo) |
Escolha a métrica que melhor se adequa ao seu modelo de embedding e ao seu caso de uso. A similaridade do cosseno é frequentemente utilizada para embeddings de texto, enquanto a distância euclidiana é geralmente usada para embeddings de imagem.
Pesquisa híbrida
A pesquisa híbrida combina a pesquisa por similaridade vetorial com a pesquisa tradicional em texto completo para fornecer resultados mais relevantes. A pesquisa vetorial destaca-se por encontrar conteúdos semanticamente semelhantes, enquanto a pesquisa em texto completo é melhor na correspondência exata de palavras-chave. Ao combinar ambas as abordagens e usar a Fusão de Classificação Recíproca (RRF) para fundir os resultados, pode construir experiências de pesquisa mais inteligentes.
O exemplo seguinte mostra como implementar pesquisa híbrida usando EF Core, combinando FreeTextTable() e VectorSearch() numa ú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:
- Realiza uma pesquisa em texto completo em
Article - Realiza uma pesquisa vetorial em
Articlee combina os resultados com os resultados da pesquisa em texto completo através de um FULL JOIN - Calcula a pontuação RRF combinando tanto o texto completo como a classificação semântica
- Ordena por pontuação RRF, seleciona 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