? Britain by the Facts

How I built a hallucination-proof RAG over 6.2 million government records, solo, in a week

Josh Brown, August 2026

I run a civic data site, the British Resilience Index, where every published figure has to trace to a primary government source. A year of that taught me two things: UK official data is astonishingly deep, and everyone who puts an LLM in front of data does it backwards. They index whatever documents they have, retrieve whatever looks similar, and hope the model does not make things up. Then they bolt on guardrails when it does.

I wanted to know what happens if you invert it: audit the corpus before retrieval ever runs, and forbid the model to speak beyond it. The result is Britain by the Facts, a paid iOS app that answers questions about the UK from official statistics alone. It holds 6.2 million observations across 1,583 series from 367 official sources, reaching from consumer prices in 1209 to last month's NHS waiting list. Every figure is reconciled to its publisher before it can be served, every answer cites its sources, and when the data is not held the system refuses and says exactly what is missing.

This is how it works, where it falls short, and what I would build next.

The architecture in one paragraph

A registry of the entire UK official data universe (one YAML file per known dataset, about 490 of them) drives ingestion. Each source gets its own parser that must pass a reconciliation gate: computed totals have to match the publisher's own headline figures or nothing loads. Clean data lands in a single DuckDB file with a strict schema: one series, one unit, one grain; publisher totals flagged so they can never be double counted; suppression as typed nulls, never sentinel values; every row carrying the ingest that produced it, back to a checksummed file. The answer engine is Claude running an agentic text to SQL loop over that file, read only, under a contract: every figure must come from a query it actually ran, derived figures must be computed in SQL, every answer ends with sources, and no data means a refusal that names the missing dataset. A 40 case eval suite with forbidden figure regexes runs the whole engine end to end.

Why it does not hallucinate numbers

Not because the model is special. Because three separate layers each make a different class of fabrication structurally hard:

  1. 1. The corpus is verified before retrieval. Most RAG failure analysis focuses on generation. In my experience the corpus is half the problem: parsing errors, double counted subtotals, stale editions. The reconciliation gate catches these at ingest. In practice it caught a sewage spill undercount, a survey where regions and their sub areas were both loaded as leaves (which would have silently doubled every aggregate), and three cases where the government's own published workbooks contradicted themselves.
  1. 2. Figures must come from executed queries. The engine's contract bans stating any number it did not query, including derived ones. This rule exists because a live fact check caught the engine inventing a windowed subtotal early on. The fix became a permanent eval case: that exact wrong number can never appear again without failing CI.
  1. 3. Superlatives need proof. No answer may call a value a record, peak, low, or an Nx multiple without an explicit MIN, MAX, ORDER BY or ratio query proving that specific claim. This one rule eliminated most of the residual error class in adversarial fact checking, which was never invented values, always sloppy context around real ones.

The refusal behaviour is the feature I am proudest of. Asked for potholes filled, it answers in three sentences that no such series is held, names the closest official dataset, and takes no payment. The refusal feeds a public roadmap where missing datasets are ranked by how many real questions hit each gap, without ever storing a user's question text.

The build, honestly

Solo, about a week of intense work for the core, using a fleet of cheap model agents for the mechanical middle. The registry and warehouse schema came first. Then dozens of parser writing agents ran in parallel, each downloading primary files, writing a build function, and iterating until the reconciliation gate passed. Agents never touched the database; a single writer ingested everything serially through the gates. The final push landed 121 sources in a day. Every answer path was then adversarially fact checked by separate verification agents that recomputed every claim independently, three rounds, until every number in the launch library survived.

Costs, since everyone asks: infrastructure is a single small VM at a few dollars a month that scales to zero. A simple answer costs 10 to 45 cents of model compute; a full multi chart briefing note costs about 90 cents and takes two minutes. The eval suite costs about six dollars per full run.

Where it can improve

I would rather list these myself than have a client find them.

What hardware or capability would move the needle

Almost none of the improvement path is GPUs, which surprises people.

What data is next

The registry already knows, which is the point of registry first design. Roughly 90 known datasets remain unloaded, and refusals are ranking them by real demand. The highlights:

The one line version

Retrieval quality is a data engineering problem before it is a prompting problem. Verify the corpus at the door, forbid the model to speak beyond it, prove every superlative, and publish what you do not know. That is the whole trick, and it is portable to any organisation's data.

If you want this done to your data, I am at support@urdev.dev.