Late-interaction reranking (ColBERT & beyond)

Architecture · ~9 min read ·

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

ArchitecturePrecompute docs?Query-time costTypical use
Bi-encoderYes (1 vector/doc)Very lowStage-1 recall over millions
Late-interaction (ColBERT)Yes (token vectors/doc)Low–mediumStage-1 or stage-1.5; lexical-ish precision
Cross-encoder rerankerNoHigh per pairStage-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:

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 →

Keep reading