Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Two loose ends, from two different chapters, turn out to be the same problem. Chapter 3 noticed that a retrieved chunk is a fragment: it doesn’t say which policy it came from unless you track that separately. Chapter 4 noticed that its demo printed an answer but never showed which chunk produced it. Both are really one gap: chunks carry no memory of where they came from.

The fix is simple to state: attach that information at chunking time, as data, and carry it through every step, retrieval, the prompt, generation, all the way to what the reader actually sees.

Chapter 5 of 10 · Building a grounded system

Where we left off: Chapter 3 noticed a retrieved chunk doesn’t say which policy it came from. Chapter 4 noticed its demo never showed which chunk produced an answer.

What we fix now: Attach provenance to every chunk as data, and carry it through retrieval, the prompt, and generation, all the way to what the reader actually sees.

By the end, you can:

  • explain what a citation does and does not prove about an answer

  • attach and carry metadata through a retrieval pipeline so mistakes become visible instead of invisible

  • build parent-document retrieval: search a precise fragment, display a clean full section

Source

Retrieval confidently returns the Vacation Policy, not the Sick Leave Policy, for a question specifically about sick days. Look back at the handbook text: sick leave and vacation share almost identical vocabulary in this corpus (both about “days,” “roll over,” “paid time off,” “employees”), so our small embedding model, which only measures topical closeness, can’t tell “these days carry over” apart from “these days explicitly do not.” That’s the same single-vector limitation Chapter 2 flagged early on, showing up again in a new, more consequential place.

If a generation step answered using only this retrieved chunk, it would have every reason to say something like “up to 5 days,” the vacation number, applied to a question about sick leave, which the real Sick Leave Policy explicitly contradicts. That’s not a hypothetical: it’s exactly the kind of fluent, plausible, wrong answer Chapter 1 opened with.

The fix: show the source, not just the answer

Nothing about the retrieval step changes here, embeddings still have the limitation above, but now every retrieved record already carries what’s needed to make that limitation visible instead of invisible.

Read that citation on its own, before even seeing a generated answer: “Source: Vacation Policy.” Someone who asked about sick days and sees that source line has enough information right there to be skeptical, without needing to fact-check the answer’s wording at all. That’s the entire value of a citation: it doesn’t make retrieval more accurate, it makes retrieval’s mistakes visible instead of invisible. Chapter 1’s original complaint about ungrounded models was that a wrong answer reads exactly like a right one. A citation is a small, cheap way to break that disguise.

Parent-document retrieval: precise match, readable context

Chapter 3’s fixed-size chunking cut words in half at chunk boundaries in the name of simplicity. Metadata fixes that problem too: search using small, precise fragments, but keep a link back to the full, readable section each fragment came from, and hand that to the reader or the model instead of the choppy fragment itself.

The fragment that won the search is choppy, it cuts off mid-word, exactly Chapter 3’s boundary problem. Nobody should read that fragment directly. But because it carries a link to its parent section, what actually gets shown is the full, clean paragraph, precise matching underneath, readable context on top.

Bonus (Colab only): a real answer, with its real citation side by side

Same limitation as every chapter so far: this needs a live network call and an API key, neither available in this page. Open in Colab to run it, using a free Gemini key from aistudio.google.com/apikey:

Open In Colab

Read the answer and the source line together. If the answer states a specific number of carried-over days, and the source line says “Vacation Policy” for a question about sick leave, that mismatch is now something the reader can catch in two seconds, instead of a wrong fact quietly absorbed as truth.

Quick check

Before moving on, answer for yourself (no peeking ahead):

  1. Why does attaching metadata to a chunk not fix the underlying retrieval mistake (Vacation Policy still gets retrieved for a sick leave question)? What does it actually fix instead?

Check your reasoning ▸

Metadata doesn’t change what gets retrieved, the embedding model still confuses Vacation Policy and Sick Leave Policy for the same reason as before. What it fixes is visibility: the wrong retrieval now carries a citation that makes the mistake obvious to a reader instead of hidden inside a fluent-sounding answer.

  1. In the parent-document demo, the system searched using a choppy, mid-word fragment but displayed a clean full section. Why search with the fragment at all, instead of just always searching with full sections like earlier chapters did?

Check your reasoning ▸

Searching with small fragments keeps the precision benefit from Chapter 3: a short, focused piece of text competes more fairly for a specific fact than an entire multi-sentence section would. The parent link lets you keep that precision at search time while still handing the reader something whole and readable, instead of choosing one or the other.

  1. Suppose a chunk’s section metadata was recorded incorrectly by a bug somewhere upstream. Would you rather have no citation at all, or a citation that’s wrong? Why?

Check your reasoning ▸

Most people reason that no citation is safer: a wrong citation actively misleads a reader into trusting a mistaken retrieval, and looks more credible than an admittedly unsourced answer. This is also a good argument for validating metadata pipelines carefully, since a wrong citation can do more harm than an honest gap.

  1. This chapter shows a citation next to an answer. What would you still need to check, as a reader, even after seeing a citation that looks correct?

Check your reasoning ▸

A correct-looking citation only proves which chunk was retrieved, not that the model actually used it correctly or that the chunk fully answers the question. A careful reader should still open the source and confirm the model’s specific claim, a number, a date, a condition, actually appears in that text.

  1. Chapter 4 built a refuse-if-missing instruction so the model says “I don’t know” when context doesn’t answer the question. Does adding citations, this chapter’s fix, make that instruction less necessary, more necessary, or unrelated? Explain with a concrete scenario.

Check your reasoning ▸

A strong answer treats these as complementary, not substitutes: refuse-if-missing tries to stop a wrong answer from being generated in the first place, while citations only help after an answer already exists, by making it checkable. Concretely: if the model ignores the refuse instruction and answers anyway from an irrelevant chunk, the citation is what lets a reader actually catch that mistake, so citations stay necessary even when refuse-if-missing works, and become the primary safeguard when it doesn’t.


You can now: attach real provenance to every chunk and carry it through retrieval, the prompt, and the final answer, so a retrieval mistake becomes visible instead of invisible.

But one problem remains: every chapter so far has compared a question against a handful of chunks directly. A real system with millions of chunks still needs a way to find candidates fast, without comparing against everything.

Next → Chapter 6: Scaling Retrieval