Evaluation harnesses
Run agents on tasks in fresh environments and grade what they leave behind.
Create a free account Try part of it here
Why it matters
You change the system prompt, add a tool, or switch models. Is the agent better? Trying a few tasks by hand can't tell you: agents vary from run to run, and a change that fixes one task can break three others. An evaluation harness runs the agent across a fixed set of tasks, many times, in clean environments, and grades what it leaves behind. It's how teams improve agents with evidence instead of hope.
What you'll be able to do
- Design agent eval tasks with a setup, an instruction and a grader that checks the outcome
- Run each trial in a fresh environment, several times per task
- Report pass@k and pass^k, and estimate pass@k without bias
- Catch reward hacking, such as an agent editing the tests to make them pass
What it covers
- Anatomy of an agent eval
- Run each task several times
- Estimating pass@k
- The tempting estimate is biased
- Reward hacking
- A hidden test catches a cheat
- Use it
- pass@k and pass^k from one success rate
- One run proves very little
Anatomy of an agent eval
Think of a driving test. Everyone takes the same route in a prepared car, and the examiner cares whether you parked safely, not which hand you used on the wheel. Each task has three parts:
- Setup: the starting state, such as a repository at a certain commit, some files, a database.
- Instruction: what the agent is asked to do, as a user would ask it.
- Grader: code that checks the outcome: do the hidden tests pass, does the file contain the right value, is the bug gone?
Benchmarks like SWE-bench work this way: a real repository, a real issue, and the project's own tests as the grader. Grade the outcome, not the path. Many different tool sequences can solve a task, and grading the exact steps punishes good solutions you didn't think of.
Graders, from most to least trusted
| grader | example | notes |
|---|---|---|
| code | run tests, compare files, query the database | fast, cheap, repeatable; use when you can |
| model | a rubric scored by an LLM judge | for open-ended output; check it against human scores |
| human | expert review | the gold standard, but slow; use to calibrate the others |
Fresh environments
A chemist washes the glassware between experiments. Each trial gets a brand-new copy of the setup: a new container, a clean checkout. If trials share state, one trial's leftover files can make the next pass or fail for the wrong reason.
Show the answer
The trials didn't each get a fresh copy of the setup
Each trial needs its own brand-new setup. Here, the second trial's pass says nothing about the agent, because leftovers from the first one did the work.
Run each task several times
One free throw tells you little about a basketball player. Agents are not deterministic either, so one run per task measures luck as much as skill. Run each task n times and report:
- pass@k: the chance at least one of k tries succeeds. Fits tasks where a person can retry, or pick the best of several.
- pass^k: the chance all k tries succeed. Measures reliability, which matters for agents that run unattended.
An agent with a 60% success rate has pass@3 near 94% but pass^3 near 22%. Same agent, very different stories.
In plain words: pass@k asks "will one of these tries work?" pass^k asks "will every try work?"
When a person can retry, pass@k is what matters. An unattended agent needs pass^k, which falls fast.
Show the answer
pass^k: how often it succeeds every time
Unattended runs get one shot each. Reliability across runs is what users feel.
Estimating pass@k
Run n trials, and c pass. The tempting estimate of pass@k, 1 - (1 - c/n) ** k, is biased. Think of your trials as marbles in a bag, with the passing ones green. You pick k marbles, and you fail only if none of them is green. The standard estimator, from the HumanEval paper, counts the ways to pick k of the n trials that are all failures:
pass@k = 1 - C(n - c, k) / C(n, k)
where C is "choose" (math.comb). If fewer than k trials failed, it's 1.
The tempting estimate is biased
Say 2 of 5 trials passed. Compare the tempting formula with the standard one from this section, for picking 3 tries.
from math import comb n, c, k = 5, 2, 3 tempting = 1 - (1 - c / n) ** k unbiased = 1 - comb(n - c, k) / comb(n, k) print("tempting:", round(tempting, 3)) print("unbiased:", round(unbiased, 3))
The two disagree by 12 points on the same five runs, and that's without changing any model. So when you compare harnesses, make sure both reports used the same formula, or the winner may only be down to the arithmetic.
Reward hacking
Tell someone the floor only has to look clean, and the dirt may end up under the rug. Agents optimize for what the grader checks, sometimes in ways you didn't intend. Coding agents have been seen deleting failing tests, special-casing test inputs, or editing the grader. Defenses:
- Keep grading tests hidden from the agent, or restore them before grading.
- Mark some files protected; any change to them fails the trial.
- Read transcripts of passing trials now and then, not only failing ones.
A hidden test catches a cheat
An agent told to make the tests pass may special-case the inputs it can see. Hidden tests with different inputs tell you whether it solved the real problem.
def honest_add(a, b): return a + b def cheating_add(a, b): if (a, b) == (2, 3): # the one input in the visible test return 5 return 0 visible = [((2, 3), 5)] hidden = [((10, 4), 14), ((0, 0), 0), ((-1, 1), 0)] def score(fn, tests): return sum(fn(*args) == want for args, want in tests), len(tests) for fn in [honest_add, cheating_add]: print(fn.__name__, "visible:", score(fn, visible), "hidden:", score(fn, hidden))
Both look perfect on the visible test. Notice the cheat even passes two hidden cases by luck, because its answer of 0 happens to be right for them. So a thin hidden set can miss a cheat too, and graders need varied inputs.
Use it
Track cost and turns as well as success. A car that's 2% faster and burns twice the fuel may not be an upgrade, and a change that adds 2% success but doubles cost may not be worth it. Keep a small, fast suite that runs on every change to the harness, like unit tests, and a large one that runs before releases.
from math import comb n, c, k = 5, 2, 2 print(round(1 - comb(n - c, k) / comb(n, k), 2))
Show the answer
0.7
There are 10 ways to pick 2 of 5 trials, and 3 of them pick only failures. So 1 − 3/10 = 0.7.
pass@k and pass^k from one success rate
The same agent, measured two ways.
from math import comb p = 0.6 for k in (1, 3, 5): print(f"k={k}: pass@k={1 - (1 - p) ** k:.2f} pass^k={p ** k:.2f}") n, c, k = 10, 6, 3 print("unbiased pass@3 from 6 of 10:", round(1 - comb(n - c, k) / comb(n, k), 3))
With trial counts, the unbiased estimate (0.967) differs from plugging 0.6 into the formula (0.936). The gap grows as n gets smaller.
Show the answer
Protect or hide the tests, and fail any trial that changes them
Protected files and hidden tests close the shortcut. Reading some passing transcripts helps too.
One run proves very little
Agents vary from run to run. A task that passes once might pass 6 times in 10. So a single run can mislead you in either direction, and a jump from 60% to 80% on a few tasks may be nothing but noise. Run each task several times, and compare the runs with some statistics before you believe a change helped.
Grade the result, not the route
It's tempting to check that the agent took the steps you expected: read this file, then call that tool. But a good agent might solve the problem another way, and it would fail your check for being clever. Worse, an agent can take all the right steps and still leave a broken result. Check what's true at the end instead. Do the tests pass? Is the file correct? Is the record in the database?
Be suspicious of big jumps
A student who goes from a D to an A overnight deserves a second look. When a pass rate leaps overnight, look before you celebrate. Check whether the agent found a way to game the grader, or whether a task leaked its answer. Read a handful of transcripts from runs that now pass.
Pass rate isn't the whole story
Two versions with the same pass rate aren't equal if one takes 40 turns and $3 a task, and the other takes 8 turns and 40 cents. Report cost, turns and time beside the pass rate, so that a change that wins on one and loses badly on another doesn't slip through.
Practice what you just read
- 3 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 5 review cards for spaced repetition
Try a quiz question
Why grade an agent's outcome rather than the exact sequence of tool calls?