Late-interaction reranking (ColBERT & beyond)
Not every retrieval stack needs a cross-encoder reranker. Late-interaction models such as ColBERT score query–document pairs with token-level interactions while keeping embeddings cacheable — often a better fit than bi-encoders and cheaper than full cross-encoders at scale.
What late-interaction means
A bi-encoder embeds the query and each document independently — fast, but no token-level matching. A cross-encoder feeds query + document through one transformer and outputs a single relevance score — accurate, but you pay full inference per pair.
Late-interaction sits between them: you still pre-compute document token embeddings offline, but at query time you run a cheap interaction (typically MaxSim — max cosine per query token) between query tokens and document tokens. You get finer matching than a single vector dot product without scoring every pair with a full cross-encoder.
Cross-encoder vs late-interaction vs bi-encoder
| Architecture | Precompute docs? | Query-time cost | Typical use |
|---|---|---|---|
| Bi-encoder | Yes (1 vector/doc) | Very low | Stage-1 recall over millions |
| Late-interaction (ColBERT) | Yes (token vectors/doc) | Low–medium | Stage-1 or stage-1.5; lexical-ish precision |
| Cross-encoder reranker | No | High per pair | Stage-2 on 50–100 candidates |
Many 2026 stacks chain them: bi-encoder or hybrid recall → optional ColBERT rescore on hundreds → cross-encoder rerank on dozens. See hybrid retrieval + rerank for the merge step.
When to skip cross-encoder rerank
Cross-encoder reranking pays off when your shortlist is small (≤100) and order quality matters for downstream LLM prompts. Consider not adding one when:
- Late-interaction already fixes order. ColBERTv2 or SPLADE++ on your eval set beats retrieval-only NDCG@10 — the marginal gain from a cross-encoder may be tiny.
- Latency budget is sub-50 ms. You cannot afford another transformer pass after retrieval; invest in better embeddings or late-interaction instead.
- Candidate count is huge per query. Reranking 500+ passages with a cross-encoder is slow; use late-interaction to prune to 50 first.
- Instruction-shaped queries. Newer APIs (e.g. Contextual AI instruction rerankers) bake task context into the score — a generic cross-encoder may underperform without prompt engineering.
- Multilingual with one strong bi-encoder. Models like Qwen3-Embedding + a tuned reranker may suffice; always measure on your labels.
Rule of thumb: if Recall@50 is low, fix retrieval — no reranker architecture invents missing chunks.
ColBERTv2 in practice
# Conceptual flow with pyserini / ragatouille-style tooling
# 1. Index token embeddings offline
# 2. At query time: MaxSim between query tokens and doc token vectors
# 3. Take top 50 for optional cross-encoder rerank
from ragatouille import RAGPretrainedModel
colbert = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")
results = colbert.search(query="reset API key", k=50)
# Pass `results` to bge-reranker or Cohere for final ordering
ColBERT shines on keyword-heavy corpora (support KBs, legal clauses, SKUs) where bi-encoders blur exact tokens. It does not run in our browser demo — different inference path — but belongs on your architecture comparison checklist.
How to pick an architecture
Millions of docs, tight latency?
→ bi-encoder (+ BM25 hybrid if lexical matters)
Recall OK but order noisy, 100–1000 candidates?
→ late-interaction (ColBERT) OR widen cross-encoder input
Top 50–100 need LLM-grade precision?
→ cross-encoder rerank (bge, Cohere, Jina, mxbai…)
Task-specific instructions in the query?
→ instruction-following hosted rerank (Contextual AI, etc.)
Unsure?
→ 30 labelled queries + NDCG@5 before/after each stage
Emerging open cross-encoders such as Qwen3-Reranker compete with bge on multilingual stacks; pair them with late-interaction when you need both scale and precision.
See cross-encoder rerank in the browser
Our demo shows stage-2 cross-encoder scoring — compare it with the bi-encoder proxy column to see what late-interaction or hybrid retrieval would need to fix.
Open the demo →