A hard gate that forces AI agents to back every claim with evidence — or declare it an assumption.
Project description
Receipts
Force AI agents to back every claim with evidence — or declare it an assumption.
Agents lie about the research they did and quietly base conclusions on guesses, because there's no cost to either. Receipts adds the cost. Every claim an agent makes must point at a tamper-evident log of what it actually did. Claims that can't be grounded are illegal — they must be surfaced as explicit assumptions, not buried in the answer.
You can't fix this with prompting ("don't assume, don't lie"). Receipts doesn't ask the model to be honest — it measures honesty against ground truth, with deterministic checks the model can't game.
Install
pip install receipts-gate
Zero dependencies. The distribution is receipts-gate (the bare name was taken);
the import and the CLI are plain receipts.
The three rules
Every claim is checked against the Ledger (the captured execution log):
| Rule | Kills | How |
|---|---|---|
| Binding | guesses stated as fact | A claim must cite real evidence in the ledger, or be demoted to an assumption. |
| Effort honesty | "I reviewed the entire codebase" | Effort claims must cite evidence of a matching kind; words like all / entire / thoroughly require machine-checkable coverage proof. |
| Support | citing a source that doesn't say it | Cited evidence must actually back the claim (deterministic heuristic by default; pluggable LLM/NLI judge for production). |
Two modes, one engine
- Live gate (
Gate.finalize) — runs inside the agent loop and blocks ungrounded output before the user ever sees it, handing the agent a precise list of what to fix. - Post-hoc auditor (
audit) — ingest a finished trace and get aVerdict. Same engine, no runtime coupling. Drop it in CI to fail PRs from ungrounded agent runs.
Quick start
from receipts import Ledger, Gate, Answer, Claim, Assumption, ClaimKind
# 1. Evidence is captured from real work, not self-reported.
ledger = Ledger()
ev = ledger.record("file_read", "config.py", "PORT = 8080")
# 2. The agent emits claims that point at evidence.
gate = Gate(ledger) # hard gate by default
answer = Answer(
summary="The service listens on port 8080.",
claims=[Claim("The port is 8080", evidence_ids=[ev.id], kind=ClaimKind.FACT)],
assumptions=[
Assumption("The paid tier is higher", reason="not verified",
impact="capacity planning wrong if false"),
],
)
# 3. The gate renders the answer, or raises UngroundedAnswerError with a fix list.
print(gate.finalize(answer))
Capture evidence automatically by wrapping tools:
from receipts import track
@track(ledger, kind="web_fetch", source=lambda url: url)
def fetch(url): ...
Or audit an existing run:
from receipts import ingest_trace, audit
ingest_trace(ledger, my_logged_events) # [{"kind","source","content"}, ...]
verdict = audit(answer, ledger)
print(verdict.report())
Semantic support via an LLM
The default support check is token overlap — transparent, but shallow. For real grounding (catching a claim that cites evidence which is related but doesn't say what the claim asserts), swap in the LLM judge. The gate stays deterministic for binding and effort; only the support check calls the model, so the thing being audited can't game the structural rules.
from receipts import Gate, AnthropicLLM, LLMSupportVerifier
gate = Gate(ledger, verifier=LLMSupportVerifier(AnthropicLLM())) # needs receipts-gate[anthropic]
LLM is a one-method protocol — drop in any backend (NLI model, embeddings, a
different vendor) or FakeLLM for tests.
Free-text extraction (no structured claims required)
Agents don't have to emit Claim objects by hand. Give Receipts the prose answer
and the ledger, and it produces a structured Answer for the gate to check. The
extraction is LLM-driven and advisory — every binding it produces is still
re-verified deterministically, so a hallucinated citation surfaces as a gate
failure, not a silent pass.
from receipts import extract_claims, Gate, AnthropicLLM
answer = extract_claims(free_text_answer, ledger, AnthropicLLM())
print(Gate(ledger).finalize(answer)) # checks the extracted claims
CLI / CI gate
receipts audit trace.json exits non-zero when any claim is ungrounded — a
drop-in CI gate on agent output. Deterministic by default (no API key); --llm
turns on the semantic judge.
receipts audit trace.json # exit 1 if blocked
receipts audit trace.json --json # machine-readable
Trace format and a copy-into-your-repo GitHub Action are in
examples/ci/receipts-example.yml.
Claims reference evidence by a local label, list index, or real id — see
trace.py.
Framework adapters
Capture is framework-agnostic; these map specific ecosystems onto the Ledger.
OpenAI / Anthropic / Claude Agent SDK traces — ingest a finished transcript, recording each tool result as evidence (pure Python, no SDK needed):
from receipts.adapters import from_openai_messages, from_anthropic_messages
from_openai_messages(ledger, openai_messages) # OpenAI chat format
from_anthropic_messages(ledger, anthropic_messages) # Messages API / Claude Agent SDK
LangChain / LangGraph — capture tool results live via a callback handler, no change to your tool code:
from receipts.adapters import langchain_handler
handler = langchain_handler(ledger)
agent.invoke(inputs, config={"callbacks": [handler]})
Layout
src/receipts/
models.py data model — Evidence, Claim, Assumption, Answer, Verdict
ledger.py append-only, hash-stamped evidence log (the ground truth)
instrument.py capture: @track decorator + ingest_trace()
verify.py pluggable support check — HeuristicVerifier + LLMSupportVerifier
llm.py LLM protocol + AnthropicLLM / FakeLLM backends
extract.py free-text answer -> structured Answer
trace.py load a portable trace file -> (Ledger, Answer)
cli.py `receipts audit` command
gate.py the three rules + Gate + audit()
adapters/ OpenAI trace, Anthropic/Claude-Agent-SDK trace, LangChain callback
examples/ research_agent.py (gate), agent_loop.py (capture->extract->gate)
tests/ pytest suite
Develop
python -m pytest -q
python examples/research_agent.py
Status / roadmap
v0.1 — core engine, hard gate, post-hoc auditor, framework-agnostic capture,
LLM support verifier, free-text extraction, OpenAI adapter, receipts audit CLI
- CI action.
Adapters: OpenAI trace, Anthropic / Claude Agent SDK trace, LangChain/LangGraph callback.
Next candidates:
- NLI / embeddings
SupportVerifierbackends (no LLM call) - Live in-loop gate wrappers for popular agent runtimes
- Published package on PyPI
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file receipts_gate-0.1.0.tar.gz.
File metadata
- Download URL: receipts_gate-0.1.0.tar.gz
- Upload date:
- Size: 24.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b6ea2dd5562aa4509dc7433b8aa6ac6acfe9b436e03c3eb869c7ff661967903
|
|
| MD5 |
f0d5662376d39bda70e8c3084b643c81
|
|
| BLAKE2b-256 |
5208ce9e1fbf640460349dbee19e3a3e3629c98471cb14a5088d933f55598998
|
Provenance
The following attestation bundles were made for receipts_gate-0.1.0.tar.gz:
Publisher:
publish.yml on Thepizzapie/Receipts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
receipts_gate-0.1.0.tar.gz -
Subject digest:
2b6ea2dd5562aa4509dc7433b8aa6ac6acfe9b436e03c3eb869c7ff661967903 - Sigstore transparency entry: 2043630640
- Sigstore integration time:
-
Permalink:
Thepizzapie/Receipts@bb4fa6d6075139e3aa7baa335a7533dd8464dd10 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Thepizzapie
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bb4fa6d6075139e3aa7baa335a7533dd8464dd10 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file receipts_gate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: receipts_gate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
794a06c6e52c6888e484fb84cae0e45eded5640bcc0ada9d7c6b473031bfa711
|
|
| MD5 |
69b48717f8cde93f105f47735c1ea5ee
|
|
| BLAKE2b-256 |
db6c779adff5cd0aff4b1a3f161beda6046dbf0bc2e8637637e5d7176f3e377f
|
Provenance
The following attestation bundles were made for receipts_gate-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Thepizzapie/Receipts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
receipts_gate-0.1.0-py3-none-any.whl -
Subject digest:
794a06c6e52c6888e484fb84cae0e45eded5640bcc0ada9d7c6b473031bfa711 - Sigstore transparency entry: 2043630666
- Sigstore integration time:
-
Permalink:
Thepizzapie/Receipts@bb4fa6d6075139e3aa7baa335a7533dd8464dd10 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Thepizzapie
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bb4fa6d6075139e3aa7baa335a7533dd8464dd10 -
Trigger Event:
workflow_dispatch
-
Statement type: