Chapter 9 · concept 5 of 6 · show all
2Tokens, cost and your first RAG
4Chunking
- 4.1Why we chunk, and the main chunking strategies
- 4.2Structure aware chunking and tables
- 4.3Chunk size, overlap and token based sizing
- 4.4Contextual chunking and late chunking
- 4.5Parent child and multi representation indexing
- 4.6Chunk metadata, provenance and the chunking debugging playbook
- Chapter overview
7Finding the right chunks
8When retrieval fails
Hallucination checks and answer verification
Check an answer after it is written: are its numbers, codes and dates in the cited sources? A simple number check passed 38 of 39 real answers on our corpus and caught 28 of 39 answers with one number changed.
A real question
The answer looks right and has a citation. How do you know it is not made up?
Check it after it is written. Pull out the numbers, codes and dates, and look for each one in the cited source. It is cheap, catches many invented values, and tells you when to retry or refuse.

The problem
Why this matters

With RAG, the model only says what the documents say.

RAG gives the model sources; it does not force it to use them. A cheap check helps: on our golden answers, a number check passed 38 of 39 correct answers and flagged 28 of 39 answers where we changed one number.
Why not the simpler way
A grounding prompt (RAG-34) lowers hallucination; it does not end it. Models still slip a wrong digit, mix two versions, or round a number the source never rounded. In domains like insurance, banking and telecom, one wrong amount is the whole answer. A check after generation catches what the prompt missed.
The idea
The idea in plain words
Verification checks the answer against the sources. Rule checks (string, number and format matching) are fast and exact but shallow. Model checks ask a second model whether each claim is supported (faithfulness, RAG-42); they are deeper, slower and need their own testing (RAG-43). Self-consistency compares several sampled answers.
A cashier counts the change twice. It does not prove the price was right, but it catches the common mistake.
How it works
Step by step
1Extract checkable facts
Numbers, amounts, dates, codes and quoted phrases. These are what users act on, and what models most often get wrong.
2Find each in the source
Look for the value in the chunk the answer cites. Normalize formats first: 3,000 and 3000 are the same.
3Act on a miss
Retry with a stricter prompt, show the answer with a warning, or abstain. For claims without numbers, use a model-based check (RAG-42).
In detail
Number check. Normalize thousands separators and decimal marks, extract numbers from the answer, and test each is in the cited chunk (or the whole context, a weaker test). Our version: 39 golden answers contain numbers; 38 had every number in the top-3 context.
Why it misses. "about 2.24 million" is a correct rounding of 2,236 thousand, but the text never writes 2.24: a false alarm. And a changed value can appear elsewhere by chance: 12 of our 39 altered answers slipped through.
Codes and dates. Same idea: product codes, error codes and dates must appear exactly.
Escalation. Pass → show. Miss → one retry with the failed value named, then abstain or show with a warning.
The code
Try it in Python
import re
def plain(s: str) -> str:
return re.sub(r"(\d)[, ](?=\d{3}\b)", r"\1", s) # 3,000 -> 3000
def unsupported_numbers(answer: str, cited_text: str) -> list[str]:
nums = re.findall(r"\d+(?:\.\d+)?", plain(answer))
src = plain(cited_text)
return [n for n in nums if n not in src]
def verify(answer, sources):
cited = " ".join(sources[int(i) - 1] for i in re.findall(r"\[(\d+)\]", answer) if 0 < int(i) <= len(sources))
missing = unsupported_numbers(answer, cited or " ".join(sources))
return ("ok", []) if not missing else ("flag", missing)
See it
What we measured
Measured · number check on expected answers against the top-3 hybrid context
Polder corpus, 34 recursive 512 token chunks, 47 golden questions, hybrid search. Expected answers from the golden set; altered answers made by adding 1 to the first number.
Measured 2026-11-04 with corpus/scripts/measure-parts-6-7.mts on the golden set's expected answers, not on model output. Checking real model answers needs model calls (not measured yet).
Try it on a real example
“What is the maximum payout for a stolen laptop?” Expected: “3,000 euros per item.”
Answer
“3000” is found in source [1]. Pass.
Choosing
Use it when, avoid it when
- Rule checks on numbers, codes and dates for every answer
- A retry or abstain path when a check fails
- Treating a passed check as proof of correctness
- Showing flagged amounts without a warning
What it costs · Rule checks: microseconds. A retry costs one more model call, only on flagged answers.
In production
Run rule checks on every answer; they cost microseconds. Log every flag with the value and the sources; a rising flag rate after a model or prompt change is a regression signal. Sample flagged and unflagged answers for human review, to learn the check's real false-alarm and miss rates on live traffic.
Watch out
Common mistakes
- Citations mean no hallucination. A cited answer can still change a number.
- A passed check means correct. 12 of 39 altered answers passed here.
- Verification needs a second model. The cheapest useful check is string matching.
Check yourself
Remember this
Remember these
- Verify answers after generation, not only before.
- Check numbers, dates and codes against the cited source.
- Our check passed 38 of 39 correct answers and caught 28 of 39 altered ones.
- It misses values that appear by chance and derived values; combine it with other checks.
Say it in an interview
- 01I verify answers after generation: numbers, codes and dates must appear in the cited source.
- 02On my golden answers, the check passed 38 of 39 correct ones and caught 28 of 39 altered ones.
- 03It misses values that appear by chance and flags derived values like roundings.
- 04A failed check triggers one retry, then abstention or a warning.
- 05For claims without numbers I add a model-based faithfulness check, tested against labels.
Quick quiz