Retrieval-Augmented Generation (RAG)
- Lewis et al. (2020) introduced RAG as a way to give LLMs access to dynamic external knowledge
- Solves two key LLM limitations: knowledge cutoff (stale training data) and hallucination (confident but wrong outputs)
- Dominant architecture for enterprise LLM deployments where accuracy and verifiability matter
RAG augments an LLM by first retrieving relevant documents from a knowledge base, then using them as context for generation. The LLM generates answers grounded in retrieved evidence, which can be cited and verified.
- Vector databases (FAISS, Pinecone) power the retrieval step using embedding similarity
- Fine-tuning (PEFT/LoRA) adjusts model weights permanently - RAG adjusts context dynamically without retraining
- GraphRAG extends RAG with knowledge graphs for multi-hop reasoning
- Explain why retrieval is the standard fix for LLM knowledge cutoff, hallucination, and attribution, and what RAG cannot fix.
- Derive the BM25 scoring function from the probabilistic relevance framework and explain term saturation and length normalization.
- Derive the DPR contrastive loss with in-batch negatives and explain why hard negatives matter once recall plateaus.
- Compare sparse, dense, and hybrid retrieval on recall, latency, OOV behavior, and multilingual coverage, and pick the right one for a given workload.
- Evaluate a RAG system end-to-end with the RAGAS metrics (faithfulness, answer relevance, context precision, context recall) instead of LLM-only judgments.
TL;DR
Retrieval-Augmented Generation glues a retriever in front of a generator so the LLM conditions on documents fetched at query time instead of memorizing every fact in its weights. Lewis et al. [1] introduced the end-to-end-trained version; in production almost everyone runs a simpler retrieve-then-read pipeline where the retriever and generator are trained separately. The retriever is the bottleneck: if the right passage is not in the top-, no amount of prompt engineering will recover it. The two retrieval families are sparse (BM25 [8], lexical, exact-term, no training) and dense (DPR [4], SBERT [5], learned embeddings), and modern systems hybridize them. Late-interaction models like ColBERT [6] retain per-token vectors and consistently beat single-vector dense retrievers on recall at the same latency budget. End-to-end evaluation needs RAGAS-style metrics that score grounding against the retrieved context, not just answer correctness.
Intuition
A pretrained LLM is a compressed snapshot of its training corpus. Three problems follow from that fact [9].
Knowledge cutoff. Anything that happened after the training data freeze is invisible. The model cannot learn it without further training.
Hallucination under uncertainty. When the model is asked something it does not know, it cannot say “I do not know”; the next-token distribution is always defined. The model produces a plausible continuation, which often looks correct on the surface but is wrong on the facts.
No attribution. The model cannot point at a source for a claim. Even when its parametric memory is correct, you have no audit trail.
Retrieval addresses all three at once. Instead of trusting parametric memory, fetch the relevant passage from an external corpus and put it in the context window. Now the model has fresh data, has a citable source, and can be held to account when it strays from the retrieved evidence.
What retrieval does not fix: it does not improve reasoning, it does not make a 7B model behave like a 70B, and it does not help when the corpus itself is wrong or missing. RAG is a knowledge prosthesis, not a capability multiplier.
- 2020Lewis et al.: RAG (NeurIPS)paper
Joint training of a DPR-style retriever and a BART generator over Wikipedia. Introduces RAG-Sequence and RAG-Token marginalization. The first end-to-end RAG paper and the source of the acronym.
- 2020REALM: retrieval inside pretrainingpaper
Guu et al. wire a learned retriever into masked language modelling pretraining itself, so the LM is rewarded for fetching helpful evidence. Establishes that retrieval can be trained as part of the LM, not just bolted on at fine-tuning.
- 2020FiD: Fusion-in-Decoderpaper
Izacard and Grave encode each retrieved passage independently in the encoder and let the decoder cross-attend to the concatenated stack. Wins NaturalQuestions and TriviaQA at the time and becomes the default open-domain QA architecture.
- 2022HyDE: hypothetical document embeddingspaper
Gao et al. generate a hypothetical answer with the LLM, embed that, and retrieve against it. Closes a chunk of the zero-shot dense-retrieval gap without any labelled query-document data.
- 2024GraphRAGsystem
Microsoft Research generalize RAG to a knowledge graph extracted from the corpus by an LLM. Community-summary retrieval gives query-focused summarization that flat chunk retrieval cannot match on global questions.
If the right passage is not in the top- that you hand the generator, the generator cannot answer correctly. No prompt engineering, no chain-of-thought, no temperature trick recovers a missing fact. Recall@k of the retriever is the hard ceiling on RAG quality, and the rest of the pipeline (chunking, reranking, prompt template, generator size) is just polishing the signal that the retriever already returned. When a RAG system underperforms, suspect retrieval first.
Anatomy of a RAG system
The retrieve-then-read pipeline has six stages, each of which is a separate engineering decision.
- Indexing. Documents are split into chunks, each chunk is embedded into a vector, and the vectors are stored in a search structure (an inverted index for sparse, an ANN index for dense). The chunk size, the splitter, and the embedding model are all chosen at this stage and are expensive to change later because the whole corpus must be re-indexed.
- Query encoding. The user query is converted into the same representation as the chunks: a token sparse vector (BM25), a single dense vector (DPR / SBERT), or a multi-vector representation (ColBERT). The query encoder must match the document encoder for dense methods.
- Retrieval. The system fetches the top- chunks by similarity. Sparse methods score with BM25; dense methods score by inner product or cosine; hybrid methods linearly combine the two.
- Reranking (optional). A second-stage cross-encoder scores each (query, chunk) pair jointly and re-orders the top-. This is the highest-quality but most expensive stage; it is typically run on .
- Augmentation. The retrieved chunks are formatted into a prompt template along with the user query. The template names the chunks (often as
[1],[2], etc.) and asks the model to cite them in the answer. - Generation. The LLM produces an answer conditioned on the context. Optionally a post-hoc verifier checks every claim against the retrieved chunks and edits or refuses unsupported claims.
Each stage has its own failure mode. Bad chunking loses information at chunk boundaries. A weak embedder misses semantic matches. A retriever with too small a misses the relevant chunk. A bad prompt template fails to ground the generator on the retrieved context. A generator that is too small ignores the context and falls back on parametric memory. The full system is only as good as its weakest stage.
Sparse retrieval: BM25 and the probabilistic relevance framework
Sparse retrieval treats a document as a bag of terms and scores it by the sum of per-term contributions. The dominant scoring function, BM25, is the empirical sweet spot of decades of probabilistic-IR research [8].
BM25: deriving the saturation-and-length-normalized score
Start from the binary independence model: assume each term occurs independently in relevant and non-relevant documents, and score a document for query by the log-odds that is relevant. Under reasonable approximations the score factorizes into a sum over query terms:
where with the corpus size and the document frequency of term . This is the inverse document frequency weight: rare terms are more discriminative than common ones, and the additive smoothing keeps the log defined when a term appears in every or no document.
The inner factor should reward documents that mention often, but with diminishing returns: a document containing the word “kidney” 50 times is not 5 times more relevant than one containing it 10 times, because after the first few mentions the marginal evidence saturates. BM25 uses the saturating form
where is the term frequency in , controls the saturation curve (typical value to ), controls length normalization (typical value ), is the length of , and is the average document length in the corpus.
Term saturation. As , . The function is concave in , with the bulk of the gain in the first few mentions. This prevents long documents that mention a term many times from artificially dominating the ranking.
Length normalization. The factor in the denominator penalizes long documents (those above average length) and rewards short ones. With the normalization is full; with it is off; the default is the empirical sweet spot. The intuition: a long document mentioning a term many times should not score higher than a short document with the same density, because the long document is more likely to mention many terms by accident.
Putting it together,
This is the entire formula. It has three free parameters (, , and the IDF smoothing constant), no training data, no embeddings, and no GPU. It is implemented in tens of lines on top of an inverted index and serves as the strong baseline that every dense retriever has to beat [8].
BM25 is still surprisingly competitive in 2024. Its strengths: zero training cost, transparent ranking, exact-term match (which handles names, acronyms, IDs, and rare technical terms that out-of-vocabulary embeddings stumble on), and trivially-updatable indexes. Its weaknesses: it cannot match a query and a document that share meaning but no surface terms (“car” versus “automobile”; “renal failure” versus “kidney disease”), it has no notion of word order beyond document level, and it does not transfer across languages.
Dense retrieval: DPR and the contrastive loss
Dense retrieval encodes the query and each candidate passage into a fixed-dimensional vector and scores by inner product. The encoders are trained so that vectors of relevant query-passage pairs are close and vectors of unrelated pairs are far apart [4].
DPR: contrastive loss with in-batch negatives
Karpukhin et al. [4] train two BERT-base encoders, for queries and for passages, and score a (query, passage) pair by inner product:
The training objective is a contrastive log-likelihood over a batch of training pairs from a labelled QA dataset (NaturalQuestions, TriviaQA, etc.):
That denominator is the InfoNCE form: for each query , the positive is its own gold passage , and the negatives are every other passage in the batch ( for ). This is the in-batch negatives trick. With batch size you get negatives per query for free, and the gradient on each example pushes the query close to its positive and far from the other passages simultaneously.
Why in-batch negatives work. The InfoNCE bound is tight when the negatives are sampled from the marginal passage distribution, which for a well-shuffled dataset is approximately what in-batch negatives give you. Larger batches mean more negatives per query, which tightens the bound; this is why DPR-style training scales with batch size up to the GPU memory limit. Empirically, recall@20 climbs steadily as batch size goes from 8 to 128 to 512 and starts to plateau past that.
Why hard negatives matter. In-batch negatives are mostly easy: a query about Albert Einstein and a randomly sampled passage about kidney disease are trivially distinguishable. Once recall plateaus, the gradient signal from easy negatives goes to zero (the model is already confident on them). The fix is hard negatives: pre-mine, per query, the top BM25 results that are not the gold passage and add one or two of those into each batch. The resulting model sees gradients from realistic confusers (“Albert Einstein” versus “Albrecht Einstein” or another physicist’s biography) and recall@20 picks up another 3-5 absolute points.
The bidirectional loss. Some implementations also add a reversed term:
which trains the passage encoder to distinguish the matching query from other queries in the batch. This symmetric InfoNCE is a small but reliable win on out-of-domain transfer and is the default in modern E5- and BGE-style training recipes.
After training, the passage encoder is run once over the whole corpus to produce a dense matrix . At query time the query is encoded once and the inner product against all rows is approximated with an ANN index (FAISS HNSW, IVF-PQ, ScaNN). Latency is under ms for ten million chunks on commodity hardware.
Embedding models: SBERT, E5, BGE, and the closed APIs
Sentence-BERT [5] was the first practical recipe for general-purpose sentence embeddings. The trick: take a pretrained BERT, attach a mean-pooling layer over the last-hidden-state tokens, and fine-tune with a Siamese architecture on natural-language-inference pairs (entailment / contradiction / neutral). The resulting embeddings make cosine similarity between sentences a reasonable proxy for semantic similarity, and they were the first to push BERT-derived embeddings past pre-deep-learning baselines on STS benchmarks at retrieval-friendly speeds.
Modern embedding models build on the SBERT scaffold but train at much larger scale and with explicit retrieval-oriented data:
- E5 (Microsoft). A T5-style encoder pretrained on a large web crawl as a contrastive denoising task, then fine-tuned on MS MARCO with hard negatives. Strong on MTEB at multiple sizes (small / base / large).
- BGE (BAAI). Beijing Academy of AI’s general embedding family, trained with a recipe similar to E5 but with more aggressive hard-negative mining and instruction-prefix prompts. The current open-weights leader on most retrieval benchmarks at the base size.
- GTE (Alibaba). A T5-encoder family with multilingual variants. Particularly strong at non-English retrieval.
- OpenAI text-embedding-3-small / large. Closed-API, but Matryoshka-trained: the same embedding can be truncated to lower dimensions without retraining, trading accuracy for index size.
- Cohere Embed v3. Closed-API, with a “search query” versus “search document” mode that prefixes the input differently and reduces the asymmetry between query and document representations.
For most production workloads the right default in 2024 is BGE-base or BGE-large for English, GTE-multilingual for non-English, and either the OpenAI or Cohere API when the team would rather pay per call than host the model.
Sparse, dense, hybrid: which retriever for which job
| Property | BM25 (sparse) | DPR / SBERT (dense) | Hybrid (BM25 + dense, RRF or weighted) |
|---|---|---|---|
| Recall@20 on NaturalQuestions | Around 60% (no training) | Around 80% (DPR), 84-86% (BGE-large) | Around 86-88%; consistently the best of the three |
| Latency at 10M chunks | Single-digit ms (inverted index) | 10-30 ms (HNSW or IVF-PQ) | 15-40 ms (run both, fuse scores) |
| Out-of-vocabulary terms | Excellent (exact match wins on names, IDs, acronyms) | Poor (subword embeddings smear rare strings) | Excellent (sparse channel handles OOV) |
| Multilingual coverage | Per-language tokenizer; weak cross-lingual | Strong with multilingual encoders (E5-multi, GTE-multi) | Strong; the dense channel does the heavy lifting |
| Training data needed | None | Tens of thousands of (query, passage) pairs (or zero-shot with a strong general encoder) | Same as the dense channel |
| Update cost when new docs land | Trivial (incremental inverted-index posting) | Embed the new docs and add to ANN index | Both, in parallel |
| When it wins | Legal, medical, code, anything with rare exact terms | Conversational queries, paraphrased questions | Default for production; consistently best on diverse query mixes |
The standard hybrid recipe is Reciprocal Rank Fusion: run BM25 and the dense retriever independently, take the top from each, then rank by
with a small smoothing constant . RRF is parameter-light and beats most hand-tuned weighted-sum schemes. The mental model: each retriever votes for its top documents, and a document that appears near the top in both gets the most votes. Production systems on diverse query workloads consistently see hybrid beat either component on its own by 2-4 absolute points of recall@20.
Vector databases: where the embeddings live
The vector database is an ANN index plus a metadata store plus an API. The four common shapes:
- FAISS (Meta). A library, not a database. In-process, no server, no metadata store; you write the persistence layer yourself. The fastest option per query when the index fits in RAM. Used as the engine inside many higher-level systems.
- Pinecone (managed SaaS). Fully managed cloud, pay-per-vector. The fastest path to production for teams that do not want to run a service. Indexes are pod-based and you scale by pod type and replica count.
- Weaviate (open-source). Self-hostable graph-aware vector DB with native hybrid search and a GraphQL-style API. Good fit when you want to query both structured fields and vectors in one call.
- Milvus (open-source). Distributed vector DB designed for the billion-scale regime, with multiple ANN backends (HNSW, IVF, DiskANN). Strong on operational tooling for the largest indexes.
- pgvector (Postgres extension). A vector type and HNSW / IVFFlat indexes inside Postgres. The right choice when the team already runs Postgres and the embedding workload is moderate (under a hundred million vectors). Hybrid with full-text search and structured filters comes for free because everything is one SQL query.
For most teams in 2024 the right default is pgvector if they already have Postgres, otherwise managed Pinecone. Self-hosted Weaviate or Milvus is worth the operational burden only when scale, cost, or feature requirements actually demand it.
Chunking: where the corpus meets the index
Chunking is the boring stage that quietly wrecks more RAG systems than any other. The retriever can only return chunks; if the answer to a query straddles a chunk boundary, no retriever recovers it.
The four common strategies:
- Fixed-size token chunks. Split into -token windows with -token overlap (typical , ). Cheap, language-agnostic, and a strong baseline. Loses information at hard boundaries inside paragraphs and sentences.
- Sentence-aware chunks. Split on sentence boundaries first, then group up to tokens. Avoids splitting mid-sentence; needs a tokenizer-aware sentence splitter.
- Recursive structural chunks. Walk the document tree (Markdown headings, HTML sections, code-file functions). Try the largest unit first; if it exceeds , recurse into its children. Preserves document structure and is the LangChain
RecursiveCharacterTextSplitterdefault. - Semantic chunking. Embed every sentence, then place a chunk boundary wherever the cosine similarity between consecutive sentences drops below a threshold. The most expensive option; the gain over recursive structural chunking is small on most corpora.
Two heuristics that hold across corpora: always overlap chunks by 10-20% of the chunk size to soften boundary loss, and prepend each chunk with breadcrumbs (the document title and section heading) so the embedding does not depend on the chunk being interpretable in isolation.
ColBERT and late interaction
Single-vector dense retrieval compresses an entire passage into one -dimensional vector. Most of the per-token information is averaged away. Late-interaction models like ColBERT keep the per-token vectors and defer the comparison to query time [6].
ColBERT: MaxSim late interaction over per-token vectors
ColBERT encodes a query as a sequence of token-level vectors and a passage as a sequence of token-level vectors . The score is
For each query token, the model finds its best-matching document token and adds that similarity. Sum over query tokens, that’s the score. This is the MaxSim operator and the “late interaction” name (the interaction between and happens at the very end, on per-token vectors, not earlier on a single pooled vector).
Why this beats single-vector retrieval. A passage about kidney transplant rejection contains the word “rejection” exactly once, surrounded by domain terms. Mean-pool that passage and the “rejection” signal is averaged with everything else. A query for “transplant rejection” then has to compete with passages that mention “rejection” generically (job rejection, paper rejection, etc.) because the single vector has lost the per-token specificity. ColBERT’s MaxSim, by contrast, lets the query token “rejection” find the document token “rejection” exactly, regardless of what the rest of the passage averages to. Empirically ColBERT recovers 2-5 absolute points of recall@10 on top of a strong single-vector dense baseline, especially on technical corpora.
Cost. The index is much larger: instead of one -dimensional vector per passage you store vectors per passage. ColBERTv2 mitigates this with residual compression: each token vector is decomposed into a centroid (one of ) plus a small residual quantized to a few bits, shrinking the index by at near-zero recall loss. With residual compression ColBERTv2 is competitive on disk footprint with single-vector retrievers and ships to production at retrieval-grade latency.
When to use it. ColBERT shines on technical or specialized corpora (code, medical, legal, scientific) where exact-term match plus paraphrase robustness both matter, and where the corpus is small enough ( to chunks) that the index footprint is acceptable. For very large corpora the operational cost still favors single-vector dense plus a cross-encoder reranker.
Advanced techniques
The retrieve-then-read baseline is the floor, not the ceiling. A handful of techniques routinely improve it.
HyDE (Hypothetical Document Embeddings). Ask the LLM to draft a hypothetical answer to the query, then embed and retrieve against that hypothetical answer instead of the raw query [7]. The hypothetical answer often contains the same vocabulary as the real answer and lands closer in embedding space than the bare query. Wins consistently on zero-shot dense retrieval where there is no labelled training data to fine-tune the encoder.
Query expansion. Have the LLM rewrite the query in several ways (synonym expansion, decomposition into sub-questions, formal-language paraphrase) and retrieve against each rewriting; merge the results. Costs an extra LLM call but typically lifts recall@10 by a few points, especially on conversational queries.
RAG-Fusion. Combine multi-query retrieval with RRF fusion. Generate rewritings, run each through the retriever, then RRF-fuse their top- lists. A small but reliable lift over single-query retrieval.
Reranking with cross-encoders. After top- retrieval, score each (query, chunk) pair jointly with a cross-encoder (such as bge-reranker-large or a Cohere rerank API). This is more expensive than dual-encoder retrieval but more accurate, and it is run on the top- only. Typical setup: dense retrieval , rerank to , generate.
Agentic RAG. Let the LLM call the retriever as a tool, observe the results, decide whether they answer the question, and re-query if they do not. The agent can also decompose multi-hop questions into sub-questions and retrieve for each. More expensive (multiple retrieval rounds) but the right shape for hard multi-hop or vague queries.
GraphRAG. Extract entities and relations from the corpus with the LLM, build a knowledge graph, summarize each community of related entities, then retrieve community summaries instead of (or alongside) flat chunks. Wins on global / query-focused-summarization questions (“what are the main themes in this corpus?”) that flat-chunk retrieval cannot answer because no single chunk contains the global view [1].
Evaluation: RAGAS and the four metrics that matter
End-to-end RAG evaluation is harder than either retrieval evaluation or LM evaluation in isolation, because errors compound across stages. The RAGAS framework decomposes evaluation into four metrics, each scoring a different stage of the pipeline.
Faithfulness. The fraction of claims in the generated answer that are supported by the retrieved context. Measure by extracting atomic claims from the answer with an LLM, then for each claim asking the same LLM “is this claim entailed by the context?”. Low faithfulness signals hallucination: the generator is making things up despite having relevant context.
Answer relevance. Whether the answer addresses the question. Measure by having an LLM generate questions for which the answer is a plausible response, then compute the average cosine similarity between those generated questions and the original. Low relevance signals off-topic answers.
Context precision. Of the retrieved chunks, how many are actually relevant to the query. Measure with an LLM judge, position-weighted so that relevant chunks should appear at the top. Low precision signals a noisy retriever whose top- contains too many distractors.
Context recall. Of the chunks needed to answer the query, how many are in the retrieved top-. Requires ground-truth answers (from a labelled QA dataset) and an LLM to check whether each ground-truth claim is supported by the retrieved context. Low recall signals a missing-fact problem: the retriever did not fetch what the generator needed.
The four metrics fail differently and the diagnosis is mechanical. High recall + high precision + low faithfulness: the generator is hallucinating despite good context, fix the prompt or change the model. High precision + low recall: the retriever is missing chunks, increase or rerank. Low precision + high recall: too much noise in the top-, add a reranker. Low answer relevance with everything else healthy: the prompt template is not channeling the context into the answer, fix it. RAGAS is not the only framework (TruLens and DeepEval ship similar metrics), but the four-metric decomposition is what separates production RAG evaluation from “LLM-as-judge gives it a 7” eyeballing.
Wiring it up: LangChain, LlamaIndex, PyTorch + FAISS
Three production-shaped recipes for the same retrieve-then-read pipeline. LangChain has the largest ecosystem of pluggable components. LlamaIndex was built around indexing and retrieval first and has the cleanest abstractions for hybrid retrieval and metadata filtering. PyTorch + FAISS is the no-framework path: more code but full control and no upgrade risk.
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. Index.
docs = DirectoryLoader("corpus/", glob="**/*.md").load()
splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64)
chunks = splitter.split_documents(docs)
embed = HuggingFaceEmbeddings(model_name="BAAI/bge-base-en-v1.5")
store = FAISS.from_documents(chunks, embed)
# 2. Retrieve + read.
retriever = store.as_retriever(search_kwargs={"k": 6})
prompt = PromptTemplate.from_template(
"Answer the question using ONLY the context.\n"
"Cite chunks like [1], [2].\n"
"If unsupported, say 'I do not know'.\n\n"
"Context:\n{context}\n\nQuestion: {question}"
)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
chain_type="stuff",
chain_type_kwargs={"prompt": prompt},
return_source_documents=True,
)
result = qa.invoke({"query": "How does BM25 saturate term frequency?"})
print(result["result"])
for d in result["source_documents"]:
print("-", d.metadata.get("source"))The choice between the three is mostly ergonomic. LangChain wins when you need many integrations in a hurry. LlamaIndex wins when retrieval is the centre of the system and you want first-class abstractions for hybrid retrieval, metadata filtering, and complex query engines. PyTorch + FAISS wins when the team is small, the requirements are stable, and the upgrade-cost of a rapidly-evolving framework is unattractive.
Pitfalls
The single most common RAG failure mode is “the model gave a confident wrong answer”, and the diagnosis is almost never “the model needs a better prompt”. It is “the right passage was not in the top- that the retriever returned”. Before any prompt engineering, measure recall@k against a labelled query-answer set: pick 50 to 200 real queries, label the gold passages, and check what fraction of gold passages your retriever puts in the top-. If recall@10 is below , no prompt fix will reach acceptable end-to-end quality. Increase , switch to hybrid, add a reranker, or fix the chunking [4].
A long answer that sits across a chunk boundary cannot be retrieved by any chunk-level retriever, because no single chunk contains the answer. The pathology is invisible from inside the system: the retriever’s recall metric is fine on its own corpus (the gold chunk is the gold chunk), but end-to-end RAG quality is poor because the truly-needed information is split across chunks. Fixes: increase chunk overlap, prepend section breadcrumbs to every chunk, or use parent-document retrieval (retrieve the chunk, then expand to the surrounding section before passing to the generator).
A single “rate this answer 1 to 10” call from another LLM tells you nothing about which stage failed. The answer can score high while being unsupported by the retrieved context (LLM hallucinated and the judge did not catch it). The answer can score low while the retrieval was correct (generator failed to use the context). Use the four-metric RAGAS decomposition (faithfulness, answer relevance, context precision, context recall) so a regression points at a stage you can fix.
Modern RAG prompts ask the model to cite chunks like [1], [2]. A weak generator will produce citations that look correct but point at the wrong chunk, or worse, at chunks that do not contain the cited claim. Citations are not guarantees of grounding. Always run an explicit faithfulness check that takes each cited claim and verifies it is entailed by the cited chunk; in safety-critical domains, refuse to surface unsupported claims at all [1].
Pop quiz
- 1. What is the term-saturation property of BM25?
- 2. In DPR training, in-batch negatives mean that:
- 3. ColBERT scores a (query, document) pair by:
- 4. On a RAGAS evaluation, the generator answers questions correctly when given the right context but the system has poor end-to-end quality. The likely diagnosis is:
- 5. Reciprocal Rank Fusion (RRF) fuses two retrievers by:
Interview Q&A
Derive the DPR contrastive loss with in-batch negatives and explain why hard negatives become necessary once recall plateaus.onsite
Setup. Two encoders: for queries and for passages, both initialized from BERT-base. Score by inner product
A training batch is where is the gold passage for from the labelled QA dataset.
Per-query InfoNCE loss. For query the loss is
Numerator: similarity to the gold passage. Denominator: similarity to the gold passage plus every other gold passage in the batch ( for ), which serve as in-batch negatives. Total loss is .
Why this is the InfoNCE bound. Treat the batch as a multiple-choice problem: given , pick the right passage out of candidates. The cross-entropy of the softmax is exactly the InfoNCE objective, which is a tractable lower bound on . Larger gives more negatives per query, tightens the bound, and empirically improves recall up to the GPU memory limit. This is why DPR training cares about batch size.
Why hard negatives matter. In-batch negatives are mostly easy: a query about Albert Einstein and a passage about kidney disease are trivially distinguishable, so the model is already confident on them and the gradient from those terms is near zero. The model still learns from the hard terms: passages that look superficially relevant to the query but are not the gold. Once recall plateaus, easy negatives stop contributing to the gradient and progress stalls.
The fix is to mine hard negatives explicitly: for each query, run BM25 on the corpus and take the top documents that are not the gold. Insert one or two of these per query into the batch alongside the in-batch negatives. The model now gets gradient signal from realistic confusers (e.g., a different physicist’s biography for an Einstein query) and recall@20 typically picks up another 3-5 absolute points. The cost is the offline BM25 mining pass; the gain is the difference between a strong retriever and a state-of-the-art one. Modern recipes (E5, BGE) all rely on hard-negative mining as part of their fine-tuning stage [4][5].
When does RAG fail, and how do you debug it without LLM-as-judge eyeballing?onsite
The retriever is the ceiling. If the chunks needed to answer a query are not in the top-, the generator cannot answer. The first debugging move is to measure recall@k on a labelled set. Build a small evaluation set of 50 to 200 real queries with the gold chunks marked. Run the retriever and check what fraction of gold chunks land in the top-. If recall@10 is under , no prompt fix reaches acceptable quality. The fix is a retrieval-side change: bigger , hybrid sparse + dense, a reranker, different chunking, or a better embedder.
Chunk-boundary loss is invisible. When the answer straddles chunks, no chunk-level retriever can recover it, but the retriever’s own recall on its own gold-chunk labels is fine. The pathology shows up as low end-to-end answer quality with healthy-looking retrieval metrics. Fixes: increase chunk overlap to 15-20%, prepend section breadcrumbs to every chunk, or use parent-document retrieval (retrieve a child chunk, then expand to the surrounding parent before generation).
Faithfulness without recall is meaningless. A model can cite chunks that look correct without those chunks supporting the cited claim. The four RAGAS metrics decompose this:
- Context recall below threshold: retriever missed required chunks. Fix retrieval.
- Context precision below threshold but recall fine: too much noise in the top-. Add a reranker.
- Faithfulness below threshold with recall and precision both fine: generator hallucinates despite good context. Tighten the prompt or use a stronger model.
- Answer relevance below threshold with everything else fine: prompt template is not channeling the context into the answer. Rewrite the template.
Operational diagnostic. For any failing query, log the top- chunks plus their similarity scores. If the gold chunk is missing, the failure is retrieval. If the gold chunk is present at rank one but the answer is wrong, the failure is the generator. Most production debugging reduces to this single inspection step. The mistake to avoid: scoring answers with another LLM and stopping there. That gives a single number with no diagnostic resolution into which stage broke [1][4].
How do you evaluate a RAG system end-to-end with the RAGAS framework, and what does each of the four metrics actually measure?research
RAGAS decomposes evaluation into four metrics, each scoring a different stage of the pipeline. Two need labelled ground-truth answers; two are reference-free and can run on any unlabelled traffic.
Faithfulness (reference-free). Of the claims made in the generated answer, what fraction are supported by the retrieved context.
- Use an LLM to extract atomic claims from the answer (split compound sentences into single-fact assertions).
- For each claim, ask the LLM “is this entailed by the retrieved context?” and aggregate.
- Score = supported claims / total claims.
Low faithfulness means hallucination: the generator made things up despite having relevant context. The fix is either a better generator, a tighter prompt, or a post-hoc verifier.
Answer relevance (reference-free). Whether the answer actually addresses the question, independent of correctness.
- Have an LLM generate questions for which the produced answer would be a plausible response.
- Compute the average cosine similarity between those generated questions and the original question, in a strong embedding space.
- Score is that average; low values mean the answer drifted off-topic.
Context precision (needs ground truth). Of the retrieved top- chunks, how many are actually relevant.
- For each retrieved chunk, ask the LLM “is this chunk needed to produce the ground-truth answer?”.
- Position-weight so relevant chunks at the top of the list count more (a relevant chunk at rank 1 is worth more than at rank 10).
- Score = position-weighted fraction of relevant chunks.
Low precision means the retriever is dragging in distractors that confuse the generator. Fix: rerank the top- before generation, or improve the retriever.
Context recall (needs ground truth). Of the chunks needed to support the ground-truth answer, how many are in the retrieved top-.
- Decompose the ground-truth answer into atomic claims.
- For each claim, check whether the retrieved context entails it.
- Score = supported ground-truth claims / total ground-truth claims.
Low recall means the retriever is missing chunks the generator needed. Fix: bigger , hybrid retrieval, reranker, better chunking, or a better embedder.
The diagnostic loop. The four metrics fail differently and the fix is mechanical:
| Symptom | Likely fix |
|---|---|
| Low context recall | Retrieval-side: bigger , hybrid, rerank, better chunking |
| Low context precision, recall fine | Reranker on top- |
| Low faithfulness, retrieval fine | Tighter prompt or stronger generator |
| Low answer relevance, others fine | Rewrite the prompt template |
Why not LLM-as-judge alone. A single “rate this answer 1 to 10” call collapses all four failure modes into one number. You cannot tell whether the system is hallucinating, retrieving the wrong chunks, or just answering the wrong question. The four-metric decomposition is the difference between “the system feels worse this week” and “context recall dropped 8 points after the corpus update last Tuesday” [1][3].
Why does ColBERT's late-interaction MaxSim beat single-vector dense retrieval, and what is the cost?research
The information-bottleneck argument. A single-vector retriever encodes a passage of tokens into one -dimensional vector by mean-pooling (or [CLS]-pooling) the per-token outputs of an encoder. The map is a massive compression. Most of the per-token information is averaged into the single vector. For a passage that contains a rare specific term surrounded by generic content, the rare term’s contribution is diluted by everything else in the passage.
MaxSim preserves per-token specificity. ColBERT keeps the per-token vectors and scores by
For each query token, the model finds its single best-matching document token and adds that similarity. Sum over query tokens. The MaxSim is robust to the rest of the document: a query token “rejection” finds the document token “rejection” regardless of what the other 500 tokens of the document say. Single-vector retrieval cannot do this; once the document is mean-pooled, the “rejection” signal is averaged with everything else.
Why it beats single-vector retrievers in practice. On standard benchmarks (MS MARCO passage, BEIR, scientific corpora) ColBERT recovers 2-5 absolute points of recall@10 on top of a strong single-vector dense baseline, with the gap widening on technical or specialized corpora where rare terms carry the signal. The reason is exactly the information-bottleneck story: when relevance is local (a rare phrase, a specific entity, a domain term), per-token interaction wins.
The cost. The index stores vectors per passage instead of , so the raw index footprint is roughly times larger. A 100-token passage produces a 100-vector index per passage, and a 1M-passage corpus has 100M vectors. Two mitigations:
- Per-token dimensionality reduction. Project the per-token vectors from to at the encoder output. Cuts index size by at near-zero recall loss.
- Residual compression (ColBERTv2). Train a -means codebook (typically centroids) over the per-token vector space. Encode each token as the nearest centroid plus a small residual quantized to a few bits. Cuts index size by another . ColBERTv2 indexes are competitive in disk footprint with single-vector retrievers.
When to use ColBERT. Specialized corpora (code, medical, legal, scientific) where exact-term match plus paraphrase robustness both matter. Smaller corpora ( to chunks) where the index footprint is acceptable. When the team can afford the operational complexity of a multi-vector index.
When to skip ColBERT. Very large corpora (over chunks) where even ColBERTv2 indexes are prohibitively large. Workloads where a strong single-vector dense retriever followed by a cross-encoder reranker reaches comparable quality at a smaller index footprint. The choice is not “ColBERT versus dense” in isolation but “what is the best recall-vs-index-size point on this specific corpus” [6].
A weekend-scale project that takes RAG from formula to a working pipeline:
- Take a corpus you actually know (your team’s docs, a textbook, a slice of Wikipedia). Chunk it three ways: fixed 512-token, recursive structural with breadcrumbs, semantic. Index each with FAISS HNSW and BGE-base embeddings.
- Hand-label 50 queries with their gold chunks. Measure recall@10 of the three chunkers. The recursive-with-breadcrumbs version should win by a clear margin.
- Add a BM25 channel (
rank_bm25or Tantivy). Fuse with RRF (). Re-measure recall@10. The hybrid should beat either component alone by 2-4 absolute points. - Add a cross-encoder reranker (
bge-reranker-base) over the top-. Re-measure recall@10 after rerank. Expect another 2-3 points. - Run the four RAGAS metrics over the top- retrieved chunks plus the LLM-generated answers. Find the failing stage on your worst-performing queries and fix it specifically (not the prompt; the actual failing stage).
- Stretch: implement a minimal ColBERTv2 index over the same corpus with
colbert-ai. Compare recall@10 against the hybrid + rerank system. Note where late interaction wins and where it does not.
Interactive Demo
RAG Pipeline Visualizer
Click any stage to learn what it does, or run a demo to watch a query flow through all stages.
What to try: Type a question and click Run Demo to see it flow through all RAG stages
References
- Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., Kuttler, H., Lewis, M., Yih, W., Rocktaschel, T., Riedel, S., and Kiela, D. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020 (arXiv preprint). arXiv:2005.11401
- Guu, K., Lee, K., Tung, Z., Pasupat, P., and Chang, M.-W. (2020). REALM: Retrieval-Augmented Language Model Pre-Training. ICML 2020 (arXiv preprint). arXiv:2002.08909
- Izacard, G., and Grave, E. (2020). Leveraging Passage Retrieval with Generative Models for Open Domain Question Answering. EACL 2021 (arXiv preprint 2020). arXiv:2007.01282
- Karpukhin, V., Oguz, B., Min, S., Lewis, P., Wu, L., Edunov, S., Chen, D., and Yih, W. (2020). Dense Passage Retrieval for Open-Domain Question Answering. EMNLP 2020 (arXiv preprint). arXiv:2004.04906
- Reimers, N., and Gurevych, I. (2019). Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP 2019 (arXiv preprint). arXiv:1908.10084
- Khattab, O., and Zaharia, M. (2020). ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT. SIGIR 2020 (arXiv preprint). arXiv:2004.12832
- Gao, L., Ma, X., Lin, J., and Callan, J. (2022). Precise Zero-Shot Dense Retrieval without Relevance Labels. ACL 2023 (arXiv preprint 2022). arXiv:2212.10496
- Robertson, S., and Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval, 3(4), 333-389. doi:10.1561/1500000019
- Goodfellow, I., Bengio, Y., and Courville, A. (2016). Deep Learning. MIT Press. https://www.deeplearningbook.org