Skip to main content

agent evaluation harness framework

Project description

demo

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

  1. Judge calibration — assertions kappa=0.00 -> LLM judge kappa=0.894 (n=90). The instrument is validated.
  2. 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/ToolProvider protocols 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 --strict clean, ruff clean.

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

aehf-0.1.1.tar.gz (77.7 MB view details)

Uploaded Source

Built Distribution

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

aehf-0.1.1-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

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

Hashes for aehf-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b61708f2f82c0c312eee4ab18b6f86a478da0739a3ca2969fdcaea3b12ff3bd9
MD5 450bf6d30bdca54a4ab3153ddd7de79b
BLAKE2b-256 f442c8979064d12eeba329e2b04bde095598bd2eb876fe1fdb7ac5140d6d39c8

See more details on using hashes here.

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

Hashes for aehf-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f8dab1bf6907d4c8929e84967bc23ba8c58d6bdc835c61b562a8d1ad11796484
MD5 c89f6b0c86385e47b9357227eab5e758
BLAKE2b-256 603e16d300ad3caabcb812eb6cd3d432bab9d25a94789a93b27932a65db2586d

See more details on using hashes here.

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