Skip to main content

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 a Verdict. 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 SupportVerifier backends (no LLM call)
  • Live in-loop gate wrappers for popular agent runtimes
  • Published package on PyPI

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

receipts_gate-0.1.0.tar.gz (24.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

receipts_gate-0.1.0-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

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

Hashes for receipts_gate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2b6ea2dd5562aa4509dc7433b8aa6ac6acfe9b436e03c3eb869c7ff661967903
MD5 f0d5662376d39bda70e8c3084b643c81
BLAKE2b-256 5208ce9e1fbf640460349dbee19e3a3e3629c98471cb14a5088d933f55598998

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipts_gate-0.1.0.tar.gz:

Publisher: publish.yml on Thepizzapie/Receipts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for receipts_gate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 794a06c6e52c6888e484fb84cae0e45eded5640bcc0ada9d7c6b473031bfa711
MD5 69b48717f8cde93f105f47735c1ea5ee
BLAKE2b-256 db6c779adff5cd0aff4b1a3f161beda6046dbf0bc2e8637637e5d7176f3e377f

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipts_gate-0.1.0-py3-none-any.whl:

Publisher: publish.yml on Thepizzapie/Receipts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page