Rerankers in RAG: When Cross-Encoders Actually Earn Their Latency
Adding a reranker to your RAG pipeline sounds obvious until you see the latency bill. Here's when cross-encoder reranking pays off, when it's a waste, and how we wire it up without destroying p95.

Every team that ships a RAG system eventually hits the same wall: the retriever returns 20 chunks, the top one looks great, and the actual answer is sitting at position 14. The default fix is a reranker. The real question is whether the extra 150–400ms is worth it for your product — and in a surprising number of cases, it isn't.
This is a field guide based on what we've seen building RAG for support bots, internal knowledge tools, and document-heavy B2B workflows. We'll cover when rerankers help, when bi-encoders are enough, how to wire up Cohere, Voyage, or an open-source cross-encoder without wrecking latency, and the eval setup that tells you whether the thing is actually earning its keep.
What a reranker actually does
A typical RAG retriever uses a bi-encoder: the query and each document are embedded independently, and similarity is computed with a dot product or cosine. It's fast because embeddings are precomputed and ANN indexes (HNSW, IVF, ScaNN) make nearest-neighbor lookup sub-10ms at millions of vectors.
A cross-encoder reranker is different. It takes (query, document) as a single input and runs a transformer over the pair, producing a relevance score. There's no shortcut — you pay a forward pass per candidate. That's why rerankers sit on top of retrieval: you grab the top 50–100 with the bi-encoder, then rerank down to 5–10.
The quality gap is real. Cross-encoders see token-level interaction between the query and the document, so they catch things like negation, qualifier mismatches ("Python 2 vs Python 3"), and entity disambiguation that pure embedding similarity misses.
The vendors worth knowing in 2026
- Cohere Rerank 3.5 — multilingual, strong on long documents, hosted API. See the Cohere rerank docs for current context limits.
- Voyage rerank-2 — competitive quality, often cheaper per 1K tokens.
- BGE reranker v2 (BAAI) — open weights, run on your own GPU. Good when you can't ship data to a third party.
- Jina Reranker v2 — small, fast, decent quality for the price.
We're deliberately not quoting benchmark numbers. Public MTEB leaderboards shift monthly and your corpus is not their corpus. Run your own evals.
When a reranker is worth it
In our experience, rerankers earn their latency in three situations:
- High-recall retrieval, low-precision ranking. If you need
top_k=5for the LLM but the right chunk is often in positions 10–30 of your bi-encoder results, a reranker is the cleanest fix. This is common when your corpus has many near-duplicates (product variants, versioned docs, policy pages). - Long or jargon-heavy queries. Bi-encoders compress queries into a single vector and lose nuance. "Does our refund policy apply to annual subscriptions cancelled within 14 days under the EU directive?" is the kind of thing cross-encoders handle noticeably better.
- Multilingual or domain-shifted corpora where your embedding model wasn't trained on your vocabulary. Rerankers (especially multilingual ones) often rescue weak embeddings without the pain of fine-tuning.
When it's a waste
Skip the reranker — or at least don't start with one — if:
- Your corpus is small (under ~50K chunks) and your bi-encoder recall@20 is already above ~0.95 on your eval set. The reranker has nothing to fix.
- Your product has strict sub-second latency budgets and you're already streaming. Adding 300ms before the first token is noticeable.
- You're using hybrid search (BM25 + vector + reciprocal rank fusion) and it's already giving good top-5 precision. Hybrid is cheaper and often closes most of the gap.
- Your main failure mode is bad chunks, not bad ranking. A reranker can't fix a chunk that doesn't contain the answer. Fix chunking first.
That last one is the most common mistake we see. Teams add a reranker to paper over retrieval problems that are really chunking or indexing problems. Rerankers reorder; they don't conjure.
A sane pipeline
Here's roughly what we deploy when a reranker makes sense. Hybrid retrieval first, rerank second, then feed the LLM.
from typing import TypedDict
import cohere
from qdrant_client import QdrantClient
class Chunk(TypedDict):
id: str
text: str
score: float
co = cohere.ClientV2()
qdrant = QdrantClient(url="...")
async def retrieve(query: str, embed_vec: list[float]) -> list[Chunk]:
# 1. Hybrid search: vector + BM25, fused server-side
hits = qdrant.query_points(
collection_name="docs",
query=embed_vec,
limit=50,
with_payload=True,
).points
return [{"id": h.id, "text": h.payload["text"], "score": h.score} for h in hits]
async def rerank(query: str, candidates: list[Chunk], top_n: int = 6) -> list[Chunk]:
if len(candidates) <= top_n:
return candidates # don't pay for a no-op
resp = co.rerank(
model="rerank-v3.5",
query=query,
documents=[c["text"] for c in candidates],
top_n=top_n,
)
return [candidates[r.index] | {"score": r.relevance_score} for r in resp.results]
A few things worth calling out:
- Skip the rerank call when candidates ≤ top_n. Easy win, often forgotten.
- Cap candidates at 50–100. Reranker latency scales roughly linearly with document count. Going from 50 to 200 rarely improves end-to-end quality enough to justify the extra time.
- Truncate documents to the reranker's effective context. Most hosted rerankers handle 4K–8K tokens per document, but quality degrades on very long inputs. If your chunks are big, consider reranking on a summary field.
Latency budgeting
A realistic budget for a chat RAG endpoint with time-to-first-token under 1.5s:
- Embed query: 20–60ms
- Vector + BM25 retrieve (top 50): 30–80ms
- Rerank 50 → 6: 150–400ms (hosted) or 80–200ms (local BGE on a warm GPU)
- LLM first token: 400–900ms
If you're already near the ceiling, the reranker is the first thing to cut or move async. One pattern we like: show the LLM's answer using the bi-encoder results, then quietly rerank in the background to warm a cache for follow-up turns in the same conversation.
Evaluating whether it actually helps
Don't ship a reranker on vibes. Build a small eval set — 100–300 real queries with human-labeled relevant chunk IDs is enough to see signal — and measure:
- Recall@k before and after reranking (should stay flat; reranking doesn't add candidates)
- nDCG@k or MRR before and after (this is where you want to see improvement)
- Answer correctness from an LLM-as-judge or human eval, measured end-to-end
The end-to-end number is the only one that matters for shipping. We've seen cases where nDCG improved meaningfully but answer quality didn't move, because the LLM was already robust to a slightly-worse top-5. If the user-visible metric doesn't move, don't pay the latency.
We wrote more about this kind of regression testing in our piece on LLM eval suites — the same discipline applies to retrieval changes.
Cost guardrails
Hosted rerankers are usually priced per search (where "search" = one query against N documents, up to a cap). Rough mental model for 2026:
- Cohere / Voyage rerank: a fraction of a cent per search at typical candidate counts
- Self-hosted BGE on a shared L4 or A10: effectively free marginal cost, but you own the GPU bill
For high-QPS products (support chat, in-product search), self-hosting a small open reranker often wins on total cost once you're past a few hundred thousand searches per month. For low-QPS internal tools, hosted is almost always cheaper than babysitting a GPU.
Two guardrails we always put in:
- Per-tenant rate limits on rerank calls, separate from LLM rate limits. A runaway crawler on your search endpoint can rack up rerank bills fast.
- Short-TTL cache keyed on
(query_hash, candidate_ids_hash). Same query over the same candidate set returns the same ranking; no need to pay twice within a few minutes.
Where we'd start
If you're adding retrieval today, don't start with a reranker. Build hybrid retrieval (vector + BM25), get your chunking right, and measure recall@20 on a real eval set. If recall is low, fix retrieval. If recall is high but precision at top-5 is poor, then bolt on a reranker — hosted first for speed to ship, with a plan to migrate to a self-hosted open model if volume justifies it. Measure end-to-end answer quality, not just ranking metrics, and be honest about whether the latency is buying you anything a user will notice. If it isn't, rip it out. The best RAG pipeline is the simplest one that hits your quality bar.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading

Prompt Caching in Production: When Claude and Gemini Actually Save You Money
Prompt caching sounds free. It isn't. Here's how we decide when to cache on Claude vs Gemini, how to size cache breakpoints, and the gotchas that quietly kill your hit rate.

Tool-Call Retries: Why Your Agent Keeps Calling the Wrong Function Twice
Retrying a failed tool call sounds trivial until your agent charges a customer twice or files duplicate tickets. Here's how we design idempotent, observable retry logic for function-calling agents.

Agent Loops That Don't Spiral: Hard Limits, Budgets, and Escape Hatches
Autonomous agents fail in two ways: they stop too early, or they never stop. Here's how we design loops that terminate cleanly, stay under budget, and tell you why they quit.
