Stack de ranking de pesquisa: BM25, embeddings e reranking
Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.
A pesquisa tem de satisfazer tanto a intenção exata como a semântica. Uma query por «wireless headphones» deve corresponder a essas palavras, mas a ordenação final também pode depender da qualidade do produto, das preferências do utilizador e da disponibilidade. Nenhum método de ranking trata bem todos esses sinais.
Este artigo constrói o stack por etapas: recuperação com BM25, embeddings densos, Reciprocal Rank Fusion, reranking com cross-encoder e, por fim, ranking listwise com um LLM. Um repositório de demonstração complementar contém código executável para estas etapas, aplicado a uma amostra dos dados de pesquisa de produtos Amazon ESCI.
Em resumo: Construa a pesquisa como um conjunto de etapas mensuráveis. Comece com BM25, adicione recuperação densa quando esta melhorar o recall nas suas queries, faça a fusão apenas se os dois recuperadores tiverem erros complementares e faça reranking apenas do conjunto de candidatos compatível com o orçamento de latência. Os resultados pré-LLM registados abaixo são um exemplo completo baseado numa amostra ESCI não reservada para validação. A comparação com LLM do demo é apenas ilustrativa, porque o parser não valida o ranking devolvido; não demonstra que todos os stacks de produção precisam de um LLM online.
Para um guia curto de seleção de etapas, consulte BM25 vs Embeddings vs Rerankers.
Escolha as etapas com base no modo de falha
O stack de produção é um funil, mas o funil adequado depende da query e da área de negócio.
| Caso de utilização | Stack inicial candidato | Validar |
|---|---|---|
| Pesquisa de produtos | BM25 + recuperação densa + RRF + cross-encoder | Recall de atributos, substituições, latência, restrições de negócio |
| Pesquisa de documentação | Recuperação híbrida + cross-encoder | Identificadores exatos, questões semânticas, filtros de versão |
| Contenção de pedidos de suporte | Recuperação híbrida + verificações de citações | Recall da recuperação, grounding, abstention |
| Marketplace ou anúncios | Filtros lexicais + recuperação densa + reranker de negócio | Disponibilidade, atualidade, políticas, diversidade de vendedores |
| Corpus interno pequeno | Baseline BM25, seguido de um reranker | Se a incompatibilidade de vocabulário justifica um índice denso |
| Pesquisa jurídica ou médica de alto risco | Recuperação orientada para recall e revisão especializada | Cobertura, proveniência, abstention calibrada |
Comece com BM25 como baseline. Adicione recuperação densa quando a incompatibilidade de vocabulário prejudicar o recall. Adicione um cross-encoder quando a primeira página tiver os candidatos certos pela ordem errada. Adicione um LLM apenas depois de conseguir suportar a latência e avaliar as decisões de ranking.
Como chegámos aqui
O stack é mais fácil de compreender como três camadas. A recuperação lexical encontra termos exatos, a recuperação densa reduz as lacunas de vocabulário e os rerankers comparam detalhadamente os candidatos mais fortes.
BM25 e recuperação lexical
Durante décadas, o BM25 foi a opção predefinida. É um modelo probabilístico que atribui pontuações aos documentos com base na frequência dos termos da consulta no documento, normalizada pelo comprimento do documento e pela frequência inversa nos documentos (IDF).
O BM25 é forte quando os termos literais exprimem a intenção: códigos de erro, SKUs de produtos, nomes e identificadores de API. A sua principal limitação é a incompatibilidade de vocabulário. Uma consulta por «portátil barato» pode não encontrar um documento sobre um «computador portátil económico» quando o texto indexado não fornece nenhuma ponte entre as expressões.
Ainda assim, o BM25 é uma baseline sólida. A tabela de classificação do BEIR apresenta um nDCG@10 médio de 0,429 em 18 conjuntos de dados para a execução BM25 multifield. A reprodução do Pyserini utiliza um índice multifield do Lucene com contents=1.0 e title=1.0, pesquisado com --bm25. Esta não é a implementação rank_bm25 simples, com tokenização por espaços em branco, usada nesta demonstração. O BM25 também supera alguns modelos neuronais em tarefas de recuperação argumentativa, como Touche-2020.
Recuperação densa e embeddings
Os encoders ao estilo do BERT tornaram prática a recuperação densa. Mapeiam consultas e documentos para um espaço vetorial partilhado e, em seguida, ordenam os candidatos com uma função de similaridade, como a similaridade do cosseno ou o produto escalar.
A arquitetura bi-encoder (ou «two-tower») processa a consulta e o documento de forma independente, através de torres de encoding separadas, produzindo embeddings de comprimento fixo. Os vetores dos documentos podem ser pré-calculados e indexados offline, sendo depois recuperados rapidamente através de algoritmos de Approximate Nearest Neighbor (ANN). Assim, «portátil barato» e «computador portátil económico» ficam próximos no espaço vetorial.
Alguns bi-encoders utilizam uma arquitetura siamesa, como no Sentence-BERT, em que ambos os lados partilham os pesos. Outros utilizam torres separadas para a consulta e para o documento. Pooling, dimensão do vetor, função de similaridade e objetivo de treino são escolhas do modelo, não propriedades de todos os recuperadores densos.
Estes modelos são treinados com aprendizagem contrastiva, normalmente utilizando a função de perda InfoNCE. Dado um batch de pares (query, positive_document), o objetivo maximiza sim(query, positive_doc) e minimiza sim(query, negative_docs). Os negativos provêm dos positivos de outras queries no mesmo batch (negativos in-batch). Um parâmetro de temperatura controla o grau de separação exigido ao modelo.
Os dados de treino são frequentemente mais importantes do que a dimensão do embedding. Os modelos de recuperação aprendem a partir de pares query-positive e de hard negatives cuidadosamente selecionados: documentos plausíveis, mas não relevantes. A secção de treino mais adiante mostra como o SimANS evita tanto negativos triviais como prováveis falsos negativos.
O custo é o bottleneck da representação. Os bi-encoders comprimem toda a nuance semântica num único vetor de tamanho fixo, pelo que muitas vezes não captam interações de granularidade fina entre termos específicos da consulta e conteúdo específico dos documentos.
Cross-encoders e LLMs
Cross-encoders (Nogueira & Cho, 2019) introduzem a query e o documento em conjunto num Transformer, como uma sequência concatenada ([CLS] Query [SEP] Document), permitindo que cada token da query atenda a todos os tokens do documento através de self-attention completa. Esta interação profunda capta nuances que a codificação independente não deteta.
O reranking com LLMs utiliza um modelo com um prompt para comparar vários candidatos em simultâneo. O RankGPT demonstrou resultados sólidos zero-shot, em modo listwise, com o GPT-4 nos benchmarks avaliados, mas a estabilidade da saída, o custo e a adequação ao domínio continuam a exigir testes separados.
Estas pontuações não podem ser pré-calculadas para queries arbitrárias, pelo que o reranking é colocado depois da retrieval. Esta assimetria de custos motiva o funnel de várias etapas.
O funnel de várias etapas
Executar um cross-encoder ou LLM dispendioso sobre milhões de documentos não é viável, pelo que as stacks de pesquisa modernas utilizam um funnel. Cada etapa reduz o conjunto de candidatos, enquanto aumenta a complexidade do modelo.
Um retriever barato também não oferece precisão final, pelo que o funnel utiliza cada modelo apenas quando o respetivo custo é razoável.
| Etapa | Escala de entrada | Objetivo principal | Métodos típicos | Medição à saída |
|---|---|---|---|---|
| Retrieval | Corpus ou índice | Recall de candidatos | BM25, bi-encoders | Recall no cutoff de candidatos |
| Pre-ranking | Conjunto grande de candidatos | Filtragem barata | Modelos leves, regras | Recall retido por milissegundo |
| Full ranking | Shortlist | Qualidade do top ranking | Cross-encoders, LLMs | NDCG/MRR, latência, custo |
| Blending | Listas ou slots finais ordenados | Restrições e mistura | Regras, ranking multiobjetivo | Política, diversidade, guardrails de negócio |
A retrieval define o teto e o reranking otimiza dentro desse limite. Se um documento relevante não sobreviver à retrieval, nenhum modelo a jusante poderá recuperá-lo.
A demo: um pipeline de cinco etapas
Para tornar isto concreto, desenvolvi uma demo de search-ranking-stack que executa um pipeline de cinco etapas no benchmark de pesquisa de produtos Amazon ESCI. Cada etapa é medida de forma independente, para que seja possível perceber de onde vêm realmente os ganhos.
O pipeline:
- Retrieval sparse com BM25 — baseline lexical (
rank_bm25) - Retrieval densa com bi-encoder — geração semântica de candidatos (
all-MiniLM-L6-v2) - Fusão híbrida com RRF — fusão baseada no ranking dos resultados sparse e densos
- Reranking com cross-encoder — pontuações de relevância pairwise (
ms-marco-MiniLM-L-12-v2) - Reranking listwise com LLM — comparação com prompt da shortlist final (Ollama, API ou modelo local)
Os passos 1—3 correspondem à etapa de retrieval do funnel (maximizar o recall); os passos 4—5 correspondem à etapa de full ranking (maximizar a precisão). A demo não inclui pre-ranking nem blending. Com cerca de 8 500 documentos, é possível enviar todos os resultados híbridos diretamente para o reranking.
Início rápido
git clone https://github.com/slavadubrov/search-ranking-stack.git
cd search-ranking-stack
uv sync
# Download and sample ESCI dataset (~2.5GB download, ~5MB sample)
uv run download-data
# Run the full pipeline (without LLM reranking)
uv run run-all
# Run with LLM reranking via Ollama
uv run run-all --llm-mode ollama
Dataset e amostragem: Amazon ESCI
A demonstração utiliza o Amazon Shopping Queries Dataset (ESCI) da KDD Cup 2022 — um benchmark real de pesquisa de produtos com etiquetas de relevância graduada em quatro níveis:
| Etiqueta | Ganho | Significado | Exemplo (consulta: “wireless headphones”) |
|---|---|---|---|
| Exact (E) | 3 | Satisfaz todos os requisitos da consulta | Sony WH-1000XM5 Wireless Headphones |
| Substitute (S) | 2 | Alternativa funcional | Wired headphones with Bluetooth adapter |
| Complement (C) | 1 | Item relacionado e útil | Headphone carrying case |
| Irrelevant (I) | 0 | Não tem uma relação significativa | USB charging cable |
A relevância graduada é importante porque permite utilizar NDCG (Normalized Discounted Cumulative Gain), que distingue uma ordenação «perfeita» de uma ordenação «apenas adequada». As métricas binárias atribuem a mesma pontuação a ambas.
Utilizei o small_version da demonstração: cerca de 500 consultas, 8 500 produtos e 12 000 avaliações. O downloader lê a única partição train de tasksource/esci, filtra a localidade inglesa us e small_version == 1 e, em seguida, utiliza a seed 42 para amostrar até 500 IDs de consulta únicos, sem reposição. O corpus contém os produtos únicos das linhas de avaliações selecionadas. Isto não é uma partição de validação nem um corpus independente. A amostra é suficientemente pequena para ser executada num portátil, mas é demasiado pequena e específica do domínio para fundamentar um sistema de ranking em produção. Utilize-a para reproduzir as várias etapas e analisar modos de falha; para tomar decisões de deployment, utilize consultas representativas mantidas de fora.
Retrieval: pesquisa híbrida
A função da camada de retrieval é maximizar o recall: colocar o maior número possível de documentos relevantes no conjunto de candidatos.
BM25: a baseline lexical
O BM25 atribui pontuações aos documentos com base na sobreposição de termos com a consulta, incorporando saturação da frequência dos termos e normalização do comprimento dos documentos:
Onde é a frequência inversa nos documentos do termo , é a frequência do termo no documento , é o comprimento do documento e é o comprimento médio dos documentos em todo o corpus. Há dois parâmetros importantes: (normalmente 1.2—2.0) controla a saturação da TF — a rapidez com que as ocorrências repetidas deixam de acrescentar valor — e (normalmente 0.75) controla a normalização do comprimento dos documentos.
A implementação é curta. Tokenização simples por espaços em branco com rank_bm25:
# src/search_ranking_stack/stages/s01_bm25.py
from rank_bm25 import BM25Okapi
def run_bm25(data: ESCIData, top_k: int = 100):
doc_ids = list(data.corpus.keys())
tokenized_corpus = [text.lower().split() for text in data.corpus.values()]
bm25 = BM25Okapi(tokenized_corpus)
results = {}
for query_id, query_text in data.queries.items():
scores = bm25.get_scores(query_text.lower().split())
top_indices = np.argsort(scores)[::-1][:top_k]
results[query_id] = {doc_ids[idx]: float(scores[idx]) for idx in top_indices}
return results
O BM25 atinge um Recall@100 de 0.741 — 74% dos produtos relevantes aparecem algures nos 100 primeiros resultados. Não é mau para um método exclusivamente lexical, mas 26% dos itens relevantes ficam invisíveis para todas as etapas seguintes.
Retrieval denso com bi-encoder
O bi-encoder mapeia consultas e documentos de forma independente para um espaço de embeddings partilhado:
# src/search_ranking_stack/stages/s02_dense.py
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
# Encode corpus once, cache to disk
corpus_embeddings = model.encode(
doc_texts,
batch_size=128,
normalize_embeddings=True, # Cosine sim = dot product
convert_to_numpy=True,
)
# At query time: encode query, compute dot product
query_embeddings = model.encode(query_texts, normalize_embeddings=True)
similarity_matrix = np.dot(query_embeddings, corpus_embeddings.T)
Com embeddings normalizados, a similaridade do cosseno reduz-se a um produto escalar. A demonstração calcula a matriz completa de consultas por corpus porque 8 500 documentos cabem confortavelmente em memória; num corpus de produção, seria normalmente utilizado um índice de approximate nearest neighbors. Neste exemplo, all-MiniLM-L6-v2 aumenta o Recall@100 de 0,741 para 0,825.
Como os bi-encoders aprendem boas representações
O treino de bi-encoders decorre normalmente em duas fases. Primeiro, o modelo é pré-treinado em conjuntos de dados de Natural Language Inference (NLI) e Semantic Textual Similarity (STS), que lhe ensinam uma compreensão semântica de uso geral. É nessa fase que o modelo aprende que “a cat sits on a mat” e “a feline rests on a rug” devem ter embeddings semelhantes. Depois, é feito fine-tuning com dados específicos de retrieval, como o MS MARCO, onde o modelo aprende que uma consulta e a passagem relevante devem ficar mais próximas entre si do que a consulta e as passagens irrelevantes.
A segunda fase depende de hard negative mining. Negativos aleatórios, como um documento sobre culinária associado a uma consulta sobre auscultadores, são trivialmente fáceis de distinguir, pelo que o modelo aprende pouco com eles. Em vez disso, utiliza-se o próprio modelo atual para encontrar documentos aos quais atribui uma classificação elevada, mas que, na realidade, não são relevantes.
A abordagem SimANS (Simple Ambiguous Negatives Sampling) formaliza este processo. Classifique todos os documentos com o bi-encoder atual e, em seguida, exclua os negativos fáceis, que obtêm uma classificação demasiado baixa para proporcionarem aprendizagem, e os potenciais falsos negativos, que obtêm uma classificação tão elevada que podem ser relevantes, mas não estão anotados como tal. O que resta no intervalo intermédio contém o sinal de treino mais importante.
# What a training triplet looks like after hard negative mining
training_triplet = {
"query": "wireless noise canceling headphones",
"positive": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
"negative": "Sony headphone replacement ear pads", # Hard negative: same brand, related product, but wrong intent
}
# The bi-encoder must learn that "ear pads" is NOT what the user wants,
# even though it shares many tokens with the positive document.
A função de perda contrastiva (InfoNCE) reúne estes elementos. Para cada consulta com um documento positivo e um conjunto de documentos negativos :
Em que é a similaridade do cosseno entre os embeddings da consulta e do documento, e é o parâmetro de temperatura (normalmente 0.05—0.1). Valores mais baixos tornam a perda mais sensível a hard negatives. É essencialmente uma softmax cross-entropy: aumentar a similaridade do par positivo relativamente à de todos os negativos. Quando é pequeno, mesmo pequenas diferenças na similaridade produzem gradientes elevados, o que força o modelo a estabelecer distinções mais granulares.
Disponibilizar embeddings de bi-encoders à escala
A vantagem arquitetural de um bi-encoder é a separação offline/online. Os embeddings dos documentos são calculados no momento da indexação e armazenados num índice vetorial. No momento da consulta, o sistema codifica a consulta e pesquisa esses vetores armazenados. A latência depende do encoder, do hardware, do índice, dos filtros e do objetivo de recall; por isso, avalie os dois passos separadamente.
Na demonstração, os cálculos são modestos: 8 500 documentos 384 dimensões 4 bytes por float = aproximadamente 13 MB de embeddings. À escala de produção, os valores deixam de ser modestos: 1 bilião de documentos com embeddings de 768 dimensões requerem aproximadamente 3 TiB de armazenamento. É aqui que entram a quantização (comprimir floats de 32 bits para inteiros de 8 bits), a product quantization (decompor vetores em subespaços) e índices suportados por SSD, como o DiskANN. A secção sobre indexação de vetores densos aborda os algoritmos de indexação.
Porquê testar a recuperação híbrida
Os dois métodos falham frequentemente de formas diferentes. O BM25 é adequado para nomes próprios, SKUs de produtos e códigos de erro. A recuperação densa consegue resolver incompatibilidades de vocabulário, como «computador portátil barato» versus «computador portátil económico». A utilidade da fusão depende da frequência com que estes casos complementares ocorrem no conjunto de queries alvo.
Uma experiência seguinte comum é a pesquisa híbrida: executar ambos os métodos de recuperação e, em seguida, fundir as listas ordenadas.
Reciprocal rank fusion (RRF)
O BM25 e a recuperação densa produzem scores com significados e escalas diferentes. Por isso, uma combinação linear requer calibração e validação sempre que os retrievers ou o corpus mudam.
A Reciprocal Rank Fusion (Cormack et al., 2009) ignora completamente os scores brutos e usa apenas a posição no ranking:
Aqui, é uma constante de suavização; 60 é um valor inicial comum. A RRF atribui maior peso aos itens que aparecem perto do topo em várias listas de entrada, sem comparar os respetivos scores brutos. Evita a calibração da escala dos scores, mas os limites de recuperação, os pesos e continuam a exigir avaliação.
A implementação:
# src/search_ranking_stack/stages/s03_hybrid_rrf.py
def reciprocal_rank_fusion(ranked_lists, k=60, top_k=100):
fused_results = {}
for query_id in all_query_ids:
rrf_scores = defaultdict(float)
for results in ranked_lists:
sorted_docs = sorted(results[query_id].items(),
key=lambda x: x[1], reverse=True)
for rank, (doc_id, _score) in enumerate(sorted_docs, start=1):
rrf_scores[doc_id] += 1.0 / (k + rank)
sorted_rrf = sorted(rrf_scores.items(),
key=lambda x: x[1], reverse=True)[:top_k]
fused_results[query_id] = dict(sorted_rrf)
return fused_results
A RRF híbrida atinge Recall@100 de 0.842 e NDCG@10 de 0.628, superando tanto o BM25 (0.585) como o Dense (0.611) isoladamente. Para sobreviverem à fusão, os documentos só precisam de obter uma boa posição num dos métodos.
Reordenação com cross-encoder
Com 100 candidatos híbridos por query, é possível usar um modelo mais dispendioso. O cross-encoder processa a query e o documento em conjunto através de um único Transformer, com cross-attention completa entre todos os tokens.
Interação ao nível dos tokens
A diferença está na matriz de atenção. Num bi-encoder, a atenção é diagonal por blocos: os tokens da query atendem apenas a outros tokens da query, e os tokens do documento atendem apenas a outros tokens do documento. As duas representações nunca se encontram ao nível dos tokens — só intersetam no fim através de um produto escalar. Um cross-encoder calcula a matriz de atenção completa, em que cada token da query atende a todos os tokens do documento e vice-versa. É essa cross-attention que torna possível uma interação profunda ao nível dos tokens.
Num bi-encoder, a query «apple» é codificada antes de qualquer documento ser analisado. Um cross-encoder vê a query e o candidato em conjunto, pelo que pode usar a relação entre os respetivos tokens. Isto pode ajudar em casos como:
- Negação: «auscultadores que não são sem fios». Um embedding com pooling pode atribuir pouco peso à negação, enquanto a codificação conjunta proporciona ao modelo uma interação direta entre a query e o documento. Esta é uma hipótese a validar com um slice específico, não uma garantia.
- Restrição: «computador portátil abaixo de $500». A codificação conjunta pode relacionar a restrição com um preço no texto do produto, embora os filtros estruturados por preço sejam mais seguros quando o campo está disponível.
A entrada do cross-encoder é formatada como [CLS] query tokens [SEP] document tokens [SEP]. [CLS] é um token de classificação cujo estado oculto final é passado por uma cabeça linear para produzir uma única pontuação de relevância. Os embeddings de segmento distinguem os tokens da consulta dos tokens do documento, e [SEP] assinala a fronteira entre segmentos.
Como são treinados os cross-encoders
Os cross-encoders podem aprender a partir de exemplos (query, document, relevance_label), usando objetivos pointwise, pairwise ou listwise. O exemplo pointwise abaixo utiliza uma única etiqueta de relevância; não é a única abordagem de treino.
# Cross-encoder training data format
training_example = {
"query": "wireless headphones",
"document": "Sony WH-1000XM5 Wireless Headphones",
"label": 1.0, # Relevant
}
# Forward pass: [CLS] hidden state → Linear layer → sigmoid → score
# Loss: binary cross-entropy between predicted score and label
Um classificador comum mapeia a representação final de [CLS] para uma pontuação. As etiquetas binárias podem usar binary cross-entropy; a relevância graduada pode usar perdas de regressão, ordinais, pairwise ou listwise. Escolha com base em métricas de ranking num conjunto de validação separado, em vez de assumir que um objetivo é universalmente melhor.
A mineração de hard negatives é ainda mais importante para cross-encoders do que para bi-encoders A mineração de hard negatives é ainda mais importante para cross-encoders. Os cross-encoders são dispendiosos de treinar — cada exemplo de treino requer uma passagem forward completa pela sequência concatenada —, pelo que não pode desperdiçar computação em negativos trivialmente fáceis. A receita prática: use um bi-encoder para obter os top-K candidatos de cada consulta de treino e, em seguida, extraia hard negatives de intervalos de ranking específicos (por exemplo, ranks 10–100). Assim, o cross-encoder recebe exemplos em que distinguir itens relevantes de irrelevantes exige, de facto, uma interação profunda entre tokens.
# src/search_ranking_stack/stages/s04_cross_encoder.py
from sentence_transformers import CrossEncoder
model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-12-v2")
def run_cross_encoder(data, hybrid_results, top_k_rerank=50):
reranked_results = {}
for query_id, query_text in data.queries.items():
candidates = list(hybrid_results[query_id].items())[:top_k_rerank]
# Form (query, document) pairs for joint encoding
pairs = []
doc_ids = []
for doc_id, _ in candidates:
doc_text = data.corpus.get(doc_id, "")[:2048]
pairs.append([query_text, doc_text])
doc_ids.append(doc_id)
# Score all pairs with full cross-attention
scores = model.predict(pairs, batch_size=64)
# Rerank by cross-encoder score
scored_docs = sorted(zip(doc_ids, scores),
key=lambda x: x[1], reverse=True)
reranked = {
doc_id: float(score) for doc_id, score in scored_docs
}
# Keep original hybrid candidates at ranks 51--100 for Recall@100.
for doc_id, score in list(hybrid_results[query_id].items())[top_k_rerank:100]:
if doc_id not in reranked:
reranked[doc_id] = float(score) * 0.01
reranked_results[query_id] = reranked
return reranked_results
Na execução de demonstração registada, ms-marco-MiniLM-L-12-v2 faz o reranking de 50 candidatos por consulta e aumenta o NDCG@10 de 0.628 para 0.645. Meça a latência no hardware de deployment; o modelo, o comprimento da sequência, o tamanho do batch e o runtime influenciam o resultado.
O compromisso entre velocidade e qualidade
Porque não utilizar cross-encoders para tudo? Porque não é possível pré-calcular as pontuações. Os embeddings dos documentos de um bi-encoder são independentes da consulta, pelo que são calculados uma vez e armazenados. A saída de um cross-encoder depende simultaneamente da consulta e do documento. A pontuação de relevância de “wireless headphones” associada a um produto Sony resulta da cross-attention completa entre esses tokens específicos. Não é possível colocá-la em cache nem reutilizá-la para uma consulta diferente.
Um bi-encoder requer uma codificação da consulta e uma pesquisa vetorial sobre embeddings de documentos pré-calculados. Um cross-encoder atribui uma pontuação a cada par consulta-documento da shortlist, com um custo que aumenta tanto com o número de candidatos como com o comprimento da sequência. O batching ajuda, mas atribuir pontuações a 100 000 candidatos continua a ser o ponto de operação errado; faça primeiro a recuperação e avalie a maior shortlist que cumpra os objetivos de qualidade e latência.
Na demonstração, o Recall@100 mantém-se estável em 0.842 ao longo da etapa do cross-encoder. O reranking pode reordenar resultados, mas não pode adicionar documentos. A recuperação define o limite superior.
Reranking listwise com LLMs
A fase final da demonstração utiliza um LLM para reranking listwise. Em vez de atribuir uma pontuação a cada documento de forma independente, o modelo recebe os 10 primeiros e devolve uma ordenação. Inspirado no RankGPT, o prompt explicita a comparação relativa, mas também introduz limites de contexto, enviesamento posicional, falhas de parsing e variabilidade entre execuções.
O prompt listwise
O template do prompt pede ao LLM que tenha em conta a hierarquia de relevância do ESCI:
# src/search_ranking_stack/stages/s05_llm_rerank.py
def _create_listwise_prompt(query, documents, max_words=200):
n = len(documents)
doc_texts = []
for i, (doc_id, doc_text) in enumerate(documents, start=1):
words = doc_text.split()[:max_words]
doc_texts.append(f"[{i}] {' '.join(words)}")
return (
f"I will provide you with {n} product listings, each indicated by "
f"a numerical identifier [1] to [{n}]. Rank the products based on "
f'their relevance to the search query: "{query}"\n\n'
"Consider:\n"
"- Exact matches should rank highest\n"
"- Substitutes should rank above complements\n"
"- Irrelevant products should rank lowest\n\n"
f"{chr(10).join(doc_texts)}\n\n"
"Output ONLY a comma-separated list of identifiers: [3], [1], [2], ...\n"
"Do not explain your reasoning."
)
Três modos de execução
A demonstração suporta três backends para o reranking com LLM:
| Modo | Modelo | Como é executado |
|---|---|---|
ollama | llama3.2:3b (configurável) | Localmente através da API do Ollama |
api | claude-haiku-4-5-20251001 | API da Anthropic |
local | Qwen/Qwen2.5-1.5B-Instruct | HuggingFace Transformers |
Parsing e fallback
Não é garantido que os outputs do LLM sigam o schema solicitado, pelo que o parsing e o fallback são importantes:
def _parse_ranking(output: str, n: int) -> list[int] | None:
"""Parse LLM output to extract ranking order."""
matches = re.findall(r"\[(\d+)\]", output)
if not matches:
return None
positions = [int(m) - 1 for m in matches]
# Pad with remaining positions if LLM returned partial output
if len(positions) < n:
seen = set(positions)
for i in range(n):
if i not in seen:
positions.append(i)
return positions[:n]
Se o parsing falhar completamente, a demonstração recorre à ordenação do cross-encoder. Num parser de produção, também se devem rejeitar identificadores fora do intervalo e duplicados, acrescentar os candidatos omitidos pela ordem anterior, registar a falha e comparar a taxa de fallback com um limiar definido para o lançamento.
Resultados desta amostra ESCI
Estes resultados registados antes do LLM utilizam o small_version da demonstração: o locale inglês us
após o filtro small_version == 1, com até 500 IDs de queries amostrados
sem reposição, utilizando a seed 42, e um corpus construído a partir dos produtos
avaliados para essas queries.
Não correspondem a uma avaliação hold-out nem a um corpus independente.
Os valores de MRR utilizam a regra binária do avaliador da demonstração, relevance > 0: um
resultado Complement, Substitute ou Exact conta como relevante, enquanto um
resultado Irrelevant não conta. recip_rank avalia cada lista de candidatos devolvida,
que contém até 100 candidatos; não corresponde a MRR@10.
| Fase | NDCG@10 | MRR | Recall@100 | Delta de NDCG |
|---|---|---|---|---|
| BM25 | 0.585 | 0.812 | 0.741 | — |
| Dense Bi-Encoder | 0.611 | 0.808 | 0.825 | +0.026 |
| Hybrid (RRF) | 0.628 | 0.834 | 0.842 | +0.017 |
| + Cross-Encoder | 0.645 | 0.860 | 0.842 | +0.017 |
Principais observações
A pesquisa híbrida supera cada método isoladamente. O NDCG do RRF (0.628) é superior ao do BM25 (0.585) e ao do Dense (0.611). Os dois métodos podem falhar em queries diferentes, e combiná-los permite recuperar documentos que qualquer um deles, por si só, não encontraria.
O recall é determinado na recuperação. O Recall@100 mantém-se em 0.842 ao longo da fase do cross-encoder. Os rerankers reordenam os documentos; não acrescentam documentos. Se pretender aumentar o recall, corrija a camada de recuperação. Os valores de MRR acima utilizam a mesma regra global da demonstração: Complement ou superior é relevante.
O resultado do LLM não é reportado. O parser aceita identificadores duplicados e fora do intervalo, e o caminho de padding pode alterar silenciosamente a lista de candidatos. Por isso, a comparação anterior com o LLM é ilustrativa, não um resultado auditável. Repita-a com validação estrita dos identificadores, comportamento de fallback registado, execuções repetidas, medições de latência e custo, e um conjunto de domínios reservado para teste, antes de atribuir qualquer ganho ao reranker.
O repositório complementar é útil para a configuração e o código, mas o README continua a reportar o NDCG@10 inválido de 0,717 em + LLM Reranker. Considere a tabela anterior ao LLM deste artigo e a ressalva acima sobre o LLM como o registo autoritativo, até que esse repositório tenha uma avaliação de LLM auditável.
A recuperação densa supera o BM25 nesta amostra. Inspecione os recortes das queries antes de atribuir a causa. A incompatibilidade de vocabulário é um fator plausível, mas a construção da amostra, o tokenizador, o domínio de treino do modelo e os campos do corpus também afetam a comparação.
Avaliação: medir o que importa
A demonstração usa três métricas, cada uma analisando o ranking de uma perspetiva diferente:
NDCG@10 (métrica principal)
Normalized Discounted Cumulative Gain mede a qualidade do ranking dos 10 primeiros resultados usando relevância graduada. Recompensa a colocação de documentos altamente relevantes no topo, com um desconto logarítmico:
Das três, o NDCG é a única métrica que utiliza integralmente a relevância graduada de quatro níveis do ESCI. Um sistema que coloca uma correspondência Exact na posição 1 obtém uma pontuação superior à de um sistema que coloca aí uma Substitute. É por isso que é a métrica principal aqui.
MRR (primeiro resultado Complement ou superior)
Mean Reciprocal Rank usa a posição do primeiro resultado considerado relevante. Nesta demonstração, o avaliador passa os qrels graduados do ESCI a pytrec_eval e usa recip_rank sobre cada lista devolvida com até 100 candidatos, pelo que relevance > 0 é o limiar binário: Complement (1), Substitute (2) e Exact (3) contam como relevantes. Irrelevant (0) não conta. Um resultado relevante na posição 1 dá um reciprocal rank de 1,0; na posição 3, dá 0,333. Este é o MRR sobre as listas de candidatos devolvidas, não o MRR@10.
Recall@100 (cobertura da recuperação)
O recall mede que fração dos documentos relevantes avaliados aparece nos 100 primeiros resultados. É uma métrica do teto de candidatos para os julgamentos avaliados: um reranker não pode adicionar um documento que a recuperação omitiu, enquanto julgamentos incompletos podem tornar incerto o teto aparente.
Indexação de vetores densos para além da demonstração
Os embeddings densos só se tornam úteis à escala quando existe um índice Approximate Nearest Neighbor (ANN). A demonstração usa similaridade de cosseno por força bruta (o que é adequado para cerca de 8500 documentos), mas os sistemas de produção precisam de índices especializados.
HNSW (hierarchical navigable small world)
HNSW constrói um grafo com várias camadas: as camadas superiores, esparsas, permitem uma navegação ampla, enquanto as camadas inferiores, mais densas, refinam a vizinhança. M controla a conectividade do grafo, enquanto efSearch estabelece um compromisso entre o trabalho de consulta e o recall. Os valores úteis dependem da dimensão, da distribuição das distâncias, dos filtros, da implementação e do recall pretendido.
As atualizações e eliminações são uma consideração operacional, porque os índices de grafos podem necessitar de reparação em segundo plano. O comportamento varia consoante a base de dados. Um problema do Qdrant, por exemplo, relata uma degradação da qualidade da pesquisa filtrada numa configuração HNSW multi-tenant. O relatório mediu uma precisão média@100 de 0.597 ± 0.0541 com um filtro library_id, enquanto a pesquisa exata atingiu 100% de recall na sua análise mais aprofundada. Alterar payload_m forçou uma reconstrução que restaurou a qualidade da pesquisa filtrada. O relatório aborda filtros por tenant, a configuração HNSW e uma reconstrução forçada do índice HNSW, não uma carga de trabalho com muitas eliminações. Reproduza o padrão de churn pretendido e inclua o comportamento de compactação ou reconstrução na avaliação.
IVF (ficheiro invertido)
Os índices IVF particionam o espaço vetorial em clusters e, em seguida, pesquisam os nprobe clusters mais próximos da consulta. Podem proporcionar um compromisso útil entre memória, tempo de construção e recall, sobretudo quando combinados com compressão. A semântica e o desempenho das atualizações dependem da implementação, não apenas da família do índice.
Para escalas extremas, IVF-RaBitQ (Gao & Long, SIGMOD 2024) comprime vetores de vírgula flutuante em representações de um único bit. Em espaços de alta dimensionalidade, o sinal (+/-) de uma coordenada contém informação angular suficiente para calcular a similaridade.
| Dimensão | Grafo HNSW | Clusters IVF |
|---|---|---|
| Controlo da consulta | efSearch | nprobe |
| Controlo da construção | Conectividade e beam de construção | Número de clusters e amostra de treino |
| Perfil de memória | Arestas do grafo e vetores | Centróides, listas e vetores armazenados |
| Comportamento das atualizações | Reparação/limpeza específica da base de dados | Manutenção de listas específica da base de dados |
| Avaliar com | Curva recall-latência-memória-churn | Curva recall-latência-memória-churn |
Num estudo de caso da Uber sobre pesquisa de entregas, reduzir um parâmetro de pesquisa ao nível do shard de 1.200 para 200 diminuiu a latência reportada em 34% e o consumo de CPU em 17%, com pouca perda de recall medida. A lição reutilizável é ajustar a curva recall-custo com tráfego semelhante ao de produção, e não copiar o valor 200.
Extensões opcionais após o pipeline principal
Depois de obter medições separadas para a recuperação e o reranking, torna-se mais fácil avaliar várias extensões sem obscurecer o pipeline principal.
Compreensão da consulta
A expansão e a reescrita de consultas podem resolver incompatibilidades de vocabulário antes da recuperação. O Query2doc gerou pseudo-documentos e reportou ganhos no BM25 nas suas experiências com o MS MARCO. A expansão também pode introduzir uma intenção incorreta; por isso, compare recall e precisão em subconjuntos de consultas ambíguas, de navegação e com identificadores exatos.
Padrões práticos: expansão de abreviaturas, enriquecimento de entidades, decomposição em subconsultas para raciocínio multi-hop e RAG-Fusion — gerar várias variantes da consulta e combinar os resultados através de RRF.
Rotulagem de relevância assistida por LLM
Os LLM podem gerar rascunhos de rótulos de relevância quando os julgamentos humanos são escassos. O TALEC e o trabalho da Pinterest sobre rotulagem de relevância apresentam dois desenhos avaliados. Um rótulo produzido por um LLM continua a ser output de um modelo: calibre-o com base em julgamentos humanos cegos, inspecione fatias de discordância e mantenha um conjunto gold humano para testes de regressão.
Controlos úteis incluem:
- uma rubrica com limites concretos de relevância e exemplos;
- calibração humana cega e reavaliações periódicas;
- aleatorização da ordem e julgamentos repetidos para casos instáveis;
- painéis de modelos quando o custo adicional melhora a concordância; e
- verificações explícitas de enviesamento por posição e de tendência para a média.
Knowledge distillation
Quando um LLM teacher acrescenta valor, mas não consegue cumprir as restrições de serving, a distillation é uma opção:
- Use um LLM poderoso (o teacher) para fazer reranking de milhares de queries de treino
- Treine um cross-encoder pequeno e rápido (o student, com ~100M–200M parâmetros) para imitar a distribuição de ranking do LLM
- Compare o student com o teacher e com o baseline em qualidade, calibração e custo de serving
O InRanker destila o MonoT5-3B em modelos com 60M e 220M parâmetros — uma redução de 50x no tamanho, com desempenho competitivo. A abordagem Rank-Without-GPT produz rerankers listwise open source de 7B que atingem 97% da eficácia do GPT-4 através de fine-tuning com QLoRA.
Os resultados publicados de compressão são pontos de partida, não rácios esperados em produção. A distillation pode herdar os enviesamentos do teacher e perder qualidade em fatias raras de queries; por isso, mantenha os julgamentos de relevância originais no ciclo de avaliação.
Personalização e enviesamento por posição
A relevância genérica só permite avançar até certo ponto. Uma pesquisa por “apple” deve devolver iPhones a um entusiasta de tecnologia e receitas com maçã a alguém que tenha estado a consultar conteúdos de culinária.
Uma arquitetura de retrieval comum para personalização utiliza um modelo de embeddings de duas torres: a torre da query codifica a query e o contexto do utilizador, enquanto a torre dos itens codifica os itens e os metadados. A separação offline/online suporta retrieval approximate-nearest-neighbor; a latência continua a depender do encoder, do índice, dos filtros e do sistema de serving.
Os embeddings de anúncios da Airbnb, o OmniSearchSage da Pinterest e os sistemas de duas torres da Uber mostram diferentes desenhos de produção. A escala e os ganhos reportados pertencem a esses sistemas; o padrão transferível é a torre de itens offline combinada com uma torre de query/utilizador online.
Os dados de cliques transportam enviesamentos de posição e de exposição. O PAL é uma abordagem de debiasing: usar a posição durante o treino e mantê-la constante durante o serving. Não é uma solução universal; intervenções aleatorizadas, métodos de inverse propensity e avaliação contrafactual podem ser mais adequados para outro produto.
Adaptação ao domínio com queries sintéticas
Um erro comum ao definir uma estratégia de pesquisa é assumir que um modelo treinado com dados gerais da Web (como o MS MARCO) funcionará bem num domínio especializado. Este é o problema out-of-domain (OOD).
Os LLM podem reduzir, mas não eliminar, o bottleneck de dados anotados através de Generative Pseudo-Labeling (GPL, InPars):
- Pegue no seu corpus documental específico do domínio
- Peça a um LLM para «Gerar uma consulta de pesquisa à qual este documento daria resposta»
- Utilize os pares sintéticos (consulta, documento) para fazer fine-tuning do seu retriever e reranker
Os pares sintéticos podem ser úteis quando há poucas consultas reais, mas refletem o gerador e o prompt. Remova duplicados, filtre consultas implausíveis e valide-as com tráfego real separado para avaliação.
Uma sequência de experiências
Adicione complexidade apenas quando a fase anterior revelar uma falha mensurável:
Passo 1 (baseline): implemente o BM25 ou o sistema lexical atual e crie um conjunto de consultas avaliadas. Registe recall, NDCG, latência e segmentos de falhas.
Passo 2 (recall de candidatos): teste a recuperação densa e a fusão apenas se o baseline não recuperar documentos relevantes. Ajuste o limite de candidatos tendo em conta o recall e o custo.
Passo 3 (precisão da ordenação): adicione um cross-encoder se os candidatos corretos existirem, mas aparecerem na ordem errada. Escolha o tamanho da shortlist com base numa curva de qualidade-latência.
Passo 4 (adequação ao domínio): faça fine-tuning ou distillation apenas depois de os modelos genéricos revelarem falhas estáveis e específicas do domínio. Mantenha as avaliações reais separadas dos dados de treino sintéticos.
Passo 5 (camada dispendiosa opcional): teste reranking listwise ou baseado em reasoning apenas quando o ganho incremental de qualidade se mantiver em execuções repetidas e justificar a complexidade adicional em termos de latência, custo, privacidade e fallback.
Linhas de investigação a avaliar separadamente
Os rerankers baseados em reasoning e os agentes que utilizam pesquisa são promissores, mas respondem a perguntas diferentes das da demonstração de pesquisa de produtos em cinco fases.
Rerankers baseados em reasoning
Rank1 treina rerankers com traces de reasoning e apresenta resultados fortes no benchmark BRIGHT. Esta evidência é relevante para recuperação intensiva em reasoning, não constituindo uma previsão direta para a pesquisa de produtos ESCI.
Na pesquisa jurídica ou científica, compare rerankers baseados em reasoning com baselines fortes de cross-encoders e listwise, recorrendo a avaliações de especialistas, citações, latência e consistência das falhas.
Pesquisa agentic
O Search-o1 estuda um modelo que faz pesquisas adicionais durante respostas a perguntas multi-hop. Este é um problema de orquestração — geração de consultas, decisão de paragem, utilização de evidência e avaliação da resposta — e não mais uma fase de reranking. Avalie-o pela correção da tarefa final e pelo suporte das citações, não apenas pelas métricas de recuperação.
Principais conclusões
-
Trate a stack como uma sequência de experiências. Estabeleça um baseline lexical e um conjunto de consultas avaliadas antes de adicionar recuperação densa, fusão ou reranking.
-
Meça separadamente o recall de candidatos e a precisão da ordenação. Nesta amostra ESCI, o Recall@100 atinge 0,842 após a fusão e mantém-se estável nas duas fases de reranking.
-
Utilize recuperação híbrida quando os erros forem complementares. A RRF melhorou tanto o Recall@100 como o NDCG@10 na demonstração, mas outro corpus pode não justificar dois índices.
-
Adicione um cross-encoder quando a shortlist estiver correta, mas a ordem estiver errada. Escolha o número de candidatos com base numa curva de qualidade-latência medida.
-
Trate o reranking com LLM como uma experiência final opcional. A comparação com LLM da demonstração atual é meramente ilustrativa, porque o respetivo parser não valida uma permutação completa dos IDs dos candidatos. O parsing estrito, os fallbacks registados, as execuções repetidas e o custo devem fazer parte da avaliação antes de reportar qualquer ganho.
-
Mantenha os tipos de evidência separados. Um resultado de um artigo científico, um case study de um fornecedor, esta demonstração num portátil e um teste A/B em produção respondem a perguntas diferentes.
O código completo do pipeline está no repositório complementar. Faça clone para reproduzir as fases anteriores ao LLM ou testar modelos e parâmetros diferentes; não trate a linha desatualizada do LLM como um resultado.
Referências
Artigos
- Reciprocal Rank Fusion — Cormack et al., 2009
- RankGPT: LLMs as Zero-Shot Listwise Rerankers — Sun et al., EMNLP 2023 Outstanding Paper
- Rank1: Reasoning-Based Reranking — Weller et al., COLM 2025
- SCaLR: Self-Calibrated Listwise Reranking — Framework de reranking listwise com autocorreção de calibração
- GCCP: Global-Consistent Comparative Pointwise — Aborda a calibração no ranking pointwise com LLM
- Rank-DistiLLM: Knowledge Distillation for Reranking — Schlatt et al., ECIR 2025
- Query2doc: LLM Query Expansion — Wang et al., EMNLP 2023
- GPL: Generative Pseudo Labeling — Adaptação de domínio para recuperação densa
- InRanker: Distilled Reranker — Redução de 50x no tamanho com desempenho competitivo
- Search-o1: Agentic Retrieval — EMNLP 2025
- TALEC: LLM-as-a-Judge for Search — Framework de avaliação
- BEIR Benchmark — Thakur et al., NeurIPS 2021
- BEIR Leaderboard — baseline
BM25 multifielde agregado de 18 datasets - Sentence-BERT — Reimers & Gurevych, EMNLP 2019
- InfoNCE / CPC — van den Oord et al., 2018
- SimANS: Hard Negative Sampling — Zhou et al., EMNLP 2022
- Passage Reranking with BERT — Nogueira & Cho, 2019
- HNSW — Malkov & Yashunin, 2016
- DiskANN — Subramanya et al., NeurIPS 2019
- RaBitQ — Gao & Long, SIGMOD 2024
- Replacing Judges with Juries — Verga et al., 2024
- RAG-Fusion — Rackauckas, 2024
- InPars — Bonifacio et al., SIGIR 2022
- BRIGHT Benchmark — Su et al., ICLR 2025
- Rank-without-GPT — Zhang et al., ECIR 2025
- Pinterest LLM Search Relevance — Wang et al., 2024
- OmniSearchSage — Agarwal et al., WWW 2024
- PAL: Position-bias Aware Learning — Guo et al., RecSys 2019
Conjuntos de dados e benchmarks
- Amazon ESCI: Shopping Queries Dataset — KDD Cup 2022
- BEIR: Benchmarking IR — Benchmark heterogéneo para avaliação zero-shot
- MTEB: Massive Text Embedding Benchmark — Classificação de modelos de embeddings
- ESCI Paper — Reddy et al., 2022
Modelos utilizados na demonstração
- all-MiniLM-L6-v2 — bi-encoder com 22M de parâmetros
- ms-marco-MiniLM-L-12-v2 — cross-encoder com 33M de parâmetros
- Sentence-Transformers — Framework de modelos neuronais de recuperação
Ferramentas e plataformas
- rank_bm25 — Implementação de BM25 em Python
- Pyserini BEIR Reproductions — comandos de indexação e avaliação de
bm25-multifield - pytrec_eval — Toolkit de avaliação TREC
- Elasticsearch — Pesquisa híbrida com a API Retrievers
- Vespa — Motor unificado de pesquisa e recomendação
- Weaviate — Base de dados vetorial com pesquisa híbrida
- Qdrant — Base de dados vetorial com consultas multi-stage
Referências da indústria
- Airbnb Listing Embeddings — Grbovic & Cheng, KDD 2018
- Uber Delivery Search — Uber Engineering, 2025
- Uber Two-Tower Embeddings — Uber Engineering, 2023
- Elastic Rerank — Elastic, 2024
Projeto de demonstração
- search-ranking-stack — Demonstração funcional com todo o código desta publicação.