Contract guard for the tool/function schemas your LLM agent exposes to a model.
You rename a parameter, tighten a type, or make an optional field required. Your tests pass — the code is internally consistent. But the model was prompted with the old schema, so it keeps emitting the old argument shape, and the tool call fails at runtime. Often silently. Often only in production, on the calls you never wrote a test for.
Agent ToolSpec diffs the schemas you are about to ship against the baseline the model already knows, classifies every change as breaking or compatible, and cross-checks that a real handler exists for every tool you declare. It exits non-zero on breaking drift, so you can wire it into CI as a gate.
The promise (and its limits)
It does: deterministic, explainable schema diffing — parameter removals, type changes, new required arguments, narrowed enums, missing handlers, and handlers the model can never reach. Every verdict is derived only from the files you attach.
It does not: read your mind about semantics. If you keep a parameter's name and type but silently change what it means, that is a semantic break no schema diff can see. It also does not call your live model or replay traffic.
Who this is for
- Teams shipping an LLM agent whose tool definitions change between releases.
- Anyone running function calling / tool use where a bad arg fails quietly.
- Operators who want a cheap, offline pre-flight gate before a deploy.
- Builders who need a small, editable starting point for schema governance.
Quick start
bash install.sh # one-shot Debian install
python agent.py init-sample --dir examples
python agent.py audit \
--baseline examples/baseline_tools.json \
--current examples/current_tools.json \
--handlers examples/handlers.json \
--deterministic
echo $? # 2 => breaking change found
The sample ships a deliberate break: search_docs.top_k flips from an optional
integer to a required string. The agent flags it and blocks.
How it reads a change
baseline_tools.json current_tools.json handlers.json
(what the model knows) (what you ship) (what code accepts)
| | |
+------------+-------------+------------+------------+
| |
diff_parameters() handler cross-check
| |
+-------------+------------+
|
classify()
|
+-------------+----------------+----------------+-------------+
| | | | |
stable compatible_change breaking_change missing_handler removed_tool
| | | | |
+------ ALLOW -+ +--------- BLOCK (exit 2) ----+
What counts as breaking
| Change | Verdict | Why |
|---|---|---|
| Parameter removed | breaking | the model still emits it; it gets rejected |
| New required parameter | breaking | the model was never told to send it |
| Parameter type changed | breaking | the model emits the old type |
| Optional → required | breaking | old calls that omit it now fail |
| Enum value removed | breaking | the model may still emit the dropped value |
| Tool removed | breaking | the model still knows the tool exists |
| Handler missing / rejects a param | breaking | the call raises at execution time |
| New optional parameter | compatible | additive; old calls stay valid |
| Required → optional | compatible | relaxed obligation |
| New tool | compatible | additive for existing callers |
Set TOOLSPEC_STRICT_NEW_TOOLS=1 to also flag a brand-new tool that ships with
a required argument.
Bring your own LLM (optional)
With claude, kimi-code, or codex on your PATH, the agent adds a written
migration review through the Reviewer prompt in PROMPTS.md. No API key —
your existing CLI subscription is the billing relationship. The LLM only
comments; it never overrides a deterministic verdict.
python agent.py status # which backend is connected, if any
python agent.py audit --baseline b.json --current c.json # LLM review attached
Without a CLI, the deterministic core runs alone and the report says so.
CI gate
python agent.py audit --baseline baseline_tools.json --current current_tools.json \
--handlers handlers.json --deterministic || exit 1
Commit baseline_tools.json as the schema your deployed prompt was built
against. Refresh it only when you re-prompt or re-deploy the model.
Files
| File | Purpose |
|---|---|
agent.py |
CLI + OODA loop + deterministic diff core |
llm_adapter.py |
BYO-LLM CLI adapter (claude/kimi/codex) |
PROMPTS.md |
Named, versioned prompts loaded by load_prompt() |
SPEC.md |
Technical spec + tunable parameters |
CUSTOMIZE.md |
Where to plug in your own schemas |
CHECKLIST.md |
24-step rollout checklist |
OPERATING_COST_ESTIMATE.md |
Provider-agnostic cost model |
test_agent.py / smoke_test.sh |
Offline tests, no network |
License
Commercial pack. White-label friendly — rename, re-theme, and ship it.