Contracts v2
A gold file lists the calls an agent must make, so every other call is
“extra” and every other path is wrong. A contract says more: which calls
are required, which are allowed but not needed, which must never happen, and
where more than one path is fine. toolscore eval, evaluate_trace(),
toolscore regrade and the agent harness take a contract wherever they take
a gold file; toolscore.evaluate_contract() evaluates one in memory.
{
"contract_version": 2,
"description": "Book a flight to Lisbon. Checking the weather is fine; cancelling is not.",
"required": [
{"tool": "search_flights", "args": {"destination": "LIS", "date": {"$regex": "^2026-12-"}}},
{
"id": "book",
"alternatives": [
[{"tool": "book_flight", "args": {"flight_id": {"$regex": "^FL-\\d+$"}}}],
[{"tool": "reserve_seat", "args": {"flight_id": {"$regex": "^FL-\\d+$"}}}]
]
}
],
"optional": [{"tool": "get_weather"}],
"forbidden": [{"tool": "cancel_booking", "reason": "never cancel an existing booking"}],
"weights": {"required_call_recall": 0.3},
"min_score": 0.9
}
Semantics
- required
Scored like gold calls, in order. An element
{"alternatives": [[calls], [calls], ...], "id": ...}stands for any one of its options (an option is a list of calls, or one call). Every combination of options is scored and the highest composite score wins; ties go to the option declared first. A top-levelalternativeslist of groups is appended afterrequired. At most 256 combinations are scored.- optional
Neither penalized when absent nor counted as extra when present: a call that matches an optional entry (tool and arguments) is left out of the score metrics, unless it is needed to fill a required call of the same tool (calls are taken in order). The honesty, credential, forbidden-call and evidence checks still see every call, and records keep it (
EvaluationResult.trace_calls;scored_callsis what the score compared).- forbidden
The forbidden-call policy (see Behavior and Safety Checks): reported under Behavior and safety and as the
forbidden-callsevidence check, not scored.toolscore eval --forbiddenrules are added to the contract’s.- weights, min_score
Score weights as for
toolscore.evaluate(), and the passing score (contract_metrics.passed; the agent harness uses it per task).
Argument matchers
Values are compared as in gold files (numbers and whitespace leniently unless
strict). In JSON a value can also be a matcher:
Matcher |
Matches |
|---|---|
|
the pattern found anywhere in the value |
|
a string or list that contains |
|
any of the values |
|
anything (the argument must be present) |
|
a JSON type (or a list of them): |
|
a number close to it; or |
In Python, any Matcher (Regex, ANY,
Approx…) works too.
What it reports
metrics["contract_metrics"]: contract_version, the counts of required,
optional and forbidden entries, alternatives.chosen (the option picked per
group and its calls) and alternatives.combinations (every combination’s
score), optional_matched (the optional calls left out of the score, by
index) and min_score / passed.
Example (examples/contracts/, real output): an agent searched flights,
checked the weather and reserved a seat instead of calling book_flight.
$ toolscore eval gold.json trace.json # search_flights, then book_flight
│ Grade F FAIL (38.3%) │
$ toolscore eval contract.json trace.json
│ Grade A PASS (95.7%) │
Top issues to fix
1. reserve_seat `reserve_seat` called with unexpected arguments (unexpected: seat)
Evidence: 7 pass, 0 fail, 0 unknown
The contract picked the reserve_seat alternative (0.957 against 0.59 for
book_flight), left get_weather out of the score, and still reports the
extra seat argument.
Schema and Python
The JSON Schema ships as toolscore/schemas/contract-v2.schema.json
(toolscore.contract.CONTRACT_SCHEMA_PATH). toolscore doctor --contract
FILE validates a contract and counts its combinations.
from toolscore import evaluate_contract, load_contract
result = evaluate_contract(load_contract("contract.json"), actual_calls,
final_answer="Booked FL-204.")
result.metrics["contract_metrics"]["alternatives"]["chosen"]
A gold file is still accepted everywhere; a file is read as a contract when it
is a JSON object with contract_version, required or optional.