Reranking

Reranking reorders a small candidate set after first-stage retrieval. BM25, dense retrieval, or hybrid search tries to avoid missing plausible candidates; the reranker spends more compute to improve the final order shown to the user or passed to a RAG context builder.

Scoring query-document pairs

A reranker scores each candidate conditioned on the full query-document pair:

The feature vector may include first-stage scores, exact-match indicators, freshness, authority, personalization, or policy features. A cross-encoder reranker folds and into one model input, which captures token interactions that dual-encoder vector indexes cannot precompute.

Worked example

This snippet starts from first-stage candidate scores and reranks them with freshness and exact-match features.

import numpy as np
 
candidates = ["docA", "docB", "docC"]
first_stage = np.array([0.91, 0.88, 0.82])
freshness = np.array([0.1, 0.9, 0.3])
exact_match = np.array([1, 0, 1])
score = 0.65 * first_stage + 0.25 * exact_match + 0.10 * freshness
print("first_stage", list(zip(candidates, first_stage.tolist())))
print("reranked", [(candidates[i], round(float(score[i]), 3)) for i in np.argsort(score)[::-1]])

Observed output:

first_stage [('docA', 0.91), ('docB', 0.88), ('docC', 0.82)]
reranked [('docA', 0.852), ('docC', 0.813), ('docB', 0.662)]

docB was strong in the first stage, but the reranker demotes it because it lacks exact-match evidence. This is the kind of trade-off that should be checked with ranking metrics by query class.

Caveats

Reranking cannot recover documents absent from the candidate set, so first-stage recall is a hard ceiling. Cross-encoders are latency-sensitive because they score each query-document pair separately. Training labels can also inherit position bias from an older ranker; offline gains should be verified against online experiments when user traffic is available.

References