FDEInterviews logo
RAG & Agent System Design / 07
medium★ EssentialGleanCohereOpenAI

Design a hybrid retrieval stack: BM25, vectors, and a reranker. What does each stage rescue?

The retrieval design question with a precise rubric: candidates who can say what each stage rescues, and what it costs in latency, clear it. Includes the RRF detail and the latency budget interviewers ask for.

Updated Sep 2026 · Grounded in real Forward Deployed Engineer interview loops and written to a senior-engineer editorial bar.

TL;DR: Run BM25 and dense retrieval in parallel to maximize recall, fuse the two ranked lists with Reciprocal Rank Fusion, then rerank the fused shortlist with a cross-encoder for precision. Defend every stage with an ablation table and a named latency budget.

How to approach it

Structure as a funnel with a job per stage: recall stages (BM25 + dense, run in parallel, cast a wide net) then a precision stage (reranker, reorder the net's contents). For each stage be ready with: what it rescues, what it costs, and how you'd measure its contribution.

A strong answer

Stage 1a, BM25 over an inverted index. Rescues: exact identifiers (SKU-88210), error codes, names, acronyms, rare in-domain jargon the embedding model has never seen. Cost: nearly free (<10ms on Elasticsearch/OpenSearch at enterprise scale). Failure: vocabulary mismatch, where paraphrased questions miss.

Stage 1b, dense retrieval (ANN over embeddings, HNSW index). Rescues: paraphrase, synonyms, conceptual queries, multilingual matching. Cost: ~10–50ms ANN lookup plus one embedding call. Failure: "related but wrong," semantically adjacent chunks that differ in the detail that matters (wrong year, wrong customer, wrong version).

Fusion: take top 50–100 from each leg and merge with Reciprocal Rank Fusion, scoring each doc by Σ 1/(60 + rank). RRF needs no score calibration (BM25 and cosine scores live on incomparable scales), which is why it's the production default over weighted-sum schemes that need tuning per corpus.

Hybrid retrieval: what each stage rescues 1 Query one string, several intents 2 Two legs, in parallel BM25 and dense ANN 3 Top 50 to 100 each recall first, precision later 4 Fuse with RRF rank-based, no calibration 5 Cross-encoder rerank query and doc read together 6 Top 3 to 8 context for the generator BM25 rescues exact identifiers, error codes and rare in-domain jargon. Dense rescues paraphrase, synonyms and conceptual queries. Each one's failure mode is the other's strength. score = sum of 1 / (60 + rank) #1 BM25 only: ~ 0.0164 #3 BM25 and #5 dense: ~ 0.0313 Consensus between legs nearly doubles a single leg's top hit, and a chunk both philosophies agree on is rarely garbage. A cross-encoder reads the query and the document together, which is why it is accurate and why it can only ever run on a shortlist.

Step 2 is one box because the two legs run at the same time, not one after the other. The stages are ordered by what they optimize: recall up to step 4, precision after it.

rendering diagram…

Working RRF once, with the constant at its standard k=60, shows why it behaves the way it does. A document ranked #1 by BM25 but absent from the dense leg scores 1/61 ≈ 0.0164. A document ranked #3 by BM25 and #5 by dense scores 1/63 + 1/65 ≈ 0.0313, nearly double, so consensus between legs beats a single leg's top hit. That is usually the behavior you want: a chunk both retrieval philosophies agree on is rarely garbage, while a single-leg #1 might be a BM25 keyword fluke or an embedding near-miss. The 60 is a damping constant, not magic: it flattens the difference between adjacent ranks (rank 1 scores 0.0164, rank 10 scores 0.0143, only ~15% apart) so one leg's confident-but-wrong head cannot steamroll the merge. All of this needs only ranks, never the raw scores, which is exactly why no per-corpus calibration exists to go stale.

Stage 2, cross-encoder reranker (Cohere Rerank, BGE-reranker, or similar) scores each (query, chunk) pair jointly with full attention, instead of comparing pre-computed vectors. Rescues: fine-grained relevance the bi-encoder bottleneck destroys. It can see that the query's "termination for convenience" doesn't match the chunk's "termination for cause." Cost: this is the expensive stage at ~50–300ms for 50–100 candidates, so it only ever sees the fused shortlist, never the corpus. Typical payoff: +5–15 points NDCG/recall-in-top-5 over fusion alone, routinely the best quality-per-engineering-hour buy in the stack.

Then top 3–8 reranked chunks go to the generator. Total retrieval budget: ~100–400ms, dominated by the reranker. That is fine for chat, worth trimming (smaller rerank candidate set) for stricter latency SLAs.

Measure each stage's contribution by ablation on a golden set: recall@10 for BM25 alone, dense alone, hybrid, hybrid+rerank, sliced by query type (ID-lookups vs natural language). That table is also your customer-facing justification for every component's existence.

The shape of that ablation:

ConfigurationRecall@10Latency
BM25 aloneLow (misses paraphrase)<10ms
Dense aloneMedium (misses exact IDs)~10–50ms
Hybrid (RRF)High~60ms
Hybrid + rerankerHighest (+5–15 NDCG/recall@5)+50–300ms

What interviewers probe next

"Where do metadata filters go?" Pre-filter both legs (date, source, ACL) inside the index, not post-filter, or your top-k drains away. "When would you skip the reranker?" Hard latency budgets or tiny corpora where k=5 recall is already high. "Query rewriting?" A stage 0: expand acronyms, decompose multi-part questions, cheap and complementary. "Can a long-context model replace all this?" Stuffing 100 chunks costs ~10–50x in tokens and reintroduces lost-in-the-middle, so retrieval precision still pays.

Common mistakes

Pure-vector answers, the giveaway of tutorial experience. Hand-waving fusion ("combine the scores") without knowing why RRF exists. Putting the reranker over the whole corpus (it's O(candidates), not an index). And presenting the stack without an ablation plan: at retrieval-centric companies like Glean, every stage must earn its latency with a measured recall delta.

Key takeaways

  • Each stage has a distinct job: BM25 rescues exact tokens, dense rescues paraphrase, the reranker rescues fine-grained precision.
  • RRF fuses incomparable score scales without per-corpus tuning, which is why it beats weighted-sum in production.
  • Retrieve wide, rerank narrow, and defend top-k and latency with an ablation table sliced by query type.
That one was free — and so are 10 answers per topic without an account. Signing in doubles that to 20, opens the Plus lessons in the courses, and remembers which topics you keep getting wrong.no card · Google sign-in · nothing to cancel
HOW DID IT GO?
0
READING SIGNED OUT

Signing in doubles your free answers, from 10 to 20 per topic, and the site starts remembering you: mastery per topic, bookmarks, and a next-focus recommendation. Free, no card.

Sign in free
UP NEXT ON YOUR JOURNEY
FEDITOR'S NOTE

The reranker is the stage with the worst latency-to-quality ratio, so the budget follow-up is the real test. A cross-encoder over 100 candidates can add tens to hundreds of milliseconds, and the answer they want is 'retrieve wide, rerank narrow' with a defended top-k. Candidates who bolt on a reranker without naming what it costs are describing a demo, not a production stack.

DISCUSSION · 0

No comments yet — be the first to share your approach.