Why does MMR appear specifically at the context-assembly stage of a RAG pipeline?
Plain top-k has no penalty for redundancy, so it can spend five slots on one fact; MMR runs at assembly and trades a little relevance for diversity so the slots cover distinct facts.
Picture packing a lunchbox. You like apples, so you fill all five slots with apples. You technically maximized apple-love, but now you have no sandwich, no juice, no chips, no snack. A smarter packer says yes apples are great, but pick one apple and use the other four slots for different foods that still go with the meal. That is what MMR does for a context block. Plain top-k stuffs the box with apples because apples score highest. MMR keeps the best apple and spends the remaining slots on neighbouring things the model also needs.
Detailed answer & concept explanation~5 min readEverything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
4 minutes: define MMR, state the score formula in words, explain why top-k cannot do this job, place MMR in the retrieve dedupe rerank-MMR pipeline, mention lambda defaults around 0.7.
import numpy as np
def mmr_select(
query_emb: np.ndarray,
cand_embs: np.ndarray, # shape (n, d)
rel_scores: np.ndarray, # reranker scores, shape (n,)
k: int = 5,
lam: float = 0.7,
) -> list[int]:
selected: list[int] = []
remaining = list(range(len(cand_embs)))
sim_matrix = cand_embs @ cand_embs.T # (n, n)
while len(selected) < k and remaining:
if not selected:
best = max(remaining, key=lambda i: rel_scores[i])
else:
def score(i: int) -> float:
max_sim = max(sim_matrix[i, j] for j in selected)
return lam * rel_scores[i] - (1 - lam) * max_sim
best = max(remaining, key=score)
selected.append(best)
remaining.remove(best)
return selectedReal products, models, and research that use this idea.
- LlamaIndex 2026 ships an MMR post-processor that runs after retrieval and reranking, with default lambda 0.7.
- LangChain's MMR retriever exposes lambda as a direct config parameter and integrates with Chroma, FAISS, Pinecone, Qdrant.
- Cohere's RAG cookbooks recommend MMR after Cohere Rerank 3.5 when the corpus is known to contain near-duplicates.
- Vespa supports diversity-aware retrieval natively as a query operator, used in production at major search-quality teams.
- Anthropic's Claude 4.7 RAG reference architecture for projects mode applies MMR-style diversification before final assembly to keep context-block chunks distinct.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow would you choose lambda for a new workload?
QWhy does MMR sit after reranking rather than before?
Don't say thisRed flags and common mistakes that signal junior thinking. Click to expand.
Red flags and common mistakes that signal junior thinking. Click to expand.
Setting MMR's lambda too low (heavy diversity penalty) so the assembly drifts to off-topic chunks. Lambda should bias toward relevance and use diversity as a tiebreaker.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.
Same topic, related formats. Practice these next.