Founders

How to Catch an Agent Inventing a Number from the Wrong Source

Antonio Evans7 min read

A split-screen illustration: on the left, a stylized AI agent holding a notepad with a large number on it, pointing to a generic document icon; on the right.
Key points

Key points

  1. 1The safe pattern is to make the agent name its source in a structured field, then have a separate process confirm that the cited document contains the claimed figure.
  2. 2Almost all wrong-source errors fall into four buckets: wrong document, wrong period, wrong unit, or a value that appears nowhere in the cited source.
  3. 3A small source-of-truth table with period, unit and definition fields lets a script check most mismatches before a human ever reads the answer.
  4. 4Sentence-level attribution beats document-level citation, because a link to a whole page gives the reviewer nothing to check against.

Why "it cited something" is not the same as "it is right"

An agent that returns a number with a link feels trustworthy. That feeling is the problem. Research on attribution systems notes that users are more likely to trust a system that offers an explanation, even when that explanation is wrong, than one that offers none at all (Batista, Vatai and Wahib). A citation attached to a hallucinated figure converts a visible error into an invisible one.

The failure has a name and a measurable base rate. Practitioners distinguish citation hallucination (the source does not exist) from statement hallucination (the source exists but does not say what the model claims) (Pickaxe's guide to research agents). Statement hallucination is the harder of the two, because every check a reviewer is likely to run, such as the link resolves and the publisher looks right, passes.

A useful framing comes from the language-model literature: retrieval improves the probability that an answer is correct, while attribution creates verifiability, the ability for a human to follow the chain from claim to evidence and decide whether it holds (Brenndoerfer's chapter on attribution and citation). Your job is not to make the model honest. It is to make its output checkable, and then to check it.

This matters most where the numbers are financial and the source set is boringly predictable. A founder update, a board pack, a finance summary: the same handful of documents feed every question, and each one has a period, a currency and a definition. That predictability is what makes automated catching possible.

Failure modeWhat the agent saysWhat the source actually showsHow to detect it
Wrong document"Revenue was 4.2 million, per the board deck"The board deck shows a different figure; 4.2 million appears only in the investor updateSearch the cited document for the number and the period string
Wrong period"Monthly recurring revenue was 610 thousand"The cited report shows 610 thousand for the prior quarterExtract the period from the source and compare to the claim
Wrong unit"Cash was 1.4"The report states 1.4 million, or 1,400 thousandRequire a unit field and normalize before comparing
Nowhere"Net burn was 388 thousand"No occurrence of 388 in the cited sourceSubstring and numeric search of the full source text

Build the source-of-truth table first

You cannot check a number against a source you have not modeled. The first artifact is a table, not a prompt. For each metric the agent is allowed to report, record where the authoritative figure lives, which period it covers, what unit it is in, and how the source defines it.

Most documents you would feed an agent already have a natural key structure. Investor updates, for instance, are supposed to report cash, burn and runway at the top, with a single headline key performance indicator tracked in every issue (Carta's investor update guide). That structure gives you exactly the fields you need.

A worked example, kept deliberately small:

MetricAuthoritative sourcePeriodUnitDefinition as stated
cash_balancebalance summary reportmonth endUSDGross bank balance, encumbered funds excluded
net_burninvestor updatecalendar monthUSDCash spent minus cash collected
mrrinvestor updatemonth endUSDRecurring subscription revenue only

The definition column is doing more work than it looks. A cash figure can mean the gross bank balance or the true usable liquidity once fixed deposits securing credit lines are removed, and updates that blur the two erode trust faster than a missed target (raisemoney.ventures on monthly updates). If your table does not pin the definition, the agent can be wrong while quoting a real number.

How you store the metric definition over time is a design decision, not an afterthought. If the definition of "net burn" changes in the source document, you need the version that was in force when the figure was published. That is a slowly changing dimension problem, and the standard options are well catalogued: Type 1 overwrites in place and loses history, while Type 2 keeps a new row per change with effective dates (datawarehouseinfo.com on SCD strategies). For metrics that get quoted in board materials, use Type 2 on the definition attribute.

-- Source-of-truth table with Type 2 history on the definition
CREATE TABLE metric_registry (
  metric_key      TEXT NOT NULL,
  period_start    DATE NOT NULL,
  period_end      DATE NOT NULL,
  unit            TEXT NOT NULL,      -- 'USD', 'USD_thousands', 'percent'
  definition      TEXT NOT NULL,
  source_doc_id   TEXT NOT NULL,
  effective_from  DATE NOT NULL,
  effective_to    DATE,               -- NULL means current
  PRIMARY KEY (metric_key, effective_from)
);

Force the attribution into fields, not prose

A claim like "cash was 1.4 million (see Q3 update)" gives a reviewer nothing to automate. The fix is a schema: every number in an agent response must arrive with the document identifier, the period, the unit and the verbatim sentence it was taken from.

This is the practical version of the finding that attribution needs to be short. Current systems often cite whole documents, which forces the reader to read the entire text to verify a single claim, making fact-checking impractical (Batista, Vatai and Wahib). Sentence-level attribution is the smallest unit that is still checkable by machine.

Return ONLY JSON. Every numeric claim must include all fields.
{
  "metric_key": "cash_balance",
  "value": 1.4,
  "unit": "USD_millions",
  "period_start": "2026-07-01",
  "period_end": "2026-07-31",
  "source_doc_id": "investor-update-2026-08",
  "source_quote": "Cash in bank at month end was $1.4M",
  "source_locator": "section: Cash and runway, paragraph 1"
}
If you cannot supply source_doc_id and source_quote from the provided
documents, set value to null and explain what is missing.

Three details in that prompt do the heavy lifting. source_quote must be verbatim, so a reviewer can search for it. source_locator narrows the check to a section rather than a 40-page file. And the null instruction gives the model a legal way out, which matters because an agent asked for a figure it does not have will produce one rather than admit the gap.

Enforce the schema at the boundary. If a response parses but a required field is missing, reject it and re-ask once before falling back to a human. A validation layer that refuses malformed attributions is cheaper than a review meeting.

Compare the claim against the document, mechanically

Now the actual catch. For each claim, run three checks in order, cheapest first.

Check one, existence. Search the full text of the cited document for the claimed value in the format it appears in the source. Normalize first: strip currency symbols, thousands separators and trailing zeros, and search for the numeric token as well as the formatted string. A value that appears nowhere in the cited document is a hard fail, no judgment required.

Check two, period and unit. Pull the period label and unit from the stored document metadata and compare to the claim. A figure of 610 thousand is not wrong because it is wrong; it is wrong because it belongs to the prior quarter. Unit mismatches are the most common false alarm in manual review, so normalize both sides to a base unit before comparing.

Check three, the quote. Confirm the source_quote string appears in the document at or near the locator. This catches the subtle case where the number is real, the document is real, and the sentence says something else entirely.

Two practical guardrails. Fetch the source through the same pipeline the agent used, not a live URL, so a page that has changed since ingestion does not masquerade as an agent error. And record every check result alongside the claim, so a reviewer sees "not found in cited source" rather than having to rediscover it.

Beware the near-miss that looks like a bug and is not. In payments data, Stripe's payments count and its balance transaction count differ for perfectly healthy reasons, because a balance transaction is created for every movement of money, including payouts, refunds and disputes, not only customer charges (acodei on why counts differ). If your source table does not say which count is authoritative for "number of transactions," the agent can cite a real figure from the wrong report and your check will correctly flag it. The fix is in the table, not the agent.

A flagging rubric that survives audit

Not every mismatch deserves the same response. Triage by severity, and write the rules down so two reviewers reach the same verdict.

VerdictConditionAction
PassValue, period, unit and quote all matchPublish
WarningValue matches, period or unit differs by one stepHold for human review, log
Fail: wrong documentValue exists but only in a document other than the cited oneBlock, flag attribution error
Fail: wrong periodValue and document match, period does notBlock, correct the period
Fail: wrong unitValue matches numerically, unit differs by a factor of ten or a thousandBlock, normalize and re-run
Fail: unsupportedValue not present anywhere in the cited sourceBlock, treat as fabrication

Two rules make this rubric stick. First, a Fail is never downgraded to a Warning without a written reason, because the most expensive errors are the ones someone decided were fine. Second, track the rate of each verdict per metric. A metric with a rising unsupported rate is usually a retrieval problem, meaning the right document is not being retrieved, not a model problem.

Measure the checker itself, too. Sample 20 passing claims a week and have a person verify them by hand. If your checker passes things a human rejects, the existence and quote checks are too loose.

Where this fits in a wider pipeline

The same discipline applies beyond finance. Any agent that reports figures from a document set, whether contracts, clinical literature or competitor filings, needs a registry, a schema and a comparator. The citation tools that exist for drafting help spot passages that need support and format references, but they do not confirm that the cited source contains the specific number (Grammarly's Citation Finder). Confirmation is the step you build.

If you are validating accuracy at the document level rather than the number level, natural-language-inference scoring of claim against source is a reasonable extension of the same idea, and the method is documented in full with an implementation (Brenndoerfer).

For teams that would rather run the whole pipeline on one key than wire up separate providers for text, embeddings and reranking, Quanta Inference exposes text, image, video, audio and embedding models through a single OpenAI-compatible API, which keeps the retrieval and checking steps in one place.

FAQ

FAQ

How is this different from asking the model to double-check its own answer?

Self-checking shares the model's blind spots. If the model misread a report, asking it to reread the same report often reproduces the error. The protocol here uses a separate comparison step against the stored source text, so the check does not depend on the model's judgment about its own output.

What if the number is correct but the agent cites a summary instead of the original?

Treat it as a Warning, not a Pass. Summaries can round or restate figures, so attribution to a summary weakens verifiability even when the value happens to match. Record the authoritative document in your source table and require the agent to cite it directly.

How many claims should I check by hand?

Enough to measure the checker, not the output. Sample around 20 passing claims per week per metric and verify them manually. If your automated checks pass claims that a person rejects, tighten the existence and quote checks before expanding coverage.

Does this work when the source is a live dashboard rather than a document?

Yes, with one change: snapshot the dashboard data at ingestion and compare against the snapshot, not the live view. Live figures move between the agent's read and yours, and without a snapshot you cannot tell a real error from a timing gap.

How do I stop the agent from inventing a figure when the source is missing?

Give it an explicit null path in the output schema and reject responses that omit required fields. An agent with no legal way to say "I do not have this" will fill the gap, because a plausible number satisfies the instruction more readily than an admission of absence.

Sources

Sources

  1. Improving the Performance of LLMs Attribution Systems
  2. How to Build an AI Research Agent That Cites Sources
  3. Attribution and Citation: Sourcing LLM Outputs - Interactive
  4. How to Write an Effective Investor Update | Tips & Templates
  5. Startup Investor Update: Monthly Founder Guide
  6. Slowly changing dimensions: implementation strategies
  7. Stripe Payments vs Balance Transactions: Why Counts Differ
  8. Citation Finder | Fact Check and Find Trusted Sources ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles