FirmGround Ledger
A reconciliation workbench for accountants. A vision model turns receipts into ledger entries, an algorithm matches them against bank statements and accounting-system exports, and agents only handle the cases the algorithm can’t settle.
1The job
Every month, accountants check that a company’s own books agree with the outside world. That check is called reconciliation, and it is often done by hand in Excel.
Two things make it slow:
- Every source arrives in its own format. Each bank and each accounting system exports different columns, date formats and sign conventions, so the files have to be lined up before anything can be compared.
- The hard part is the exceptions. Most lines match cleanly. The time goes into the few that don’t: finding out why, and deciding what to do about each one.
2The rule
The whole design follows one rule: algorithms do the matching, agents handle the exceptions, accountants decide.
Algorithm
Every line, every time
Ingestion, matching and break classification are deterministic. Known file formats are read with zero model calls.
Agents
Only the ambiguous cases
Three narrow agents propose a mapping, a match or a cause. What they produce is a suggestion, nothing more.
Accountant
Every decision
Nothing an agent suggests is applied until an accountant confirms it. Confirmed decisions are recorded and can be replayed.
Reconciliation has to be repeatable and explainable: the same statement should produce the same result, and an auditor should be able to see why each line was matched. An algorithm gives that for free. A language model doesn’t, so it is kept to the cases where judgement is actually needed.
3The life of a statement
- Ingestsaved template, or a new mapping
- Matchexact, tolerance, fuzzy, subset
- Classify breakswhat is left gets a type
- Agent suggestsonly for ambiguous cases
- Accountant confirmsnothing applies before this
- Audit logconfirmed decisions replay deterministically
Ingest
Receipts are read by a vision-language model, which turns each one into a ledger entry. Bank statements and ERP exports are read through a format template: a saved mapping from that file’s columns to the workbench’s own fields.
A file in a known format uses its template, with zero model calls. A file in a new format goes to the format-mapping agent, which proposes a mapping. Once an accountant approves it, the mapping is saved as a new versioned template, so the next file in that format needs no model at all.
A format template, illustrated bank_hk_statement · v2 Txn Date → date (DD/MM/YYYY) Particulars → description Withdrawal → amount (negative) Deposit → amount (positive) Ref No. → referenceMatch
The algorithm runs four passes, from strictest to loosest. Each pass only sees the lines the earlier ones left.
The four matching passes Pass What it catches Example 1Exact Same amount, same date, same reference. Bank 1,240.00 on 3 Mar ↔ ledger 1,240.00 on 3 Mar 2Tolerance Small, allowed differences in date or amount, such as a payment that clears a day late. Bank 412.75 on 5 Mar ↔ ledger 412.75 on 4 Mar 3Fuzzy Descriptions that refer to the same thing but are written differently. “ACME HK LTD PMT” ↔ “Acme (Hong Kong) Ltd” 4Subset One line that equals the sum of several on the other side, such as a single deposit covering three invoices. Bank 3,900.00 ↔ ledger 1,300.00 + 1,300.00 + 1,300.00 Classify breaks
Whatever is still unmatched is a break, and the algorithm sorts each one by type. Typical breaks in reconciliation include a payment that hasn’t cleared yet, a bank fee nobody recorded, an amount that is slightly off, or an entry recorded twice.
Agent suggests
Only the ambiguous cases reach an agent. Each of the three agents runs as a ReAct agent in its own sandboxed context, so one task’s material never leaks into another’s decision. What comes back is a suggestion card, not a change to the books.
Match reviewSuggestionBank↔2,015.3015 Mar · ACME HK LTDLedger2,015.0014 Mar · Acme (Hong Kong) LtdSame customer, one day apart, 0.30 difference. Likely a currency-conversion rounding difference. Suggest: match, and post 0.30 to exchange differences.
ConfirmRejectIllustration with synthetic figures. Accountant confirms
The accountant confirms or rejects each suggestion. Until then the agent’s output stays a suggestion: nothing is posted, matched or closed on an agent’s word alone.
Audit log
Every confirmed decision is recorded. Because the steps before it are deterministic, replaying the log on the same files reproduces the same result. That gives a full audit trail: for any line, you can show how it was matched and who approved it.
4The three agents
Each agent has one job, its own tools and its own isolated context.
| Agent | When it runs | What it returns |
|---|---|---|
| Format mapping | A statement or export arrives in a format with no saved template | A proposed column mapping |
| Match review | A line has candidate matches the algorithm can’t settle on its own | A suggested match, with its reasons |
| Root-cause investigation | A break is left unexplained after matching and classification | A likely cause, with the evidence behind it |
Every output in the right-hand column goes to an accountant as a suggestion. None is applied automatically.
5Result
- Manual Excel bookkeeping
- 100
- FirmGround Ledger
- < 40
10 multi-page bank statements. The Ledger time includes the accountant’s review and correction.
On 10 multi-page statements, processing took more than 60% less time than manual Excel bookkeeping, counting the time to review and correct the output.
Not measured yet
- How much reaches an agent. The share of lines that the algorithm settled on its own, and the share that needed an agent.
- How often suggestions are accepted. The share of agent suggestions accountants confirm unchanged, by break type.
- Scale. Ten statements is a small test. A larger set, across more banks and formats, would show how the time saving holds up.
6Trade-offs
Why not ask a model to match every line?
Most lines match exactly. An algorithm settles them faster, at no cost, and gives the same answer every time, which is what an audit trail needs. A model would add cost and variation to the easy cases without improving them.
Why three agents instead of one?
Each task needs different tools and different material. Mapping a file format, reviewing a match and investigating a break have little in common. Keeping them in separate contexts stops one task’s material from influencing another’s decision, and makes each agent’s behaviour easier to test.
Why make every suggestion wait for a person?
It is slower than letting agents post changes directly. But an accountant signs off on the books, so they need to see and approve every judgement call. The time result above already includes that review.