04-03-03 — Hybrid search: vector + BM25
TL;DR
Busca vetorial é ótima para semântica mas falha em termos exatos (siglas, nomes próprios, números de processo). BM25 (keyword search) é o oposto: perfeito para termos exatos, mas cego ao significado. Hybrid search combina os dois via Reciprocal Rank Fusion (RRF). Em benchmarks, hybrid supera qualquer uma das abordagens isoladas em quase todos os domínios. É a configuração recomendada para produção.
Por que keyword search ainda importa
Busca semântica via embeddings tem um ponto cego: termos exatos e raros.
Exemplos onde busca vetorial falha e keyword search acerta:
- "processo nº 0001234-56.2023.8.19.0001" → o embedding não distingue números de processo
- "LGPD art. 46" → acrônimos e referências legais específicas
- "CVE-2024-1234" → identificadores de vulnerabilidades
- "NF-e chave 43230214200166000139550010001234561234567890" → números de nota fiscal
- Nomes próprios incomuns
Para esses casos, BM25 é preciso e rápido. O problema é que BM25 falha quando o usuário pergunta "quais são as penalidades por quebra de contrato?" e o documento diz "multa rescisória" — as palavras não coincidem mas o significado sim. Daí a necessidade de combinar os dois.
BM25 — uma revisão rápida
BM25 (Best Matching 25) é o algoritmo de ranking de busca por keywords padrão da indústria. Ele pontua documentos baseado em:
- TF (Term Frequency): quantas vezes o termo aparece no documento
- IDF (Inverse Document Frequency): termos raros valem mais que termos comuns
- Saturação: repetição do mesmo termo tem retornos decrescentes
- Normalização de comprimento: documentos longos não têm vantagem injusta
Elasticsearch, Apache Solr, Azure AI Search — todos usam BM25 como base para busca textual.
Reciprocal Rank Fusion (RRF)
O desafio de combinar busca vetorial e BM25 é que os scores são incomparáveis: cosine similarity vai de 0 a 1, BM25 pode ser qualquer número positivo. Somar os scores diretamente não faz sentido.
RRF resolve isso usando apenas a posição no ranking, não o score absoluto:
RRF_score(d) = Σ 1 / (k + rank(d))
Onde k é uma constante (tipicamente 60) que suaviza a influência de rankings muito altos.
Implementação manual com RRF
def reciprocal_rank_fusion(rankings: list[list[str]], k: int = 60) -> list[tuple[str, float]]:
"""
rankings: lista de listas de IDs de documentos, cada lista é um ranking
retorna: lista de (doc_id, rrf_score) ordenada por score decrescente
"""
scores = {}
for ranking in rankings:
for position, doc_id in enumerate(ranking):
if doc_id not in scores:
scores[doc_id] = 0.0
scores[doc_id] += 1.0 / (k + position + 1)
return sorted(scores.items(), key=lambda x: x[1], reverse=True)
# Exemplo de uso
vector_results = ["chunk-A", "chunk-C", "chunk-B", "chunk-E"] # IDs por cosine
keyword_results = ["chunk-B", "chunk-A", "chunk-D", "chunk-C"] # IDs por BM25
fused = reciprocal_rank_fusion([vector_results, keyword_results])
top_5 = [doc_id for doc_id, score in fused[:5]]
Azure AI Search hybrid + semantic reranker
Azure AI Search implementa hybrid search + RRF nativamente. Adicionando o semantic ranker, você tem um pipeline de 3 níveis:
- Fase 1 — Candidate retrieval: BM25 busca top-50, vetorial busca top-50
- Fase 2 — RRF fusion: combina os dois rankings → top-50 unificado
- Fase 3 — Semantic reranker: modelo de linguagem reordena os 50 → retorna top-k
from azure.search.documents import SearchClient
from azure.search.documents.models import VectorizedQuery
results = search_client.search(
search_text=user_query, # ativa BM25
vector_queries=[VectorizedQuery(
vector=embed(user_query),
k_nearest_neighbors=50,
fields="content_vector" # ativa busca vetorial
)],
# RRF é automático quando ambos estão ativos
query_type="semantic", # ativa semantic reranker (3ª camada)
semantic_configuration_name="my-semantic-config",
top=5 # retorna top 5 após reranking
)
Microsoft publicou que em testes internos, hybrid search supera busca puramente vetorial em 10-15% de relevância (medida por NDCG@10). Com semantic reranker adicional, o ganho chega a 25-30% em benchmarks de QA sobre documentos corporativos. A combinação é o estado da arte para RAG enterprise.
O Azure AI Search implementa hybrid search com RRF sem configuração extra — basta incluir tanto search_text quanto vector_queries na mesma chamada. O parâmetro query_type="semantic" adiciona o semantic reranker como terceira camada. Custo do semantic ranker: veja tier e limite gratuito em 04-02-03.
Como isso se conecta
- 04-04-01 — Re-ranking — o semantic reranker é a terceira camada após hybrid search
- 04-02-03 — Azure AI Search — implementação enterprise do hybrid search
- 04-05-01 — Métricas de RAG — como medir a melhoria do hybrid vs vetorial puro
Fontes
- Cormack, G., Clarke, C., Buettcher, S. (2009). Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR. dl.acm.org
- Microsoft. Hybrid search using vectors and full text in Azure AI Search. learn.microsoft.com
- Pinecone. Sparse-Dense Hybrid Search. pinecone.io/learn/hybrid-search-intro
- Robertson, S., Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. nowpublishers.com