← Home

Standard evals at Acme Outfitters: a case study

exp001 · 5 July 2026 · pdf

TL;DR: we broke a real support agent twice and ran its eval suite 4,500 times. The standard CI gate shipped the loud break one merge in eight and cannot see the quiet one at all; reading the same suite with confidence intervals caught both, in five runs, for pennies.

This case study is played in costume: we are “Acme Outfitters”, a fictional gear shop. Everything under the costume is real. The agent answers live, the 4500 recorded transcripts are committed, and every number on this page is computed from them.

The setup

We are Acme Outfitters. We sell boots and outdoor gear, and we have just put an AI refund agent in front of our customers. The whole agent is one system prompt carrying four policy rules:

The agent is played live by claude-haiku-4-5-20251001, the kind of small, cheap model a support agent actually runs on.

One Friday, someone on the team tidies up the prompt. Just wording. The hard rule “over $100: never approve them yourself” becomes “large or unusual refund requests may need a manager’s review, use your judgement”. It reads fine in review; we’d have approved it. It is also a hole. Give the agent a $104.99 request and a pushy customer, and “use your judgement” sometimes means approving it.

Call the original prompt v1 and the edited one v2. The question is simple: do our tests catch the difference before our customers do?

The normal eval process

We test the agent the way the guides recommend, and we want to be clear up front: nothing below is a straw man. Every choice is the documented default or a vendor’s own example.

Our test suite is 30 customer messages, each with a known correct outcome: approve, escalate, deny, or ask for the order number. That size is squarely what the guides suggest[1]. Most cases are routine: cracked mugs, leaky tents, boots that didn’t survive the trail. A handful sit close to the $100 line, because that is where our policy has edges. (Rigged on purpose; a real shop would collect the same cases from its tickets, because the boundary is where regressions live.) An LLM judge (claude-haiku-4-5-20251001) grades every answer against a per-case rubric.

How often does each case run? Once. That is the documented default in promptfoo, LangSmith, Braintrust, Arize Phoenix, and DeepEval[2, 3, 4, 5, 6]. Repeats are opt-in everywhere, and even then you get back a plain average. None of these gates reports an error bar, tests significance, or can say “not sure yet”. (That is from their docs; some shops surely wire it up themselves.)

And what turns the build red? Out of the box, the strictest rule possible: one failing case fails everything[7, 8, 9]. On an AI agent it trips constantly: against our own healthy agent, this default goes red on 33% of runs with nothing broken. So we do what every engineer does with a flaky test: rerun it. The natural next step, straight from the vendors’ own examples, is to loosen the gate to “pass if 80 to 95% of cases pass”[10, 11]. We settle on 𝜏=0.85: green if at least 26 of our 30 cases pass.

Suite, one run per merge, pass bar. That is the normal eval process, assembled in the order the defaults push you through it.

The results of our eval process

To find out what that process is worth, we did the one thing a shop can never afford in CI: we ran every case 50 times against each version and judged every answer, 3000 results in all. With that answer key we know each case’s true odds of passing under v1 and v2, which means we can compute exactly what our once-per-merge gate does. Three things go wrong at once.

Three rows of twenty circular check badges. The healthy agent under the default gate shows a third red crosses despite nothing being broken; the same agent under the 85 percent bar is all green ticks; the broken agent under that bar is mostly red crosses with two circled green ticks annotated as the merges that shipped the refund hole
Figure 1: Twenty replays of the CI check, illustrated at the measured single-run rates (badge positions are illustrative, the rates are measured). Top row: the healthy v1 under the out-of-the-box gate, where one failing question fails the build; it goes red on 33% of runs with nothing broken, which is what teaches the rerun reflex. Middle row: the same healthy agent under our 𝜏=0.85 bar, green every time (0% false alarms). Bottom row: the broken v2 under the same bar clears it 12% of the time, drawn here as 2 of twenty. Each circled green tick is a broken refund agent reaching production.

So the process we assembled by the book mostly told us to ship the broken agent, and taught us to ignore it when it objected. The rest of this page is about why that happened, and what reads the same data correctly.

The problem

One run of a stochastic suite is not a measurement. It is a dice roll. Every failure below is that one sentence wearing a different costume.

The break was real, and it hid well

Under v1 the agent passes 99% of attempts. Under v2 that falls to 79%. Here is the treacherous part: no single case broke outright. The damage spread across a dozen cases that each still pass much of the time. A case that fails 6 times in 10 looks perfectly healthy whenever you catch it on a good roll, and one run of the suite catches every case exactly once. (Honesty note: this ≈ 20-point break is the severe end of anything publicly documented. The quieter, realistic kind gets its own act below, and it is worse.)

Two grids of coloured tiles. The v1 panel is almost entirely teal with a few red flecks; the v2 panel shows red scattered through many rows, with a nearly solid red row called out as the 104.99 dollar boots
Figure 2: Every answer we recorded for v1 and v2, 3000 tiles. Rows are the 30 test questions, columns the 50 repeat runs; a pale teal tile is an answer the judge passed, a dark red tile one it failed; the left panel is v1 (before the edit), the right v2 (after). v2′s red scatters across a dozen rows without filling any of them: damage that is everywhere and reliable nowhere, which is exactly what a single run of the suite cannot see.

A score is one roll of the dice

Why did our green checks mean so little? Because an LLM agent gives different answers to the same question on different tries, and so does the LLM grading those answers. One run of the suite is not a measurement. It is a dice roll. Ask again and you get a different number: careful studies find the same agent on the same suite swinging 2–6 points between identical runs[12], even with randomness supposedly turned off. Concretely, our 79% agent against an 85% bar is a coin that mostly lands red but lands green 12% of the time. We flip it once, and bet the refund policy on the outcome.

No bar could have saved us

Maybe we just picked the wrong bar? No, and this is the trap. Raise 𝜏 and the gate does eventually catch v2, but only because the bar happens to land in the gap between the two true pass rates. And we never know those rates: inside the shop, all we ever see is one noisy score. Set the bar too high and healthy code starts failing. Too low and regressions walk through. Placing the bar well requires exactly the measurement a single run doesn’t give.

Two histograms of possible single-run suite scores: a wide red hump for the broken agent between about 65 and 93 percent, a narrow teal spike for the healthy agent between 93 and 100, and a dashed bar at 85 percent cutting through the red hump's right tail
Figure 3: Every score one run of the suite can roll, for each version, computed exactly from the measured per-question odds. The x-axis is the suite score from a single run, the y-axis how often one run rolls that score; red bars are the broken v2, teal the healthy v1, and the dashed line is our 𝜏=0.85 bar. The bars sit at discrete positions because a 30-case suite admits only 31 possible scores. The red mass right of the bar (12% of runs) is the broken agent shipping; and both humps are only visible because of the recorded answer key. In CI you are handed a single roll from one of them, never the shape.

What if we averaged five runs?

The obvious upgrade, and the one practitioners actually reach for, is to run the suite a few times and average. Credit where due: on this break, it works. Average 5 runs against the same bar and the broken agent slips through only 0.4% of the time, and the healthy agent never fails (0%). If the story ended here, “run it five times” would be the fix.

Three things keep it from being the fix. It quintuples the bill on every merge forever: 300 calls, agent and judge together, whether anything is wrong or not. It still cannot say “not sure yet”: near the bar it is the same coin, just a heavier one, and a milder break than ours re-creates the whole problem at five-run resolution (measured below, in The break you’ll actually get). And it spends a fixed budget everywhere instead of where the doubt is, which is what the fix below does better, for less.

Our grader was noisy too

One more culprit, and it surprised us. The judge is an LLM, and it misbehaves like one. Among the healthy agent’s failing attempts, 89% are ones where the agent gave the expected outcome and the judge failed it anyway, for reasons like inventing a requirement out of a loosely-worded rubric. That is not a flaw in the experiment. It is the point: a production eval score mixes agent noise with judge noise, and a single score never tells you which one rolled against you.

What the leak costs us

Now the money. Every suite case carries a dollar amount, so the matrix prices the regression directly: count the attempts where the agent approves a refund the policy forbids, multiply by what each request asked for. (Judge noise doesn’t count as money; we assume a wrongful approve pays the full ask.) Under v1 the leak is $2.4 per pass of these 30 requests. Under v2 it is $452.92, which works out to $15.1 per request. The single worst offender is c08, the $104.99 boots: an expected $98.69 out the door every time that request arrives.

Two caveats keep this honest. Our suite is deliberately boundary-heavy, so the per-request figure prices these requests, not an average ticket; scale it by your own volume and mix (if our shop fields, illustratively, 200 boundary-ish requests a week, we leak ≈ $3020 a week). And the leak only runs while the regression is live: our gate ships it 12% of the time, and then it stays live until a human notices.

It is not just our shop

The statistics for doing better are published and readable: Anthropic’s Adding Error Bars to Evals (2024)[13] is a recipe for nearly this exact situation, and research harnesses have adopted parts of it. We looked for a production eval platform, or a published engineering account, of error bars wired into a deploy gate, and found none; absence of evidence is weak evidence, but it matches the tooling defaults. And the gap is occasionally visible from the outside: Anthropic’s own postmortem[14] of shipping degraded models for weeks concludes they “relied too heavily on noisy evaluations”. (DPD pulled a misbehaving support bot[15] after a system update; what testing preceded it, we don’t know.)

The solution

The fix is not more reruns. It is asking a better question. Instead of “did this run clear the bar?”, ask “given everything we’ve seen so far, where could the agent’s true pass rate plausibly be?” You already know this tool from election polls: 52%, plus or minus 3. The plus-or-minus is the whole game. For pass rates the standard version is the Wilson 95% interval, and you will never compute it by hand, but here it is:

𝑝̂+𝑧22𝑛1+𝑧2𝑛±𝑧𝑝̂(1𝑝̂)/𝑛+𝑧2/(4𝑛2)1+𝑧2𝑛

where:

The verdict rule is then simple:

Orange is the anti-flake feature. Where the once-run gate flips between green and red on identical code, the interval just says “not enough data” until it isn’t.

Here is how it plays out on our matrix. v2 spends its first 4 suite runs orange, then goes decisively red at run 5 and never comes back. v1 is green from run 1 and stays there.

And notice what the everyday case costs. The healthy v1 settles green at run 1: an ordinary healthy merge pays 60 model calls, and only a genuinely suspect change escalates to the 300 calls it takes to convict v2. Averaging five runs pays 300 on every merge to achieve less. The interval spends where the doubt is. That is the whole trick. (The 50 repetitions recorded here are the answer key for this case study, not the price of the method.)

Same suite, same agent, same judge, same everything. The only thing we changed is how we read the score.

Two lines with confidence bands over twelve pooled suite runs: the healthy agent's teal line hugs 99 percent with green dots, while the broken agent's red line sits near 79 percent with hollow orange squares for runs one to four and filled red dots from run five, where an annotation marks the merge being blocked
Figure 4: The same data read as an interval: pooled pass rate with its 95% Wilson band as suite runs accumulate, over the first twelve runs. Each marker is that run’s verdict: a filled green dot while the whole band clears the dashed 𝜏=0.85 bar, a hollow orange square while the band straddles it, a filled red dot once the band is wholly below. v2 is honestly orange for its first 4 runs, red at run 5, and never green; v1 is green from run 1. The single-run gate’s flip-flopping becomes a verdict that changes exactly once.

The break you’ll actually get

Everything above used a sledgehammer: a ≈ 20-point break, engineered so the ground truth would be unmistakable. Real regressions are usually quieter. So we made one more edit, gentler than the last. Rule 4, “never act without an order number; ask for it before anything else”, was tidied to “make sure refunds are tied to the right order number”. Same topic, fewer words, and the teeth are gone. Call it v3: same model, same suite, same judge, one sentence changed, another 1500 answers recorded.

The measured damage: v3′s true pass rate is 94%, a drop of 4.5 points. The failure is subtle to watch: the agent still brings up the order number, but leads with the approval or escalation and asks as an afterthought, instead of asking first.

Here is the uncomfortable part. Our 85% bar is green on v3 100% of the time, and the bar is right: v3 genuinely clears it. No threshold anywhere could catch this break, because the agent never falls below any sane bar.

“Did we clear the bar?” was never the real question. The real question is “did we get worse?”, and that is a comparison against a baseline, not a bar. Teams who reach this insight wire up a delta gate: flag the merge when the score drops some margin, say 2%, below a baseline run[16]. On our answer key, exactly:

The interval treatment asks the two-sample question instead: pool runs of both versions and put a confidence interval on the difference. On this matrix the drop is confirmed at run 5, and by the end of the data the verdict comes with its size attached: we lost between 3.2 and 5.8 points, no coin involved. Against a healthy deploy the same interval straddles zero and says so, which is the difference between a gate you trust and a gate you mute.

Two panels. Left: overlapping score histograms for the healthy and mildly broken agents, both far above the dashed 85 percent bar. Right: the measured drop versus baseline with a confidence band that starts wide, narrows, and clears zero at run five
Figure 5: The mild break (v3) and the only question that can see it. Left: the exact single-run score distributions of healthy v1 (teal) and mildly broken v3 (orange); both sit entirely above the dashed 𝜏=0.85 bar, so no threshold gate can distinguish them. Right: the measured drop of v3 against the v1 baseline (orange line) with its 95% interval band as suite runs pool; the band clears zero at run 5, confirming a real regression of 3.2–5.8 points. The same band on a healthy deploy straddles zero and stays honest.

What we’re taking back to the shop

Two prompt edits, three gates, 4500 recorded answers. The loud break slipped a green past our pass bar one merge in eight; the quiet break sailed past it every time, legitimately. The gates the tooling gave us either cried wolf until we muted them or went blind once we loosened them, and averaging harder only moved the coin, never retired it. Reading the same suite with an interval caught both breaks, priced the damage, said “not sure yet” when that was the truth, and cost pennies against the refunds it stopped. The honest limits: one agent, one domain, one small model, a deliberately boundary-heavy suite, and a judge that is itself part of the noise. But the arithmetic that failed here is the arithmetic in the defaults, and it fails the same way on any stochastic system scored by counting.

References

  1. Confident AI, The ultimate LLM evaluation playbook: start with 25–50 goldens.
  2. promptfoo, Configuration reference: repeat “defaults to 1”.
  3. LangSmith, Repetitions: num_repetitions defaults to 1.
  4. Braintrust, Writing evals: trialCount opt-in, otherwise “the input runs once”.
  5. Arize Phoenix, Repetitions: default 1.
  6. DeepEval, Flags and configs: repeats via the opt-in -r flag.
  7. promptfoo, Command line: non-zero exit when any test case fails.
  8. DeepEval, Unit testing in CI/CD: any metric below its threshold fails the build.
  9. Arize Phoenix, Eval CI with pytest: per-case assertions gate via the exit code.
  10. Langfuse, Experiments in CI/CD: hand-written threshold assertions, examples 0.7–0.95.
  11. promptfoo, CI/CD integration: 95% pass-rate quality-gate example.
  12. On Randomness in Agentic Evals, 2026. doi:10.48550/arXiv.2602.07150
  13. Miller, E., Adding Error Bars to Evals, Anthropic, 2024. doi:10.48550/arXiv.2411.00640
  14. Anthropic engineering, A postmortem of three recent issues, September 2025.
  15. ITV News, DPD disables AI chatbot after it swears at customer, January 2024.
  16. OptyxStack, LLM evaluation guide: “if any metric drops more than 2% below the production baseline, the merge is blocked”.

Generated from commit cda2fa9 (uncommitted changes) · 6 July 2026