[!NOTE] Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.

Pilha de Classificação de Pesquisas em 2026: BM25, Embeddings, Codificadores Cruzados e LLM Reranking

A busca deve satisfazer tanto a intenção exata como a semântica. Uma consulta para “headphones sem fios” deve corresponder a essas palavras, mas a ordem final pode também depender da qualidade do produto, das preferências do utilizador e da disponibilidade. Nenhum método de classificação consegue processar eficazmente todos esses fatores em simultâneo.

Este artigo constrói a pilha técnica etapa por etapa: recuperação com BM25, embeddings denso, Fusão de Classificações Recíprocas, reranking de codificador cruzado e, por fim, classificação lista a lista com LLM. A demo em funcionamento benchmarks em cada fase do processo de busca por produtos da Amazon ESCI dataset.

TL;DR: Construa o sistema de busca em fases sequenciais e bem delimitadas. Comece com o BM25, adicione a recuperação densa sempre que isso melhorar a taxa de recall nas suas consultas, faça a fusão apenas quando os dois mecanismos de recuperação apresentarem erros complementares, e reclassifique somente o conjunto de resultados que se enquadre no limite de latência estabelecido. Nesta demonstração ESCI do tamanho de um portátil, a implementação completa de pipeline elevou o valor de NDCG@10 de 0,585 para 0,717. Esse resultado é um exemplo prático, e não uma prova de que toda stack de produção necessite das cinco fases ou de um LLM em tempo real.


Escolher fases de acordo com o modo de falha

A pilha de produção funciona como um funil, mas o tipo de funil adequado depende da consulta e das necessidades específicas do negócio.

Caso de usoPilha inicial do candidatoValidar
Pesquisa de produtosBM25 + recuperação densa + RRF + codificador cruzadoRecuperação de atributos, substituições, latência, restrições comerciais
Pesquisa de documentaçãoRecuperação híbrida + codificador cruzado
Suporte à deflexãoRecuperação híbrida + verificações de citação
Mercado ou listagensFiltros lexicais + recuperação densa + negócios rerankerDisponibilidade, atualidade, política e diversidade de vendedores
Pequeno corpus internolinha de base BM25, seguida por um rerankerSe a incompatibilidade de vocabulário justifica a utilização de um índice denso
Busca jurídica ou médica de alto riscoRecuperação focada em recall aliada à revisão por especialistasCobertura, proveniência e abstenção calibrada

Comece com o BM25 como linha de base. Adicione uma recuperação densa sempre que a discrepância no vocabulário prejudique a taxa de recall. Implemente um codificador cruzado quando a primeira página apresentar os candidatos corretos, mas na ordem errada. Introduza um LLM somente após ser possível suportar o atraso de execução e avaliar adequadamente as decisões de classificação.

Como chegámos aqui

A arquitetura torna-se mais fácil de compreender quando dividida em três camadas. A recuperação lexical identifica termos exatos, a recuperação densa preenche lacunas no vocabulário e rerankers permite comparar detalhadamente os candidatos mais fortes.

BM25 e recuperação lexical

Há décadas, BM25 Era o padrão por defeito. Trata‑se de um modelo probabilístico que avalia os documentos com base na frequência dos termos de consulta neles presentes, normalizada pela comprimento do documento e pela frequência inversa de documento (IDF).

O BM25 apresenta um desempenho elevado quando os termos literais refletem diretamente a intenção do utilizador: códigos de erro, códigos SKU de produtos, nomes e identificadores API. A sua principal limitação reside no desalinhamento do vocabulário. Uma consulta como “portátil barato” pode não encontrar um documento sobre um “computador portátil económico”, caso o texto indexado não disponha de uma ligação que associe estas expressões.

Por isso, o BM25 constitui uma base sólida. Ele atinge uma média de nDCG@10 de 0,429 em todos os casos. BEIR benchmark’s 18 datasets e Continua a ser superior a alguns modelos neurais. em tarefas de recuperação argumentativa como Touche-2020.

Recuperação densa e embeddings

Os codificadores no estilo BERT tornaram a recuperação densa viável. Eles mapeiam consultas e documentos para um espaço vetorial partilhado e, em seguida, classificam os candidatos através de uma função de similaridade, como a similaridade de cosseno ou o produto escalar.

A arquitetura de bi-encoder (ou “duas torres”) processa a consulta e o documento de forma independente, através de torres de codificador separadas, gerando vetores de comprimento fixo embeddings. Os vetores de documento podem ser pré-calculados e indexados offline, sendo posteriormente recuperados rapidamente por meio de algoritmos de Vizinho Mais Próximo Aproximado (ANN). Assim, termos como “portátil económico” e “notebook de baixo custo” acabam por ficar próximos um do outro no espaço vetorial.

Alguns bi-encoders utilizam uma arquitetura Siamesa, tal como em Sentence-BERT, Onde ambos os lados partilham pesos. Outros sistemas utilizam torres de consulta e de documento separadas. O pooling, o tamanho do vetor, a função de similaridade e o objetivo de treino são escolhas do modelo, e não propriedades inerentes a todos os recuperadores densos.

Estes modelos são treinados com aprendizagem contrastiva, geralmente com o InfoNCE perda. Dado um lote de pares (consulta, documento_positivo), o objetivo é maximizar sim(query, positive_doc) enquanto se minimiza sim(query, negative_docs). Os negativos provêm dos positivos de outras consultas no mesmo lote (negativos intra-lote). Um parâmetro de temperatura τ\tau controla o quão acentuada é a distribuição: valores mais baixos incentivam o modelo a fazer distinções mais precisas entre positivos e negativos.

Os dados de treino costumam ser mais importantes do que a dimensão embedding. Os modelos de recuperação aprendem com pares de consulta-positiva e negativos difíceis cuidadosamente selecionados: documentos plausíveis, mas não relevantes. A secção seguinte sobre treino demonstra como SimANS evita tanto negativos triviais como falsos negativos prováveis.

O custo reside no gargalo de representação. Os bi-encoders comprimem todas as nuances semânticas num único vetor de tamanho fixo, pelo que frequentemente falham em detetar interações detalhadas entre termos de consulta específicos e conteúdos de documento específicos.

Codificadores cruzados e LLMs

Cross-encoders (Nogueira e Cho, 2019) incorporar a consulta e o documento num Transformer como uma sequência concatenada ([CLS] Query [SEP] Document), permitindo assim que cada token de consulta preste atenção a cada token de documento através de autoatenção total. Essa interação profunda capta as nuances que uma codificação independente não consegue detetar.

LLM reranking utiliza um modelo acionado por prompts para comparar vários candidatos em simultâneo. RankGPT Obteve resultados significativos em abordagem zero-shot e de forma lista a lista com o GPT-4 nos benchmarks avaliados, mas a estabilidade da saída, o custo operacional e a adequação ao domínio ainda exigem testes separados.

Estas pontuações não podem ser calculadas previamente para consultas arbitrárias, pelo que reranking é inserido após a recuperação dos dados. Essa assimetria de custos é o que motiva a utilização de um funil em várias fases.


O funil multiestágio

Executar um codificador cruzado dispendioso ou LLM em milhões de documentos não é viável, por isso as arquiteturas de busca modernas recorrem a um funil. Cada fase reduz o conjunto de candidatos à medida que a complexidade do modelo aumenta.

Um modelo complexo é demasiado lento para avaliar todo o corpus, enquanto um recuperador económico carece de precisão final. O funil utiliza cada modelo apenas nos casos em que o seu custo é razoável.

Funil de Classificação em Várias Fases

FaseEscala de entradaObjetivo principalMétodos típicosMedição de saída
RecuperaçãoCorpo de dados ou índiceRecuperação de candidatosBM25, bi-encodersRecuperação no ponto de corte dos candidatos
Pré-classificaçãoGrande conjunto de candidatosFiltragem económicaModelos leves, regrasRecall retido por milissegundo
Classificação completaLista de pré-seleçãoQualidade de topo de classificaçãoEncodificadores cruzados, LLMsNDCG/MRR, latência, custo
BlendagemListas finais classificadas ou posições atribuídasRestrições e misturaRegras, classificação multiobjetivoPolítica, diversidade e restrições de negócio

A recuperação de dados define o limite superior, sendo que o reranking otimiza o processo dentro desse limite. Se um documento relevante não for recuperado durante a fase de busca, nenhum modelo posterior conseguirá recuperá-lo.


A demonstração: um processo em cinco fases pipeline

Para concretizar isto, construí um stack de classificação de buscas demo que executa um pipeline de cinco fases no Amazon ESCI Pesquisa de produtos benchmark. Cada fase é avaliada de forma independente, permitindo assim identificar exatamente de onde provêm as melhorias.

Demonstração da Arquitetura Pipeline

O pipeline:

  1. Recuperação esparsa BM25 — linha de base léxica (rank_bm25)
  2. Recuperação com bi-encoder denso — geração de candidatos semânticos (all-MiniLM-L6-v2)
  3. Fusão híbrida RRF — fusão baseada em classificação de resultados esparsos e densos
  4. Codificador cruzado reranking — pontuações de relevância entre pares (ms-marco-MiniLM-L-12-v2)
  5. LLM em lista reranking — comparação solicitada entre a lista final de opções (Ollama, API ou modelo local)

Os passos 1–3 correspondem à fase de retrieval do funil (com o objetivo de maximizar o recall); os passos 4–5 referem‑se à fase de full ranking (com o objetivo de maximizar a precisão). A demonstração omite as etapas de pré‑ranking e blending. Com cerca de 8.500 documentos, é possível enviar diretamente todos os resultados híbridos para 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 as Amazon Shopping Queries Dataset (ESCI) provenientes de KDD Cup 2022 — uma verdadeira funcionalidade de busca de produtos benchmark com etiquetas de relevância graduadas em quatro níveis:

RótuloObter
Exato (E)3Cumpre todos os requisitos da consulta.Fones de ouvido sem fios Sony WH-1000XM5
Substituir (S)2Alternativa funcionalAuscultadores com fio equipados com adaptador Bluetooth
Complemento (C)1Item útil relacionadoEstojo de transporte para fones de ouvido
Irrelevante (I)0Não existe uma relação significativa.Cabos de carregamento USB

A relevância graduada é importante porque permite a utilização do NDCG (Normalized Discounted Cumulative Gain), que distingue uma classificação “perfeita” de uma “apenas adequada”. As métricas binárias tratam ambas como igualmente relevantes e não conseguem diferenciar diferentes níveis de relevância na mesma posição.

Utilizei a demonstração do small_version Exemplo: cerca de 500 consultas, 8.500 produtos e 12.000 julgamentos. É suficientemente pequeno para ser executado num portátil, mas demasiado reduzido e específico de um domínio para permitir a criação de um sistema de classificação em produção. Utilize-o para reproduzir as diferentes fases do processo e analisar os modos de falha; recorra a consultas representativas, mantidas de lado, para tomar decisões relativas à implementação.


Recuperação de Informações: busca híbrida

A função da camada de recuperação é maximizar o recall — é preciso lançar a rede mais ampla possível para que nada de relevante escape.

BM25: a linha de base léxica

O BM25 avalia os documentos com base na sobreposição de termos em relação à consulta, aplicando saturação da frequência de termos e normalização do comprimento do documento:

BM25(q,d)=tqIDF(t)tf(t,d)(k1+1)tf(t,d)+k1(1b+bd/avgdl)\text{BM25}(q, d) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{tf(t,d) \cdot (k_1 + 1)}{tf(t,d) + k_1 \cdot (1 - b + b \cdot |d|/\text{avgdl})}

Onde \text{IDF}(t) é a frequência de documento inversa do termo tt; tf(t,d)tf(t,d) representa a frequência do termo no documento dd.|d|representaocomprimentododocumento,enquantorepresenta o comprimento do documento, enquanto\text{avgdl}indicaocomprimentomeˊdiodosdocumentosemtodoocorpus.Doispara^metrossa~oespecialmenteimportantes:indica o comprimento médio dos documentos em todo o corpus. Dois parâmetros são especialmente importantes:k_1(geralmenteentre1,2e2,0)controlaasaturac\ca~odoTFouseja,qua~orapidamentetermosrepetidosdeixamdecontribuirparaaanaˊlisee(geralmente entre 1,2 e 2,0) controla a saturação do TF — ou seja, quão rapidamente termos repetidos deixam de contribuir para a análise — eb$ (normalmente 0,75) regula a normalização do comprimento dos documentos.

A implementação é breve. Tokenização simples de 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 em algum lugar entre os 100 primeiros resultados. Não é mau para um método puramente lexical, mas 26% dos itens relevantes permanecem invisíveis em todas as fases subsequentes do processo.

Recuperação com bi-encoder denso

O bi-encoder mapeia consultas e documentos de forma independente para um espaço comum embedding:

# 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 normalizado, a semelhança de cosseno reduz-se a um produto escalar. A demonstração calcula a matriz completa de consulta por corpus, uma vez que 8.500 documentos cabem facilmente na memória; num corpus de produção, seria normalmente utilizado um índice de vizinho mais próximo aproximado. Neste exemplo, all-MiniLM-L6-v2 Aumenta o Recall@100 de 0,741 para 0,825.

Como os bi-encoders aprendem representações de qualidade

O treino de bi-encoders geralmente ocorre em duas fases. Primeiro, o modelo é pré-treinado em Inferência de Linguagem Natural (NLI) e Semelhança Textual Semântica (STS) datasets, técnicas que visam desenvolver uma compreensão semântica de uso geral — o modelo aprende que frases como “um gato senta-se num tapete” e “um felino descansa sobre um carpete” devem apresentar valores semelhantes de embeddings. Em seguida, ele é ajustado com dados específicos para recuperação de informação, como o MS MARCO, onde aprende que uma consulta de busca e o seu trecho relevante devem estar mais próximos um do outro do que a consulta e os trechos irrelevantes.

O ingrediente essencial na segunda fase é a extração de negativos difíceis. Negativos aleatórios (por exemplo, um documento sobre culinária associado a uma consulta sobre fones de ouvido) são extremamente fáceis de distinguir — o modelo não aprende nada com eles. Em vez disso, utiliza-se o próprio modelo atual para identificar documentos que ele classifica como altamente relevantes, mas que na realidade não o são.

O SimANS A abordagem de (Simple Ambiguous Negatives Sampling) formaliza este processo: classifica todos os documentos com o bi-encoder atual, excluindo posteriormente os negativos fáceis (classificados com uma posição muito baixa — já tratados pelo modelo) e os potenciais falsos negativos (classificados com uma posição demasiado alta — que podem na verdade ser relevantes, mas não rotulados). O “terreno intermediário difícil” gera assim o sinal de aprendizagem máximo.

# 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) une tudo isto. Para cada consulta qq com documento positivo d+d^+ e um conjunto de documentos negativos {d1,,dn}\{d^-_1, \ldots, d^-_n\}:

L=logesim(q,d+)/τesim(q,d+)/τ+i=1nesim(q,di)/τ\mathcal{L} = -\log \frac{e^{\text{sim}(q, d^+) / \tau}}{e^{\text{sim}(q, d^+) / \tau} + \sum_{i=1}^{n} e^{\text{sim}(q, d^-_i) / \tau}}

Onde sim(q,d)\text{sim}(q, d) representa a semelhança de cosseno entre a consulta e o documento embeddings, e τ\tau é o parâmetro de temperatura (geralmente entre 0,05 e 0,1), que controla o grau de nitidez da distribuição — valores mais baixos tornam a perda mais sensível aos negativos fortes. Trata-se, basicamente, de um entropia cruzada softmax: Aumentar a similaridade do par positivo em relação a todos os negativos. Quando τ\tau é pequeno, até mesmo diferenças mínimas na similaridade geram gradientes elevados, o que obriga o modelo a fazer distinções mais detalhadas.

Treino de Bi-Encoder Pipeline

Serving bi-encoder embeddings em larga escala

A vantagem arquitetónica de um bi-encoder reside na divisão offline/online. Os documentos embeddings são calculados no momento da indexação e armazenados num índice vetorial. No momento da consulta, o sistema codifica a mesma e procura entre esses vetores armazenados. A latência depende do encoder, do hardware, do índice, dos filtros e do objetivo de recuperação, pelo que é necessário realizar um perfilamento separado para os dois passos.

Na demonstração, os cálculos são modestos: 8.500 documentos ×\times 384 dimensões ×\times 4 bytes por número flutuante = ~13 MB de embeddings. Em escala de produção, esses valores deixam de ser modestos: 1 bilhão de documentos com embeddings de 768 dimensões exigem aproximadamente 3 TiB de armazenamento. É aí que entra a quantização (a compressão de números flutuantes de 32 bits em inteiros de 8 bits), quantização de produto (descomposição de vetores em subespaços) e índices suportados por SSD como DiskANN Entre. O Secção de indexação de vetores densos aborda os algoritmos de índice.

Bi-Encoder Serving Pipeline

Por que testar a recuperação híbrida

Os dois métodos falham frequentemente de maneiras distintas. O BM25 é particularmente adequado para nomes próprios, códigos SKU de produtos e códigos de erro. A recuperação densa consegue resolver discrepâncias no vocabulário, como no caso de “laptop barato” em oposição a “notebook económico”. Se a fusão de métodos traz benefícios depende da frequência com que esses casos complementares ocorrem no conjunto de consultas alvo.

Um experimento comum seguinte é a pesquisa híbrida: executar ambos os métodos de recuperação e, em seguida, fundir as suas listas ordenadas.

Fusão de classificação recíproca (RRF)

O BM25 e a recuperação densa geram pontuações com significados e escalas distintos. Por isso, uma combinação linear requer calibração e validação sempre que os mecanismos de recuperação ou o corpus forem alterados.

Busca Híbrida com RRF

Fusão de Rank Recíproco (Cormack et al., 2009) descarta completamente as pontuações brutas e utiliza apenas a posição de classificação:

RRF(d)=rRankings1k+rank(d,r)\text{RRF}(d) = \sum_{r \in \text{Rankings}} \frac{1}{k + \text{rank}(d, r)}

Aqui, kk é uma constante de suavização; 60 é um valor de partida comum. O RRF recompensa os itens que se classificam perto do topo nas listas de entrada, sem comparar as suas pontuações brutas. Evita a calibração da escala de pontuação, mas os limites de recuperação, os pesos e o kk 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

O RRF híbrido atinge um Recall@100 de 0,842 e um NDCG@10 de 0,628 — superando tanto o BM25 (0,585) como o Dense (0,611) quando utilizados isoladamente. Para que os documentos sobrevivam à fusão, basta que se classifiquem bem em um dos métodos.


Codificador Cruzado reranking

Com 100 candidatos híbridos por consulta, é possível optar por um modelo mais caro. O cross-encoder processa a consulta e o documento em conjunto, através de um único Transformer, com atenção cruzada total entre todos os tokens.

Bi-Encoder versus Cross-Encoder

Interação a nível de token

A verdadeira diferença reside na matriz de atenção. Num bi-encoder, a atenção é bloco-diagonal: os tokens de consulta prestam atenção apenas a outros tokens de consulta, e os tokens de documento prestam atenção apenas a outros tokens de documento. As duas representações nunca se encontram ao nível dos tokens — interseçam-se apenas no final através de um produto escalar. Um cross-encoder calcula a matriz de atenção completa, na qual cada token de consulta presta atenção a cada token de documento e vice-versa. É essa atenção cruzada que permite interações profundas ao nível dos tokens.

Arquitetura de Atenção de Codificador Cruzado

Num bi-encoder, a consulta “apple” é codificada antes mesmo de qualquer documento ser processado. Um cross-encoder, por sua vez, analisa a consulta e o documento em conjunto, permitindo assim utilizar a relação entre eles a nível de tokens. Isso pode ser útil em cenários como:

A entrada do cross-encoder é formatada como [CLS] query tokens [SEP] document tokens [SEP]. [CLS] é um token de classificação cujo estado oculto final é alimentado através de uma cabeça linear para gerar uma única pontuação de relevância. O segmento embeddings serve para distinguir os tokens de consulta dos tokens de documento, e [SEP] marca o limite entre os segmentos.

Como são treinados os cross-encoders

Os cross-encoders conseguem aprender com (query, document, relevance_label) Exemplos com objetivos ponto a ponto, par a par ou por lista. O exemplo ponto a ponto apresentado abaixo utiliza um único rótulo de relevância; este não é o único esquema de treino possível.

# 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 o resultado final [CLS] A conversão da representação em uma pontuação. Os rótulos binários podem utilizar entropia cruzada binária; já a relevância graduada pode recorrer a perdas de regressão, ordinais, entre pares ou listadas. Escolha a abordagem com base em métricas de classificação testadas separadamente, em vez de assumir que um objetivo seja universalmente superior.

A extração de negativos fortes é ainda mais importante para codificadores cruzados. em comparação com os bi-encoders. Os cross-encoders são dispendiosos de treinar — cada exemplo de treino exige uma passagem completa em frente pela sequência concatenada — por isso não se pode permitir desperdiçar recursos de processamento em exemplos negativos que são trivialmente fáceis de obter. A abordagem prática consiste em utilizar um bi-encoder para recuperar os K melhores candidatos para cada consulta de treino e, em seguida, selecionar negativos difíceis a partir de intervalos específicos de classificação (por exemplo, das posições 10 a 100). Isso fornece ao cross-encoder exemplos nos quais distinguir o relevante do irrelevante realmente requer interações profundas entre os 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):
    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_results[query_id] = {
            doc_id: float(score) for doc_id, score in scored_docs
        }

Na execução da demonstração gravada, ms-marco-MiniLM-L-12-v2 Reclassifica 50 candidatos por consulta, elevando o NDCG@10 de 0,628 para 0,645. É necessário medir a sua latência no hardware de deploy, pois o modelo, o comprimento da sequência, o tamanho do lote e runtime influenciam diretamente os resultados.

O compromisso entre velocidade e qualidade

Por que não utilizar cross-encoders para tudo? Porque a pré-computação é impossível. Os bi-encoders de documentos embeddings são independentes da consulta, pelo que podem ser calculados uma única vez e armazenados. A saída de um cross-encoder depende tanto da consulta como do documento em conjunto. A pontuação de relevância para “fones de ouvido sem fios” associados a um produto da Sony resulta da atenção cruzada completa entre esses tokens específicos. Não é possível armazená-la em cache nem reutilizá-la para uma consulta diferente.

Um bi-encoder requer uma codificação da consulta, além de uma busca vetorial em documentos pré-computados embeddings. Um cross-encoder avalia cada par consulta-documento na lista preliminar, sendo que o custo aumenta à medida que tanto o número de candidatos quanto o comprimento da sequência crescem. O agrupamento em lotes pode ajudar, mas avaliar 100.000 candidatos continua a ser um ponto de operação inadequado; deve-se primeiro recuperar os dados e benchmark selecionar a maior lista preliminar que atenda aos objetivos de qualidade e latência.

A regra confirmada pela demonstração é a seguinte: o Recall@100 mantém-se constante em 0,842 em ambas as fases reranking. Reranking consegue reordenar os resultados, mas nunca adicionar novos documentos. O processo de recuperação define o limite máximo possível.


LLM de forma lista a lista reranking

A fase final de demonstração utiliza um LLM para realizar reranking de forma lista a lista. Em vez de avaliar cada documento de forma independente, o modelo analisa os 10 melhores resultados e retorna uma ordem de classificação. Inspirado em RankGPT, O prompt torna a comparação relativa explícita, mas também introduz limites de contexto, viés de posição, falhas de análise sintática e variabilidade entre execuções.

LLM Reranking Abordagens

A lista prompt

O modelo prompt solicita ao LLM que analise a hierarquia de relevância 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 LLM reranking:

ModoModeloComo Funciona
ollamallama3.2:3b (configurável)Carregar localmente via Ollama API
apiclaude-haiku-4-5-20251001Anthropic API
localQwen/Qwen2.5-1.5B-InstructHuggingFace Transformers

Análise e mecanismo de fallback

Os resultados gerados por LLM não têm garantia de que sigam o esquema solicitado, pelo que a análise estrutural e um caminho alternativo são essenciais:

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 a análise falhar completamente, a demonstração recorre à ordem de codificação cruzada. Um analisador em ambiente de produção deve também rejeitar identificadores fora do intervalo válido ou duplicados, adicionar os candidatos omitidos na sua ordem anterior, registar a falha e comparar a taxa de recurso a esta solução alternativa com um limiar definido no lançamento.


Resultados deste exemplo ESCI

Aqui estão os resultados da execução do pipeline completo em cerca de 500 consultas ESCI:

Resultados Pipeline

FaseNDCG@10MRR@10Recall@100Delta NDCG
BM250.5850.8120.741
Dense Bi-Encoder0.6110.8080.825+0.026
Híbrido (RRF)0.6280.8340.842+0.017
+ Codificador Cruzado0.6450.8600.842+0.017
+ LLM Reranker0.7170.9010.842+0.072

Observações principais

A busca híbrida supera cada método utilizado isoladamente. O RRF NDCG (0.628) obtém o melhor resultado, ultrapassando tanto o BM25 (0.585) como o Dense (0.611). Os métodos de recuperação esparsa e densa apresentam padrões de falha complementares, e a sua combinação permite recuperar documentos que seriam perdidos se fossem utilizados apenas um deles.

O recall é definido durante a fase de recuperação. O valor do Recall@100 mantém‑se constante em 0,842 em ambas as fases de reranking. Após a reordenação realizada por Rerankers, não são adicionados novos documentos. Se desejar um recall mais elevado, deve corrigir a camada de recuperação.

O LLM estimulado gera o maior salto medido nesta execução. O valor do NDCG@10 aumenta 0,072 após a fase do LLM. Como o prompt leva em conta a hierarquia de relevância ESCI, o resultado serve para avaliar esta combinação de modelo-prompt-dataset. São necessárias execuções repetidas, intervalos de confiança, medições de latência e custo, além de um conjunto de domínios reservado, antes de se poder atribuir esse ganho a um reranker globalmente superior.

A recuperação densa supera o BM25 neste exemplo. É necessário analisar as fatias da consulta antes de determinar a causa. Uma discrepância no vocabulário é um fator plausível, mas a estrutura do exemplo, o tokenizador, o domínio de treino do modelo e os campos do corpus também influenciam a comparação.


Avaliação: medir o que é relevante

A demonstração utiliza três métricas complementares. Cada uma delas analisa o ranking sob um ângulo diferente:

NDCG@10 (métrica principal)

Ganho Acumulado Descontado Normalizado mede a qualidade da classificação dos 10 melhores resultados através da relevância graduada. Este indicador recompensa a colocação de documentos altamente relevantes perto do topo, aplicando um desconto logarítmico:

DCG@k=i=1k2reli1log2(i+1)NDCG@k=DCG@kIDCG@k\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{rel_i} - 1}{\log_2(i + 1)} \qquad \text{NDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

O NDCG é a única métrica que utiliza na totalidade a classificação de relevância em quatro níveis da ESCI — um sistema no qual uma correspondência exata na posição 1 obtém uma pontuação mais alta do que aquela em que um substituto ocupa esse mesmo lugar. É por isso que ele se torna a métrica principal para avaliar a qualidade geral das pesquisas.

MRR@10 (primeiro resultado relevante)

Rank Recíproco Médio utiliza a posição do primeiro resultado considerado relevante. Se este se encontrar na posição 1, o rank recíproco é 1,0; se estiver na posição 3, o valor é 0,333. No caso de etiquetas graduadas como a ESCI, deve ser indicado o limiar de relevância utilizado para converter essas classificações numa decisão binária.

Recall@100 (cobertura de recuperação)

A taxa de recall indica qual a fração dos documentos considerados relevantes que aparecem nos 100 primeiros resultados. Trata‑se de uma métrica de teto para os julgamentos avaliados: um reranker não pode incluir um documento que tenha sido omitido pela recuperação, enquanto julgamentos incompletos podem tornar esse teto aparente incerto.


Indexação de vetores densos para além da demonstração

Os métodos embeddings densos só se tornam úteis em larga escala quando se dispõe de um índice de Vizinho Mais Próximo Aproximado (ANN). A demonstração utiliza a similaridade de cosseno por força bruta (o que é aceitável para cerca de 8.500 documentos), mas os sistemas em produção exigem índices especializados.

HNSW (mundo pequeno hierárquico e navegável)

HNSW Constrói um grafo multicamada: 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, ao mesmo tempo que efSearch O trabalho de consulta de transações é substituído pelo conceito de recall. Os valores úteis dependem da dimensão, da distribuição de distâncias, dos filtros, da implementação e do recall desejado.

As atualizações e exclusões constituem um fator operacional importante, uma vez que os índices de grafo podem manter registros inativos ou exigir reparação em segundo plano. O comportamento varia consoante o banco de dados. A Problema no Qdrant, Por exemplo, os relatórios indicam uma qualidade degradada após um determinado workload de exclusão em massa. É necessário reproduzir o padrão de churn alvo e incluir no processo de avaliação o comportamento de compactação ou reconstrução dos dados.

IVF (ficheiro invertido)

Os índices IVF dividem o espaço vetorial em clusters e, em seguida, realizam uma varredura dos nprobe os clusters mais próximos da consulta. Eles permitem obter um equilíbrio útil entre memória utilizada, tempo de construção e taxa de recuperação de resultados, especialmente quando combinados com técnicas de compressão. A semântica das atualizações e o desempenho dependem da implementação específica, e não apenas da família de índices utilizada.

Para escalas extremas, IVF_RaBitQ (Gao & Long, SIGMOD 2024) compacta vetores de ponto flutuante em representações de um único bit. No espaço de alta dimensão, o sinal de uma coordenada (+/-) contém informação angular suficiente para o cálculo de similaridade.

DimensãoHNSW grafoClusters de IVF
Controlo de consultaefSearchnprobe
Controlo de construçãoConectividade e feixe de construçãoNúmero de clusters e amostras de treino
Perfil de memóriaArestas de grafo mais vetoresCentroides, listas e vetores armazenados
Comportamento de atualizaçãoReparação/limpeza específica para bases de dadosManutenção de listas específicas para bases de dados
Avaliar comCurva de churn de memória, latência e recallCurva de recall, latência, memória e rotatividade

Em um Estudo de caso de busca por entregas da Uber, A redução de um parâmetro de busca a nível de shard, de 1.200 para 200, permitiu diminuir a latência registada em 34% e CPU em 17%, com uma perda mínima na precisão medida. A lição a reter é a de ajustar a curva custo-precisão com tráfego semelhante ao de produção, em vez de simplesmente copiar o valor 200.


Extensões opcionais após a parte principal pipeline

Assim que a recuperação de dados e o reranking passam a ter medições distintas, torna‑se mais fácil avaliar várias extensões sem comprometer o pipeline principal.

Compreensão da consulta

A expansão e a reescrita de consultas permitem resolver discrepâncias de vocabulário antes da recuperação de dados. Query2Doc Foram gerados pseudo-documentos e foram reportados ganhos com o algoritmo BM25 nos experimentos realizados com o MS MARCO. A expansão de resultados também pode introduzir a intenção incorreta; por isso, é necessário comparar a taxa de recuperação e a precisão em conjuntos de consultas ambíguas, de navegação e que exigem identificadores exatos.

Padrões práticos: expansão de abreviaturas, enriquecimento de entidades, decomposição de subconsultas para raciocínio multi-hop e RAG - Fusão — gerar múltiplas variantes de consulta e combinar os resultados através do RRF.

LLM-rotulagem de relevância assistida

LLMs consegue gerar rótulos de relevância quando os julgamentos humanos são escassos. TALEC e O trabalho de rotulagem de relevância do Pinterest Forneça dois projetos já avaliados. Uma etiqueta LLM representa ainda uma saída do modelo: calibre-a com base em julgamentos humanos cegos, analise as regiões de discordância e mantenha um conjunto de referência humano para testes de regressão.

Controlos úteis incluem:

Destilação de conhecimento

Quando um professor LLM agrega valor, mas não consegue cumprir as restrições serving, a destilação é uma das opções disponíveis:

  1. Utilize um LLM poderoso (o professor) para reclassificar milhares de consultas de treino.
  2. Treine um codificador cruzado pequeno e rápido (o aluno, com ~100M–200M de parâmetros) para imitar a distribuição de classificação do LLM.
  3. Compare o desempenho do aluno, tanto em termos de qualidade como de calibração, quanto em relação ao custo de serving, face ao professor e à solução de referência.

Terminador de Classificação destila o MonoT5-3B em modelos com 60M e 220M de parâmetros — uma redução de tamanho de 50 vezes, mantendo um desempenho competitivo. O Classificação Sem GPT Esta abordagem gera uma lista de 7 milhões de modelos de código aberto aplicados de forma independente rerankers, que atingem 97% da eficácia do GPT-4 através do uso de QLoRA fine-tuning.

Os resultados de compressão publicados servem como pontos de partida, e não como valores esperados para produção em escala real. O processo de destilação pode herdar os vieses do “professor” e perder qualidade em casos de consultas pouco frequentes; por isso, mantenha os julgamentos de relevância originais no ciclo de avaliação.


Personalização e viés de posição

A relevância genérica só o levará até um certo ponto. Uma busca por “maçã” deve retornar iPhones para um entusiasta de tecnologia e receitas com maçãs para quem tem consultado conteúdos de culinária.

Uma arquitetura comum de recuperação para personalização utiliza um modelo de duas torres embedding: a torre de consulta codifica a consulta e o contexto do utilizador, enquanto a torre de itens codifica os próprios itens e os metadados. A separação entre modo offline e online permite a recuperação por vizinho mais próximo aproximado; a sua latência continua a depender do codificador, do índice, dos filtros e do sistema serving.

A ficha de anúncio da Airbnb embeddings, do Pinterest OmniSearchSage, e Os sistemas de duas torres da Uber Exiba diferentes projetos de produção. A sua escala e os ganhos reportados pertencem a esses sistemas; o padrão transferível consiste numa torre de itens offline associada a uma torre de consultas/utilizadores online.

Os dados de clique apresentam viés de posição e viés de exposição. PAL Uma abordagem de desbiasamento consiste em utilizar a posição durante o treinamento e, em seguida, mantê‑la constante durante serving. Trata‑se de uma solução não universal; intervenções aleatórias, métodos de inversa propensidade e avaliação contrafactual podem ser mais adequados para outro produto.


Adaptação de domínio com consultas sintéticas

Um erro frequente na estratégia de busca consiste em supor que um modelo treinado com dados gerais da Web (como o MS MARCO) funcionará bem num domínio especializado. Trata‑se do problema out-of-domain (OOD).

LLMs consegue reduzir, mas não eliminar, o gargalo dos dados rotulados através do Generative Pseudo-Labeling (GPL, EmPars):

  1. Utilize o seu corpus de documentos específico da sua área de atuação.
  2. Prompt um LLM com o objetivo de “Gerar uma consulta de busca à qual este documento responderia”.
  3. Aproveite os pares sintéticos (consulta, documento) para ajustar com precisão o seu mecanismo de recuperação e o reranker.

Os pares sintéticos podem ser úteis quando as consultas reais são escassas, mas refletem o gerador e prompt. É necessário eliminá-los em duplicado, filtrar as consultas implausíveis e validá-los com o tráfego real não utilizado nos testes.

Uma sequência de experimentos

Adicione complexidade apenas quando a fase anterior revelar uma falha mensurável:

Rota Prática de Amadurecimento

Passo 1 (linha de base): implementar o BM25 ou o sistema léxico atual e criar um conjunto de consultas avaliadas. Registar a taxa de recuperação, o NDCG, a latência e as porções de falha.

Passo 2 (recuperação de candidatos): teste a recuperação densa e a fusão apenas se a linha de base não encontrar documentos relevantes. Ajuste o limite de candidatos em função da taxa de recuperação e do custo associado.

Passo 3 (precisão de classificação): adicione um codificador cruzado caso existam os candidatos corretos, mas estes apareçam na ordem errada. Escolha o tamanho da lista de seleção a partir de uma curva qualidade-latência.

Passo 4 (adequação ao domínio): faça o ajuste fino ou a destilação apenas após modelos genéricos apresentarem falhas estáveis específicas para esse domínio. Mantenha os julgamentos reais não utilizados separados dos dados de treino sintéticos.

Passo 5 (camada opcional e dispendiosa): realizar testes de forma lista a lista ou baseados em raciocínio reranking apenas quando a sua melhoria incremental se mantém após execuções repetidas e justifica o atraso, o custo, as questões de privacidade e a complexidade associada às soluções alternativas.


Direções de investigação a avaliar separadamente

O raciocínio rerankers e os agentes baseados em busca são promissores, mas respondem a questões diferentes das demonstradas no processo de busca por produto em cinco fases.

Raciocínio baseado em rerankers

Rank1 treina rerankers com rastros de raciocínio e obtém resultados excelentes no BRIGHT benchmark. Essas evidências são relevantes para recuperação de informações que exige grande capacidade de raciocínio, e não para previsões diretas na busca por produtos ESCI.

Para buscas jurídicas ou científicas, compare o processo de raciocínio rerankers com bases de referência baseadas em codificadores cruzados robustos e abordagens por lista, avaliando critérios como julgamentos de especialistas, citações, latência e consistência nas falhas.

Agentic busca

Search-o1 Analisa um modelo que realiza buscas adicionais durante o processo de resposta a perguntas em vários passos. Trata‑se de um problema de orquestração — geração de consultas, interrupção do processo, utilização de evidências e avaliação da resposta — e não de mais uma etapa reranking. Deve ser avaliado com base na correção final da tarefa e no suporte fornecido pelas citações, e não apenas por métricas de recuperação de informações.


Principais conclusões

  1. Trate a pilha como uma sequência de experimentos. Estabeleça uma linha de base léxica e um conjunto de consultas avaliadas antes de adicionar recuperação densa, fusão ou reranking.

  2. Meça o recall dos candidatos separadamente da precisão de classificação. Neste conjunto de dados ESCI, o Recall@100 atinge 0,842 após a fusão e mantém-se constante em ambas as fases de reranking.

  3. Utilize recuperação híbrida quando os erros são complementares. O RRF melhorou tanto o Recall@100 como o NDCG@10 na demonstração, mas outro corpus pode não justificar a utilização de dois índices.

  4. Adicione um codificador cruzado quando a lista de candidatos estiver correta, mas a ordem estiver incorreta. Escolha o número de candidatos com base numa curva de qualidade-latência medida.

  5. Trate LLM reranking como um experimento final opcional. O ganho de 0,072 em NDCG@10 obtido aqui pertence a esta prompt, ao modelo, às amostras e à escala de relevância aplicada. Execuções repetidas, análise rigorosa, soluções alternativas e os custos associados devem ser considerados na avaliação.

  6. Mantenha os tipos de evidências separados. Um resultado de artigo científico, um estudo de caso de um fornecedor, esta demonstração no portátil e um teste A/B em produção respondem a questões diferentes.

O código completo de pipeline encontra-se em github.com/slavadubrov/search-ranking-stack. Clone-o, execute-o, substitua por modelos e parâmetros diferentes e observe os resultados por si mesmo.

Referências

Artigos Científicos

Datasets e benchmarks

Modelos utilizados na demonstração

Ferramentas e plataformas

Referências do setor

Projeto de demonstração