AI Should Read the Ledger, Not Write It
Most of the damage I have seen from AI in finance functions did not come from the model being wrong. It came from the model being wrong somewhere nobody could check. The fix is not a better model. It is a boundary: code computes, the model interprets, and a human signs anything that becomes a statement of fact.
I build these systems and I run finance functions that depend on them, so I have no interest in arguing against AI in finance. The argument I want to make is narrower: a language model belongs in specific places in a finance stack, and outside those places it does damage that is slow to surface and expensive to unwind. Over the last two years I have watched the same failure repeat at different companies, and it is almost never a failure of the model. It is a failure of where the model was put.
The version I keep coming back to is a bank reconciliation. A company I advised had an agent "doing the rec" — reading the bank feed, marking payments as settled. It matched an incoming payment to an invoice for the same customer with a similar amount. Wrong invoice, wrong period. The invoice that had actually been paid stayed open, a reminder went to a customer who owed nothing, and revenue in the board pack was correct only because two errors happened to cancel. A human found it six weeks later, doing the job the agent was supposed to have replaced. When we ran the same prompt on the same data again, the model matched it correctly. That last detail is the whole problem, not a footnote to it.
Why finance is different from the rest of the company
A finance function produces numbers that other people are entitled to rely on: investors, the tax authority, auditors, the board, the founder deciding whether to make a hire. That entitlement imposes three properties on every number that leaves the function. It must be reproducible — the same inputs give the same output next week, and after the vendor ships a new model version. It must be traceable — every figure can be followed back to the source rows that produced it, because "show me" is the first thing an auditor says and the second thing a good investor says. And it must be accountable — a named person stands behind it.
Language models, as a class, have none of these properties by default. They are probabilistic: even at the most conservative settings, the same prompt can return a different answer on a different day, and it will certainly behave differently after the provider updates the model underneath you. They produce no trace unless you build one around them. And they cannot be accountable, because accountability is not a property software can have. None of this is a criticism; it is what the tool is, and why it is so good at what it is good at. A first draft of marketing copy is allowed to vary. A trial balance is not.
A finance stack is trustworthy to the extent that its numbers can be recomputed by someone who does not trust it.
The systems that hold up are the ones where the model's contribution can be removed, re-run, or overruled without the numbers changing — which tells you where the model must sit.
Where a model earns its place
There are four jobs in a finance function at which a language model is better than anything that existed before it, and I would not run a function without it in any of them.
The first is extraction. Supplier invoices as PDFs, contracts with payment terms buried on page eleven, bank statements that export something only technically a CSV — turning that mess into structured fields used to be a person's entire job, and the model does it faster and more consistently. The output is a proposal, though, and it is checked before anything is done with it: line items have to sum to the total, a due date has to fall after the issue date, and the source document travels with whatever the extraction becomes. A field that fails a check is a field a human looks at.
The second is classification. Which account, which cost centre, which customer. The model proposes an account and says how confident it is, and its accuracy is measured against history rather than assumed. Small, routine, high-confidence transactions go through with a batch review; anything large, unusual, or uncertain goes to a person. This is the job that removes real hours from a close. It is also the job where a model working from a messy chart of accounts reproduces the mess at scale, so the chart is cleaned before the model sees it.
The third is narrative. Variance commentary, the first draft of the board pack text, the paragraph in the investor update that explains why gross margin moved. Here the rule is absolute: the model is handed the numbers, it is never asked for them. The deltas are computed by code from the ledger; the model turns a table into prose. And one check catches a whole class of problems — any figure that appears in the generated text and not in the computed table fails the build. It is a few lines of code and it has caught invented percentages more than once.
The fourth is querying. "What was net burn in the second quarter, excluding the one-off legal fees?" is a question a founder should be able to ask in plain language. The right way to answer it is for the model to write the query, for the query to be shown, and for the number to come from the database. The model translates the question; it does not answer it from memory. When the number is wrong, the query is there to read, and the fix is a correction to a query rather than an argument with a chatbot.
The common thread is that the model sits at the edges. It turns unstructured input into structure on the way in and structure into language on the way out. The ledger in the middle — the thing that is actually the source of truth — is never touched by it.
Where it does not belong
Writing to the ledger is the first exclusion, and the one that sounds obvious until you look at how many "AI bookkeeping" products are built. Posting an entry is a deterministic act with a trail: this document, this account, this amount, this person, this time. A model may propose every part of that entry. The posting is done by code that has checked the proposal, or by a person who has.
Deciding a reconciliation match is the second, which is where the story at the top went wrong. Matching is a rules problem before it is a judgment problem. Exact amount plus reference plus a date window clears most of a bank feed on its own; tolerance rules for FX differences and card fees clear most of what is left. What remains is a short residue, and that is the only place the model belongs — proposing candidate matches, each with a stated reason, for a person to confirm. A model that closes a match on its own authority has been given the pen, and the pen is the thing it should never hold.
Any calculation is the third. Foreign exchange conversions, tax, revenue recognition schedules, depreciation, interest, payroll accruals. A model computing a number is a model guessing at arithmetic it could have delegated to a function that gets it right every time. The model can decide which function to call and explain the result; it does not do the sum.
The fourth is the final statement of fact. Anything that goes to the board, to investors, to the auditors, or to a tax authority as a claim about what happened carries a human signature. The model can draft every word of it. Someone with a name signs it, and that person has to be able to reconstruct any figure in it from the ledger without the model's help. If they cannot, the document is not ready.
Can this output be re-run tomorrow and match today's? Can every figure in it be traced to the source rows that produced it? When the model is wrong — and it will be — is the error caught by a check inside the system, or by an auditor outside it? And who, by name, signed off? A "no" or a "nobody" on any of the four means the model is one layer too deep.
The pattern: propose, validate, approve
Every finance tool I have built in the last two years has ended up with the same three-step shape. The model proposes: structured output, with a reason and a confidence attached, never free text that a later step has to parse. Deterministic code validates: debits equal credits, totals tie, the account exists, the date falls in the open period, the counterparty is known, the amount sits inside its historical band, the invoice number being referenced actually exists. A proposal that fails a check never reaches anyone as "done"; it reaches them as an exception, with the failed check named. And a human approves — every exception individually, and a sample of everything else.
Materiality decides how much of that approval is batch and how much is individual: below a threshold, with high confidence and every check passed, a proposal goes into a draft state and is reviewed in bulk; above it, someone looks at the specific transaction. The threshold is a finance decision, set by the person who will sign the accounts, not a parameter left at its default.
Then everything is logged: a hash of the input, the prompt version, the model identifier, the raw output, the result of every validator, who approved, and when. This log is not overhead. It is the product. It is what you hand an auditor when they ask why a transaction was booked where it was, and it is the difference between "the model said so" and an audit trail.
Prompts are code, and they need tests
The part of this that finance teams most often skip is the one an engineer would never skip. A prompt that decides where transactions get booked is production code. It lives in version control, it is reviewed before it changes, and it has a test suite. The test suite, in practice, is a golden set: a few hundred historical transactions with the correct treatment already known, drawn from the messy end of the ledger rather than the easy end. Accuracy is measured against it, per account category, and a new prompt — or a new model version — has to beat the current baseline before it goes live.
The model version matters more than people expect. Providers update their models, and a change that improves a benchmark somewhere can quietly shift how one type of transaction gets classified in your books. Without a golden set, that drift is found at quarter-end by whoever notices that software costs have migrated into professional fees. With one, it is found the morning it happens, by a test that fails.
One proportion is worth stating. In the finance tools I build, the code that talks to a model is a small fraction of the whole; validators, matching rules, reconciliation logic, and the audit log are most of it. That is the boring part, and it is what makes the tool safe to run unwatched. If most of a system is prompts, the boundary is in the wrong place.
What this means for the continuous close
I have written before about redesigning the finance function around AI — a continuous close, judgment spent on exceptions rather than transactions. That redesign only works on top of the boundary described here. A continuous close in which the automatic steps cannot be checked is not a faster close; it is a faster way of being wrong, with the errors compounding daily instead of monthly.
For a founder evaluating a tool, a vendor, or a fractional CFO who promises to automate the books, the questions follow directly. Show me the audit trail for one number in last month's pack. Show me what happens when the model provider ships an update. Re-run last month and show me it matches. The answers separate systems built for finance from demos built for a sales call, and they take fifteen minutes to get.
The point of all this is not less AI in finance. A model reading the ledger — extracting, classifying, narrating, querying — is the most productive thing to happen to a small finance team in my career, and it is the reason a two-person function can now do what a six-person one did. It just should not hold the pen. The ledger is written by code that can be checked and signed by a person who can be asked, and the model stays on the reading side of that line.