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.