TAI BUI
Back to curriculum
CapstonePython

RAG Evaluation: Precision, Recall, MRR, nDCG, Faithfulness, Answer Relevance

Retrieval and generation fail on different axes, so they get different metrics. Grade both, or you can't tell which stage broke.

TIME: ~90 minutesPREREQUISITES: Phase 11 lessons 06 (RAG), 10 (evaluation); Phase 19 Track B foundations (lessons 20-29); Phase 19 lessons 64, 65, 66, 67

Learning Objectives

  • Compute four retrieval metrics from gold qrels: precision@k, recall@k, MRR (mean reciprocal rank), and nDCG@k.
  • Compute two answer-grade metrics: faithfulness (every claim grounded in retrieved context) and answer relevance (the answer addresses the question).
  • Build a fixture qrels file (queries, gold doc ids, gold answer text) that the eval reads end to end.
  • Read the metric values to diagnose where a pipeline is failing: retrieval, ranking, generation, or grounding.

The Problem

A RAG system has at least four moving parts: chunker, retriever, reranker, generator. Any of them can be the reason an answer is wrong, and without per-stage metrics you are guessing which one.

Say a user reports a bad answer. Maybe the chunker cut the answer span in half. Maybe the retriever left the right chunk out of the top-k. Maybe the reranker pushed it below position one, or the generator ignored it and made something up. The answer text alone can't tell you which. You need four kinds of signal:

  • Retrieval metrics grade what came out of the retriever.
  • Ranking metrics grade where the right chunk sat in the order.
  • Faithfulness grades whether the generator stayed inside the retrieved context.
  • Answer relevance grades whether the answer addresses the question at all.

The code builds all six metrics on a fixture qrels file. The eval runs offline and deterministically, using a mock judge. In production you swap in a real LLM-as-judge.

Warm-up0/3
1. In a RAG pipeline, which component decides which documents the generator gets to see?Warm-up
2. To measure whether a retriever works, what do you need besides its output?Warm-up
3. An LLM answer states a fact that appears in none of the sources it was given. What is this failure usually called?Warm-up
Check: The Problem0/1
1. Why does the lesson's code grade faithfulness with a mock judge instead of an LLM call?Explain

The Concept

Mermaid diagram

Precision@k

Precision@k is the fraction of the top-k retrieved documents that are in the gold set. If gold has three documents and the top-3 contains two of them plus one wrong one, precision@3 is 2 / 3. Reach for it when an irrelevant chunk is expensive: the generator burns tokens on it, or the chunk poisons the answer.

Recall@k

Recall@k is the fraction of the gold documents that appear in the top-k. If gold has three documents and the top-5 contains all three, recall@5 is 1.0. Reach for it when a missed answer is expensive and you'd rather show one extra wrong chunk than lose the answer chunk.

Recall@k is the number people usually quote in production RAG. A generator can usually drop an irrelevant chunk, but it can't build an answer from a chunk that was never retrieved.

MRR (Mean Reciprocal Rank)

MRR is the mean, across queries, of 1 / position of the first relevant document in the ranked list. It is a single-number summary of how reliably the retriever puts a good answer at the top.

Position one dominates. A gold doc at rank 1 contributes 1.0, rank 2 contributes 0.5, rank 10 contributes 0.1, so the score mostly reflects the head of the list.

nDCG@k

nDCG@k is normalized discounted cumulative gain: the DCG of your ranking divided by the ideal DCG, the score you'd get by ranking perfectly. The result runs from 0 to 1. DCG assigns a gain to each retrieved document, discounts it by the log of its position, and sums.

nDCG also handles graded relevance. The gold can say "doc A is 3, doc B is 2, doc C is 1", where MRR and recall@k flatten everything to relevant or not. Use nDCG when a query has several partially relevant documents.

Faithfulness

Faithfulness is the fraction of claims in the generated answer that the retrieved context supports. The standard implementation prompts an LLM-as-judge with (claim, context) and gets back yes or no for each claim. You may also see it called groundedness, support, or attribution.

It catches the generator that invents content. The retriever can return exactly the right chunks and the system is still broken if the generator hallucinates on top of them.

The code uses a deterministic mock judge: a claim counts as supported when the share of its content tokens that also appear in the retrieved context reaches a threshold (0.4 by default). A real model call replaces the judge in production, and the shape of the metric stays the same.

Answer relevance

Answer relevance asks whether the answer addresses the question. Faithfulness asks whether the answer is grounded in the context. The two come apart: a faithful but off-topic answer scores high on faithfulness and low on relevance, and a short on-topic answer that ignores the context does the opposite.

The standard implementation is also an LLM-as-judge over (question, answer). The code substitutes a token-overlap check for it, scored as 1.0 or 0.0 per query.

Check: The Concept0/13
1. What does precision@k measure for one query?Define
2. What does recall@k measure for one query?Define
3. What is MRR (mean reciprocal rank)?Define
4. What is nDCG@k?Define
5. What is faithfulness in RAG evaluation?Define
6. What does answer relevance assess?Define
7. Which retrieval mistake is usually costlier for a RAG pipeline?Explain
8. When should you reach for nDCG instead of recall@k?Explain
9. A gold document at rank 1 contributes 1.0 to MRR, rank 2 contributes 0.5, and rank 10 contributes 0.1. What does this mean for how MRR behaves?Explain
10. Why does nDCG divide the DCG of your ranking by the ideal DCG?Explain
11. What is the difference between faithfulness and answer relevance?Explain
12. A query has three gold documents. The top-5 retrieved contain two of them plus three wrong documents. What are precision@5 and recall@5?Apply
13. answer_relevance scores 1.0 or 0.0 per query. The judge marks the answers to 3 of 4 queries as relevant. What is the reported answer relevance?Apply

The fixture qrels

python
{
  "qid": "q1",
  "query": "what is the threshold before a multipart upload is dropped",
  "gold_doc_ids": ["d3", "d1"],
  "gold_answer_substring": "three failed parts",
  "graded_relevance": {"d3": 3, "d1": 2, "d2": 1},
}

Each query carries:

  • the query string,
  • a set of gold doc ids (for precision / recall / MRR),
  • a graded relevance dict (for nDCG),
  • the gold answer substring (kept as reference metadata on each qrel; faithfulness here is computed by judging extracted claims against the retrieved context, not against this substring).

The query says "dropped" while the corpus says "aborted", which is exactly the vocabulary gap a plain term-overlap baseline cannot bridge. The fixture has four queries over a twelve-document corpus.

In production you label these. The fixture here is hand-built so the eval runs out of the box.

Check: The fixture qrels0/2
1. What are qrels in a retrieval evaluation?Define
2. Which fixture qrels field feeds the nDCG calculation?Explain

Build It

code/main.py implements:

  • precision_at_k(retrieved, gold, k) - hits in the top-k divided by k, even when the retriever returns fewer than k documents.
  • recall_at_k(retrieved, gold, k) - hits in the top-k divided by the number of gold documents.
  • mean_reciprocal_rank(retrieved_per_query, gold_per_query) - the mean over queries of reciprocal_rank.
  • ndcg_at_k(retrieved, graded_relevance, k) - DCG / IDCG with graded gains only: each document contributes (2**rel - 1) / log2(rank + 1). There is no binary mode, so every gold document needs a grade.
  • extract_claims(answer) - splits an answer into sentence-shaped claims.
  • faithfulness(claims, context_texts, judge) - fraction of claims judged supported.
  • answer_relevance(question, answer, judge) - judge on whether the answer addresses the question.
  • MockJudge - deterministic token-overlap judge so the eval runs offline.
  • evaluate_pipeline(pipeline_fn, qrels, ks) - the orchestrator that runs all the metrics.
  • A demo that runs three pipeline variants against the qrels and prints a metrics table: a bag-of-words term-overlap baseline over whole documents (the demo has no chunker), a hybrid that adds a synonym pass and a title bonus, and hybrid plus a title-match rerank.

Run it:

bash
python3 code/main.py

The output is one metrics table with precision@k, recall@k, nDCG@k, MRR, faithfulness, and answer relevance for each variant. Read it carefully, because it is less flattering than a demo usually is:

  • Hybrid beats the baseline only at the top of the list: recall@1 is 0.875 vs 0.375, nDCG@1 is 1.000 vs 0.500, and MRR is 1.000 vs 0.750. Recall@3 and recall@5 are 1.000 for all three variants.
  • Hybrid plus rerank equals hybrid on every metric, because hybrid already ranks a gold document first for all four queries. The fixture cannot show a rerank gain.
  • Faithfulness is 1.000 everywhere because each answer is built by concatenating retrieved document bodies, so every claim overlaps its context by construction.
Check: Build It0/9
1. A pipeline returns only two documents, both gold, and you compute precision@5 with the lesson's code. What do you get?Apply
2. With one gold document per query, the ranked lists are [a, b], [x, a], and [y, z] and the gold id is a in each. What does mean_reciprocal_rank return?Apply
3. A query's only graded document has grade 3, and the retriever returns [x, a] where a is that document at rank 2. With the lesson's gain and discount formula, what is ndcg_at_k at k=2?Apply
4. For q1 (gold d3 and d1) the baseline pipeline returns [d3, d9, d1]. What are recall@1 and precision@3 for that query?Apply
5. extract_claims splits an answer on sentence boundaries. How many claims does it return for 'First claim. Second claim! Third claim? Trailing'?Apply
6. What does faithfulness return when the answer yields no claims, an empty list, for any context?Apply
7. You run the demo and see faithfulness 1.000 for every pipeline, including the weakest one. What is the most likely explanation?Analyze
8. In the demo, hybrid and hybrid plus rerank tie on every metric. What is the correct reading?Analyze
9. Every query in a fixture has one or two gold documents. A retriever scores recall@5 of 1.0 but precision@5 of only 0.25. Is it malfunctioning?Analyze

Reading the metrics to diagnose failures

SymptomLikely causeWhat to fix
Low recall@k, low precision@kChunker cut the answer or retriever cannot find itChunker boundaries (lesson 64) or retriever modality (lesson 65)
Decent recall@k, low MRRRight chunk is in top-k but not at position 1Reranker (lesson 66)
High MRR, low faithfulnessGenerator invents content despite right contextGeneration prompt; force-cite-or-refuse
High faithfulness, low relevanceAnswer is grounded but off-topicQuery rewriter (lesson 67) or generation prompt
All four high, users still complainEval set is unrepresentativeExpand qrels with real user queries
Check: Reading the metrics to diagnose failures0/4
1. A pipeline shows decent recall@5 but low MRR. Which stage do you investigate first?Analyze
2. A pipeline has high MRR but low faithfulness. What does the diagnostic table point to?Analyze
3. A pipeline scores high faithfulness but low answer relevance. Which diagnosis does the lesson's table give?Analyze
4. Precision, recall, MRR, and faithfulness are all high but users still complain. What does the diagnostic table list as the cause?Analyze

Failure modes the demo will hide

LLM-as-judge bias. A model tends to rate its own outputs as more faithful than they are. Use a different model family for the judge than for the generator, or hand-grade a sample.

Qrels rot. Gold answers drift as the corpus changes. A doc that was gold for q1 in January 2024 may be wrong by October 2024 because the team renamed the function. Schedule a quarterly qrels review.

Faithfulness micro-checks miss macro-claims. Per-sentence faithfulness can pass while the overall structure of the answer misleads. Add a sampled qualitative review on top of the automated metric.

Recall@k masks per-query failures. A 90% average recall can hide a query class that keeps missing. Slice the qrels by query class (literal, paraphrased, multi-topic) and report per slice.

Check: Failure modes the demo will hide0/5
1. What production hazard makes a frozen qrels file dangerous over time?Define
2. What is the LLM-as-judge bias this lesson warns about?Define
3. Why can per-sentence faithfulness pass while an answer still misleads?Explain
4. Average recall@k is 90 percent, yet one kind of question, such as paraphrased queries, keeps getting no answer. What explains this?Analyze
5. You must pick an LLM judge to grade faithfulness for answers written by one model. Which approach follows the lesson?Design

Use It

Production patterns:

  • Run the eval on each retriever or generator change, and treat a recall@k regression like a failing test.
  • Persist the metric trace per query. When a user complains, find the matching qrels entry and check whether the eval would have caught it.
  • Tier the qrels: a smoke set of 20 queries in CI, a regression set of 200 nightly, a deep set of 2000 weekly.
Check: Use It0/2
1. Your team changes the retriever or generator several times a week. Which eval setup follows the lesson's production patterns?Design
2. After you swap the embedding model, MRR rises slightly but recall@5 falls on the eval set. What should the team do?Design

Ship It

Lesson 69 wires the full pipeline (chunker, retriever, reranker, generator) together and runs this eval against the end-to-end system.

Exercises

  1. Add a fifth retrieval metric: hit-rate@k. Compare it against recall@k. Explain when they differ.
  2. Implement a graded faithfulness: 0 (unsupported), 1 (partially supported), 2 (fully supported). Update the metric accordingly.
  3. Replace the mock judge with a real model call. Measure the disagreement between the mock and the real judge on the fixture.
  4. Add a query-class slice ("literal", "paraphrased", "multi-topic"). Report per-slice metrics.
  5. Add an "answer length" metric and correlate it with faithfulness. Plot the curve.

Key Terms

TermWhat people sayWhat it actually means
Precision@k"Hit rate over retrieved"Fraction of top-k that are gold
Recall@k"Hit rate over gold"Fraction of gold in top-k
MRR"First-hit position"Mean of 1 / rank of first relevant document
nDCG@k"Graded ranking quality"DCG over the top-k divided by ideal DCG
Faithfulness"Groundedness"Fraction of answer claims supported by retrieved context
Answer relevance"Did it address the question?"Whether the answer matches the question's intent
Qrels"Gold labels"The labeled set of queries and their gold documents and answers
Author's Note

I treat the demo table as a plumbing check, not as evidence about the pipelines. The fixture is a handful of hand-built queries, and the faithfulness judge only counts token overlap with the retrieved context, so a high faithfulness score here tells you the code runs, not that a generator is grounded. Faithfulness is 1.000 for every variant because the answers are stitched from retrieved text, and hybrid plus rerank ties hybrid, so the table shows the harness working rather than a rerank win. I'd want a real qrels set and a real judge before trusting a conclusion about a retriever.

When I read a table like this, I look at recall@k first. If the right chunk never reached the generator, MRR and faithfulness are much harder to interpret.

Further Reading

  • Buckley, Voorhees, "Evaluating Evaluation Measure Stability", SIGIR 2000 - the canonical paper on ranking metrics
  • Jarvelin, Kekalainen, "Cumulated Gain-based Evaluation of IR Techniques" - the nDCG paper
  • Ragas: Automated Evaluation of RAG Pipelines
  • Anthropic, Evaluating RAG
  • Phase 11 lesson 10 - evaluation framework foundations
  • Phase 19 lessons 64-67 - components evaluated here
  • Phase 19 lesson 69 - the end-to-end pipeline this eval grades

Code