1. Install
Python 3.10 or newer. The guard is one package, asasked on PyPI; its only required dependency is cryptography. The same files are attached to the v0.1.10 release on GitHub, for installing without PyPI.
pip install asaskedExtras add what you use:
| extra | adds |
|---|---|
asasked[anthropic] | Claude as an extractor; Gemini and OpenAI-compatible endpoints need nothing extra |
asasked[mcp] | the MCP server, asasked-mcp |
asasked[claude-agent] | the hook for Claude Agent SDK |
asasked[openai-agents] | the guardrail for OpenAI Agents SDK |
asasked[langchain] | the middleware for LangChain 1.x |
As an MCP server. asasked-mcp is a local (stdio) server with one tool, check_order, that the agent calls before paying. It is a second opinion the agent chooses to ask for, not a lock: if payment goes through your own code, call the guard there instead.
# A. nothing to install first, with uv
uvx --from "asasked[mcp,anthropic]" asasked-mcp
# B. installed with pip
pip install "asasked[mcp,anthropic]"
which asasked-mcp # the full path to put in the app's configReady configs for Claude Code, Claude Desktop, Cursor, VS Code, Codex CLI and Gemini CLI are on the Install page.
From source, to run the sandbox store and reproduce the benchmark (sections 5–7). Checkout in the store builds a real Verifiable Intent chain, so the reference SDK is expected as a sibling checkout:
git clone https://github.com/agent-intent/verifiable-intent.git
git clone https://github.com/ginjikurata/asasked.git
cd asasked
python3 -m venv .venv && source .venv/bin/activate
pip install -e ../verifiable-intent -e ".[dev]"
pip install -e ".[anthropic]" # if Claude extracts2. Choose the extractors
Turning words into facts is the only step that uses a model. One model works; two are safer, because a fact they disagree on only warns. Use Gemini, Claude or an OpenAI-compatible endpoint, alone or in any pair. The decision itself is plain code.
# environment variables; in a checkout, copy .env.example
# one extractor works; two are compared
LLM_AGREE_PROVIDERS=gemini,anthropic
LLM_API_KEY=... # Gemini, Google's own API
CLAUDE_API_KEY=... # Claude (CLAUDE_BASE_URL: a proxy)
OPENAI_COMPAT_BASE_URL=... # any /chat/completions
OPENAI_COMPAT_API_KEY=...
OPENAI_COMPAT_MODEL=...3. Check a cart
Facts are extracted once per request; each check compares them with a cart. today turns “by Saturday” into a number of days; it defaults to the current date.
from asasked.guard import Guard
from asasked.store.store import Mandate, Store
store = Store() # the sandbox catalog; in production, your own find()
sku = "ASI-GELRESOLUTION9-42-BLA-OUT"
mandate = Mandate(prompt_summary="Black ASICS, size 42, returnable",
allowlist=(sku,), quantity_cap=1,
amount_min_cents=0, amount_max_cents=20000)
guard = Guard(request="Black ASICS in size 42, I need to be able to return them.",
today="Monday, 2 November 2026")
guard.check([(sku, 1)], mandate, store.find){'decision': 'ask',
'reasons': ['Asked to be able to return it, the item is final sale:
ASICS Gel-Resolution 9, size 42, black.'],
'question': 'Before I buy this, please check:\n'
' - Asked to be able to return it, the item is final sale:
ASICS Gel-Resolution 9, size 42, black.\n'
'Should I still go ahead?',
'notes': []}reasons block the purchase; notes are mismatches on facts the person marked as optional, that the two models disagreed on, or that the item does not say (no returnable field, say). A requirement no field holds, such as “gluten-free”, is kept in the person's words: if it is a must, the guard asks; if not, it adds a note. If extraction fails, the guard answers ask: it fails closed. For tests or replays, pass extract= with ready facts instead of calling models.
4. Your own catalog
find(sku) returns the item as a dict. These are the attributes the checks read; a check whose attribute is missing cannot run.
| field | meaning |
|---|---|
name, brand | what the item is; brand is compared case-insensitively |
color, size_label, level | variant attributes; level is beginner, intermediate, pro or any |
refundable | whether the item can be returned |
ships_in_days | days until it arrives |
price_cents | price in minor units |
units (optional) | how many the person would count: a 4-can pack is 4 |
The mandate supplies the amount band and the allowlist, so the guard can tell “expensive” from “there was nothing cheaper”.
5. The sandbox store
A deterministic store with 456 products from 4 merchants, driven through tools: search_products, get_product, add_to_cart, remove_from_cart, view_cart, ask_user, checkout. Every order goes through a real Verifiable Intent chain. The guard runs in one of three modes: off, advisory (the agent gets a check_before_checkout tool) or enforced (checkout holds the order until the agent has asked the person).
session = store.new_session(mandate, guard=guard, guard_mode="enforced")
session.call("search_products", {"query": "black asics 42", "limit": 2})
session.call("add_to_cart", {"sku": sku, "quantity": 1})
session.call("checkout", {"confirm": True}){'status': 'needs_confirmation',
'reasons': ['Asked to be able to return it, the item is final sale: ...'],
'question': 'Before I buy this, please check: ...',
'next_step': 'Ask the person with ask_user. If they agree, call checkout
again; otherwise change the cart or stop.'}Agents outside Python reach the same store over HTTP on localhost:
python scripts/serve_store.py --port 8765
GET /tools the tools, as JSON schemas
POST /sessions {"mandate": {...}, "user_reply": "..."}
POST /sessions/<id>/call {"tool": "search_products", "args": {...}}
GET /sessions/<id>/transcript every call and the final order6. Run the benchmark
The same agent runs each task without the guard and with it enforced. Every run is one JSON line in runs/ with the full tool-call transcript. Agents are scored against the human label, not against the guard: a right purchase, a wrong purchase that payment let through, or no purchase.
python scripts/run_agents.py --tasks v1-open --guard both \
--agents naive
python scripts/run_agents.py --tasks v1-open --guard both \
--agents claude-sonnet-5,gpt-6-sol --parallel 8
python scripts/run_agents.py --tasks v1-open --guard both \
--agents openai_compat:your/model
python scripts/run_agents.py --tasks v2-open --guard both \
--agents claude-sonnet-5The open half of the dataset is in the repository; see Dataset. So is the open half of v2 (--tasks v2-open). The rest of v1 can be rebuilt from the seed in the repository; the rest of v2 cannot.
7. Tests
pytest covers the deterministic check, the Verifiable Intent chain, the store and the harness, with no model and no network. The behaviour every implementation must match is in the spec.
pytest -q