M07 · Retrieval-augmented generation (RAG)07-1033 min read

Lesson 50 of 106 · Module 8 of 14 · Week 4

Threads:The measurement threadThe weights threadThe infrastructure threadThe control thread

Debugging RAG: retrieval failure versus generation failure

When a RAG answer is wrong, the first and only question worth asking is whether the correct passage was in the context the model received. If it was not, you have a retrieval failure and the fix is upstream in chunking, embedding, or ranking. If it was, you have a generation failure and the fix is in assembly, prompting, or grounding. Asking this before anything else resolves roughly half of all RAG debugging time, because it eliminates the entire wrong half of the pipeline in a single step.

01

What the retrieval-versus-generation split is

It is a binary partition of every possible RAG failure, drawn at the context boundary:

text
                    a wrong answer
                          │
        ┌─────────────────┴─────────────────┐
        │                                   │
  Was the correct passage IN THE CONTEXT the model received?
        │                                   │
       NO                                  YES
        │                                   │
  RETRIEVAL FAILURE                  GENERATION FAILURE
  the model never had it             the model had it and misused it
        │                                   │
  stages 1–7:                        stages 8–9:
  parse, chunk, embed,               assemble, prompt,
  index, query-prep,                 ground, cite
  retrieve, rerank                          │
        │                                   │
  fix: 06-01, 06-02, 07-02,          fix: 07-08, 07-11,
       07-04, 07-06, 07-07                 05-02, 09-12

Why the boundary is drawn at the context and not somewhere else. The context window is the only place in the pipeline where you can make a clean, verifiable statement about what the model knew. Everything before it is about getting evidence; everything after is about using it. That makes the boundary a genuine information barrier: no upstream fix can change how the model used a passage it received, and no downstream fix can conjure a passage that was never retrieved.

The two failure classes have disjoint fixes, and that is the whole payoff. This is not a taxonomy for its own sake. Consider the wasted work each misdiagnosis causes:

MisdiagnosisWhat the team doesWhy it cannot work
Retrieval failure diagnosed as generation failureRewrites prompts, switches to a bigger LLM, adds chain-of-thoughtThe passage is not in the context. No prompt can make the model read text it was not given
Generation failure diagnosed as retrieval failureDeploys a vector database, changes the embedding model, adds hybrid searchThe passage was already at rank 1. Better retrieval returns the same passage

Both are common. The second is more expensive, because changing the embedding model means re-embedding the corpus (12-12).

A third class exists and must be named to keep the binary honest: the answer may be unavailable because the information is not in the corpus at all. That is neither a retrieval nor a generation failure — it is an ingestion or curation failure (06-01, 08-01), and in some cases the correct response is that the question is unanswerable and the system should say so (07-11). Folding it into "retrieval failure" is a mistake, because it sends you tuning a retriever over a corpus that does not contain the answer.

02

How to run the diagnosis, step by step

L1 — The intuition you can carry into an exam

Read the prompt the model actually received. Is the answer in there? If no, fix retrieval. If yes, fix generation.

Three facts to carry:

  • The first question is always "was the correct passage in the context?"
  • Retrieval and generation failures have disjoint fixes. Diagnose before intervening.
  • You cannot run this diagnosis without logging the assembled context. That log is the prerequisite for all of it.

L2 — The four-question decision tree

The full procedure is four questions, each one narrowing further. Run them in order; do not skip ahead.

Question 1: Is the answer in the corpus at all?

Search the corpus directly — by keyword, by grep, by whatever crude means you have — for the fact the answer needed.

FindingClassWhere to look
The fact is nowhere in the indexed textIngestion failure06-01 parsing, 08-01 curation. The document may exist but have parsed to nothing
The fact is in the corpus but mangled — a table turned into interleaved words, a scanned page with no text layerIngestion failure06-01. This is the silent-failure case
The fact is in the corpus, intactcontinue to Q2

This question first, because it is the cheapest and because everything downstream is meaningless if it fails. It is also the question people never ask, which is why a parsing defect can survive months of retrieval tuning.

Question 2: Was the correct chunk in the retrieved candidate pool?

Retrieve the top-50 for the failing query — deeper than production k — and search the list for the chunk containing the answer.

FindingClassWhere to look
Absent from the top-50Retrieval / recall failureSee the sub-tree below
Present at rank 4–50Ranking / precision failure07-07 reranking, or fusion depth (07-06)
Present at rank 1–3continue to Q3

If it is absent from the top-50, drill one level:

Sub-checkLikely causeFix
Does the chunk contain the whole answer, or was it split across a boundary?chunking06-02
Is the chunk enormous, so its vector averages many topics?chunking06-02
Does self-retrieval on that chunk's own text return it at rank 1, similarity ~1.0?encoder mismatch07-02 — the single highest-value check
Does BM25 alone find it?dense-only blind spot — identifiers, unseen vocabularyadd the sparse leg (07-06)
Does dense alone find it?sparse-only blind spot — synonyms, paraphraseadd the dense leg (07-06)
Is it excluded by a metadata filter?over-restrictive filter — status, date, or ACL07-03, 07-05
Is it in the index at all?index staleness or a failed insert12-12
Does exact brute-force search find it when ANN does not?index recall too lowraise ef_search / nprobe (07-04)

Question 3: Was the correct chunk in the assembled context?

Present in the top-3 does not mean present in the prompt. Read the logged context.

FindingClassWhere to look
Absent — dropped by a relevance floorthreshold too strict07-08
Absent — dropped by a token budgetcontext overflow or truncation07-08, 04-06
Absent — dropped by post-retrieval filteringfilter applied in the wrong place07-05
Present, but buried mid-context among many chunksassembly failure07-08 — lost-in-the-middle
Present, near the start or endcontinue to Q4

Question 4: The passage was in the context and the answer is still wrong. Which generation failure is it?

SymptomFailureFix
The answer contradicts the contextunfaithful generationgrounding instruction, citation requirement (07-11)
The answer adds facts not in the contexthallucination on top of groundingrequire quoted support; constrain (09-12, 07-11)
The answer uses the model's parametric knowledge instead of the contextinstruction failureexplicit "use only the provided sources" (05-02)
The answer is right but omits part of a multi-part questionpartial use — often a mid-context chunkedge-load (07-08)
The answer picks the wrong one of two contradictory chunksprecedence not specifiedrender dates, instruct precedence (07-03, 07-08)
The answer inverts polarity — says supported when the context says not supportednegationreranking helps; require quoted support (07-03, 07-11)
The answer says it does not know when the context contains the answerover-conservative instructionsoften the decline threshold (07-11)
The citation points at a source that does not contain the claimcitation fabricationvalidate citations programmatically (07-11)
The answer is truncated mid-sentenceno output budget reserved07-08, 02-03

L3 — What makes the diagnosis possible, and what makes it impossible

The procedure above is trivial to run if you have one artefact and impossible without it: the logged assembled context. Not the retrieved ids, not the scores — the actual text the model received, or at minimum the ordered list of chunk ids with their scores plus a way to reconstruct the text.

The minimum viable trace, per request:

text
request_id
query_raw            the user's text
query_prepared       after rewriting (12-11)
filter_predicate     the entitlement + status filter applied (07-05)
sparse_top_n         [(chunk_id, bm25_score, rank), ...]
dense_top_n          [(chunk_id, cosine, rank), ...]
fused                [(chunk_id, rrf_score, rank), ...]
reranked             [(chunk_id, rerank_score, rank), ...]
assembled            [chunk_id in prompt order]
prompt_tokens        the count actually sent
answer               the generated text
citations            [claimed source ids]

Every field earns its place. Without sparse_top_n and dense_top_n separately you cannot tell which leg failed. Without filter_predicate you cannot see that an over-restrictive status filter excluded the answer. Without assembled in prompt order you cannot diagnose lost-in-the-middle. Without prompt_tokens you cannot see truncation.

One caution that is not optional. 07-05 established that retrieved chunk bodies may be restricted content, and that logs are a leak site. The resolution: log ids, scores, ranks, and predicates always; log chunk bodies only in environments whose corpus contains nothing restricted. In production, reconstruct the text from ids at debug time, under the same authorisation as any other read.

Why the order of questions is not arbitrary. Each question is chosen to eliminate the largest amount of pipeline per unit of effort, and to run before any question whose answer it could invalidate:

QuestionCost to answerPipeline eliminated if it fails
Q1 in corpus?seconds — grepstages 3–9 all become irrelevant
Q2 in top-50?one queryeither stages 1–6 or stages 7–9
Q3 in context?read the logassembly versus everything upstream
Q4 which generation failure?read the answer against the contextnarrows to one prompt fix

Asking Q4 first — "let me improve the prompt" — is the intuitive move and the wasteful one, because it is the question that eliminates the least.

03

Retrieval failure vs generation failure vs ingestion failure vs an unanswerable question

Ingestion failureRetrieval failureRanking failureAssembly failureGeneration failureUnanswerable
Correct passage exists in corpusno, or mangledyesyesyesyesno
In the top-50 candidatesn/anoyes, ranked lowyesyesn/a
In the assembled contextnonousually nopresent but poorly placed, or droppedyesno
Model could have answerednononomaybeyesno
Typical symptom"we know that document exists but the system never uses it""it never finds this""it finds it eventually but not in the top 3""sometimes right, sometimes not""it had the answer and said something else""it makes something up"
Fix lives in06-01, 08-0106-02, 07-02, 07-04, 07-0607-07, 07-0607-0807-11, 05-0207-11 — decline
Silent?yes — completelyno, if you look at the poolno, if you lookpartlyno — visible in the answerno
Cost of misdiagnosing itmonths of retrieval tuning over a corpus missing the dataprompt engineering that cannot worka vector-database migration that changes nothingsamere-embedding the corpus for nothinga hallucination shipped as an answer

Three readings that exam items probe.

Ranking failure is a sub-case of retrieval failure with a completely different fix. Both mean "the passage was not in the context", but "absent from the top-50" and "at rank 12 of 50" point at opposite ends of the retrieval stack — recall interventions versus precision interventions (07-06 versus 07-07). Collapsing them is the most common way this diagnosis goes half-right.

Ingestion failure is the only fully silent class, and it is the one whose misdiagnosis is most expensive. A parser that dropped every table produces a system that retrieves fluently, ranks well, assembles cleanly, and answers wrongly on every table-derived question — and every metric downstream of parsing looks healthy.

"Unanswerable" is a correct outcome, not a failure. If the corpus genuinely does not contain the answer, the right behaviour is a decline (07-11). A system that hallucinates instead has a generation failure on top of an unanswerable question, and the fix is the decline path, not better retrieval.

04

Worked example: five failing queries, five different diagnoses

This is a constructed diagnostic walkthrough with invented values, built so each decision point is inspectable. The scores and counts are illustrative, not measured.

The system is the nine-stage pipeline from 07-09: 12,000 chunks, hybrid retrieval, cross-encoder reranking with a 0.5 floor, edge-loaded assembly of up to 3 chunks. Five user complaints arrive. Same symptom class — "the answer was wrong" — five different stages.

Case 1 — "It never finds anything about the licence renewal process"

Q1: is it in the corpus? Grep the indexed text for "licence renewal". Zero hits. The source document exists — a scanned PDF of the licensing agreement.

text
Parse log for licence-agreement.pdf:
  extracted characters: 41
  source estimate:      ~38,000
  assertion (0.8×–1.2×): FAILED — flagged, but nobody read the flag

Diagnosis: ingestion failure. The PDF is a scan with no text layer, so parsing yielded 41 characters of metadata and nothing else. Fix: OCR at the parse stage (06-01). Diagnosis time: ninety seconds, entirely because the assertion existed.

Note the counterfactual. Without that parse assertion the team would have seen a retrieval symptom, added hybrid search, changed the embedding model, and deployed a vector database — none of which can retrieve text that was never extracted.

Case 2 — "Asking about the 60-day international travel window returns nothing useful"

Q1: in the corpus? Yes — the sentence is present, intact.

Q2: in the top-50? No. Absent entirely.

Drill down:

text
Self-retrieval on that chunk's own text → rank 1, similarity 0.997     ✓ encoder fine
BM25 alone on "international travel expense window"   → rank 34         ← found, barely
Dense alone                                            → not in top-50
Chunk length                                           → 1,940 tokens   ← the problem

The chunk is 1,940 tokens covering the entire expenses policy — domestic windows, international windows, receipt rules, currency conversion, approval chains. Its single vector is the average of six topics, so it is moderately similar to everything and strongly similar to nothing. BM25 found it weakly on term overlap; the dense leg could not, because its vector does not point at "international travel" specifically.

Diagnosis: retrieval failure caused by chunking (06-02). Fix: re-chunk at ~400 tokens with overlap. After re-chunking, the international-travel paragraph becomes its own chunk and appears at dense rank 2.

The instructive part: this looked like an embedding-model problem and was a chunking problem. The self-retrieval check exonerated the encoder in one call, which is what made the next hypothesis cheap to reach.

Case 3 — "It knows about certificate rotation but the restart step comes out fourth"

Q1: in the corpus. Q2: in the top-50 — at fused rank 4.

text
Fused ranking:   c8801 (0.0325) > c5512 (0.0323) > c2170 (0.0161) > c6033 (0.0159)
                                                                     ↑ the answer
Assembled (k=3): c8801, c5512, c2170
                 → c6033 never entered the context

Q3: in the assembled context? No — cut by k=3.

Diagnosis: ranking failure (07-07). The chunk was retrieved and then discarded by the context cutoff. It fused to rank 4 because only the dense leg found it, and RRF rewards mutual presence over single-leg conviction (07-06).

Fix: add the cross-encoder reranker. It scores c6033 at 0.93 — highest of the pool — promoting it to rank 1. No retrieval change was needed at all; the passage was always there.

This is precisely the case where a team without the diagnosis reaches for the embedding model, re-embeds 12,000 chunks, and finds the ranking unchanged — because the retrieval was never the problem.

Case 4 — "Sometimes it gets the international travel exception, sometimes it doesn't"

Q1: in the corpus. Q2: top-50, rank 2. Q3: in the assembled context? Yes — but look at where.

text
Assembled context, naive rank order, k=8:
  position 1  e1   30-day rule            ← strong position
  position 2  e2   60-day intl exception  ← being pushed toward the middle
  position 3  e3   review timeline
  position 4  e4   portal location
  position 5  e6   currency conversion
  position 6  e7   receipt requirements
  position 7  e8   approval chain
  position 8  e5   revision history       ← the WEAKEST chunk in the strongest end position

Diagnosis: assembly failure (07-08). Both answer-bearing chunks were present. e1 at position 1 is used reliably, which is why the 30-day answer is always right. e2 at position 2 of 8 sits in the region the model attends to least reliably — the lost-in-the-middle U-curve — which is why the exception appears intermittently. Meanwhile a revision-history line occupies the strong final position.

The intermittency is the tell. A failure that appears and disappears for the same question, with the same retrieval, is a positional or attention effect, not a retrieval one.

Fix, in two lines of assembly code: apply the 0.5 relevance floor (dropping e4, e5, and others) and edge-load — e1 first, e2 last. Chunk tokens fall, and both answer-bearing chunks land in strong positions.

Case 5 — "It told a customer to disable certificate verification"

Q1: in the corpus — an archived forum post says exactly that. Q2: top-50, rank 1. Q3: in the assembled context, position 1.

Q4: which generation failure?

text
Context position 1: archived forum post — "just turned off cert verification, worked for me"
Context position 3: official spec — "the server MUST abort the handshake …"

Answer: "Disable certificate verification on the client."
Citations: none — no citation instruction was in the prompt

Diagnosis: this is not primarily a generation failure. The model faithfully used its highest-ranked source. It did exactly what it was told. The failure is upstream, in two places:

  1. Authority blindness (07-03). The forum post ranked first because it matches the query's register — first-person, symptomatic, informal — while the specification is written like a specification. Fix: trust-tier metadata at ingestion and a filter or boost on it; ideally, do not index content you would not cite.
  2. No citation requirement (07-11). With citations, a reader would see the answer came from an archived forum post and could reject it. Without them, a dangerous answer is indistinguishable from a good one.

There is a genuine generation-side fix too — instruct the model to prefer authoritative sources and to flag when its best source is community content — but the primary defect is that the corpus offered a bad answer at rank 1.

This case is why the diagnosis has four questions rather than two. A crude retrieval-versus-generation split would land on "generation failure: the model said something dangerous", and the prompt fix would be a patch over a corpus problem.

Reading the five cases together

CaseSymptomQ1 in corpusQ2 in top-50Q3 in contextDiagnosisFixLesson
1never foundnoingestionOCR at parse06-01
2never foundyesnoretrieval / chunkingre-chunk to ~400 tokens06-02
3found but not usedyesrank 4norankingadd reranking07-07
4intermittentyesrank 2mid-contextassemblyfloor + edge-load07-08
5dangerous answeryesrank 1position 1authority + no citationstrust tiers + citations07-03, 07-11

Five identical-sounding complaints, five stages, five disjoint fixes. A team that responds to all five by changing the embedding model fixes none of them and re-embeds the corpus five times. The diagnosis costs minutes per case; the misdiagnosis costs weeks.

05

Decision table: symptom to stage

Use this as a lookup once you have run the four questions — it maps observations onto the stage to inspect.

ObservationMost likely stageFixLesson
A document you know exists is never used, for any phrasingparsecheck extracted length; OCR; structured extraction06-01
A table's contents are never retrievableparsetable-aware extraction06-01
Answers exist in long documents but are never found specificallychunkreduce chunk size06-02
An answer spanning two paragraphs is never retrieved wholechunkincrease overlap; semantic chunking06-02
A footer or nav block is returned for every querydedupstrip boilerplate before indexing06-04
All similarities cluster in a narrow band; nothing is distinctivededup / chunkboilerplate dominating chunk text06-04
Confident, fluent, irrelevant results with normal-looking scoresembedself-retrieval assertion; pin model, version, prefix, pooling07-02
Exact error codes and part numbers are never foundretrieveadd the sparse leg07-01, 07-06
Paraphrased questions are never matchedretrieveadd the dense leg07-02, 07-06
Brute-force search finds it but the index does notindexraise ef_search / nprobe; measure index recall07-04
Filtered queries return fewer results than kindex / filterpre-filter instead of post-filter07-04, 07-05
The correct chunk is at rank 4–50rerankadd or deepen reranking07-07
The best single-leg hit is buried mid-rankingfusedeeper fusion pool, then rerank07-06, 07-07
Superseded documents are citedquery prep / metadatastatus filter; do not index superseded content07-03
Community content outranks official docsmetadatatrust tiers; filter or boost07-03
Users see documents they cannot accessquery preppre-filter in every leg07-05
Intermittent correctness on the same questionassembleedge-load; reduce k07-08
Multi-part questions answered partiallyassembleedge-load; ensure one chunk per part07-08
Answers truncated mid-sentenceassemblereserve output budget07-08, 02-03
Answer contradicts the provided contextgenerategrounding instruction; require quoted support07-11
Answer adds facts not in the contextgeneratecitation requirement; constrain07-11, 09-12
Citations point at sources lacking the claimgeneratevalidate citations programmatically07-11
Model declines when the answer is presentgeneratesoften the decline threshold07-11
Cost or latency far above forecastassemblecount tokens; k is a multiplier07-08, 12-09, 12-10

Two entries in that table are worth memorising as signatures because they identify their stage almost uniquely:

"Confident, fluent, irrelevant, normal-looking scores, no errors" is the embedding-mismatch signature. Nothing else in the pipeline produces exactly that combination, and the self-retrieval assertion confirms or excludes it in one call.

"Intermittently correct for the same question with the same retrieval" is the assembly signature. Retrieval failures are consistent — the chunk is either found or it is not. Positional and attention effects are probabilistic, so intermittency points at assembly rather than upstream.

06

Why debugging RAG by failure attribution is on the NCA-GENL exam

The official job-role frame describes an associate who implements testing and debugging, performs system analysis against specifications, and assesses and resolves performance issues [OFFICIAL]. That is a description of this lesson. The objectives it serves:

  • 1.3 / 4.2 — Build LLM use cases such as RAG, chatbots, and summarizers. Building includes debugging.
  • 4.5 — Monitor functioning of data collection, experiments, and other software processes [OFFICIAL]. The per-request trace is that monitoring.
  • 4.1 / 1.1 — Assist in deployment and evaluation of model scalability, performance, and reliability under supervision of senior team members [OFFICIAL]. Attribution is what makes an evaluation actionable.
  • 1.4 — Curate and embed content datasets for RAGs. Half of all diagnoses land in curation.
  • 1.9 — Use prompt engineering principles to create prompts to achieve desired results. Generation-side fixes are prompt fixes.
  • Derived Experimentation scope — separating retrieval quality from generation quality is exactly the RAG evaluation decomposition the course index names, and 09-07 owns the metrics.

The exam is scenario-heavy and calibrated to correct practice and tool choice rather than deep technical derivation [FIELD]. Debugging scenarios are the ideal vehicle for that: a symptom is described, four plausible interventions are offered, and the keyed answer is the one matching the stage the symptom implicates. These are among the most answerable questions on the exam if you know the symptom-to-stage map, and among the most guessable-wrong if you do not.

Question phrasings you should recognise:

PhrasingTestingAnswer shape
"A RAG answer is incorrect. What should be determined first?"the primary splitwhether the correct passage was retrieved into the context
"The retrieved context contains the answer but the response contradicts it. What kind of failure?"generationgeneration / faithfulness failure — fix grounding and citations
"The correct document is never retrieved regardless of phrasing. Where should the team look?"retrieval / ingestionparsing, chunking, embedding consistency, index freshness
"Results are fluent and confidently irrelevant with no errors. What is the likely cause?"the embedding signatureinconsistent query/passage encoding
"The right passage is retrieved at rank 9 but only 3 chunks are used. What should be added?"rankinga reranking stage
"The same question is answered correctly some of the time. What does this suggest?"the assembly signaturepositional effects — the passage is mid-context
"Why measure retrieval quality separately from generation quality?"decompositionso a failure can be attributed to a stage and the right fix chosen
"A known document produces no retrievable text. Which stage failed?"ingestionparsing
"What single artefact makes RAG debugging possible?"the tracethe logged assembled context

Distractor families:

  • "Use a larger LLM." The default wrong answer for retrieval failures. A larger model cannot read a passage it was not given.
  • "Change the embedding model." The default wrong answer for generation and ranking failures, and the most expensive one because it forces a full re-embed (12-12).
  • "Increase k." Superficially addresses recall, frequently worsens assembly (07-08).
  • "Add hybrid search" offered for recency, authority, permissions, or ranking problems. It addresses lexical/semantic coverage only (07-06).
  • "Fine-tune the model on the documents." Does not add retrievable facts reliably, removes provenance, and cannot be filtered per user (11-08, 13-05).
  • "Lower the similarity threshold." The wrong passage often scored high (07-03).
  • Prompt engineering offered for a retrieval failure, and retrieval changes offered for a generation failure. The two canonical inversions.
  • "Check the logs" where no context trace is logged. The distractor is plausible precisely because most systems cannot do it.
07

Common mistakes when debugging RAG

#SymptomCauseFix
1Weeks of tuning with no improvementNo attribution — the team fixed the interesting stage, not the broken oneRun the four questions before changing anything
2The diagnosis cannot be run at allThe assembled context is not loggedLog query, filter, per-leg candidates, fused, reranked, assembled order, token count, answer, citations
3Retrieval judged "fine" because end-to-end scores are acceptableAggregate metrics hide stage-level failuresMeasure recall and faithfulness separately (09-07)
4An embedding-mismatch failure survives for monthsNo self-retrieval assertionOne embed, one search, in CI (07-02)
5A parsing failure diagnosed as a retrieval failureParsing is silent and nobody checked the corpus firstQ1 always comes first: is it in the corpus?
6"Absent from top-50" and "rank 12 of 50" treated as one problemRecall and precision conflatedThey have opposite fixes — 07-06 versus 07-07
7Four fixes shipped together; nobody knows which workedNo isolationOne change at a time against a frozen eval set (03-04)
8An intermittent failure dismissed as model non-determinismPositional effects mistaken for sampling randomnessIntermittency with fixed retrieval points at assembly (07-08, 09-11)
9A single anecdote drives an architecture changeOne query is not evidenceAdd the failing query to the eval set, then measure (01-08, 09-09)
10Restricted chunk text ends up in debug logsBodies logged for debuggabilityLog ids and scores; reconstruct text under authorisation (07-05)
11The fix works for the reported query and breaks two othersNo regression suiteEvery diagnosed failure becomes a permanent eval item (10-04)
12A hallucination treated as a generation bug when the corpus lacked the answerThe unanswerable case was not distinguishedAdd a decline path and count declines as correct when appropriate (07-11)

Mistake 11 is the one that converts debugging from firefighting into progress. Every diagnosed failure should end as a new item in the evaluation set, labelled with the correct chunk id. Over a few months that turns a 20-item hand-written eval set (01-08) into a hundred-item suite grown from real failures (09-01) — and it means a fix that regresses an old failure fails the build instead of a customer (10-04).

Mistake 9 deserves a note too. A single wrong answer is a hypothesis generator, not evidence. Users report individual failures; the honest response is to reproduce it, diagnose it, add it to the eval set, and then measure whether the proposed fix helps the set rather than the anecdote. A change that fixes one query and breaks three is a regression that felt like a success.

08

Why does asking "was the passage retrieved?" first save so much time?

Because it eliminates roughly half the pipeline with one cheap observation, and because the two halves have no fixes in common.

Count the stages. The nine-stage pipeline (07-09) splits into stages 1–7 (getting evidence) and stages 8–9 (using it). A single look at the logged context tells you which half to ignore. No other question in RAG debugging has that yield:

Question asked firstStages eliminated by the answerCost
Was the passage in the context?~7 of 9, either wayread one log line
Is the prompt good enough?0hours of iteration
Is the embedding model good enough?0a full re-embed to find out
Is the vector database fast enough?0a migration
Is the LLM strong enough?0a model swap and re-evaluation

The asymmetry is not subtle. And the disjointness of the fixes is what makes it consequential rather than merely efficient: work done on the wrong half is not partial credit, it is zero. Prompt engineering applied to a retrieval failure yields nothing, however good the prompt gets, because the text is not there. A new embedding model applied to a generation failure yields nothing, because the same passage arrives at rank 1 and the model misuses it the same way.

There is a second-order benefit worth naming: the question is cheap to answer correctly, which means it is hard to get wrong. Judging whether a prompt is good enough is a matter of taste; judging whether a specific sentence appears in a logged context is a matter of fact. Diagnostic steps that produce facts should always precede ones that produce opinions.

And a third: it makes the conversation concrete. "RAG isn't working well" cannot be assigned to anyone. "For query X, chunk c6033 was retrieved at fused rank 4 and cut by k=3" has an owner, a fix, and a test. That translation is most of what makes a RAG system operable.

09

How do I tell an unfaithful answer from a genuine retrieval miss?

By reading the context and the answer side by side and asking whether every claim in the answer is supported by something in the context. This is the faithfulness (or groundedness) check, and it is the generation-side counterpart to retrieval recall.

What you observeClassFix
The answer's claims are all in the context, but the context lacks what the user askedretrieval miss — the model was faithful to insufficient evidencefix retrieval; the generation was correct behaviour
The answer's claims are not in the contextunfaithful generationgrounding instruction, quoted support, citation validation (07-11)
Some claims are supported, others are notpartial hallucination — usually the model filling a gap from parametric knowledgerequire per-claim citation; instruct explicitly to use only provided sources
The answer says it does not know, and the context truly lacks the answercorrect behaviourthis is a success, and should be scored as one
The answer says it does not know, and the context contains the answerover-conservative generationsoften the decline threshold; check whether the passage was mid-context (07-08)

The distinction that catches people: a model that faithfully answers from bad evidence is not hallucinating. It did its job. Scoring it as a generation failure sends you to fix the prompt when the corpus or the retriever is at fault. This is one reason RAG evaluation must report retrieval and generation metrics separately rather than as one quality number (09-07).

Three checks that make this mechanical rather than impressionistic:

Per-claim citation. Instruct the model to attach a source id to every factual claim (07-11). Then an unsupported claim is visibly unsupported — it has no citation, or a citation you can check.

Programmatic citation validation. For each cited id, verify the claim's key terms actually appear in that chunk. This is crude and catches the worst cases: citations pointing at chunks that do not contain the claim. It is cheap enough to run on every response in a test suite.

LLM-as-a-judge for faithfulness, with its biases understood (09-10). Give a judge model the context and the answer and ask whether every claim is supported. Useful at scale, unreliable enough that you should not trust it alone — and note this is a different use of an LLM than reranking (07-07).

Grounding via retrieval and citation of sources are named hallucination controls [NVIDIA-DOC], and NeMo Guardrails is the NVIDIA instrument for keeping applications accurate, appropriate, on-topic, and secure — 13-02 covers it and 09-12 covers hallucination types. The point for this lesson is narrower: without citations you cannot run this diagnosis at all, because you have no way to check which claim came from which source.

10

What should a RAG debugging session actually look like?

Thirty minutes, five steps, one artefact at the end. Concretely:

Step 1 — Reproduce with the trace (5 min). Re-run the failing query and capture the full trace: prepared query, filter predicate, per-leg candidates with scores, fused ranking, reranked ranking, assembled order, token count, answer, citations. If you cannot capture this, stop and build the logging — everything else is speculation.

Step 2 — Run the four questions (5 min). In corpus? In top-50? In context? Which generation failure? Write down the answer to each; do not skip forward because you have a hunch. The hunch is usually the interesting stage, not the broken one.

Step 3 — Name the stage (1 min). One stage, by name, from the nine. If you cannot name one, you have not finished step 2. "Retrieval is bad" is not a stage; "chunking produced a 1,940-token chunk whose vector averages six topics" is.

Step 4 — Add the query to the eval set (5 min). With the correct chunk id labelled. This is the step that makes the session cumulative instead of disposable. Do it before the fix, so you have a failing test.

Step 5 — Fix one thing and re-measure (10 min plus). The single intervention that matches the named stage. Re-run the whole eval set, not just the failing query, because a fix that helps one query and breaks three is a regression (03-04).

What you have at the end, and this is the point: a named stage, a permanent test, a measured before-and-after, and a one-line explanation someone else can read. Compare that to the alternative session, which ends with "I changed some things and it seems better."

Two habits that compound over a project:

Keep the four-configuration table current. Sparse alone, dense alone, hybrid, hybrid plus rerank, each with recall@3, recall@10, and MRR on the frozen set (07-01, 07-06, 07-07). It answers most architecture questions before they are asked, and it stops the recurring proposal to remove a component nobody can defend.

Put the assertions in CI (10-04). Self-retrieval similarity, parse-length ratios, maximum chunk length, retrieval recall thresholds, and faithfulness on the eval set. A CI gate that fails the build catches the silent stages; a dashboard does not, because nobody reads a dashboard on a Tuesday. That gate is the week-5 deliverable of this course, and it is the difference between a system you operate and one you hope about.

Glossary recap: the terms this lesson introduced

TermDefinition
Retrieval failureThe correct passage was not in the context the model received; the fix is upstream of generation
Generation failureThe correct passage was in the context and the model produced a wrong answer anyway
Ingestion failureThe information is absent or mangled in the indexed corpus — the only fully silent failure class
Ranking failureThe correct passage was retrieved but ordered below the context cutoff — a retrieval failure with a precision fix
Assembly failureThe correct passage entered the context but was placed or budgeted so the model under-used it
Failure attributionAssigning an observed failure to one named pipeline stage before choosing a fix
The four-question treeIn corpus? → In top-50? → In context? → Which generation failure?
The traceThe per-request log of query, filter, per-leg candidates, fused and reranked rankings, assembled order, tokens, answer, and citations
Self-retrieval assertionEmbedding a known chunk's own text as a query and requiring it back at rank 1 near 1.0 — the embedding-stage check
Embedding-mismatch signatureConfident, fluent, irrelevant results with normal-looking scores and no errors
Assembly signatureIntermittent correctness on the same query with unchanged retrieval
Faithfulness / groundednessWhether every claim in the answer is supported by the provided context
Faithful to insufficient evidenceA correct generation over a bad retrieval — not a hallucination, and misdiagnosing it sends you to the wrong stage
Regression suite growthTurning every diagnosed failure into a permanent labelled eval item

Key takeaways on debugging RAG

  • Ask one question first: was the correct passage in the context the model received? It eliminates roughly seven of nine stages either way, and it costs one log line.
  • Retrieval and generation failures have disjoint fixes. Work on the wrong half is not partial credit; it is zero.
  • Run four questions in order: is it in the corpus, is it in the top-50, is it in the assembled context, and which generation failure is it. Each eliminates more than the next.
  • The worked example's headline result: five identical-sounding complaints diagnosed to five different stages — parse, chunk, rank, assemble, and corpus authority — with five disjoint fixes. Changing the embedding model would have fixed none of them.
  • Two signatures worth memorising. Confident-fluent-irrelevant with normal scores and no errors is embedding mismatch. Intermittent correctness with unchanged retrieval is assembly.
  • Ingestion failure is the only fully silent class, and Q1 exists to catch it before you tune anything downstream.
  • "Absent from the top-50" and "rank 12 of 50" are different problems with opposite fixes — recall interventions versus reranking.
  • A model that faithfully answers from bad evidence is not hallucinating. Scoring it as a generation failure sends you to the wrong stage.
  • You cannot run this diagnosis without the trace. Log ids, scores, ranks, filter predicates, assembled order, and token counts — bodies only where nothing is restricted (07-05).
  • Every diagnosed failure becomes a permanent eval item. That is what makes debugging cumulative rather than disposable.

Next: grounding, citations, and letting a model say "I don't know"

Several diagnoses in this lesson ended at the same place: the model produced a claim, and there was no mechanism to check whether the context supported it. Citations are what make that checkable, and a decline path is what makes an empty retrieval produce an honest answer instead of an invented one. Next: 07-11 covers grounding and citations as hallucination controls — how to instruct a model to answer only from provided sources, how to render source ids so citations are verifiable rather than decorative, how to validate them programmatically, and why letting a model say "I don't know" is a feature that has to be deliberately designed rather than a failure to be prompted away.