{"slug": "what-production-finance-agents-actually-do-all-day", "title": "What Production Finance Agents Actually Do All Day", "summary": "Artifi's production finance agents operate as a team of narrow specialists with explicit permissions and institutional knowledge, not as a single general agent. The bill-processing agent uses deterministic checks and tiered vendor resolution to avoid posting errors, with learned shortcuts cross-checked against document-extracted identity. The system encodes production scars, such as a fix for memory keyed on ambient context, and uses three-layered classification for expense accounts to ensure comparability.", "body_md": "*This is part 3 of Building the Agent Platform. Part 1: Anatomy of a governed finance-agent platform. Part 2: Agent blocks.*\n\nThere's a picture of AI agents that lives in demos: one brilliant general agent that \"handles finance.\" After a year of running agents on real companies' books, I can report that production looks nothing like that. It looks like a *team* of narrow specialists — each with a job description, a budget, explicit permissions, and a manager to escalate to — plus an enormous amount of institutional knowledge encoded as thresholds, guards, and things-we-learned-the-hard-way.\n\nThis is a walk through that team: what the agents at Artifi actually do, with real confidence thresholds and real incidents. Parts [1](/blog/agent-platform-architecture) and [2](/blog/agent-blocks-composition) of this series covered the runtime and the composition machinery; this part is the payload. I've written it for both audiences at once — finance readers will recognize the work; engineers will recognize the patterns — because the whole point of finance agents is that those two groups now share a system.\n\n## The bill processor: reading is the easy part\n\nThe flagship agent processes supplier bills arriving by email or upload. The naive version of this is \"OCR plus GPT\" — extract vendor, amount, date, post it. The production version is a pipeline where extraction is maybe a fifth of the work, and the rest is *deciding whether to believe the extraction*.\n\n**First: is this bill even ours?** Forwarded emails, group companies, personal purchases — bills arrive addressed to all sorts of entities. Before anything posts, a deterministic relevance check runs in priority order: our tax ID appearing in the document confirms it (confidence 0.95); a fuzzy match on our legal name (≥85% of tokens, legal suffixes stripped) confirms it; address components vote with weights (postal code 0.35, city 0.25, street 0.25, country 0.15, threshold 60%); a known employee as sender counts (work email 0.85, personal 0.75); an explicitly different addressee *blocks* processing. Nothing AI-fancy — a scoring function — but it's the difference between an agent and a liability, because posting someone else's bill into your VAT return is not a cute failure mode.\n\n**Second: who is this vendor?** Vendor resolution runs through tiers: a run-local cache, then a shared memory — one \"vendor brain\" that the bill, bank, and card agents all read and write, keyed by a normalized counterparty fingerprint — then a validated sender-memory, then database search, and finally, only for genuine ambiguity, a small-model disambiguation that sees a numbered list of up to 150 existing vendors and must answer with a number or NONE (cost: about a tenth of a cent; instruction: prefer NONE when unsure).\n\nThat word \"validated\" carries a production scar. The agent used to trust its memory of \"bills from this sender belong to vendor X\" unconditionally. Then someone forwarded a batch of bills from different vendors, and every one of them got pinned to the vendor of the *first* bill ever learned from that sender. Six bills, one wrong vendor. The fix is general and worth engraving: **memory keyed on ambient context (who sent it) must be cross-checked against document-extracted identity (whose bill it is) before use.** Learned shortcuts are hypotheses, not facts.\n\n**Third: which expense account?** This is where finance judgment lives, and where we learned that model choice is an accounting decision: the smaller model scattered the same SaaS vendor across different GL accounts bill by bill — cloud hosting on one account this month, another the next — which technically balances and practically ruins comparability. The fix was three-layered: a stronger model for classification; a **vendor-default pin** (set \"this vendor → this account\" once on the vendor record and every future line is deterministically pinned — no model consulted); and for unpinned vendors, a **learning loop** that recalls this vendor's past service-to-account decisions as prior context, writes back new ones after validation (so hallucinated accounts can never be learned), deduplicates, and caps the memory at 25 entries per vendor.\n\n**Fourth: the tax trap.** VAT extraction has a failure mode that nearly always ends in a wrong ledger: misread a cost component as a tax amount, and the system helpfully finds a tax code to justify it. Our rule set is deterministic and humble: compute the effective rate from the extracted amounts, accept a tax code only within **0.3 percentage points** of a real rate (23.96% matches the 24% code), and treat airline taxes, fuel surcharges, and booking fees as cost components, never VAT. The old tolerance was 1.0 points — wide enough that a misread airline invoice (32.45 over 345 ≈ 9.4%) force-matched a 9% code and grossed up every line. Tolerance tightening as a bug fix.\n\n**Fifth: refuse to balance by force.** If extracted lines don't sum to the total, the agent adds a rounding adjustment only within **5 cents**. Any larger gap routes to a human — no plug entries, ever. An agent that \"makes it balance\" is an agent that hides extraction errors inside your P&L.\n\nAll of these gates land in the same place: a review queue. Five conditions route a bill to a person — overall extraction confidence below **0.85** (scored additively: keywords, total found, vendor found, invoice number, date, line items), an entity mismatch, any credit memo (always reviewed), a line-total mismatch, ambiguous tax. The review screen shows the agent's extraction beside the source PDF; on approval, the bill posts through the same workflow gateway as everything else. The design principle: **the first three triggers doubt the extraction; the last two doubt the accounting.** A confidently misread invoice sails past confidence scoring — you need correctness gates, not just confidence gates.\n\n## The bank agent: the human's answer is the classification\n\nBank and card transactions flow through a second specialist, and its architecture inverts what most people expect from \"AI classification.\"\n\nThe cheap, deterministic checks run first. A **refund pre-detector** looks for a recently posted opposite-direction transaction — same bank account, same merchant, amount within ±0.5%, original within 90 days — and books a proper refund against the original instead of inventing a new transaction. This exists because of a specific bug: a vendor *refund* once became a phantom *customer payment*, complete with a phantom customer record. The general lesson: LLMs classify what a thing looks like; only the ledger knows what a thing *is in relation to other things*. Relationship checks must precede classification.\n\nThen memory (past classifications of this counterparty fingerprint), and only then a small model choosing among twelve transaction types — with a prompt that explains each type's *accounting purpose* rather than listing keywords, a redesign forced by \"transfer between own accounts\" getting classified as a vendor payment. On a first real statement run, 14 of 39 lines needed the model; on the second run, zero — memory had taken over. That's the cost curve you want: intelligence up front, muscle memory forever after.\n\nSome lines legitimately stump every tier. Those get batched into a single review request, and here's the inversion: **the lines are not posted with a guess awaiting correction — they are not posted at all until the human answers, and the answer becomes the posting.** The accountant writes, in a form or free text, \"the 3,200 to Maria is salary, the 5,000 is a dividend\" — a strict parser (that degrades to \"unclear\" rather than guessing) turns that into classifications, salary settles the payroll accrual, the dividend posts as an owner distribution. Then the learning rule with a subtlety I love: patterns where *all* lines resolved identically get memorized; the person who receives both salary *and* dividends is deliberately *never* memorized and will be asked about every time. Ambiguity, once discovered, is respected.\n\nTwo hard guards ride on top, both born from incidents: card payments to government or tax authorities are always reviewed regardless of what any model says (they settle liabilities, not expenses, and models flip-flop on them); and a credit from a known *vendor* that no refund-matcher could pair goes to review instead of becoming revenue. Guards are cheaper than corrections.\n\n## Reconciliation: six passes and one refusal\n\nMatching payments to invoices is the agent that most rewards engineering discipline, because its failure mode — the wrong match — is quiet. The matcher runs six passes in strict order of trustworthiness: exact amount-and-window matches (confidence 1.0), reference-number matches (0.95), invoice numbers found inside payment descriptions and vice versa (0.92–0.95), FX-tolerant approximate matches (0.85–0.95, scaled by how far the amounts diverge, hard-capped at 5%), combinatorial grouping for one-payment-many-invoices (0.50–0.95, with limits to keep the combinatorics sane), and finally — for the dregs — a small-model fuzzy pass that lands at 0.70, which is *below* the auto-apply line by design.\n\nThat line is **0.85**: above it, matches apply automatically; below it, they queue for a person. And one refusal encodes more accounting wisdom than any prompt I've written: when matched amounts differ in the *same currency*, the agent never writes off the difference automatically — not even a cent-level one. A same-currency variance means a wrong match, an undisclosed discount, or a short payment, and auto-writing it off would *mask* exactly the situations a person needs to see. Cross-currency differences, by contrast, post automatically as FX gain/loss — that's arithmetic, not judgment. Knowing which differences are arithmetic and which are information: that's the job.\n\n## The month-end close: an agent that is forbidden to fix anything\n\nThe most institutionally interesting agent runs the close — as a saga of **13 phases** with dependency ordering (bank completeness, card completeness, expense reports, subledgers, payroll, reconciliation, GL validation, tax integrity… through to the close package), where each phase runs a registry of checks with fixed severities: **blocking** checks gate the close; **advisory** checks warn and proceed. Clients can configure whether a check applies to them; they can never weaken a blocking check into an advisory one. Governance you can configure away isn't governance.\n\nThree design rules make it trustworthy:\n\n**The verifier never fixes.** When a blocking check fails, the close agent doesn't repair the data — it opens a request to the responsible specialist agent, waits, and re-checks (up to three iterations, then it *pauses* for a human rather than failing). Separation of duties, applied to software: the entity that certifies is never the entity that remediates.\n\n**Dry-run is the default at every layer.** Config, launcher, scheduler, UI — everything defaults to a mode where all checks run, all findings are recorded, and nothing is delegated or closed. Going live is an explicit, per-organization flip. The first production dry-run — on a real company — surfaced five false-failure bugs in the checks themselves. That's the point: **the dry-run's first job is to close-test the closer.**\n\n**The LLM is a junior analyst with no signing authority.** A handful of checks use a model to review manual journal entries and scan for anomalies — and those checks are *permanently advisory*, so a model outage can never block a close. Better: their findings pass through a fabrication guard that drops any finding containing a number that doesn't appear in the supplied data. Models turned out to judge well and *invent arithmetic* while explaining — so we keep the judgment and strip the invented numbers. And when everything is green, the agent still doesn't close the period: it submits the close through the approval lane, and a human clicks. The reopen action, incidentally, requires the *highest* approval tier — reopening a closed period is the more dangerous direction, and for a while, embarrassingly, it wasn't gated at all.\n\n## The money-movers: where determinism is non-negotiable\n\nPayments are where \"the agent got creative\" stops being an anecdote and starts being a regulator's question, so the payment agents contain **no model calls at all**.\n\nThe **payment proposal agent** assembles batches deterministically: due AP invoices and approved expense reimbursements, grouped by (payment method, currency, source bank) because a SEPA file and an ACH file cannot mix; one line per invoice for reviewability; capped at 200 lines. Which of the company's banks pays a given vendor isn't guessed — it was *learned* by the bank agent from the first observed real payment and stored on the vendor record (currency-matched), a nice example of one agent's observation becoming another agent's configuration. The batch then goes to approval: any manager for normal batches, senior approval above **$50,000** with a 48-hour escalation, line-level exclusion in the approval screen (uncheck two suspicious lines, approve the rest), and a hard rule that **the submitter cannot approve** — separation of duties survives the automation. And the GL posts only when the bank statement confirms execution — the statement, not the transmission, is the source of truth.\n\nThe **collection agent** initiates charges (cards, direct debit) only for contracts explicitly configured for active collection — the default for any unconfigured contract is *do nothing*, because the safe default for touching customers' money is inaction. SEPA direct debit revenue posts only when the settlement webhook confirms it, days later — not when the charge is initiated. Dunning retries at 3, 7, and 14 days, each retry a scheduled event tied to its transaction and attempt number, capped at three attempts.\n\nThe two **chaser agents** — one asks vendors' side of the house for missing bills (payments with no invoice), one asks employees for missing receipts — share an elegant non-design: escalation with no state machine. The *age of the item is the state*: 0–7 days gets a friendly tone, 7–21 firm, 21+ urgent (configurable, with per-bracket recipient overrides — 45+ days can go to the CFO). Per-item cooldowns prevent nagging; the email is drafted by a small model with a hardcoded fallback template. My favorite bug in the whole system lives here: with 91 outstanding payments, the model-drafted email hit its token limit and *truncated the list mid-sentence* — so above 20 items, the agent switches to generating a CSV attachment. LLMs summarize; files enumerate. Know which one you need.\n\nAround them, the routine machine: the **billing agent** generates invoices from contracts (each contract in its own database transaction so one failure can't take down the run; idempotent via the advancing next-billing-date; \"already billed\" explicitly reported as *skipped*, not failed, to keep failure dashboards meaningful), and each created invoice fans out — by event, not by call — to the **delivery agent** (PDF, email, logged) and the collection agent in parallel. A customer missing a tax profile doesn't crash billing: the contract is skipped, a request goes to the master-data agent, and the callback re-bills automatically. The delivery agent's contribution to the pattern catalog: a customer with no email is a **skip, not a failure** — a data gap and an error are different things, and conflating them buries real errors under noise.\n\n## The patterns, extracted\n\nAcross eleven agents, the same ideas keep recurring. If you're building agents for finance — or hiring someone who will — this is the checklist I'd interview against:\n\n**Deterministic first, model second, human third.** Every agent runs its cheap, reliable checks before its clever ones, and escalates what neither can settle. The model is a middle layer, not the architecture.**Confidence gates AND correctness gates.** Confidence catches what the model doubts; invariants (lines must sum, rates must exist, relationships must reconcile) catch what the model wrongly believes.**The human's answer is input, not absolution.** Reviews*drive posting*— and consistent answers become memory while ambiguous ones deliberately don't.**Learn with validation, cap what you learn, respect discovered ambiguity.** Every learning loop has a write-back guard and a size limit; every memory is a hypothesis until cross-checked.**Refuse the convenient fiction.** No plug entries to force balance, no auto-write-offs in same currency, no posting salary lines without a real accrual to settle, no revenue on charge initiation. Each refusal keeps a human-shaped hole where judgment belongs.**Separate the doer from the certifier from the approver.** The close agent can't fix; the payment proposer can't approve; the reopen needs more authority than the close.**Events chain the team.** Billing → delivery + collection; bank posting → reconciliation → bill chasing. No orchestrator god-object; each agent does its job and announces the result, and the announcement is the next agent's trigger.\n\nThe demo fantasy is one agent that understands finance. The production reality is a dozen narrow agents that each understand one job *plus* the accumulated scar tissue of every way that job goes wrong — with humans wired in exactly where the scars taught us to put them. It's less romantic. It closes the books.\n\n*Andrew Rudchuk is the founder of Artifi. All thresholds, incidents, and mechanisms in this article are from the platform's production documentation. Part 1 covers the runtime architecture; part 2 covers the block and pipeline composition system.*", "url": "https://wpnews.pro/news/what-production-finance-agents-actually-do-all-day", "canonical_source": "https://ar-ti-fi.com/blog/production-finance-agents-anatomy", "published_at": "2026-07-13 00:00:00+00:00", "updated_at": "2026-07-13 21:04:58.368812+00:00", "lang": "en", "topics": ["ai-agents", "ai-products", "ai-tools", "ai-infrastructure"], "entities": ["Artifi"], "alternates": {"html": "https://wpnews.pro/news/what-production-finance-agents-actually-do-all-day", "markdown": "https://wpnews.pro/news/what-production-finance-agents-actually-do-all-day.md", "text": "https://wpnews.pro/news/what-production-finance-agents-actually-do-all-day.txt", "jsonld": "https://wpnews.pro/news/what-production-finance-agents-actually-do-all-day.jsonld"}}