SKIP TO CONTENT
AI Agents9 minAgents in Production / Ep. 3

Verified Citations and Abstention in RAG for Finance

Adam Boudjemaa
SHARE
Pixel-art illustration of a magnifying glass over a document, one sentence tied by a glowing thread to its source while an unsupported sentence dissolves into pixels.

KEY TAKEAWAYS

  • A RAG system that cannot say 'I don't know' is a liability. The goal near a fund's books is zero confident wrong answers, not maximum coverage.
  • A verified citation is a source span the system proved supports the claim, not a link it pasted next to the answer and hoped was relevant.
  • Evidence-based abstention turns the residual failures into visible refusals instead of confident fabrications. That is what makes RAG safe near regulated money.
  • I built this inside a US real-estate private-equity fund with roughly $3B AUM: retrieval, NLI plus numeric citation checks, abstention, evals, and red-teaming.
  • There is no honest single accuracy number for a system like this. The real artifact is the abstention behavior and the eval rubric, not a headline percentage.
0
CONFIDENT WRONG ANSWERS
the only count you can afford near the books
$3B
AUM
roughly, the fund this ran inside

A RAG system that cannot say 'I don't know' is a liability, not a feature. Inside a US real-estate private-equity fund with roughly $3B in assets under management, I shipped one that verifies every citation and abstains when the evidence is thin, because a confident wrong answer near the books costs more than a slow one.

The lesson landed the day a demo hallucinated in front of a compliance officer. The assistant answered a question about a fund document, cited a source, and sounded completely sure. The citation was real. It just did not say what the answer claimed it said.

The room went quiet, and the person whose whole job is to catch that kind of error had just watched a machine produce it with a straight face. So I stopped optimizing for coverage and started optimizing for something narrower and harder: never be confidently wrong. This is the design that came out of it, and why I would build it the same way again.

What a verified citation actually means

Start with the word everyone uses loosely. A citation, in most RAG systems, is a link the model placed next to its answer. Nobody checked that the link supports the claim. It looks like evidence, which is worse than no evidence, because it buys trust it did not earn.

The pipeline: retrieve, cite, verify, abstain

The system runs the same four steps on every question, and the last one is a fork, not a finish line.

RETRIEVE · CITE · VERIFY · ABSTAIN
Flow diagram: 4 stepsFlow diagram: 4 steps. Retrieve, then Cite as you draft. Cite as you draft, then Verify each citation. Verify each citation, then Decide. Decide branches into 2: If every claim is supported, then Answer. If any claim fails, then Abstain.EVERY CLAIM ISSUPPORTEDANY CLAIM FAILS1RetrievePull the source spans that couldanswer the question from a vectorstore (Postgres with pgvector).Nothing gets drafted without documentsbehind it.2Cite as you draftEvery sentence the model writescarries the exact span it came from. Aclaim with no span attached neverleaves this step.3Verify each citationAn entailment check asks whether thespan actually supports the sentence.A numeric check confirms any figurematches the source to the digit.4DecideThe same check, two exits. Nothinghere is a judgement call.AnswerWith thecitationsattached, so areviewer canclick straight tothe source.AbstainDo not patch it.Hand the questionto a person withwhat it found andwhere it stopped.
Nothing gets drafted without a document behind it, and nothing ships until every citation survives the check. Step 4 is a fork, not a finish line.

This ran as the shared answer layer underneath the fund's 11-agent platform, so every agent that read a document inherited the same citation checks instead of reinventing them.

When the system is allowed to refuse

Abstention is a policy, not a mood. The system does not refuse because it feels unsure. It refuses when the evidence is in a specific state, and those states are written down. Here is each state, next to what a vendor bot does with it.

Evidence state
What a vendor bot does
What this system does
The cited span plainly states it
Answers
Answers, citation attached
No retrieved source covers the question
Answers anyway, from the model's memory
Abstains, and says what it searched for
Two sources disagree
Picks one and sounds confident
Abstains, and escalates the conflict
The number does not match the source
Ships the plausible figure
Blocks the answer, flags the mismatch
ABSTENTION POLICY
Decide: What state is the evidence in?Decision tree: What state is the evidence in? If The cited span plainly states it, then Answer. If No retrieved source covers the question, then Abstain. If Two sources disagree, then Abstain and escalate. If The number does not match the source, then Block.DECIDEWhat state is the evidence in?IF The cited span plainly states itAnswerShip it with the citation attached,so a reviewer can click straight tothe source.IF No retrieved source covers thequestionAbstainRefuse, and say what it searchedfor.IF Two sources disagreeAbstain and escalateHand the conflict to a personinstead of picking a side andsounding confident.IF The number does not match thesourceBlockStop the answer and flag themismatch.
Abstention is a policy, not a mood. The branch the system takes is decided by the state of the evidence, and the states are written down.

How you verify a citation, not just attach one

Attaching a citation is easy. Verifying it is the actual work, and it is two checks, not one.

The first is entailment, or NLI: given the claim and the cited span, does the span support the claim, contradict it, or neither? A 'neither' is not good enough. Neutral means unsupported, and unsupported routes to abstention.

The second is numbers. Language models are fluent with figures and wrong with them. If a claim states an amount, a date, or a count, it has to match the source exactly, not 'about right'. In finance, 'about right' is how you book a wrong number with confidence.

verify-citation.ts
type Verdict = 'supported' | 'contradicted' | 'insufficient'

// A claim ships only if the cited span survives BOTH checks.
async function verifyClaim(claim: Claim, span: SourceSpan): Promise<Verdict> {
  // 1. Entailment (NLI): does the source actually support the
  //    sentence, or does it just sit near it and look relevant?
  const relation = await nliCheck(claim.text, span.text)
  if (relation === 'contradiction') return 'contradicted'
  if (relation === 'neutral') return 'insufficient'

  // 2. Numbers: a figure in the claim must match the source to the
  //    digit. "Close" is a fabrication with good manners.
  if (claim.hasNumber && !numbersMatch(claim, span)) {
    return 'contradicted'
  }

  return 'supported'
}

// One failing claim abstains the WHOLE answer. No partial credit.
function decide(verdicts: Verdict[]): 'answer' | 'abstain' {
  return verdicts.every((v) => v === 'supported') ? 'answer' : 'abstain'
}

Two lines carry the whole design. The entailment call is what separates a verified citation from a decorative one. The numeric check is what keeps a fluent paragraph from quietly changing a figure.

And the decision function is deliberately unforgiving. One unsupported claim abstains the entire answer. There is no 'answer the easy part and hedge the rest', because a half-answer with one confident error is the exact failure the compliance officer was paid to catch.

VERDICT STATES
type Verdict
supported→ answer

Every claim traces to a span that entails it. Ship the answer with the citation attached.

contradicted→ block

The span refutes the claim, or a figure does not match the source. Block the answer and flag the mismatch.

insufficient→ abstain

The span is neutral, it neither supports nor refutes. Abstain, and escalate to a person.

The three verdicts the verifier can return, and the branch each one takes. A single contradicted or insufficient claim abstains the whole answer.

The failure modes that survive, and how we scored them

None of this is trustworthy because I say so. It is trustworthy because it was evaluated and red-teamed before it went near a real question, and the evaluation scored the things that actually fail.

The useful artifact is the rubric: what we measured, and why each dimension gates a release.

Dimension
What we scored
Why it gates
Citation support
Every claim traces to a span that entails it
An unsupported claim is a fabrication, however fluent
Numeric fidelity
Figures match the source to the digit
A wrong number in finance is not a rounding error
Abstention correctness
It refuses when, and only when, evidence is thin
Over-refusing is useless; under-refusing is dangerous
Adversarial robustness
It holds under red-team prompts that bait a guess
Attackers probe for the confident wrong answer

The version of this rubric that decides whether a release ships, thresholds and all, is the eval suite that turns abstention into a release gate. This post is the mechanism; that one is the verdict.

The citation exists and looks perfect. It just does not support the claim it is attached to. Attaching a link cannot catch this; only the entailment check can. This is the exact failure that embarrassed the demo, and the reason verification is a check and not a formatting step.

The model sees two figures in the sources and produces a plausible third that is neither. It reads beautifully. The digit-level numeric check is the only thing standing between that sentence and the fund's records.

The failure in the other direction. A system tuned to refuse will refuse things it could have answered from a source sitting right there. That is not safe, it is useless, and it is why abstention correctness is scored in both directions, not just as 'refuse more'.

Why regulated finance is the proving ground

You could ask why bother, when most RAG assistants just answer and move on. The answer is the setting. Regulated finance is the proving ground precisely because the cost of a confident wrong answer is not embarrassment, it is a finding in an audit.

That constraint is a gift. It forces the honest version of the system, the one that would survive anywhere the cost of being wrong is real.

Vendor RAG

Always answers. Cites a source that looks right. Impressive in a demo, and impossible to put near the books, because nobody can tell its right answers from its confident wrong ones.

RAG you can put near the books

Verifies every citation, abstains on thin evidence, escalates conflicts. Slower to build and less magical in a demo, and the only version a regulated fund keeps.

If you have to prove that accuracy to a regulator rather than assert it, the abstention log and the citation checks are the evidence, which is the same argument I make about evidencing accuracy under the EU AI Act.

FAQ

It is the system refusing to answer when the retrieved evidence does not support an answer, instead of generating a plausible one from the model's memory. The refusal is triggered by the state of the evidence: no relevant source, conflicting sources, or a citation that fails verification. In regulated work, a refusal you can act on beats a fluent answer you cannot trust.

A normal citation is a link placed next to an answer; nobody checked it supports the claim. A verified citation has passed an entailment check that the cited span actually backs the sentence, plus a numeric check when the claim contains a figure. If it fails either, the claim counts as unsupported and the answer abstains.

Only if it abstains when it did not need to, which is a real failure I score against. The goal is to refuse when, and only when, the evidence is thin. A system that answers everything is more useful in a demo and less useful in production, because you cannot separate its right answers from its confident wrong ones. The escalations are also the cases that genuinely needed a human.

Because a single headline accuracy figure is exactly the confident-sounding number that hides the failure mode I built this to prevent. There is no honest one-line score for a system whose whole job is to behave differently by evidence state. The real artifacts are the abstention behavior and the eval rubric, not a percentage. I also designed it on the assumption that retrieval reduces but does not eliminate fabrication, so the residual failures had to become visible as refusals instead of hidden as confident answers.

Yes. The pattern, verify citations then abstain on thin evidence, transfers anywhere a confident wrong answer is expensive: legal, medical, or any place a human will act on the output. Finance just makes the cost obvious. If being wrong in your domain is cheap, you probably do not need this, and that is a fair reason not to build it.

Agents in Production

Episode 3 · 10 published

Adam Boudjemaa

Adam Boudjemaa

Former CTO of Integra. Named author (1 of 5) of ERC-3643, first author of ERC-6960, co-author of ERC-7410, and co-author of ERC-8203, which is still a draft. Building production AI and regulated Web3 systems.

Enjoyed this post?

Get more like it in your inbox every Tuesday.