Chapter 2 of 7 · 13 min read
Fluent prose passes review; unsupported claims survive it
On this page
Faithfulness, from the previous chapter, is the share of an answer's claims that the retrieved passages entail. That definition names two objects a procedure has to produce before any number exists: a list of claims, and a verdict for each one against the evidence. Neither falls out of an answer on its own. An answer is prose, written to be read as a whole, with assertions welded together by pronouns and shared context.
This chapter is about producing those two objects, and it starts from the failure that makes the procedure worth its cost. Teams reviewing answers by hand, carefully and in volume, report a healthy system while an audit of the same answers finds one in eight carrying a claim no passage supports. The two processes are not measuring the same thing badly. They are measuring different things, and only one of them is looking at the passages.
What a reviewer is actually checking
Casual review is fast because it runs on two cheap signals. The first is fluency: does this read like a competent reply, without contradictions, hedging tangles or broken references. The second is topical fit: is this about the thing that was asked. Both signals are computed from the question and the answer alone, which puts a queue reviewer in the same position as the judge from chapter 1 that never sees the passages.
Follow that one step and the failure stops being a lapse and becomes a prediction. A language model's characteristic failure is producing text that reads as competent and on-topic while asserting something it was not given. The output of that failure is therefore, by construction, the input that scores highest on fluency and topical fit. The two signals do not merely miss an invented claim; they are constant in the property that separates an invented claim from a supported one, since neither signal reads a passage. A reviewer running them is not being careless, and running them more carefully does not help.
Here is an answer that a queue view passed twice.
Q: "Can I export audit logs to S3 on the Business plan?"
retrieved[0] (p-2071, security/audit-logs.md)
"Audit log export is available on the Business and Enterprise plans.
Exports are delivered to a customer-owned S3 bucket."
retrieved[1] (p-2072, security/audit-logs.md)
"Configure the destination bucket under Settings > Security > Audit Log
Export. The bucket policy must allow s3:PutObject for our export role."
answer:
"Yes — audit log export to S3 is available on the Business plan. Configure
the destination under Settings > Security > Audit Log Export, and make sure
the bucket policy allows s3:PutObject for our export role. Exports run
hourly and are retained in your bucket indefinitely. [p-2071, p-2072]"The last sentence is invented. Nothing in either passage mentions an export cadence or a retention behaviour, and the real cadence is daily at 02:00 UTC. A reviewer reading that answer in a queue sees a response that is correct in its first three claims, written in the same register as the rest of the documentation, arranged in the order a good answer would be arranged, and ending on the kind of operational detail that a well-informed reply tends to include. There is no textual signal of the fabrication, because the fabricated sentence was produced by the same model, in the same style, conditioned on the same prompt as the sentences that are true.
There is a third thing reviewers do, which is check claims against their own knowledge, and it is the one that makes the process feel rigorous. It fails for a different reason. A support engineer who has worked on the product for two years reads "exports run hourly" and either knows it is wrong, in which case they catch it, or has no strong belief about the export cadence, in which case the sentence slides past. Product knowledge covers the claims a reviewer happens to know, which is a shrinking fraction of the corpus as the corpus grows, and it never covers the case that matters most for a RAG system: a claim that is true in the world but appears in no passage the pipeline retrieved. That answer is fine for this user and is evidence that the pipeline is answering from model weights rather than from the corpus, which is a property you very much want to know about before the corpus changes and the weights do not.
| Casual queue review | Claim-level audit | |
|---|---|---|
| Inputs on screen | question, answer | question, answer, the retrieved passages |
| Unit judged | the response as a whole | each atomic claim |
| Catches fabrication | only when the reviewer independently knows the fact | always, when no passage entails the claim |
| Catches unsupported-but-true | no | yes, and labels it as unsupported |
| Time per answer | 15–25 seconds | 3–5 minutes at 5–7 claims |
| Throughput per reviewer-day | ~1,000 answers | ~100 answers |
The last two rows are one fact stated twice, and checking that they agree is worth thirty seconds, because a throughput figure that does not follow from a per-answer time is a figure somebody guessed. A thousand answers at twenty seconds apiece is a little over five and a half hours of continuous reading, which is a working day with breaks in it. A hundred answers at four minutes apiece is a little under seven hours, which is the same working day. Both rows are the same day divided by two different per-answer costs, so the tenfold throughput gap is the tenfold time gap and nothing else — there is no economy of scale hiding in either column.
That gap is why nobody audits by hand at volume, and it is also why the audit is the thing you build once and then automate. A 150-answer audit took a little under ten reviewer-hours spread over two people, which is a shade under four minutes an answer and sits inside the 3–5 minute band the table quotes, arrived at from the other direction. Running the same procedure weekly on 200 answers is therefore about thirteen reviewer-hours a week — a third of a headcount if each answer is labelled once, and most of a headcount once each answer is labelled independently by two people and their disagreements adjudicated, which chapter 5 shows is what it takes to trust the labels at all. Either way it is a standing commitment rather than a task, which is the point at which you replace the human with a judge — and the only way to trust that judge is to have the human labels to compare it against, which is chapter 5's problem.
Splitting an answer into claims
A claim is atomic when it asserts one thing and can be judged true or false against a passage without reference to the rest of the answer. That definition does real work, because natural answers bundle several assertions into a sentence and rely on pronouns and ellipsis to carry context between them. Decomposition resolves those references and separates the assertions.
The audit answer above decomposes into five claims:
{
"claims": [
{"id": 1, "text": "Audit log export to S3 is available on the Business plan.",
"verdict": "supported", "evidence": "p-2071: available on the Business and Enterprise plans"},
{"id": 2, "text": "The export destination is configured under Settings > Security > Audit Log Export.",
"verdict": "supported", "evidence": "p-2072: Configure the destination bucket under Settings > ..."},
{"id": 3, "text": "The destination bucket policy must allow s3:PutObject for the export role.",
"verdict": "supported", "evidence": "p-2072: must allow s3:PutObject for our export role"},
{"id": 4, "text": "Audit log exports run hourly.",
"verdict": "unsupported", "evidence": null},
{"id": 5, "text": "Exported audit logs are retained in the customer's bucket indefinitely.",
"verdict": "unsupported", "evidence": null}
]
}Four decisions in that decomposition are worth stating as rules, because they are where two people doing the same audit diverge. Pronouns and definite references are resolved against the answer, so "make sure the bucket policy allows" becomes "the destination bucket policy must allow" and can stand alone. Coordinated assertions are split, so a sentence carrying a cadence and a retention behaviour yields two claims rather than one, which matters because they can have different verdicts — and in this answer they do not have different verdicts but do have different truth values, which is the distinction the last section returns to. Non-assertive text is dropped rather than scored: greetings, "let me know if that helps", and the restatement of the question contribute no claims, and leaving them in inflates the denominator and flatters the score. Hedges are kept with the claim they modify, so "exports are typically delivered within an hour" is judged as a claim about typical delivery time, not silently strengthened into a guarantee.
The verdict vocabulary matters as much as the split. Three values are enough: supported, when the passage entails the claim; contradicted, when a passage asserts the opposite; and unsupported, when no passage speaks to it either way. Collapsing the last two into "wrong" loses the distinction that drives the fix, because contradiction means the generator read a passage and departed from it, while unsupported usually means the generator filled a gap the passages left. Across the 18 failing answers in the audit, 21 claims were bad and only 2 of them were contradictions; the other 19 were unsupported additions of exactly the shape of claims 4 and 5. That ratio is typical, and it is why teams who go looking for the model "disagreeing with the docs" find almost nothing and conclude the system is healthy — they are searching the smaller of two categories, at about one bad claim in ten.
With the claim list in hand, faithfulness for this answer is 3/5 = 0.60. The score is a ratio over claims rather than over answers, which has a consequence worth planning for. Hold the number of fabrications fixed at one and vary the length of the answer: a long answer with one fabricated sentence among twelve supported ones scores 0.92, and a terse two-claim answer with one fabrication scores 0.50. The user experience is identical in both cases — one sentence they should not act on — and the metric differs by forty-two points, purely because the denominator grew. If you report the mean of per-answer faithfulness, verbose answers are structurally advantaged, and a prompt change that makes answers longer will improve the metric without changing the fabrication rate at all. Reporting the fraction of answers containing at least one unsupported claim — 12% in the audit, which is 18 of 150 — is the number that corresponds to what a user experiences, and most teams end up tracking both.
Two calls beat one
Automating the procedure has one design decision in it. The obvious implementation hands a judge the answer and the passages in a single call and asks for the whole JSON object above. The version that holds up separates the two jobs, because they need different inputs and they fail differently.
def decompose(answer: str, judge: Judge) -> list[str]:
# sees the answer only: splitting must not be influenced by the passages
return judge(DECOMPOSE_PROMPT.format(answer=answer))["claims"]
def verify(claim: str, passages: list[Passage], judge: Judge) -> Verdict:
numbered = "\n\n".join(f"[{p.id}] {p.text}" for p in passages)
return judge(VERIFY_PROMPT.format(claim=claim, passages=numbered))
def faithfulness(answer: str, passages: list[Passage], judge: Judge) -> float:
claims = decompose(answer, judge)
verdicts = [verify(c, passages, judge) for c in claims]
scored = [v for v in verdicts if v.label != "non_assertive"]
return sum(v.label == "supported" for v in scored) / len(scored)Keeping the passages out of decompose prevents a specific and hard-to-notice
degradation: a judge that can see the evidence while splitting will quietly
split along the evidence, merging an unsupported clause into a supported
neighbour so the resulting claim is entailed as a whole. On the audit answer, a
single-call judge produced four claims rather than five twice out of ten runs,
having folded "exports run hourly and are retained indefinitely" into a claim
about export configuration, and scored the answer 0.75 instead of 0.60.
Both of those scores are correct arithmetic over their own claim lists, which is what makes the degradation hard to see. Three supported out of five is 0.60 and three out of four is 0.75; nothing miscounted. What moved was the denominator, and it moved because the splitter was allowed to see which way each split would score. A metric whose unit of measurement is decided by the same model that grades the units is a metric with a free parameter in it, and the fix is to take the parameter away rather than to instruct the model not to use it.
Verifying one claim at a time costs more — five calls instead of one, each carrying the full passage set at roughly 900 input tokens — but it keeps each verdict independent and gives you a per-claim record you can sample when the aggregate looks wrong. The bill is worth working out once, because it decides whether this runs per build or per quarter. One decomposition call plus one verification per claim is six calls for a five-claim answer, and six calls each carrying about 900 input tokens is 5,400 input tokens per answer, which is exactly the figure the whole thing runs at. So a 400-question run costs a few dollars on a mid-tier judge model and finishes in six or seven minutes at a concurrency of 8. That is cheap enough to attach to every build, which is the bar a component metric has to clear to matter.
An unsupported claim is not a false claim
Claims 4 and 5 are both unsupported; claim 4 is also false, since exports run daily, and claim 5 happens to be true, because the file does stay in the customer's bucket until the customer deletes it. The procedure in this chapter cannot separate those, and it is not supposed to — it measures whether the pipeline's output is entailed by the evidence the pipeline retrieved, which is the property that makes the citation on the answer mean something. Establishing that claim 5 is true requires a person who knows the product or a reference answer, and that is a correctness check with a different reference, in the sense the previous chapter set out.
Decomposition is also silent about anything the answer left out. A response that says only "Yes, audit log export to S3 is available on the Business plan" scores 1.00, having made one supported claim, while omitting the bucket policy requirement that will cause the user's first export to fail. Faithfulness has no notion of completeness, so a system that is trained, prompted or tuned toward it drifts toward saying less, which is a real and observable dynamic when teams put faithfulness on a dashboard as the primary generation metric. Answer relevance pushes back on that a little, since a thin answer scores lower on how directly it addresses the question, but neither metric is a coverage measure.
What decomposition does deliver is a change in what is on the screen when a judgement is made, and that is the whole of its value. Fluency and topical fit are properties of two texts; support is a property of three. A process that holds two of them cannot report on the third however carefully it is run, however many answers it covers, and however well the reviewers know the product. Adding the passages and cutting the answer into units small enough to be checked against them is what converts a review that feels thorough into one that can be wrong in a way somebody would notice.
Everything in this chapter assumed the reviewer, human or model, had exactly two things in front of them: the claims and the retrieved passages. That constraint is easy to state and surprisingly easy to break once the audit becomes code, because an eval harness naturally has more than two things lying around — the question, the answer, the context, and the gold answer written for correctness scoring — and passing all of them to every metric is the obvious way to write it.
RAG-specific metrics — read the current version at https://daviddominguez.dev/learn/en/rag-specific-metrics/fluent-prose-passes-review-unsupported-claims-survive-it