The grounded prompt
From passages to a cited answer
Grounding is mostly one disciplined prompt: number the passages, hand them to the model, and demand a citation on every claim.
- Number the evidence. Each reranked passage becomes a block
[n] (source_id) text— the numbernis just its position in the ranked list, and it’s the anchor everything hangs off. - One blunt instruction. The system prompt says: answer only from these passages, cite every claim with
[n], and if they don’t cover it, say so. - Parse what comes back. A single regex,
\[(\d+)\], pulls the markers out of the reply and maps each one to the chunk it points at — and itssource_id, e.g.claim/88431. - A hallucinated citation can’t resolve. A
[9]when only three passages were retrieved is out of range, so it’s dropped — a citation always lands on a real record or it isn’t a citation.
Step through the assembly below, then click a [n] in the answer to trace it: marker → chunk → source record. That chain is exactly what parse_citations builds into a Citation(marker, chunk_id, source_id).
Retrieval handed us three ranked passages. Step through how they become a prompt, then click any [n] in the answer to trace it back to a real record.
A citation is a pointer, not decoration. [2] means this sentence came from passage 2 — claim/88431 — so a reviewer can open the record and check it.
Same three records. Ask something they cover and the score clears the bar and the cited answer ships. Ask something they don’t and the score drops — and the system says so instead of guessing.
↳ Every sentence's wording is backed by a passage, so the score clears the bar and the cited answer ships.
This mirrors the trace the guardrails node writes to the audit log — the reason an answer was withheld is recorded, not hidden.
The prompt lane in that toy isn’t a mock-up — it’s literally what build_prompt emits: the rules block, the numbered passages, then the question and the answer cue.
And parsing the reply is one small, unglamorous loop — the part that turns a [n] into a real record id, and quietly drops any marker that points nowhere:
The ground node just wires those two together and records a trace of what it cited:
Why grounding
Confident is not the same as correct
A base model will happily answer from memory — fluent, plausible, and with no idea whether it’s true for this policy.
- Grounded — answers from the retrieved POL-55012 records, cites
[1][2][3], and its claims trace back to the claims and policy systems of record. Checkable. - Ungrounded — no retrieval, no citations. It invents a plausible reason (“likely a rate increase across your region”) that has nothing to do with this policy’s actual claim.
- Same fluency, different trust. Both read well. Only one can be verified against a system of record — and in insurance, unverifiable is unusable.
- Grounding is a constraint, not a model upgrade. The win comes from restricting the model to the evidence, not from a bigger model.
The abstain gate
When the records don’t cover it, say so
A grounded system’s best answer is sometimes no answer — and that’s the feature, not the bug. The groundedness meter in the toy above is this gate: pick the multi-policy discount question and watch the score fall below the bar.
- Score how supported the answer is. A groundedness score (0–1) measures how much of the answer is actually backed by the retrieved passages.
- Below the bar → abstain. If the score falls under
settings.groundedness_threshold, the answer is dropped and replaced with a safe “I don’t know.” - The default score is deliberately simple. It’s a transparent lexical-overlap check (does each sentence’s wording appear in a passage?) — fast, deterministic, easy to defend in an audit. A production deploy swaps in an LLM-judge or RAGAS; the gate stays the same.
- Abstaining is what makes it shippable. In a regulated shop, “I’m not sure” beats a confident wrong answer that ends up in an underwriting decision.
The ground node produces the cited candidate answer — it does not abstain. The abstain decision fires one node later, in the guardrails node, which scores groundedness and — if the score is under the threshold — withholds the answer. Grounding sets it up; guardrails pulls the trigger. That gate is the subject of Post 06.
The meter is a proxy — it checks how much of the answer’s wording is supported by the passages, not deep meaning. That’s on purpose: cheap, deterministic, and easy to explain when an auditor asks why did it refuse?
This whole post is one repo at one tag. Clone it, ask a question the seed data covers, and you get a grounded, cited answer on your laptop — zero cloud, no keys. Then ask something it doesn’t cover and watch it abstain.
Teaching-grade reference implementation, not a production insurance product. It reproduces the ideas — grounded prompting, citation parsing, and a transparent groundedness gate; bring your own data and keys. MIT-licensed.
Explain it back
An underwriter asks your system a question whose answer isn’t in any retrieved record. What should it do, and why is that the “senior” behavior?
Reveal a model answer
It should abstain — return a plain “I don’t have enough grounded information to answer this reliably” instead of a guess. Grounding means the model may only answer from the retrieved passages; if those passages don’t support an answer, the groundedness score falls below the threshold and the guardrails node withholds it. In a regulated domain a confident wrong answer is worse than no answer: it can end up in an underwriting or claims decision with no source to check. Abstaining, plus citing every claim it does make, is what turns a demo into something an insurer can actually put in front of a user.
We just saw a single answer abstain. Post 06 turns that into a gate: a groundedness eval wired into CI that blocks a release when faithfulness drops, plus the prompt-injection and PII screens that run on every answer.
Continue to Evals & guardrails →