Groundwork — From AI Prototype to Safe Production | PharmaTools.AI
Groundwork · Open Source

From AI prototype to production.

Deployment-readiness harnesses for document QA and structured extraction — built for the small team with a working prototype, real users waiting, and no ML engineer.

Deterministic checks No LLM judge No API key required MIT licensed
The problem

The gap between "it works in the demo" and "we can put this in front of people"

Plenty of small teams have built an AI assistant that works well most of the time — answering questions from documents, or turning referrals and forms into structured records. What they don't have is the apparatus around it: a privacy pre-step, an evaluation built from their own failures, and a gate that stops a quiet prompt tweak from shipping a system that fabricates. That apparatus usually requires an ML engineer. Groundwork scaffolds it instead.

Groundwork's units are deployment patterns — think Terraform modules for trustworthy AI. One command writes the whole harness for a pattern into your repo; one file connects it to your system. The checks are deterministic — same inputs, same result, every time, free.

npm install -D @pharmatools/groundwork npx groundwork init # document QA: documents → grounded answers npx groundwork init extraction # extraction: documents → structured fields
Sixty seconds

See the gate catch fabrication before you wire anything

The repo ships fully offline demos — tiny systems wrapped in the exact harness init scaffolds. Run one, then flip the system into the failure modes real systems actually have and watch the build turn red:

# document QA: the whole loop passes, fully offline npm run demo # the system starts inventing figures and answering when it should abstain npm run demo:break ✗ GROUNDING demo-savings: ungrounded number "14" — not in the provided context ✗ GROUNDING demo-unanswerable-appeals: unanswerable question — the answer did not abstain
# extraction: the same loop — a guessed date of birth, a dropped required field npm run demo:extraction:break ✗ EXTRACTION demo-referral: SCHEMA: /referral_date must be string ✗ EXTRACTION demo-referral: FABRICATED field "date_of_birth": document does not state it

That red build is the product: a fabricated figure, a guessed field, or a failed abstention becomes a CI failure, not a user complaint. The guessed date of birth is the failure worth staring at — the record looks more complete, and everything downstream trusts it.

The course

Seven lessons, about two and a half hours

Every lesson ends with a checkpoint you can verify — not "you have read this," but "this specific thing now works on your machine." The biggest block is Lesson 3, because writing gold cases is the part that's actually yours. Take it on GitHub →

10 min

Orientation — see the gate fail correctly

Run the whole loop on a toy system, then flip it into fabrication mode. You don't trust a safety gate you've never seen catch something.

✓ demo green, demo:break red — on your machine
15 min

Wire your system — one file

Edit groundwork/adapter.mjs: it receives { question, context } and returns { text }. HTTP endpoint or local module — the eval never touches your system except through this file.

✓ a one-liner returns your system's answer
10 min

The privacy pre-step — and where it stops

Identifiers become labelled tokens locally, before anything is sent. You'll also learn the honest boundary: keyword-anchored redaction won't catch names in free prose — that stays with your review process, on purpose.

✓ a synthetic identifier comes back as a token
45+ min

Gold cases from real failures

The rule of the house: every case comes from a question a user really asked or a failure you really saw. Anchors pin required facts; every number must trace to your documents; at least two cases must be unanswerable — where the only correct behaviour is to say so. Optionally, retrieval fidelity cases guard the layer underneath: the sections your ingestion serves, so a truncated chunk can't silently poison every answer built on it.

✓ five real cases running; every anchor defensible out loud
20 min

The gate — freeze a floor, then try to break it

check --baseline makes a good run the floor; the scaffolded GitHub Action fails any pull request below it. Then comes the course's signature move — see below.

✓ sabotage PR red · revert green · nothing merged
15 min

Costs and the habit

Measured token usage, savings in leverage order — and the two habits that keep the harness alive: weekly transcripts into gold cases, and a deployment log that is your measurement, because Groundwork sends no telemetry at all.

✓ cost shows real usage; log's first row filled
20 min

Structured extraction — the second archetype

The same loop on the extraction pattern: schemas where nullability is the abstention contract, gold labelled by hand from real documents (null = the document doesn't state it), and the gate naming a guessed field — FABRICATED field "date_of_birth" — the extraction twin of a fabricated dose.

✓ demo:extraction green, demo:extraction:break red; one real case labelled
The sabotage test

Break your own system on purpose

A gate you've never seen fail is a gate you're trusting on faith. On a branch, make your system worse deliberately — tell the prompt to "give your best estimate even if the documents don't say." Open the PR. Watch CI go red with the failures named. Revert, watch it go green.

That red build is exactly what a well-meaning teammate's bad prompt change will look like six months from now — except now you've already proven it gets caught.

A case, concretely

What a gold case looks like

Plain JSON, written by the person who knows the documents — no eval expertise required:

{ "id": "max-daily", "question": "What is the maximum amount I can take in 24 hours?", "context": "…the passage your retrieval returned…", "answerAnchors": [{ "value": "100 mg", "aliases": ["100mg"] }], "allowedNewNumbers": [], "answerable": true }

The scorer checks three things, deterministically: the anchored facts appear, every number traces to the context, and unanswerable questions get a refusal instead of an invention.

An extraction case is the same idea for records — the document, your JSON Schema, and gold labelled by hand, where null means the document does not state it:

{ "id": "referral-dob", "kind": "extraction", "document": "…the referral text as your system receives it…", "schema": { "type": "object", "required": ["referral_date", "date_of_birth"], "properties": { "referral_date": { "type": "string" }, "date_of_birth": { "type": ["string", "null"] } } }, "gold": { "referral_date": "2026-03-14", "date_of_birth": null }, "normalize": { "referral_date": "date" } }

Values compare exactly after per-field normalisation — dates in ISO, money in minor units — so there is no paraphrase problem and nothing for a judge model to judge. A value extracted into that null field fails, named per field.

Costs

Measure first, optimise second

groundwork cost reads real token usage from your eval runs and recommends savings in leverage order: prompt caching for the documents that ride along with every question, batching for anything not user-facing-urgent, context trimming (which is also safer), and model routing only after those — re-checked through the gate, because cheaper is only better if the grounding score doesn't move.

In Claude

The same gate, conversationally

Groundwork ships an MCP server, so Claude Code, Cowork, and Claude Desktop can run the checks as tools — scaffold a harness for either archetype, run the full readiness gate on a repo, or paste a single answer (or a single extracted record with its schema and gold) and get the deterministic verdict with named issues, no repo required. The conversational checks import the exact same gate logic as CI, so the two can never drift apart.

{ "mcpServers": { "groundwork": { "command": "npx", "args": ["-y", "-p", "@pharmatools/groundwork", "groundwork-mcp"] } } }

There's also an Agent Skill that teaches an agent to run the gate and report results honestly — including refusing to present a green check as a safety certification. Both ship in the npm package.

Honestly

What Groundwork is not

Groundwork is a strong floor, not a guarantee. It automates the checks that can be automated — redaction, grounding, schema validation, field accuracy, regression gating, cost hygiene. It cannot certify an AI system safe, and it does not replace human review for outputs that affect someone's health, money, legal standing, or safety — and extraction raises the stakes quietly, because the record feeds decisions, so a wrong field is a wrong decision. Keep a person in that loop. That is not a limitation of this tool so much as the current state of the field, stated plainly.

The course's completion checklist ends the same way: after the machine-checkable part comes the human part — a named person reviews high-stakes outputs before they reach the person they affect, and everyone on the team can say who. That item isn't a transitional arrangement to be automated away later; it's part of the design.

Under the hood

Built on tools you can inspect

OpenGATE

The evaluation engine: gold-anchored, LLM-judge-free scoring for evidence-grounded AI.

About OpenGATE →

Redacta

The privacy pre-step: deterministic, on-device pseudonymisation of identifiers before AI sees your text.

About Redacta →

A real, live example

A Claude-backed document-QA system with prompt caching, key-gated evals, and measured token usage — plus a mock API so the whole loop runs offline in CI.

examples/claude-doc-qa →

The full walkthrough

Install to gated CI in about half an hour, most of it spent writing gold cases — the half hour that matters.

Getting started →