AI Engineer Dojo Contents
Chapter 1

Why LLMs Broke Testing

Fifty years of software testing rests on one move: run the code, assert the output equals the expected value. The moment a language model enters the loop, that move stops working — and understanding exactly why is the foundation of the whole job.

Let's make it concrete. You build a summarizer and write the test you've written ten thousand times:

The test that betrays you

def test_summary():
    out = summarize("The cat sat on the mat in the warm afternoon sun.")
    assert out == "A cat rested on a mat in the sun."   # one blessed answer

Run it five times:

RunOutputGood?== expected
1"A cat rested on a mat in the afternoon sun."YesFAIL
2"A cat sat on a mat, warmed by the sun."YesFAIL
3"In the afternoon sun, a cat sat on a mat."YesFAIL
4"A cat lounged on a mat in warm sunlight."YesFAIL
5"A cat rested on a mat in the sun."YesPASS

Five good summaries. Exact-match accuracy: 20% — and 0% if run 5 hadn't matched by luck. The metric measures phrasing, not quality. There are thousands of equally good summaries, so "correct" is replaced by "good," and good is a distribution, not a point.

The deeper trap: even your score is noisy

So you ditch exact match for a quality scorer (0–1 per case). You run 20 cases: 0.80. Next week, new prompt, 20 cases: 0.85. You ship a "5-point win." You just fooled yourself. A score over n cases has a margin of error — the rough 95% interval is 1.96 × √(p(1−p)/n):

nScore95% intervalWhat you can honestly say
200.80±0.18 (0.62–0.98)Almost nothing
2000.80±0.055 (0.74–0.85)"Roughly 80%"
20000.80±0.018 (0.78–0.82)"80%, confidently"

At n=20 your "win" is smaller than the noise. This is the shift that trips up every engineer from deterministic testing: you stop thinking in test cases and start thinking in measurements with sample sizes. A score without an n is a rumor.

The core reframe

Traditional QA asks "is this output correct?" AI evaluation asks "how good is the system on average, across the cases I care about, with what confidence, and is it improving?" Every technique in this book follows from that one change.

Try it · ~10 minFree · no API key

Feel the noise: watch a confidence interval shrink

Pure simulation — no key, no spend. Pretend a system's true quality is 80%, "evaluate" it at three sample sizes, and watch the margin of error. Step 1: run it. Step 2: could a "0.85" and a "0.80" at n=20 be the same system? (Yes.) Step 3: set TRUE_QUALITY = 0.95 and rerun — why do the intervals tighten? (Because p(1−p) is largest at 0.5.)

Lab code — runs free

import random
from math import sqrt

TRUE_QUALITY = 0.80          # the system's real pass-rate (unknown in real life)

def simulated_eval(n):
    passes = sum(random.random() < TRUE_QUALITY for _ in range(n))
    p  = passes / n
    ci = 1.96 * sqrt(p * (1 - p) / n)        # 95% confidence interval
    return p, ci

random.seed(0)
for n in (20, 200, 2000):
    p, ci = simulated_eval(n)
    print(f"n={n:>4}:  score={p:.2f}   95% CI = +/- {ci:.3f}   "
          f"({p-ci:.2f} to {p+ci:.2f})")
Worked solution

Output (with seed(0)):

n=  20:  score=0.85   95% CI = +/- 0.156   (0.69 to 1.01)
n= 200:  score=0.80   95% CI = +/- 0.055   (0.74 to 0.85)
n=2000:  score=0.81   95% CI = +/- 0.017   (0.79 to 0.82)

At n=20 the interval (0.69–1.01) swallows almost any difference you'd care about — the 0.80-vs-0.85 "win" lives entirely inside the noise. The rule you just felt: to halve an interval you must quadruple the sample size (it scales as 1/√n). That governs how big every eval set you build needs to be — and it's why "we tried a few examples, looked great" is not an evaluation.

Case study

Quill: the five-point "win" that was noise

Quill, a writing-assistant startup, ran a quality scorer on their summarizer and measured 0.80. After a prompt change they measured 0.85 on the same 20-case set, declared a "5-point improvement," and shipped it. The problem: at n=20 the rough 95% interval is about ±0.18, so 0.80 and 0.85 are statistically indistinguishable — the "win" was sampling noise.

When they later re-ran both versions on 500 cases, the "improved" prompt was actually slightly worse. They had shipped a regression and celebrated it. Nothing was wrong with their scorer — the failure was reporting a difference smaller than the margin of error. The lesson that reframed their whole practice: a score without a sample size is a rumor, and a difference inside the noise band is not a result. They moved to fixed, larger eval sets and started quoting intervals, not just point scores.

Running case · Meridian × Remi

One system runs through every chapter. Meridian, a Series-B fintech, is building Remi, a support assistant for billing questions. This chapter: their first test was an exact-match assertion — does Remi's reply equal the expected string? Four of five genuinely correct billing answers "failed" purely because Remi worded them differently. Same lesson as the chapter: "correct" gives way to "good," and good is a distribution — so they scrap exact match and commit to measuring Remi over many real questions with a sample size. (Ch 3: they build that dataset.)

Quiz · Chapter 1

  1. You score 20 cases at 85% this week; a different 20 scored 80% last week. Did quality improve?
  2. A perfectly good summarizer scores ~0% on exact-match accuracy because:
  3. The meaningful "unit of truth" in AI evaluation is:
  4. To halve your eval's confidence interval you should roughly:
  5. The most honest answer to "what's our accuracy?" is:
← Back Continue →

AI Evaluation Engineer — New-Grad Edition · AI Engineer Dojo · aiengineerdojo.com