AI SystemsADVANCEDDifficulty: ADVANCED~20h build

AI Research & Fact-Checking System

A multi-step agent that researches a claim, cites its sources, and reports its own uncertainty

By Renjith ·

Prerequisites

  • Comfortable with Python and async I/O
  • Basic understanding of embeddings and vector search
  • An API key for at least one language model provider
  • PostgreSQL 15+ with the pgvector extension

The problem

Ask a language model whether a claim is true and it will answer confidently either way. That is the actual failure: not that models are sometimes wrong, but that a wrong answer is indistinguishable in tone from a right one.

For anything that touches a decision — a market claim in a report, a compliance statement, a technical assertion in documentation — an assistant that cannot say "the evidence disagrees" or "I could not verify this" is worse than no assistant, because it launders a guess into something that reads like a finding.

What was built

A verification pipeline rather than a chat wrapper. A claim enters, gets decomposed into independently checkable sub-claims, and each one is researched separately against multiple retrieval sources. Evidence is scored for support, contradiction or irrelevance, and agreement between independent sources — not model confidence — determines the verdict.

The output is a structured verdict with per-claim citations, an explicit uncertainty band, and a list of what could not be checked. "Insufficient evidence" is a first-class result the system is designed to return, not a failure mode.

How it works

Why agreement beats confidence#

The core design decision is that the model never issues the verdict. It classifies evidence; arithmetic issues the verdict.

That sounds like a small distinction and it changes everything. A model asked "is this true?" produces a fluent answer shaped by its priors. A model asked "does this passage support, contradict, or fail to address this specific sentence?" is doing a much narrower job that it is genuinely good at. Aggregating those narrow judgements across independent sources gives a verdict you can audit line by line.

verdict.pypython
def aggregate(evidence: list[Evidence]) -> Verdict:
    support = [e for e in evidence if e.label == "SUPPORTS"]
    contradict = [e for e in evidence if e.label == "CONTRADICTS"]

    sources_supporting = {e.source_domain for e in support}
    sources_contradicting = {e.source_domain for e in contradict}

    if len(sources_supporting) >= 2 and not sources_contradicting:
        return Verdict.SUPPORTED
    if len(sources_contradicting) >= 2 and not sources_supporting:
        return Verdict.CONTRADICTED
    if sources_supporting and sources_contradicting:
        return Verdict.DISPUTED
    return Verdict.INSUFFICIENT_EVIDENCE

Counting distinct domains rather than passages is deliberate. Five paragraphs from one content farm is one source, and treating it as five was the first bug worth fixing.

The uncomfortable finding

On the evaluation set, the biggest accuracy gain did not come from a better model or better prompts. It came from adding INSUFFICIENT_EVIDENCE as an allowed output. Forcing a binary true/false verdict pushed the system into confident errors on roughly one claim in five.

Cost control as a design constraint#

Naïve agent loops are unbounded by construction: each retry costs money and there is nothing stopping the loop from taking twelve of them. Three constraints keep this predictable:

Control Mechanism
Per-run token budget Hard cap checked before every call; exceeding it ends the run with a partial verdict rather than silently continuing
Retrieval cache Sub-claims are hashed; identical questions inside a session hit the cache
Model tiering Decomposition and scoring use a small model; only aggregation-time disagreements escalate

Tiering alone cut cost per verified claim by roughly two thirds with no measurable accuracy change — which is the sort of result that only shows up when you have an evaluation set to measure against.

What the architecture looks like#

Retrieval, scoring and aggregation are separate services behind a queue rather than one long function. That is not architectural ceremony: verification runs take tens of seconds, and a synchronous request that occasionally takes a minute is a support ticket waiting to happen. Persisting each state transition also means a failed run resumes from its last good node instead of restarting and re-spending the budget.

Build steps

  1. 1

    Model the claim graph

    Define claims, sub-claims, evidence and verdicts as explicit tables before writing any agent code. Making the data model the contract stops the agent logic sprawling later.

  2. 2

    Build the decomposition step

    One focused call turns a compound claim into atomic, independently checkable statements. Constrained output — a schema, not free text — keeps this reliable.

  3. 3

    Add multi-source retrieval

    Retrieve from at least two independent sources per sub-claim. Independence is the whole mechanism: agreement between correlated sources tells you nothing.

  4. 4

    Score evidence, not vibes

    Each passage is classified as supporting, contradicting or irrelevant, with a span quote. The verdict is computed from those labels, not asked for directly.

  5. 5

    Wire the state machine

    LangGraph nodes for decompose → retrieve → score → aggregate, with retry limits and a hard budget cap. Every state transition is persisted, so a run can be replayed and audited.

  6. 6

    Build the evaluation set

    A hundred labelled claims spanning true, false, and genuinely ambiguous. Without this, changes to prompts are guesswork.

Lessons learned

Let the system say "I don't know." Adding an explicit insufficient-evidence verdict did more for accuracy than any model upgrade.

Independence is what makes corroboration mean anything. Counting passages instead of distinct sources produced impressive-looking agreement that was worth nothing.

Structure the sub-task, not the conclusion. Models are far more reliable classifying a single passage against a single sentence than they are issuing a judgement.

Build the evaluation set before the second prompt iteration. Everything before that point was preference dressed up as improvement.

Limitations

Retrieval quality caps everything — a claim whose evidence is not reachable by the configured sources returns insufficient evidence regardless of how good the reasoning is.

Evidence scoring is itself model-driven and inherits model bias, particularly on politically or commercially contested claims.

The evaluation set is a hundred claims written by one person. It is enough to catch regressions, not enough to make a general accuracy claim.

Latency is tens of seconds per claim. This is a research and reporting tool, not something to put in front of an interactive chat.

Future improvements

Source reputation weighting, so a peer-reviewed paper and a blog post do not count equally toward corroboration.

Temporal awareness — many claims are true as of a date, and the current system flattens that.

An active-learning loop that routes low-agreement claims to human review and feeds the labels back into the evaluation set.

Related course

Production AI Engineering: RAG, Agents, Evals & MCP

Retrieval that holds up, agents that fail safely, evaluation that catches regressions, and MCP for tool integration.

Related builds and resources

UDEMY$119

Production AI Engineering: RAG, Agents, Evals & MCP

Retrieval that holds up, agents that fail safely, evaluation that catches regressions, and MCP for tool integration.

AdvancedDifficulty: Advanced★ 4.914h480 studentsPython · LangGraph · Ragas
AI & LLMFree

AI Token Cost Calculator

Model input and output tokens, request volume and caching to project monthly cost across model tiers.

1,020 runs
AI Systems

RAG Evaluation Lab

Most RAG systems are tuned by vibes. This is a reproducible harness that measures retrieval and generation separately, so you can see which half is failing.

IntermediateDifficulty: Intermediate~12h buildPython · Ragas · DuckDB
Code
ServiceFrom $7,000

AI Workflow Development

Design and build of a language-model system with evaluation, cost control and safe failure built in.