Skip to content

tutorial

Chapter 0 of 6

The Document Is Derived, Never Written

by Rod Rivera Published

Ask an agent for a document and it writes one, with figures nobody can trace. Build the other thing: the conversation edits state, the document is derived.

Here is what a capable model produces when you ask it for a client suitability record. This is not a strawman — it is the good version, from a careful prompt:

SUITABILITY RECORD — Marged Ellis
Portfolio: Ellis Family Income

Your portfolio is currently valued at approximately £486,000, held across
a balanced mix of global equities (around 58%), sterling corporate bonds
(roughly 30%), and property funds (about 12%). This allocation remains
well suited to your Balanced risk profile and your objective of drawing a
steady income from 2028.

Ongoing charges of 0.62% and an advice fee of 0.45% apply.

That is fluent, correctly formatted, and plausible in every particular. It is also unusable, and the reason is not a mistake you can point at.

Nothing in it is wrong. That is the problem

Read it again with one question in mind: which of those numbers came from a record, and which came from the model?

You cannot tell. Neither can the client. Neither can the compliance officer who reads it in eighteen months. Every figure is stated in the same confident register, and the register is the only evidence on offer.

Now the specifics:

  • “approximately £486,000” — the extract says £486,210.44. Something rounded it, and nothing recorded that it did.
  • “about 12%” — the property funds line is 11.9%. Close. Also: this portfolio’s property holding was never sourced at all in our fixture, so a model producing this sentence has produced a figure for a position it was never given.
  • “remains well suited to your Balanced risk profile” — this is a suitability determination. A sentence a regulator reads as a professional judgment, generated by next-token prediction.
  • “Ongoing charges of 0.62%” — correct, as it happens. Indistinguishable from the ones that are not.

The document is not false. It is unfounded, which is worse, because false documents get caught and unfounded ones get filed.

The missing piece

Every figure needs to carry the record it came from, and a figure with no record must be incapable of appearing — not discouraged, not flagged in review, incapable.

That rules out the obvious architecture. If a model writes the document text, then provenance is at best an annotation the model also writes, and a model that can write a footnote can write a wrong one. The fix is not a better prompt or a checking pass. It is to take the pen away.

What you will build

An agent that assembles a suitability record and cannot write a word of it.

The conversation edits structured state — a declared set of fields, each one pointed at a source record. The document is a pure function of that state, recomputed from scratch every time anyone asks for it. Delete a field and it disappears from the document. Point a field at a record that does not exist and it renders blank, and the blank is reported.

By the end you will have run this, and understood why it is the load-bearing test:

REFUSED: 1 figure(s) no longer match the record they cite. The document was
not rendered.
  total_value: document says '486210.44', source says '911000.0'
      cited as: Custodian position extract · VAL-2026-08-29-PF4402 · total_value_gbp

The custodian restated the valuation after those figures were read into state. Every figure in the document was still footnoted; one footnote had quietly stopped being true. The renderer produced nothing at all.

The six steps

StepWhat it teaches
Artifact as outcomeThe deliverable is a file, not a reply, and that changes the design
State, not proseFields live in declared memory; the model negotiates, tools write
Grounded fieldsEvery figure traces to a record. Unsourced renders blank, never plausible
Derived renderingTemplate plus state equals document, recomputed on the far side
Revision as diffAn edit changes state and re-renders; history is append-only
Stated limitsWhat it refuses, in the same words the code uses

Before you start

Clone the companion repository. Chapters 1 through 5 need no credentials, no network, and no model — the proof runs under bare python3. Only the last chapter talks to the agent.

cd tutorials/rasa-document-artifact-tutorial
make prove

If that exits zero, you have everything you need.