Skip to main content

An agent that reconciles two ledgers, and shows its work

· 2 min read
Vivek Gudapuri
Building Distri in public

Reconciliation is the kind of work agents are actually good at: mechanical, high-volume, and easy to get subtly wrong. The trick is not just doing the match, it's making the result legible, so a human can trust it in ten seconds.

Here's a recorded run against a real grid: an internal ledger and an external export, matched by reference, with every disagreement called out.

Recorded run, run_reconciliation, then highlight_discrepancies, then add_note. The narrated figures match exactly what the grid computes.

The agent pairs 16 records by reference and lands on the honest summary: 6 matched, 1 discrepancy, 2 unmatched, with the internal ledger at $27,146.80 against the external $26,621.80, a $525.00 gap. Then it does the part that builds trust: it explains where the gap comes from.

  • INT003 / EXT003, same PO, but $5,000.00 booked vs $4,800.00 on the export. A $200.00 mismatch, flagged with a note right on the row.
  • INT007 ($450.00) and EXT008 ($125.00, reference UNKNOWN) have no counterpart at all.

$200 mismatch + $450 unmatched internal − $125 unmatched external = the full $525.00 variance. Nothing hand-waved.

The tools are just the grid's methods

The grid already knows how to reconcile itself and describe its state. The agent gets those capabilities as tools:

export const getReconciliationTools = (gridRef): DistriFnTool[] => [
{
name: 'run_reconciliation',
description: 'Match internal and external records by reference',
parameters: { type: 'object', properties: {} },
handler: async () => JSON.stringify(gridRef.current.runReconciliation()),
},
{
name: 'highlight_discrepancies',
description: 'Visually highlight discrepancies and unmatched records',
parameters: { type: 'object', properties: {} },
handler: async () => {
const ids = gridRef.current.highlightDiscrepancies();
return `Highlighted ${ids.length}: ${ids.join(', ')}`;
},
},
{
name: 'add_note',
description: 'Attach an explanation to a specific record',
parameters: {
type: 'object',
properties: { record_id: { type: 'string' }, note: { type: 'string' } },
required: ['record_id', 'note'],
},
handler: async ({ record_id, note }) => {
gridRef.current.addNote(record_id, note);
return `Note added to ${record_id}`;
},
},
];

Read-only tools (get_discrepancies, explain_record) let the agent inspect without changing anything; mutating tools (run_reconciliation, add_note) are the ones that light up a row. The agent chooses the sequence; your grid stays the source of truth.

Try it

See the live sample with source, or read how we make these demos deterministic and driftless in the harness docs. Next up: clearing a bank statement.