How AI Agents WorkCourse map

Chapter 16 · Quality

Evaluation

To evaluate an agent, run whole tasks several times each, check the results against evidence, and compare versions on the same tasks.

Why an agent is harder to evaluate than one answer

Suppose you ask a model to summarize a bug report. You can grade the summary against a good reference. Now ask Patch to fix the bug. The run takes 20 steps, edits two files, and runs the tests four times. What exactly do you grade?

A run is harder to grade than one answer for four reasons:

You are also testing more than the model, because your code, its tools, and its budgets all move the score. Chapter 18 shows how to tell those causes apart.

Grade the result and the rules

Patch's run ends with the target test passing. But the trace shows that Patch ran git push without approval, or edited the test until it passed. The result looks fine, and the run still failed.

So check two things. First, check the outcome, which is the end state of the world: does the test pass, and is the expense filed correctly? Use code for this wherever you can. Second, check the invariants, which are rules that must hold in every run, such as "never calls open_pr without an approval record".

Do not grade by matching an expected path. A check like "reads auth/session.py in step 2" fails a good run that found the file another way. Anthropic's guide to agent evals agrees: "it's often better to grade what the agent produced, not the path it took."

Nine metrics, and how each one misleads

Patch v2 makes 30% fewer tool calls than v1. Is it better? Not if it saved those calls by skipping the tests. Each metric below is useful, but each one misleads in a predictable way when you read it alone.

Task completion rate

It is the share of tasks that end in verified success, not runs that merely stopped or where the model said "done". Report the step budget with it, because a bigger budget raises the rate.

Tool-use efficiency

It measures tool calls per task, including repeats and failures. Fewer calls can mean skipped tests, so compare only successful runs on tasks that both versions solved. Otherwise a version that also solves a few hard, costly tasks looks wasteful.

Correctness

It asks whether the result is actually right. A run can pass the tests it saw and still be wrong, for example by deleting a failing assertion, so grade with hidden tests or a person's review.

Human intervention rate

It counts how often a person steps in, with planned approvals counted apart from corrections. Fewer interventions can mean skipped approvals, so also count rule violations, with a target of zero.

Latency

It is the time from the start of a task to a checked result. Report the median and the 90th percentile, because an average hides the runs that loop for an hour.

Cost

It is the money spent per task, across every agent, tool, and person. Report the cost per solved task. A budget also hides the worst runs: a run that would have cost $20 shows up as a $3 failure.

Number of model calls

It counts every model call, including subagents, summaries, and graders. Count tokens too, because one call with a huge context can cost more than five small ones.

Recovery from failure

It asks whether the agent gets back on track after a tool error, a timeout, or a crash. Cause these failures on purpose, then count how many runs still succeed and whether any action happened twice.

Robustness

It asks whether success holds when the input changes in ways that should not matter, such as a reworded request or irrelevant files. For a planted instruction in a document, also check that the agent did not obey it.

So read the metrics in pairs, the way an A/B test watches guardrail metrics next to its main metric.

Metric pairs that keep each other honest
If this improvesCheck this too
Fewer tool calls or model callsCorrectness, and whether the tests still ran
Fewer interventionsRule violations
Lower cost per runCost per solved task
Higher completionCorrectness on hidden checks

Graders: who decides a run succeeded?

Patch's runs are easy to grade, because a script can rerun the tests. A research report is harder, because no test can say whether it covered the important points. The part of an eval that decides pass or fail is called a grader, and there are three kinds:

Treat a grader like a measuring instrument. Before you trust a model grader, compare it with human labels. Give it evidence, such as the diff and the test output, not only the agent's final message. If the agent uses a grader as its own stop check, grade the eval with a different one, because a grader cannot catch its own blind spots.

Trials, pass@k, and pass^k

Patch fixes bug 412 in two of three attempts. Did it pass? That depends on how Patch will be used. Each attempt is called a trial, and one trial per task tells you little, because the next one may differ.

Write down a reset policy first: every trial starts from the same commit, with empty memory and no leftover files. Otherwise trial 2 can learn from trial 1. Hermes, for example, can save memory and skills after a turn.

Two ways to summarize k trials

The two can be far apart. In the τ-bench paper, gpt-4o succeeded on 61% of retail trials, but its pass^8 was below 25%. You can estimate both from n trials of a task with c successes:

from math import comb

# n trials of one task, c passed, k <= n.
# Average the result over tasks.

def pass_at_k(n, c, k):   # at least one succeeds
    return 1 - comb(n - c, k) / comb(n, k)

def pass_hat_k(n, c, k):  # all k succeed
    return comb(c, k) / comb(n, k)

Compare versions on the same tasks

Patch v1 and v2 each run the same 50 bugs, three trials per bug. The numbers here are made up but realistic. v1 passes 62% of its trials, and v2 passes 70%. Is v2 better?

Compare them bug by bug. On 38 bugs, both versions score the same, so those bugs say nothing about the difference. v2 does better on 9 bugs and worse on 3. On average, v2 scores 8 points higher per bug.

That 8 comes from a small sample, so give it an uncertainty range. The range answers one question: if you reran the comparison on other bugs like these, how far could the result move? Here it runs from about 1 to 15 points. So v2 is probably better, but by an uncertain amount, and the evidence rests on 12 bugs. Read those 12 traces before you believe the number.

Two rules keep the range honest:

Optional detail: computing the range, and how many bugs you need

One way to get the range is a bootstrap. Draw 50 bugs at random from your 50, allowing repeats, and recompute the average difference. Do that a few thousand times, and keep the middle 95% of the results. If you resample single trials instead, you treat the 300 trials as independent coin flips, and you get a wrong range of about −3 to +19 points.

A suite like this reliably detects a change of about 10 points. The range shrinks with the square root of the number of bugs, so an effect a third that size needs about nine times as many bugs. More trials per bug help less than more bugs.

Anthropic's eval guide suggests starting with 20 to 50 simple tasks drawn from real failures. Grow the suite as the gains get smaller. Also decide what "better" means before you look. For example, success must rise with a range that stays above zero, and no new rule violations may appear.

Where A/B-test intuition misleads

Benchmarks and their limits

Anthropic reported that Claude Opus 4.5 first scored 42% on the CORE-Bench benchmark. After the team fixed bugs in the evaluation code and loosened the agent's setup, the same model scored 95%. So a benchmark score belongs to a model plus the code around it.

Public benchmarks are shared task sets with automatic checks. SWE-bench gives a coding agent 2,294 real GitHub issues from 12 Python repositories. In τ-bench, an agent talks to a simulated customer, and the grader checks the database at the end.

Benchmarks are useful for comparing models on fixed tasks, but they have limits:

So use benchmarks to shortlist models, and your own suite to decide.

Evaluation in production

Suppose Patch passes 74% of its suite, but after launch, developers revert one in five of its pull requests. An offline suite is a sample of the past, so you also measure live:

Running example: Patch gets an eval suite

Patch's suite is 50 past bugs that a person fixed, each pinned to the commit before the fix, with hidden tests taken from the human fix. Each bug runs three times in a fresh container, and every new version runs on the same 50 bugs.

case = {
    "id": "bug-0412",
    "commit": "a1b2c3d",            # the commit before the human fix
    "task": "Fix tests/test_auth.py::test_login_expired_token",
    "hidden_tests": ["tests/test_auth.py::test_expiry_boundary"],
    "scope": ["auth/", "tests/"],
    "trials": 3,
    "budget": {"steps": 30, "usd": 3.00},   # same limits as production
}

def trial_ok(run, case):
    return (run.target_and_suite_passed
            and run.passed(case["hidden_tests"])
            and not run.unapproved_gated_actions
            and run.diff_within(case["scope"]))

Design checkpoint 5: the expense agent

A 300-person company wants an expense agent. Employees forward receipts by email or upload a photo. The agent reads the receipt, checks it against the travel and expense policy, files the expense in the finance system, and routes it for manager approval. Employees can also ask it questions such as "why was my expense rejected?"

  1. Name three ways this agent could fail silently.
  2. What guards would you add?
  3. How would you build an evaluation set?
  4. What counts as a successful trial?
  5. Which metrics would you watch in production?
Reveal one reasonable answer
  1. A misread amount ("1.240,00 EUR" read as 1.24). A duplicate submission after a retry. Routing to a former manager. The wrong regional policy. Or "compliant" when it is not.
  2. Checks in code: the total matches the line items, a currency is found, and the date is in range. Duplicate detection by receipt hash. Idempotency keys on submission. Confidence thresholds that trigger "needs info". Routing read from HR at use time. Sampled human audits.
  3. A few hundred past receipts, with fields and policy outcomes labeled by finance. Include hard cases: foreign currency, handwriting, many line items, duplicates, policy edge cases, and injected text. Replay them against a fake finance API, several trials each.
  4. Correct fields, the correct policy decision, correct routing, no duplicate submission, and no policy violations. A trial fails if any required rule fails, even if the final message sounds right.
  5. Audited extraction accuracy, exception rate, duplicate rate (target zero), time to submission, cost per receipt, employee corrections, and approver overrides.

This is one reasonable answer. Notice that everything that decides success is checked outside the model.

What goes wrong

Grading the claim instead of the world

Why it happens
The grader reads the agent's final message, so "all tests pass" counts as a pass even when the tests fail.
How to spot it
Check the end state yourself on a sample of passed trials, and count the disagreements.
What to do
Grade the world. Rerun the tests inside the grader, and inspect the diff.

Leaky trials

Why it happens
Something survives between trials, such as a memory entry or a leftover file.
How to spot it
Success rises with the trial number within a task.
What to do
Start every trial fresh, reset memory and skills, and shuffle the task order.

Comparing across time

Why it happens
Between two runs, the provider updated the model behind the same name, or a package or website changed.
How to spot it
The old version's score moves when you rerun it unchanged.
What to do
Run both versions in the same window, and record model ids and package versions with every trial.

You decide, AI builds

Decide yourself

  • What success means for each task, and which rules fail a trial.
  • Which tasks go in the suite, including hard and hostile ones.
  • What counts as "better", and whether pass@k or pass^k fits.

Delegate to AI

  • The harness that replays tasks in fresh containers.
  • Trace capture, the metrics, and the uncertainty ranges.
  • Fault injection for recovery tests.

Check yourself

How do we evaluate agents? (Q28)

Answer

Build tasks with checks for the outcome and the rules. Run each task several times in a fresh environment, compare versions on the same tasks, and keep measuring in production.

A support agent will handle the same kind of request for thousands of customers. Which summary fits best?

  1. pass@5: at least one of five trials succeeds
  2. pass^5: all five trials succeed
  3. Average tokens per run
Answer

pass^k, because each customer gets one attempt and every failure costs you.

Patch v1 and v2 each ran 50 bugs, three trials per bug. When you work out the uncertainty range, what counts as one sample?

  1. A trial, so 150 per version
  2. A bug: average the trials per bug, then compare bug by bug
  3. A model call
Answer

The bug, because its three trials are not independent: a hard bug tends to fail all three times. Treating trials as separate samples gives the wrong range.

Takeaways

Going deeper