A cash flow forecasting agent's job is narrow: pull the right receivables, payables, and recurring transaction data out of the ERP, and turn it into a projection with stated assumptions — not a magic number. The failure mode to design against is a confident-sounding forecast built on a hallucinated growth rate or a misread aging bucket. The fix isn't a smarter prompt; it's forcing every number in the output to trace back to a tool call result.
Grounding every number in a tool call
The agent should never generate a dollar figure from its own reasoning. Instead, it calls tools that query AR aging, AP aging, recurring billing schedules, and historical collection rates directly from the ERP database, and the LLM's role is limited to combining those numbers under stated assumptions ("assume 85% of current AR collects within terms, based on the trailing 90-day collection rate"). If a number in the final report can't be traced to a specific tool call in the transcript, that's a bug, not a stylistic quirk — treat it as a hallucination and reject the output.
def get_ar_aging_buckets(as_of_date: str) -> dict:
"""Returns AR balances bucketed by days overdue,
pulled directly from the AR ledger. No estimation."""
return {
"current": 184200.00,
"1_30_days": 42100.00,
"31_60_days": 11800.00,
"60_plus_days": 6400.00,
"as_of": as_of_date,
}
def get_recurring_payables(window_days: int) -> list[dict]:
"""Scheduled AP items due within window_days,
from the payables schedule — not projected."""
...
Separating known cash from projected cash
A forecast mixes two very different kinds of numbers: contractually known amounts (an invoice due on a specific date, a payroll run on the 15th) and projected amounts (expected new sales, expected collection rate on overdue AR). Collapsing these into a single line total is where trust breaks down — a finance controller needs to see which parts of the number are facts pulled from the ledger and which parts are the model's estimate. Structure the output as two explicit sections, and require the agent to label every projected figure with the assumption behind it.
LLMs will sometimes round a $42,133.17 balance to "about $42k" or subtly adjust a number to make prose read better. For financial output, pass ledger values through as exact strings the model must echo, not numbers it re-generates. Diff the final output against the raw tool results before it reaches a human.
Human approval before the forecast ships
Cash flow forecasts inform real decisions — whether to draw on a credit line, delay a purchase, or chase collections harder. That makes this a human-in-the-loop workflow, not a fully autonomous one. The agent produces a draft forecast with its assumptions listed inline, a controller reviews and can override any assumption (e.g. "assume the Acme Corp invoice slips another 30 days" based on information the agent doesn't have), and only the reviewed version gets distributed. Log every override — it's a strong signal for which assumptions the model gets systematically wrong.
Keeping the transaction data within budget
A mid-size company can have thousands of open AR and AP lines. Dumping all of them into the prompt burns tokens and pushes older, less relevant context out of the model's effective attention. The tools should pre-aggregate at the database layer — return bucketed totals and the handful of largest individual line items, not every row — and let the agent request drill-down detail only for the buckets that materially affect the forecast. This keeps a 13-week rolling forecast comfortably inside a normal context window even for a company with a large AR book.
Evaluating forecast accuracy over time
Run the agent against historical periods where the actual cash position is already known, and score its 4-week and 13-week projections against what actually happened. Track error separately for the "known" line items (should be near zero — these are just data retrieval) versus the "projected" line items (where error is expected and the interesting metric is whether it's improving). A forecast that's consistently overconfident — narrow ranges that miss the actual outcome — is more dangerous than one that's honestly wide, because it invites decisions made on false precision.
| Category | Source | Agent's role |
|---|---|---|
| Contractually due AP/AR | Ledger tool call, exact figures | Aggregate and display, never alter |
| Projected collections | Historical collection rate + aging | Apply stated assumption, flag confidence |
| Projected new sales | Pipeline data or manual input | Combine under explicit assumption |
| Final report | Human review | Approve, override, or reject before distribution |
Wrapping up
A cash flow forecasting agent earns trust by being boring in the right way: every hard number traces to a ledger query, every soft number carries a visible assumption, and nothing ships without a human sign-off. The value isn't in the model's ability to reason about cash flow in the abstract — it's in the plumbing that keeps its reasoning tethered to what the ERP actually says.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.