Vibe Engines
YouTube
AI System Design

Design a Reranking Service

Step 1 / 9

Learn AI system design by building a reranking service — the precision tier of two-stage retrieval for search and RAG — step by step.

The numbers to beatstage 1retrieve ~100+ (recall)stage 2rerank to top-N (precision)costexpensive model on few

The whole design, in writing

Learn AI system design by building a reranking service — the precision tier of two-stage retrieval for search and RAG — step by step. An interactive guide covering why vector similarity alone gets the top result wrong, the retrieve-wide-then-rerank-narrow pattern, cross-encoder scoring, the top-K latency/cost tradeoff, serving and caching, hybrid retrieval fusion, and the unhappy paths (recall gates precision, latency budget, domain tuning).

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

What is a reranker for?

Vector search is great at finding relevant documents but mediocre at ordering them — the genuinely best passage often lands at rank 5, not rank 1. For search that's a worse experience; for RAG it's worse still, because the LLM only reads the top few passages, so a bad ordering feeds it weak context and the answer suffers.

Clienta query
New in this step: Client.

A reranking service is the precision tier of two-stage retrieval: a fast first stage retrieves a wide candidate set (high recall), then a slower, more accurate reranker reorders the top of it (high precision), so the best passages rise to the top. It's the cheapest big win in retrieval quality — and the key to good RAG context.

What the new pieces do

Clientclient
A search or RAG request that wants the *best* passages at the very top — not just a bag of roughly-relevant ones.

Step 1 · Good recall, rough order

Why vector search isn't enough

Embedding-based (bi-encoder) retrieval embeds the query and every document separately, then compares vectors. That's fast and finds relevant docs — so what's the weakness that makes the top result often wrong?

Clienta query
The system as it stands at this step.

Bi-encoder vector search finds relevant docs fast. Why is its top-1 often not the best?

  1. A bi-encoder turns the query and each document into vectors separately and compares them, which is fast and scalable (you can precompute doc vectors) but never lets the model look at the query and a specific document together, so it misses fine-grained relevance signals. It reliably finds relevant candidates but orders them imperfectly — the best one is often not ranked first.

  2. Dimension isn't the issue. The limitation is that query and document are encoded independently, so subtle query-document interactions that decide true relevance are never modeled — great recall, rough precision.

  3. Bi-encoders scale extremely well (that's their point — precompute doc vectors, fast nearest-neighbor). Their weakness is ordering precision, because they never score the query and a document jointly.

A bi-encoder embeds the query and each document independently and compares vectors. That's why it scales (doc vectors are precomputed, search is fast nearest-neighbor) — but it never looks at the query and a specific document together, so it misses the fine-grained interactions that decide true relevance. Result: excellent recall (it finds the relevant docs) but imperfect ordering (the best one often isn't first).

Why this piece earns its place

The gap between finding and ordering survives in production because nothing reports it. A vector search hands back a full result set with plausible scores whether the best passage sits at position one or position sixty — no error, no latency spike, no dashboard that moves. The score is no help either: cosine similarity is comparable within one query and close to meaningless across queries, because the number depends on where that particular query happens to land in the embedding space, so there is no distribution to threshold on. It also decays without anyone touching the code. Every document you add is another chance for something plausible to sit above the right answer, so the order a query got at launch is not the order it gets a year and a corpus later, and the release notes for that regression are empty. The structural problem is the worse one. Document vectors are written at index time, so first-stage ordering is a property of how the corpus was built, not a knob available at query time — improving it means re-embedding everything you have. That asymmetry, not the accuracy gap on its own, is what makes a second stage worth paying for.

Step 2 · Two stages

Retrieve wide, then rerank narrow

You want the accuracy of scoring the query against each document jointly, but that model is far too slow to run over a whole corpus. How do you get both scale and precision?

Clienta queryRetrieval APItwo-stage
New in this step: Retrieval API.

A joint query-document scorer is accurate but too slow for the whole corpus. How do you use it?

  1. Scoring the query jointly against millions of documents is computationally impossible at query time — that's exactly why you can't use it as the first stage. You run it only on a small candidate set.

  2. That leaves the top-result-often-wrong problem, which especially hurts RAG. The fix isn't to abandon precision but to apply the expensive, accurate model to only the few candidates that matter.

  3. Stage one uses the cheap bi-encoder/keyword retrieval to pull, say, the top 100–200 candidates (high recall, low cost). Stage two runs the expensive joint scorer on only those candidates to reorder them precisely. You get corpus-scale recall AND top-of-list precision, paying the expensive model only on a small set.

Split into two stages. Stage 1 — retrieve wide: use fast, cheap retrieval (vector ANN + keyword) to pull a broad candidate set (e.g. top 100–200) with high recall. Stage 2 — rerank narrow: run the expensive, accurate joint scorer on only those candidates to reorder them with high precision, returning the sharpened top-N. You get corpus-scale recall and top-of-list precision, paying the costly model on a small set instead of the whole corpus.

Why this piece earns its place

Splitting the pipeline creates an interface, and two properties of it are worth settling before anything is built. The first is the unit. If the index holds passages but the reranker is handed whole documents, the stages are counting different things, and the candidate count stops describing how much distinct material is on the table — the same number is a wide net when the hits come from different sources and a narrow one when most of them are cut out of the same manual. Pick the unit once, make the first stage deduplicate to it, or top-K stops meaning anything you can reason about. The second is the payload. A cross-encoder reads text, not identifiers, so either the first stage carries the text of every candidate in its response, or the second stage fetches it from the document store — a fan-out on the hot path that nobody costs at design time and that shows up first at load test. Both are workable once chosen. The reward for taking the seam seriously is that the first stage becomes swappable: add a keyword index, a recency filter, a second retriever, and nothing downstream is retrained, because the second stage wants only a query and some text.

  • stage 1retrieve ~100+ (recall)
  • stage 2rerank to top-N (precision)
  • costexpensive model on few

What the new pieces do

Retrieval APIgateway
Runs the two-stage pipeline: retrieve a wide candidate set fast, then rerank the top of it for precision.

Step 3 · Cast a wide net

First-stage retrieval (recall)

The first stage's one job is to make sure the truly relevant documents are somewhere in the candidate set — because the reranker can only reorder what it's given. How do you maximize recall cheaply?

Retrievervector + keywordIndexvectors + terms
New in this step: Retriever, Index.

Run fast, high-recall retrieval: vector (ANN) search for semantic matches plus keyword/BM25 for exact terms, fused (hybrid retrieval) so you catch both meaning and specific words/names/IDs. Pull a generous candidate set (top 100–200) — wide enough that the best passages are almost certainly included, cheap enough to run over the whole index. This stage optimizes recall, not order; getting the ordering right is the reranker's job.

Why this piece earns its place

Fusing two result lists is where hybrid retrieval usually goes wrong. BM25 scores and cosine similarities do not share a scale — the first is an unbounded sum of corpus statistics, the second is bounded and query-dependent — so a weighted sum needs both normalized first, and the normalization is the unstable part: it is fitted to a corpus that keeps growing, and it drifts as the corpus does. Rank fusion sidesteps the problem by throwing the scores away and combining positions instead, which is why reciprocal rank fusion is the default rather than a clever arithmetic blend. The weighting that does earn its keep is which list to lean on, and that is a per-query judgement rather than a global constant — so it belongs in the request, set by a cheap classifier in front of the retriever, not frozen in the index config. Then two arithmetic traps. The two lists overlap, so each retriever has to be asked for more than the fused set you want, or the wide net is narrower than the config reads. And a metadata filter applied after the ANN search — tenant, permissions, recency — shrinks that set again, so a request can reach the reranker holding a handful of candidates while the config still says top 100–200. Filter inside the index, or over-fetch on purpose.

  • vector + keywordhybrid
  • top 100–200wide net
  • recallbest doc must be present

What the new pieces do

Retrieverservice
First stage: fast, high-recall retrieval (vector ANN + keyword/BM25, fused) that casts a wide net for candidates.
Indexdata
The vector (and keyword) index the first stage queries to pull the top candidate documents cheaply.

Step 4 · Score them properly

The cross-encoder reranker (precision)

Now reorder the candidates by true relevance to the query. What model does this, and why is it accurate but expensive?

Rerankercross-encoderRerank Modelcross-encoder
New in this step: Reranker, Rerank Model.

Use a cross-encoder: it takes the query and a candidate together as one input and outputs a precise relevance score, because the model attends over both jointly and captures the fine-grained interactions a bi-encoder can't. Run it on each of the ~100 candidates, then sort by score to get the final order. It's far more accurate than vector similarity — and far more expensive, since it's a full model pass per candidate (you can't precompute it, as the score depends on the specific query). That cost is exactly why it runs only on the small candidate set, not the corpus.

Why this piece earns its place

Two things about a cross-encoder only show up once it is serving traffic. The first is that the input window is a shared budget. Query and candidate are concatenated into one sequence with a fixed limit, so a long query — the user's whole question, plus whatever the product prepends to it — spends length the candidate then does not get, and what falls off the end is the tail of the candidate. Track the share of pairs that hit the limit; it is the one number that tells you whether the model is reading your candidates or the first half of them. The second is what a real score buys beyond sort order. Because a cross-encoder judges a pair rather than a point in space, its output can be calibrated against labelled data into a usable cutoff — return the candidates that clear the bar instead of always returning N, which for RAG is worth more than the ordering itself. But that calibration belongs to one checkpoint. A new model's scores sit on their own scale, so a threshold carried across a version bump either floods the prompt with weak passages or starves it. Version the cutoff with the model, and re-fit it before the swap rather than after.

  • cross-encoderquery+doc jointly
  • per candidatea model pass each
  • sortby relevance score

What the new pieces do

Rerankerservice
Second stage: scores each (query, candidate) pair together with a slow but accurate cross-encoder, then sorts.
Rerank Modeldata
The cross-encoder that reads the query and a candidate jointly and outputs a precise relevance score.

Step 5 · How many, how fast

The top-K latency/cost tradeoff

Reranking cost is linear in the number of candidates — rerank 100 and it's 10× the cost/latency of reranking 10. Rerank too few and you might miss the best doc (it was at rank 60); rerank too many and you blow the latency budget. What's the knob?

Retrieval APIretrieve → rerankRerankercross-encoderTop Resultsprecise order
New in this step: Top Results.

Reranking cost scales with candidate count. How do you choose how many to rerank?

  1. The number of candidates reranked (top-K) is the core dial: large enough that the best passage is almost always in the set (recall), small enough to fit the latency and cost budget (each is a cross-encoder pass). You tune it empirically and batch all K candidates through the GPU together for throughput.

  2. Reranking everything the first stage returns can blow the latency budget if that set is large, with diminishing returns past a point. You pick a top-K that captures the best docs without over-paying.

  3. Reranking too few defeats the purpose — if the best passage was at rank 30 in stage one, a reranker that only sees the top 2 can never surface it. You need enough candidates to include it.

Tune top-K — how many candidates you rerank — as the core dial. It must be large enough that the best passage is almost always within it (so the first stage's recall is realized) but small enough to fit the latency and cost budget, since each candidate is a full cross-encoder pass. Batch all K candidates through the GPU together for throughput, keep the reranker model warm, and pick K empirically (often tens to a couple hundred). This knob directly trades quality against speed and cost.

Why this piece earns its place

Linear in candidates is the billing model, not the latency model. The K pairs go through as one padded batch, so a request's wall time is set by the longest sequence in that batch rather than the average, and the batch dimension runs in parallel until the hardware is full. Two consequences follow, and they point at different knobs. Inside a request, one long candidate slows every other candidate beside it, so tail latency tracks the longest document you admit — which means a hard truncation length, or bucketing candidates by length, buys latency that trimming K does not. Across the fleet, the work is K model passes per query however fast any single pass is, so capacity is K multiplied by queries per second and raising K is a hardware decision written as a one-line config edit. That is the rule for which knob to reach for: when one request misses the budget, cap length first; when the fleet cannot hold the rate, a smaller K is the honest fix, and it is the one that costs quality. Under light traffic K looks free and is quietly spending money; once the GPUs saturate, the queue forms and it starts spending latency too. Load-test at the traffic you actually have.

  • top-Kthe core dial
  • linear costa pass per candidate
  • batch KGPU throughput

What the new pieces do

Top Resultsdata
The final, sharply-ordered top-N passages returned to search, or handed to the LLM as RAG context.

Step 6 · Serve it in the path

Serving, caching & fusion

The reranker lives on the query hot path (search or RAG), so it must be fast and cheap to operate. How do you serve it well?

Retrieval APIretrieve → rerankCachequery → order
New in this step: Cache.

Run reranking as a service on the retrieval path: batch candidates, keep the model warm/autoscaled (GPU), and cache reranked orders for repeated queries so hot searches skip the cross-encoder entirely. Fuse first-stage signals (vector + keyword scores) before reranking to feed the reranker a strong candidate set, and optionally blend the rerank score with other signals (recency, popularity, business rules) for the final order. The output feeds search results or becomes the top passages for RAG.

Why this piece earns its place

The cache is the piece that looks trivial and leaks. A reranked order is reusable only if everything that produced it is in the key: the normalized query, the filter set, the tenant, K, and the model version. Leave the tenant out and one customer is served an order computed over another's documents. Leave the model version out and a deploy quietly serves last week's ranking until entries age out. And these entries do not go stale when the query changes — they go stale when the corpus does, an event the cache never sees, so a TTL matched to how fast the index moves is the honest answer rather than real invalidation. Then size it for the consumer you actually have, because hit rate splits hard between them and the split is not obvious from the outside. RAG is the one to check first: its questions arrive as whole sentences, phrased differently every time, so a cache that pays for itself in front of a search box earns close to nothing on that path, and the same money buys more as batch headroom and a warmer pool. Two consumers, one service, and only one of them should be capacity-planned around cache hits.

  • cacherepeated queries
  • batch + warmaffordable serving
  • fuserelevance + rules

What the new pieces do

Cachecache
Caches reranked results for repeated queries so hot searches skip the expensive cross-encoder pass.

Step 7 · Recall gates precision

The reranker can only sort what it's given

It's tempting to think a great reranker fixes retrieval. It can't. What's the hard limit, and what does it mean for how you invest?

ClientRetrieval APICacheRetrieverIndexRerankerRerank ModelTop Results
The system as it stands at this step. · swipe to pan the diagram

A reranker only reorders the candidates it receives — it cannot conjure a relevant document the first stage failed to retrieve. So if the right passage isn't in the candidate set, no reranker can surface it: recall gates precision. This means the first stage's job (don't miss the relevant docs) and the second stage's job (sort them to the top) are complementary and both essential. Invest in first-stage recall (hybrid retrieval, enough candidates) and second-stage precision (a good reranker, right top-K) — neither substitutes for the other.

Why this piece earns its place

This is measurable, and the measurement is the part worth carrying into the interview. Run the first stage alone over a judged query set and compute recall at exactly the K you rerank — not at the candidate count, at K. That single number is the reranker's ceiling for top-one accuracy, and no second stage can beat it. Compute it before you build, because it prices the work: a ceiling far above what you ship today says the ordering is where your quality is hiding, while a ceiling sitting barely above it says every hour spent on the reranker is capped by the first stage until the first stage moves. The same instrument settles the arguments afterwards. A wrong result is either a recall miss or an ordering miss, and the output alone cannot tell you which, so log each result's rank in both the pre-rerank and post-rerank orders. Never in the list at all: widen K or fix the first stage, and reranker work is wasted. Deep in the list before and still deep after: the reranker is the problem. Two repairs, opposite directions, and one log line keeps you off the wrong one.

  • rerankerreorders only
  • recallgates precision
  • both stagesessential

Step 8 · The sharp edges

Latency, domain fit & cost

Real reranking must fit a latency budget on the hot path, generalize (or not) to your domain, and stay cost-effective while genuinely improving quality.

ClientRetrieval APICacheRetrieverIndexRerankerRerank ModelTop Results
The system as it stands at this step. · swipe to pan the diagram

Hold the latency budget with a tuned top-K, GPU batching, a warm autoscaled reranker, and caching — accepting a smaller K under tight budgets. Watch domain fit: off-the-shelf rerankers may underperform on specialized domains, so evaluate on your data and fine-tune if needed. Manage cost (linear in K) with caching, right-sized models, and reranking only when it helps (skip for trivial queries). And measure: confirm reranking actually lifts your retrieval metrics (nDCG, top-k accuracy) and downstream RAG answer quality — it usually does, which is why it's the highest-ROI retrieval upgrade.

Why this piece earns its place

Fine-tuning on your domain is the right instinct, and labels are what stop it. You have no relevance judgments, but you do have clicks — and citations from the RAG side — so those become the training data, and they were produced by the order you already showed. People click what is near the top. Train on that and you teach the reranker to reproduce your current ranking: it scores well offline and moves nothing in production. Usable labels mean breaking the loop on purpose — interleave or randomize a slice of traffic, or buy human judgments over queries you genuinely receive rather than queries someone imagined. The second-order point is which artifact deserves the care. Everything else here is replaceable: the checkpoint, the embedding model, the fusion weighting, K. Each of those swaps is either a gain or a regression, and nothing on the serving path can tell the two apart — only the same judged queries, run before and after. So build that set once, version it apart from the corpus, and keep it frozen between releases; otherwise a bad week and a changed set look identical.

You did it

You just designed a reranking service.

ClientRetrieval APICacheRetrieverIndexRerankerRerank ModelTop Results
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • Vector (bi-encoder) search has great recall but rough ordering — the best passage often isn't first.
  • Two-stage retrieval: retrieve wide (fast, recall) then rerank narrow (slow, precise).
  • First stage = hybrid (vector + keyword) high-recall retrieval pulling a wide candidate set.
  • Second stage = a cross-encoder scoring each (query, candidate) jointly for precise relevance.
  • Top-K is the core dial: enough candidates to contain the best doc, few enough for the latency/cost budget.
  • Serve it batched, warm and cached on the query path; fuse signals; feed the top passages to RAG.
  • Recall gates precision — a reranker only reorders what retrieval found, so both stages are essential.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Why does fast vector search need a reranker at all?

    Because embedding-based (bi-encoder) retrieval is optimized for scale, not for fine-grained ordering. A bi-encoder encodes the query and each document into vectors independently and then compares those vectors, which is what makes it fast enough to run over millions of documents — you can precompute every document's vector ahead of time and do a cheap nearest-neighbor search at query time. But precisely because the query and document are encoded separately, the model never gets to look at them together, so it can't capture the subtle query-document interactions that determine which of several relevant documents is actually the best answer. The practical consequence is that vector search has excellent recall (the relevant documents reliably show up somewhere in the results) but imperfect precision at the top (the genuinely best passage often lands at rank 5 instead of rank 1). For plain search that's a worse experience, and for RAG it's worse still, because the LLM only reads the top few passages, so a rough ordering feeds it weaker context and degrades the answer. A reranker fixes the ordering by scoring the query against each candidate jointly.

  2. What is two-stage retrieval and why is it structured that way?

    Two-stage retrieval separates the job of finding relevant documents from the job of ordering them precisely, because the models good at each have opposite scaling properties. The accurate way to judge relevance is to score the query and a document jointly with a cross-encoder, but that requires a full model pass per document and is far too slow to run over an entire corpus at query time. So stage one uses cheap, fast retrieval — a bi-encoder vector search, usually fused with keyword/BM25 — to pull a wide candidate set (say the top 100 to 200 documents) with high recall at low cost, ensuring the best passages are almost certainly somewhere in that set. Stage two then runs the expensive, accurate cross-encoder on only those candidates, reordering them by true relevance to produce the final sharply-ordered top-N. This structure gives you both properties that seemed mutually exclusive: corpus-scale recall from the cheap first stage, and top-of-list precision from the expensive second stage, with the costly model only ever applied to a small, affordable candidate set instead of the whole corpus.

  3. Cross-encoder vs bi-encoder — what's the actual difference and why does it matter?

    They differ in whether the query and document are processed together or separately, which drives a fundamental speed-versus-accuracy tradeoff. A bi-encoder encodes the query and each document into separate vectors and measures similarity between them; because the document vectors don't depend on the query, you can precompute and index them all in advance, making retrieval fast and scalable to millions of documents — but the model never sees a query and a specific document jointly, so it misses fine-grained relevance signals and orders results only coarsely. A cross-encoder instead takes the query and a candidate document concatenated as a single input and runs the full model over them together, letting it attend across both and produce a much more precise relevance score — but that score depends on the specific query, so it cannot be precomputed, and each candidate requires its own model pass, making it far too expensive to run over a whole corpus. The reranker is exactly the cross-encoder applied only to the small shortlist from the first stage: it delivers the accuracy you couldn't afford across everything, made affordable by scoring just a hundred or so candidates rather than millions.

  4. How do you choose how many candidates to rerank (top-K)?

    Top-K is the central tuning knob of a reranking system because reranking cost and latency scale linearly with the number of candidates — each candidate is a full cross-encoder pass — so reranking 100 documents costs ten times as much as reranking 10. The tension is that K must be large enough to realize the first stage's recall: if the genuinely best passage was ranked, say, 60th by the fast retriever, then a reranker that only sees the top 10 candidates can never recover it, so too-small a K throws away good documents the first stage actually found. But K must also be small enough to fit the latency and cost budget on the query hot path, and beyond some point adding more candidates yields diminishing returns because the best documents are already included. So you pick K empirically for your data and budget — commonly somewhere from a few dozen to a couple hundred — measuring where retrieval quality plateaus, and you batch all K query-document pairs through the GPU together and keep the model warm so the expensive stage still meets interactive latency. The result is a deliberate, measured balance between quality (recall realized) and speed/cost.

  5. Why does "recall gates precision," and what does it mean for where you invest?

    It means the reranker can only reorder the candidates it is given and can never introduce a document that the first stage failed to retrieve — so if the relevant passage isn't in the candidate set, no reranker, however good, can surface it. The two stages therefore have complementary, non-substitutable jobs: the first stage's job is recall (make sure the relevant documents are somewhere in the candidate set), and the second stage's job is precision (sort those candidates so the best ones rise to the top). A perfect reranker over a candidate set that's missing the answer still fails, and perfect recall left in a jumbled order still buries the best document — so the overall system is only as strong as its weaker stage. The practical implication is that you must invest in both: strong first-stage recall through hybrid retrieval (vector plus keyword) and a generous candidate count, and strong second-stage precision through a good reranker and a well-tuned top-K. It's a common mistake to think a great reranker compensates for weak retrieval; it cannot, because it never sees what retrieval missed.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Vector (bi-encoder) search needs a reranker because it…

    It never scores query+doc jointly, so the best passage often isn't ranked first.

  2. Two-stage retrieval is…

    The expensive accurate model runs only on the small candidate set, not the corpus.

  3. The reranker uses a cross-encoder, which…

    Joint scoring captures fine-grained relevance; cost is linear in candidates, so run it on few.

  4. Top-K (candidates reranked) must be…

    Cost is linear per candidate; batch K on the GPU and tune empirically.

  5. "Recall gates precision" means…

    If the best doc isn't in the candidate set, no reranker can surface it.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Retrieve wide, rerank narrow: a fast first stage casts a wide candidate net, a slower second stage reorders the top of it.
  • First-stage recall: hybrid vector + keyword retrieval pulls a generous candidate set (top 100–200).
  • Rerank for precision: a cross-encoder scores each (query, candidate) jointly and sorts to a sharp top-N.
  • Tune top-K: enough candidates to contain the best passage, few enough to fit the latency/cost budget.
  • Serve on the path: batch and warm the reranker, cache reranked orders for repeat queries, feed the top passages to RAG.

The qualities that shape everything

Each one names the mechanism that buys it.

Corpus-scale recall AND top-of-list precision
Two-stage retrieval: cheap wide retrieval for recall, then an expensive accurate scorer run on only the small candidate set for precision.
Don’t miss the relevant document
Fast high-recall hybrid retrieval — vector ANN plus keyword/BM25, fused — pulling a wide candidate set so the best passages are almost certainly present.
Order by true relevance
A cross-encoder reads the query and each candidate together, capturing the fine-grained interactions a bi-encoder can’t, then sorts by score.
Fit the latency/cost budget
Tune top-K (recall realized vs cost, since each candidate is a model pass), batch the K pairs through the GPU, and keep the model warm.
Affordable on the hot path
Run reranking as a batched, warm, autoscaled service and cache reranked orders for repeated queries so hot searches skip the cross-encoder.
A reranker can’t fix weak retrieval
Recall gates precision — the reranker only reorders what it’s given, so invest in first-stage recall and second-stage precision both.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Two-stage retrieve-then-rerank over running the accurate scorer over every document

Scoring the query jointly against millions of documents is computationally impossible at query time; you run the expensive model only on a small candidate set.

A cross-encoder reranker over trusting the bi-encoder similarity order

A bi-encoder encodes query and document independently and never models their joint interaction — great recall, imprecise ordering, so the best passage often isn’t ranked first.

Hybrid vector + keyword retrieval over vector search alone

Vector catches semantic matches but fumbles rare terms, codes, and names; fusing keyword/BM25 maximizes recall so the candidate set almost always contains the answer.

A tuned top-K over reranking the whole set (or just the top 1–2)

Cost is linear per candidate, so reranking everything blows the budget while reranking too few can’t recover a great doc ranked 60th — K balances recall realized against latency/cost.

Invest in both stages over assuming a great reranker fixes retrieval

Recall gates precision — a reranker only reorders what it’s given and can’t conjure a document the first stage missed, so the system is only as good as its weaker stage.

The answer, out loud

What a strong answer to “Design a Reranking Service” sounds like, first question to last trade-off. It is about 6 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Pin down what “good” means

    Before I draw anything, I want to pin down what this service is for. The clients are search and RAG, and both already get relevant documents back from vector search. The complaint is the order: the genuinely best passage lands at rank five, not rank one. For search that’s a worse page; for RAG it’s worse still, because the model only reads the top few passages. So the number users feel is what sits at the very top. The real question is how to get precision at the top of the list without paying for it across the whole corpus.

  2. 3–8 min

    Why vector search orders badly

    First, why the order is rough. Vector search uses a bi-encoder: it turns the query and each document into vectors separately, then compares them. That's why it scales — document vectors are precomputed and search is a fast nearest-neighbor lookup. But the model never looks at the query and a specific document together, so it misses the fine-grained signals that decide which of several relevant documents is actually best. Great recall — the right documents show up — but imprecise ordering.

    Built in step 1: Why vector search isn't enough
  3. 8–13 min

    Retrieve wide, rerank narrow

    The accurate fix is a model that reads the query and a document together, but running that over millions of documents at query time is computationally impossible. Giving up on precision leaves RAG with weak context. So I’d split the work into two stages. Stage one retrieves wide and cheap — the top hundred to two hundred candidates, tuned for recall. Stage two runs the expensive, accurate scorer on only those, to reorder them for precision. We get corpus-scale recall and top-of-list precision, and pay for the costly model on a small set.

    Built in step 2: Retrieve wide, then rerank narrow
  4. 13–19 min

    Cast a wide net first

    The first stage has one job: make sure the right documents are somewhere in the candidate set, because the reranker can only reorder what it’s given. I’d run hybrid retrieval — vector search for meaning plus keyword search, BM25, for exact terms — and fuse the two lists, for example with reciprocal rank fusion. Vector search alone fumbles rare terms, codes and names. This stage optimizes recall, not order; the cost is running and fusing two retrievers instead of one.

    Built in step 3: First-stage retrieval (recall)
  5. 19–25 min

    Score each candidate properly

    The second stage is a cross-encoder. It takes the query and one candidate as a single input, attends across both, and outputs a relevance score; then I sort by that score. It’s far more accurate than vector similarity and far more expensive: a full model pass per candidate, and none of it can be precomputed, because the score depends on this particular query. That cost is the whole reason it only ever sees the shortlist.

    Built in step 4: The cross-encoder reranker (precision)
  6. 25–31 min

    Top-K is the dial

    That makes top-K — how many candidates I rerank — the core knob. Cost is linear: reranking a hundred costs ten times as much as reranking ten. Reranking just the top one or two can’t recover a great document the first stage put at rank thirty. Reranking everything stage one returns can blow the latency budget for diminishing returns. So I’d tune K empirically on our data — often tens to a couple hundred — batch all K pairs through the GPU together, and keep the model warm.

    Built in step 5: The top-K latency/cost tradeoff
  7. 31–36 min

    Serve it on the hot path

    The reranker sits on the query hot path, so I’d run it as its own service: batched, warm and autoscaled on GPUs. Queries repeat, so I’d cache reranked orders and let hot searches skip the cross-encoder entirely. Before reranking I fuse the first-stage scores to hand it a strong candidate set, and afterwards I can blend the rerank score with recency, popularity or business rules. The output is search results, or the top passages for RAG.

    Built in step 6: Serving, caching & fusion
  8. 36–42 min

    What I’d watch, and how it fails

    On the dashboard: latency against the budget, and whether reranking actually lifts our ranking metrics — nDCG, which rewards putting the best results highest, and top-k accuracy — plus downstream RAG answer quality. Off-the-shelf rerankers can underperform on specialized domains, so I’d evaluate on our own data. Then two failures. If the reranker goes down, results still come back in the first stage’s rough order — degraded, not broken. If first-stage retrieval breaks, nothing downstream can save it: recall gates precision, because a reranker can’t surface a document it was never given.

  9. 42–45 min

    Close on the trade-off

    To close, in one breath: hybrid retrieval casts a wide net for recall, a cross-encoder reranks a tuned top-K for precision, and it’s served batched, warm and cached on the hot path. The system is only as good as its weaker stage, and every knob trades quality against latency and cost. With more time I’d fine-tune the reranker on our domain, skip reranking for trivial queries where it doesn’t help, and work out how to blend relevance with product signals.

What this teaches

Learn AI system design by building a reranking service — the precision tier of two-stage retrieval for search and RAG — step by step. An interactive guide covering why vector similarity alone gets the top result wrong, the retrieve-wide-then-rerank-narrow pattern, cross-encoder scoring, the top-K latency/cost tradeoff, serving and caching, hybrid retrieval fusion, and the unhappy paths (recall gates precision, latency budget, domain tuning).

Key takeaways

  • Vector (bi-encoder) search has great recall but rough ordering — the best passage often isn't first.
  • Two-stage retrieval: retrieve wide (fast, recall) then rerank narrow (slow, precise).
  • First stage = hybrid (vector + keyword) high-recall retrieval pulling a wide candidate set.
  • Second stage = a cross-encoder scoring each (query, candidate) jointly for precise relevance.
  • Top-K is the core dial: enough candidates to contain the best doc, few enough for the latency/cost budget.
  • Serve it batched, warm and cached on the query path; fuse signals; feed the top passages to RAG.
  • Recall gates precision — a reranker only reorders what retrieval found, so both stages are essential.

Concepts covered

  • What is a reranker for?
  • Why vector search isn't enough
  • Retrieve wide, then rerank narrow
  • First-stage retrieval (recall)
  • The cross-encoder reranker (precision)
  • The top-K latency/cost tradeoff
  • Serving, caching & fusion
  • The reranker can only sort what it's given
  • Latency, domain fit & cost
RUN IT YOURSELF

The score you can threshold, and the one you cannot

Both stages of this page in one runnable file: a bi-encoder that vectorizes query and passage separately, and a cross-encoder that reads only the top-K candidates the first stage hands it. It scores the same six passages both ways and prints the number that decides whether an I don't know is even possible. Edit a passage, add a query, or call pipeline(q, k) with a smaller k, hit Run, and watch the cutoff appear or vanish.

HOW TO READ THE CODE — 4 IDEAS
  1. The nearest vector to q1 is a page blurb that states no rule; the passage holding fourteen days ranks 3rd (step 1).
  2. Stage two reads only the shortlist stage one hands over: at K=2 the answer never reaches it, at K=3 it comes 1st — recall gates precision (steps 2 and 5).
  3. Both judged answers are equally correct, yet cosine gives them 0.402 and 0.707: the number is scaled by passage length, so it means nothing across queries.
  4. Hence the printed gap. No single cosine cutoff separates right from wrong (−0.316); one cross-encoder cutoff does (+0.400), which is what lets the held-out query answer I don't know.
CPython · WebAssembly
built to make the best answer actually rank first — make the calls, kill the reranker, run the gauntlet.
Finished this one? 0 / 65 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs