You automate narrative financial reporting by splitting the work in two. The reporting pipeline produces reconciled figures with lineage, and the model writes prose that cites those figures and nothing else. It never recalculates a total or infers a variance. A post-generation check reads each cited cell back from the schedule and fails any draft whose numbers have drifted.
A board pack is mostly the same document every month with different numbers in it. The variance analysis, the run-rate commentary, the “why did headcount cost move” paragraph: the structure is fixed, the figures change, and an analyst spends an afternoon retyping context around numbers that were finalised that morning. Separating the prose from the arithmetic is what makes the afternoon disappear.
That separation is the whole game. A model asked to generate management commentary from a prompt describing your business will write fluent, plausible sentences containing numbers it made up. The dangerous failure is fluent. A confident sentence says gross margin improved 180 basis points when the schedule says 130, and a board reads it as fact.
Ground the prose in a resolved figure table
Before any text gets generated, the reporting pipeline has to hand the model a table it cannot argue with. Every number that could appear in the commentary is computed once, reconciled against the ledger, and given an identifier. Revenue by segment, the prior-period comparative, the budget line, the resulting variance, all of it lives in one resolved structure with a reference on each cell.
The model does not see your general ledger. It sees this table. When it writes “EMEA revenue grew 12% against budget,” that 12% is emea.revenue.var_vs_budget_pct, a value the pipeline already calculated and tied back to source. The prose renders the table; it makes no independent claim about the business.
- Point-in-time correctness matters here as much as in a model feature store. The figures must reflect the ledger as of the reporting cut, not as of generation time, or a late journal entry silently changes a number the commentary already described.
- Reconciliation happens before generation, never inside it. If the segment revenues do not sum to the consolidated total, the pipeline stops. You do not want the model papering over a break with a smooth sentence.
- Entity resolution earns its keep when the same cost centre is “Sales - EMEA” in one system and “EMEA Commercial” in another. The commentary refers to one canonical entity because the figure table already resolved the two.
Apply the same discipline you would apply to a training set. Leakage here is the model reaching for a number that was not in the reconciled table: a value it half-remembers from the prompt, or a total it recomputed on its own. Both are defects, and the figure table exists so the model never has to reach.
Make every claim cite its figure
Grounding the input is half the design. The other half forces the output to carry its references so you can check it. We have the model emit commentary where each numeric claim is tagged with the cell reference it came from. Internally the sentence looks like “gross margin improved [gross_margin.var_bps] basis points,” and the renderer substitutes the resolved value at the end.
This gives you a mechanical check that runs on every draft before a human reads it:
- Take each tagged claim, re-read the referenced cell from the figure table, and confirm the rendered number matches. A drift of even one basis point fails the draft.
- Flag any numeric literal in the prose that has no reference tag at all. An untagged number is a number the model produced from nowhere, and it does not ship.
- Check direction words against the sign of the underlying variance. If the figure fell and the sentence says “grew,” that is a caught error, not a style note.
None of this judges whether the commentary is insightful. It judges whether the commentary is true to the numbers, which is the one property a board pack cannot be wrong about. Whether the analyst agrees with the story the draft tells is exactly what you want a human spending the afternoon on instead of retyping figures.
Keep a held-out set of past reporting periods with their signed-off commentary. When you change the prompt, the figure schema, or the model, run generation against those periods and diff the output. This is your eval set, and it is the only honest way to know whether a change that looks better on this month’s pack quietly broke last quarter’s.
Keep the audit trail and a human in the loop
Board and management reporting is reviewed, and sometimes it is examined after the fact. That shapes what you have to retain. For every published narrative, keep the figure table it was generated from, the model version, the prompt, and the reference-check result. If someone asks in nine months why the March commentary described a margin movement a particular way, you can reconstruct exactly what the model saw and what it was told.
Straight-through processing is the wrong goal for this document. The pipeline should change what the analyst does rather than remove the analyst. It delivers a cited draft that has already passed the numeric check, and the analyst reads for judgment. Does this variance need operational context the numbers cannot show? Is the model burying a movement that deserves its own paragraph? Their edits are the review that matters, because a person who understands the business signs off on the story instead of proofreading arithmetic.
- Route drafts by materiality. A routine month with small variances needs a light read; a period with a restructuring charge or an acquisition gets a full one, and the pipeline should surface which is which rather than treating every pack the same.
- Track where analysts consistently rewrite. If the model keeps mischaracterising the same recurring item, that is drift you can fix in the prompt or the figure schema, and the edit log is where you see it.
- Version the template alongside the code. When the CFO wants the commentary to lead with cash rather than revenue, that is a change you make once, test against the held-out periods, and ship, not a reformat every analyst does by hand.
The commentary that used to take an afternoon becomes a review that takes twenty minutes, and the number in the third paragraph is right because the pipeline proved it against the schedule, not because someone remembered to double-check.
FAQ
Can the model read numbers straight from our data warehouse?
No. It reads a resolved figure table that your reporting pipeline already computes and reconciles. The model composes prose around numbers it is handed, so it never recalculates a total or invents a variance.
How do we stop the commentary from stating a number that disagrees with the schedule?
Every figure the model uses carries a cell reference, and a post-generation check re-reads that reference and compares it against the sentence. A mismatch fails the draft before a human sees it.
Does this replace the FP&A analyst who writes the board pack?
It replaces the copy-and-arrange part of their afternoon. The analyst still sets the story, decides what matters, and signs off, but starts from a cited draft instead of a blank page.