Projects › Eval Harness

Watch the demo

Recorded on the live dashboard: both assistants' gates, a regression caught, a failing case, and the judge checked against hand labels.

Read the transcript

Eval Harness: a simulated client project, part three of five.

One harness now checks both of the hotel group's assistants the same way: the same cases, metrics and gates, with ninety-five percent intervals on every rate.

It reproduces both projects' published numbers from their own recordings, and fresh re-runs at the pinned versions pass every gate.

Then I made a realistic bad change on a throwaway branch: two passages instead of five, to save tokens. Retrieval only fell from ninety-eight to ninety-three percent, inside the interval.

The gate still failed, because two questions that always passed now always fail. The message names them. The branch was never merged.

Here is one: a spa question about a sixteen-year-old. The right passage dropped out, and every metric shows it.

An AI judge rates tone and clarity. I labelled forty replies by hand. It agreed ninety-five percent of the time, but kappa was zero: my labels had no failures, so agreement proved nothing. No judge score is trusted, and none is a gate.

The code, the reports and every decision are linked on screen.

Open the live dashboard Narration: AI voice (Piper, open source) · captions with the CC button · read-only, no sign-in

Free hosting: if the app is asleep, click ‘wake up’ and give it about 30 seconds.

Case study · stage 3 of 5

Eval Harness

One evaluation system for both of the hotel group's AI assistants: the same cases, metrics, statistics and regression gates, so a prompt, model or retrieval change can't lower quality without a red check.

Independent project: a simulated client engagement for the same fictional hotel group as stages 1 and 2. Documents, guests and emails are synthetic. Every number below comes from a saved run in the repo; the business figures are projections from stated assumptions.

01 · Problem

Why evals rot

Both assistants were measured carefully when they shipped. Then the changes start: a cheaper model, a shorter prompt, “send 2 passages instead of 5 to save tokens”. Each change is reasonable, and each project had its own eval scripts, its own report format and nothing in CI that would fail.

One of those changes quietly stops the staff assistant from finding the spa's age rule. Nobody notices until a front-desk agent books a 16-year-old for a facial.

02 · Approach

The approach

  • One case bank: 181 labelled cases. 161 imported from both projects without changing what they test; 20 new held-out cases where coverage was thin (cross-property leaks, Spanish, a superseded document, injections, safety wording).
  • Recorded first: saved outputs are re-scored with no API call, so CI is free and the same every time. Live mode runs a project's real pipeline at a pinned commit.
  • The projects' own metrics: copied byte for byte at the pinned commits, behind one registry, so the harness reproduces both projects' published numbers from the same recordings.
  • Honest statistics: a 95% interval on every rate, counted over distinct cases; repeated runs to find flaky cases.
  • Gates that name names: hard 100% gates for escalation, prompt injection and forbidden promises, floors, the baseline interval, and any case that always passed and now always fails.
  • A judge only where code can't judge: completeness, tone and clarity, trusted only if it agrees with my hand labels beyond chance.
The dashboard overview: Project 1's latest run gated against its baseline, 8 of 8 gated checks passing, 116 cases, p95 3.3 s, $0.17 per 1,000
The read-only dashboard: each assistant's latest run against its baseline.
03 · Architecture

Architecture

  1. CasesOne schema for both projects; dev, held-out and visitor splits.
  2. RunnersRecorded outputs by default; or the real pipeline at a pinned commit, exported so the checkout is never touched.
  3. Metrics + judgeThe projects' own deterministic checks; an LLM judge only for completeness, tone and clarity.
  4. StatisticsWilson intervals, run-to-run consistency, flaky cases, categories.
  5. GatesCandidate vs baseline: hard 100%, floors, the interval, stable flips.
  6. Dashboard and CIReports in the repo, a read-only dashboard, and one CI job in each project's repo.
From a labelled case to a red or green check. Full diagrams in the architecture doc →
04 · Results

Results

From the projects' own recordings, the harness reproduces their published numbers exactly: stage 1 at 98% answer accuracy and 27 of 27 correct refusals, stage 2 at 100% on escalation, injection and forbidden promises. Then it re-ran both live at their pinned versions, 3 runs each, on 2026-10-14.

Fresh re-runs at the pinned versions (95% interval over cases)
MeasureStage 1, 54 questionsStage 2, 40 emails
Gate against the saved baselinePassPass
Hard gates: escalation, injection, no promises100%100%
Answer accuracy / requests found98% (90–100%)100% (91–100%)
Strict retrieval / facts grounded98% (88–100%)100% (91–100%)
Expected source cited98% (88–100%)94% (72–99%)
Same verdict in all 3 runs98%90%
Median / 95th percentile1.6 s / 3.3 s4.3 s / 8.9 s
Cost per 1,000$0.17$1.30

The 20 new held-out cases found real gaps, reported rather than tuned away. Stage 1 answered 67% of its 13 correctly (interval 40–86%): Spanish questions are blocked before they reach the model, injected text pushed the resort-fee passage out of the search results, and the safety rule misses “smoke coming from under a door” and “incendio”. Stage 2's 7 new emails kept escalation and injection at 100%.

05 · The judge

The judge calibration story

An LLM judge (Claude Haiku 5.5, versioned rubric, structured output) rates completeness, tone and clarity. Before any of its numbers count, I labelled 40 stratified replies by hand, pass or fail, without seeing its verdicts.

I passed all 40. The judge agreed on 38: 95% agreement, which sounds good. Cohen's kappa was 0.00. With no failures in my labels, agreement can't be told apart from a judge that always says “pass”. So no judge metric is trusted, and none is a gate.

  • One rubric revision fixed the item it targeted (stage 1's fixed “couldn't find this, check with a manager” sentence is clear, not vague). Agreement stayed 95%, kappa 0.00. A second revision could only have fitted these 40 labels.
  • Judging the same 40 prompts three times, the judge changed its verdict on one item: a disagreement that comes and goes on identical input is noise.
  • On the full re-runs the judge fails 13–17% of replies on clarity, stricter than my labels. Which of us is right needs labels that contain real failures.
The Judge calibration page: 40 hand labels, 95% agreement, Cohen's kappa 0.00, rubric v2, and a confusion matrix with 2 judge-only fails
95% agreement, kappa 0.00: the labels held no failures to agree on.
06 · Caught

The regression it caught

On a throwaway branch I made a realistic change to stage 1: send 2 retrieved passages instead of 5, to cut input tokens. Strict retrieval fell from 98% to 93%, which is still inside the baseline's interval (88–100%). An interval-only gate would have passed it.

The harness failed it anyway: two questions passed in every baseline run and failed in every run after the change, q13 (“Can a 16 year old book a facial?”) and q35. In recorded mode (saved answers, retrieval recomputed, no API) 1 gate failed; in live mode 4 did, each naming the cases. The branch was deleted, never pushed and never merged.

hoteleval gate on the regression demo run: FAIL, 4 gate failures, each naming q13 and q35
The real gate output, naming the questions that flipped.
The Cases page: q13 in the demo run, the expected passage about age 18, and three runs that each refused
q13 drilled down: the age rule dropped out of the search results, so every run refused.
07 · What the eval taught me

What the eval taught me

  • Agreement isn't evidence. 95% agreement with kappa 0.00 meant my sample had nothing to disagree about. Sampling replies a deterministic check had flagged gave me wrong facts, not badly written replies.
  • An interval alone misses small, real regressions. Two lost questions out of 45 stay inside it; “always passed, now always fails” caught them.
  • Re-running unchanged code failed the gate. One model miss in a 3-case group broke a 90% floor. Floors now need 10 or more cases.
  • The graders were wrong four times: a cited policy description read as a promise, two correct injection rejections, and a Spanish “50 $”. Each fix is a test with the exact sentence.
08 · Trade-offs

Trade-offs

Trade-offs
ChoiceWhyWhat it costs
Copy each project's metric code at a pinned commitOne definition per metric; published numbers reproduce exactlyA project fix reaches the harness only when the pin moves
Recorded mode by defaultFree, deterministic CIA model change needs a live run to be measured
Intervals over distinct casesThree runs of one question aren't three samplesWide intervals on small groups, shown rather than hidden
Stable flips fail a buildCatches the small, real regression an interval missesOne real flip on a model metric can be noise; the message names it for a person
The judge never gates until trustedAn unverified judge would gate on its own opinionNo automated tone or clarity score yet
09 · Engineering

Engineering

  • Tests: 86 tests at 93% coverage, including every metric passing and failing, kappa and interval math, gates, the judge with a stand-in model, and every dashboard page.
  • CI: lint and format, a dependency audit, a 90% coverage gate, recorded-mode gates and a re-score of every saved report. Stages 1 and 2 each gained one CI job that installs the harness at v1.0.0; their own tests and numbers are unchanged.
  • Cost: the whole project's API spend was $0.38: two live re-runs, the demo and 318 judge calls.
  • Decisions: 19 recorded, each with context, trade-off and result. Decision log →
10 · Business case (projection)

Business case (a projection)

Projected from stated assumptions and shown as a range; nothing here is a measured saving. The base case assumes 4 changes a month per assistant, 3 hours to evaluate one by hand against 30 minutes with the harness, $90 an hour, and one shipped regression a quarter. Only the API cost of a full re-run ($0.29) is measured.

Projected net value per year, two assistants
ScenarioNet value
Pessimistic$1,493
Base$26,273
Optimistic$67,613

Full business case, pilot plan and go / no-go KPIs →

11 · What's next

What's next

  • Calibration labels that contain real failures, and a second labeler
  • Held-out questions and emails written by front-desk staff
  • Stage 4, Production Deployment: run history in a database, monitoring, sign-in and personal-data redaction

See all five stages →