Chapter 110 · Evaluation

Measuring groundedness and catching confident invention

Turning a judgement into an enumeration. Claim extraction, per-claim verification, the four kinds of invention, and why the fluent wrong answer is the one that reaches a customer.

8 min read

Chapter 106 found the suite's worst-calibrated criterion: no-invention, raw agreement 0.95, kappa 0.48. A judge barely better than always saying nothing was invented, scoring the property the whole system exists to control.

The fix was structural rather than a better prompt, and this chapter is that fix.

Why asking directly fails

// The question that produces 0.48. const BAD = `Does this response contain any information not supported by the sources? Answer yes or no.`

It asks for a judgement over the whole response at once. The response is twelve sentences, eleven of which are perfectly grounded, and the model's overall impression is of a grounded answer — which it then reports. Invention is rare, local and surrounded by correct material, and a global judgement averages it away.

A rare local defect cannot be detected by a global impression. The instrument has to look at the same granularity as the defect.

Claim extraction, then per-claim verification

// Two stages. The first produces a list; the second checks each item. // Neither stage is asked to form an overall opinion. interface Claim { text: string // one verifiable assertion, quoted or paraphrased minimally kind: 'figure' | 'date' | 'name' | 'policy' | 'relation' citation?: number }
const EXTRACT = ` List every factual claim in the RESPONSE as a separate item. A factual claim states something about the world that could be checked: a figure, a date, a name, a policy, or a relationship between two things. Do not include hedges, offers to help, or restatements of the question. Split compound sentences into one claim each. RESPONSE: {{response}} Output JSON: {"claims": [{"text": "...", "kind": "...", "citation": 2}]} `
// Then each claim, independently, against only its cited source. Running // it against all sources lets a claim be "supported" by a document the // answer never pointed at, which is a different and weaker property. const VERIFY = ` Does the SOURCE state the CLAIM? Answer exactly "stated", "contradicted", "absent", or "partial". "stated" requires the source to assert the claim, not merely to be about the same subject. A source discussing the topic without asserting the claim is "absent". SOURCE: {{source}} CLAIM: {{claim}} `

Kappa went from 0.48 to 0.76, against a human ceiling of 0.79. The criterion went from uninformative to near the limit of what two people agree on, and nothing about the model or the prompt wording carried that — it was the decomposition.

// The score is now a ratio with a denominator, which is far more useful // than a boolean: "9 of 11 claims stated, 1 absent, 1 partial". function groundedness(claims: Verdict[]): number { return claims.filter((v) => v === 'stated').length / Math.max(claims.length, 1) }

Four kinds of invention

Enumerating claims made the failure modes visible and countable. Over 600 Atlas answers:

KindShare of invented claimsExample
Plausible specifics46%A figure the source does not give
Bridging inference27%Two true facts joined by an untrue "therefore"
Confident generalisation18%"Customers in this tier typically..."
Fabricated provenance9%A citation to a document that exists, for a claim it does not make

Plausible specifics dominate and are the hardest to notice by reading. The source says "returns are assessed individually after the standard window"; the answer says "returns are assessed individually after 30 days". Thirty is correct elsewhere in the corpus, the sentence is fluent, and nothing signals that the number was supplied rather than found.

// The cheapest high-yield check in the whole suite, and it needs no model. // Every number, date and proper noun in the response must appear in the // cited sources. It catches most of the 46%. export function unsourcedSpecifics(response: string, sources: string[]): string[] { const joined = sources.join(' ') return extractSpecifics(response).filter((s) => !joined.includes(s)) } function extractSpecifics(text: string): string[] { return [ ...text.matchAll(/\b\d[\d,.]*%?\b/g), // figures ...text.matchAll(/\b(?:\d{4}-\d{2}-\d{2}|[A-Z][a-z]+ \d{1,2}, \d{4})\b/g), // dates ].map((m) => m[0]) }

Atlas runs this on every production answer, not only in the suite. It costs nothing, it fires on 0.9% of answers, and a firing answer is flagged for review rather than blocked — chapter 62 made the same choice about entailment, for the same reason.

Bridging inference is the one no mechanical check reaches. Both facts are in the sources; the connective is not. It needs the per-claim judge, and relation claims are the kind it scores worst on.

Groundedness against correctness

They are different properties and conflating them hides a whole failure class.

// Four quadrants, and two of them are bad in different ways. // // grounded not grounded // correct ✅ the goal ⚠️ right by luck // incorrect ⚠️ the source ❌ invention // is wrong
QuadrantAtlasWhat it means
grounded + correct91.2%
grounded + incorrect4.8%The corpus is wrong, not the model
not grounded + correct2.6%Right from training data, not from your data
not grounded + incorrect1.4%Invention

The 4.8% is the row teams do not expect and it is the larger problem. The system faithfully reported a superseded document, an out-of-date price list, a policy page nobody had updated. No amount of model work fixes it; it is chapter 19's problem, surfaced by an eval suite that distinguishes the two.

The 2.6% is quietly dangerous. The answer is right and came from the model's training rather than from the company's documents, which means it will be wrong the moment the company's answer differs from the general one — and it has already demonstrated that it will answer without a source.

Reporting it

// Three numbers, never one. A single "groundedness: 0.96" collapses the // quadrant table into something unactionable. interface GroundednessReport { claimLevel: number // share of claims stated by their cited source answerLevel: number // share of answers with every claim stated inventionRate: number // share of answers with >=1 'absent' claim bySource: Record<string, number> // which sources produce ungrounded answers }

bySource earns its place. Atlas's ungrounded answers concentrated heavily in two sources — the scanned PDF archive and an email export — at four times the rate of the rest. That is a parsing and chunking finding arriving through the eval suite, and it redirected work away from the prompt entirely.

Key points

  • Asking a judge whether a response contains invention scores kappa 0.48. A rare local defect cannot be found by a global impression.
  • Extract claims, then verify each against its own cited source. Kappa 0.76 against a human ceiling of 0.79, from decomposition alone.
  • Verify against the cited source, not all sources. Supported-by-something-else is a weaker property.
  • Four kinds of invention: plausible specifics (46%), bridging inference (27%), confident generalisation (18%), fabricated provenance (9%).
  • Check that every number and date in a response appears in the cited sources. It is free, catches most of the 46%, and runs on every production answer.
  • Bridging inference needs the per-claim judge. No mechanical check reaches a false connective between two true facts.
  • Separate groundedness from correctness. Atlas's 4.8% grounded-and-wrong is a corpus freshness problem no model work fixes.
  • The 2.6% ungrounded-and-right came from training data. It is right today and has shown it will answer without a source.
  • Report claim-level, answer-level, invention rate and a by-source breakdown. The by-source column sent Atlas's work to parsing rather than prompting.

Next: measuring refusal — the property with the lowest human agreement in the whole rubric.