agent evaluation harness framework
Project description
aehf — agent evaluation harness framework
A framework-agnostic harness for evaluating tool-using LLM agents: run a suite of cases, judge the transcripts, and get statistically honest pass rates and regression checks — not a single flaky number.
The point of aehf is not just to score agents, but to validate the judge doing the scoring first, then use that trusted judge to make claims you can defend.
Headline finding
On 90 hand-labeled agent transcripts:
| Judge | Raw agreement | Cohen's kappa |
|---|---|---|
| AssertionJudge | 97% | 0.000 |
| LLMJudge (v1) | 97% | 0.894 |
Both judges agree with the human ~97% of the time, yet one is worthless and one
is excellent. Raw agreement doesn't correct for chance given the base rates;
Cohen's kappa does. The assertion judge passes essentially everything, so its
97% is meaningless (kappa 0); the calibrated LLM judge reaches "almost perfect"
agreement. This gap is the whole reason the harness measures kappa before
trusting any downstream number. (Details and caveats: docs/calibration.md.)
Architecture — everything is a protocol
The eval core is decoupled from any provider by three Python Protocols. The
runner, judges, and stats know only the interfaces:
flowchart TD
%% Main Flow
EvalCase([EvalCase YAML]) --> Runner
subgraph ProtocolBlock [Agent & Evaluation Lifecycle]
Runner[runner] --> Run[Agent.run case -> Transcript]
Adapter[AnthropicAdapter, FakeAgent, ...] -.->|Configures| Run
Run --> Tool[ToolProvider.execute]
ToolMode[mock / record / replay] -.->|Modes| Tool
Tool --> Judge[Judge.score case, transcript]
JudgeType[AssertionJudge, LLMJudge] -.->|Types| Judge
Judge --> Verdict([Verdict])
end
Verdict --> Stats[stats]
Verdict --> Calibration[calibration]
Verdict --> Regression[regression]
%% Labels & Details
Stats --> StatsDetails[Wilson, McNemar, n=k]
Calibration --> CalDetails[kappa vs human]
Regression --> RegDetails[store, diff, CI gate]
%% Right-side Protocol Tags
ProtocolBlock -.-> AgentProto[Agent protocol<br><i>bring your own: LangChain, raw, ...</i>]
Tool -.-> ToolProto[ToolProvider protocol]
Judge -.-> JudgeProto[Judge protocol]
%% Styling
style EvalCase fill:#f9f9f9,stroke:#333,stroke-width:2px
style Verdict fill:#f9f9f9,stroke:#333,stroke-width:2px
style ProtocolBlock fill:#fff,stroke:#333,stroke-dasharray: 5 5
The agent under test is a black box behind Agent; aehf evaluates whatever
implements run(case) -> Transcript. The shipped AnthropicAdapter is a
reference implementation, not the framework.
What's inside
- Runner — async, budget/timeout-enforced, captures agent crashes as failed cases (never harness crashes), bounded concurrency.
- Tools — mock fixtures, plus record/replay for deterministic offline reruns.
- Judges — programmatic assertions and a versioned LLM judge with structured (forced-tool) verdicts; calibrated against human labels with Cohen's kappa.
- Stats — n-sample execution, per-case Wilson confidence intervals, flakiness flags, and McNemar's exact paired test for model/prompt comparison.
- Regression — results store keyed by (git SHA, model, judge version),
aehf diff, a markdown scorecard, and a PR-gate GitHub Action.
Install
pip install -e ".[dev]" # editable, with dev tooling
Set ANTHROPIC_API_KEY (a .env file is loaded automatically) for anything that
runs a live model. The test suite runs green without a key — CI is free and
offline.
Quickstart
# run a suite with mock tools, assertion judge (no API cost for the judge)
aehf run examples/calibration_suite.yaml anthropic mock --judgechoice assertion
# n-sampled run: per-case pass rate + Wilson CI + flakiness
aehf run examples/calibration_suite.yaml anthropic mock --n-samples 5
# calibrate a judge against human labels -> Cohen's kappa + disagreements
aehf calibrate labels/labels_filled.jsonl llm --prompt-version v1
# compare two saved runs with McNemar's test
aehf compare runA.json runB.json
# regression diff between two stored runs (CI gate)
aehf diff <base-sha> <head-sha> --store .aehf
Full command list: run, calibrate, export-labels, label, compare,
diff.
Two findings, in one narrative
- Judge calibration — assertions kappa=0.00 -> LLM judge kappa=0.894 (n=90). The instrument is validated.
- Model comparison — using that validated judge, Haiku and Sonnet were
statistically indistinguishable on the suite (15/18 vs 14/18, McNemar
p=1.0). The apparent edge is noise; a single-run comparison would have misled.
(
docs/calibration.md.)
Both come with honest caveats stated in the docs (single annotator; a saturated
suite that can't discriminate the two models). Reporting the null result honestly
is the point — see docs/decision.md for every design choice and rejected
alternative.
Scope (v0.1)
- Ships an Anthropic reference adapter and judge. The
Agent/Judge/ToolProviderprotocols make other providers drop-in; a second provider is the planned demonstration of the framework-agnostic claim. - The CI eval gate compares committed baseline runs (golden-file style),
because replay pins tool results but not the model. See
docs/eval-gate.md. - Tested: 115 tests,
mypy --strictclean, ruff clean.
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 aehf-0.1.1.tar.gz.
File metadata
- Download URL: aehf-0.1.1.tar.gz
- Upload date:
- Size: 77.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b61708f2f82c0c312eee4ab18b6f86a478da0739a3ca2969fdcaea3b12ff3bd9
|
|
| MD5 |
450bf6d30bdca54a4ab3153ddd7de79b
|
|
| BLAKE2b-256 |
f442c8979064d12eeba329e2b04bde095598bd2eb876fe1fdb7ac5140d6d39c8
|
File details
Details for the file aehf-0.1.1-py3-none-any.whl.
File metadata
- Download URL: aehf-0.1.1-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f8dab1bf6907d4c8929e84967bc23ba8c58d6bdc835c61b562a8d1ad11796484
|
|
| MD5 |
c89f6b0c86385e47b9357227eab5e758
|
|
| BLAKE2b-256 |
603e16d300ad3caabcb812eb6cd3d432bab9d25a94789a93b27932a65db2586d
|