AnyLearn
All lessons
AIintermediate

LLM evaluation: how to know your model output is actually good

Why traditional software testing falls apart on LLMs, the four evaluation regimes that work in practice (golden sets, LLM-as-judge, human review, online metrics), and how to wire them together without drowning in ungrounded scores.

Not signed in โ€” your progress and quiz score won't be saved.
Lesson progress1 / 8

Why "just write tests" doesn't work

Traditional unit tests assert exact outputs: assert f(2) == 4. LLMs produce text that's correct in many possible forms โ€” different phrasing, different structure, different tone โ€” all acceptable. There's no canonical equality.

You also have failure modes traditional tests don't catch: hallucinations, tone drift, format violations, partial correctness, regression-on-edge-cases. Every behaviour you care about needs its own way of being measured.

LLM evaluation is closer to product analytics than to unit testing. You're sampling behaviour over a population of inputs and watching aggregate quality move. You can't assertEqual your way through it.

Full lesson text

All 8 steps on one page โ€” for reading, reference, and search.

Show

1. Why "just write tests" doesn't work

Traditional unit tests assert exact outputs: assert f(2) == 4. LLMs produce text that's correct in many possible forms โ€” different phrasing, different structure, different tone โ€” all acceptable. There's no canonical equality.

You also have failure modes traditional tests don't catch: hallucinations, tone drift, format violations, partial correctness, regression-on-edge-cases. Every behaviour you care about needs its own way of being measured.

LLM evaluation is closer to product analytics than to unit testing. You're sampling behaviour over a population of inputs and watching aggregate quality move. You can't assertEqual your way through it.

2. Four regimes you'll use

Practical eval strategies, roughly in order of cost and signal:

  1. Golden-set tests โ€” a small curated dataset of inputs with expected behaviours. Cheap, deterministic, catch regressions. Doesn't generalise.
  2. LLM-as-judge โ€” use another LLM to score outputs against a rubric. Scales, but the judge has its own biases.
  3. Human evaluation โ€” paid raters or experts. Gold standard but slow and expensive.
  4. Online metrics โ€” real users in production: click-through, completion rate, thumbs-up, retention. Most ecologically valid but lagging.

A mature eval stack uses all four. Each catches what the others miss.

3. Golden sets: small, sharp, maintained

Build a fixture set of 20โ€“200 (input, expected-behaviour) pairs that cover your tricky cases. Behaviours are checked by assertions, not exact match:

def test_no_refund_made_up(case):
    out = pipeline(case["input"])
    assert case["customer_id"] in out                      # cites the right customer
    assert "$" in out                                       # mentions money
    assert not re.search(r"\$[0-9]{3,}", out)               # but not big numbers we don't have
    assert any(p in out for p in ["refund", "credit"])     # mentions the action

The checks combine must-include, must-not-include, regex shape, and occasionally LLM-judge for fuzzier rules. Run on every prompt change. New failure modes you encounter in production go into the set as new cases.

4. LLM-as-judge: power and pitfalls

An LLM scores outputs against a rubric. Looks like:

judge_prompt = """Given USER_QUESTION and ASSISTANT_REPLY, score the reply 1-5 on:
- Factuality: does it match the source?
- Relevance: does it answer what was asked?
- Tone: is it neutral and professional?
Return JSON: {"factuality": int, "relevance": int, "tone": int, "reasoning": str}"""

Known biases:

  • Length bias: judges prefer longer answers, often regardless of quality.
  • Position bias: when comparing A vs B, the first option scores higher more often than chance.
  • Self-preference: an LLM rates outputs from its own model family higher.

Mitigations: randomise A/B order, use a different model family as judge, calibrate the judge against human scores on a small subset.

5. An eval loop that doesn't lie to you

Golden + LLM-judge + occasional human spot-check.

flowchart LR
  Change["Prompt or model change"] --> Run["Run on golden set"]
  Run --> Asserts["Deterministic checks"]
  Run --> Judge["LLM-as-judge"]
  Asserts --> Report["Aggregate scores"]
  Judge --> Report
  Report --> Sample["Sample for human review"]
  Sample --> Report

6. Eval drift and the silent-degradation problem

Two kinds of drift bite even well-tested systems:

  1. Model drift: the vendor updates the model. "gpt-4o" today is not exactly "gpt-4o" six months ago. Your eval scores can move without any code change.
  2. Input drift: real-world usage diverges from your golden set. The set was hard six months ago; today's hard cases look different.

Mitigations: pin model versions where the API allows (Anthropic's date-stamped IDs, OpenAI's date-suffix names). Refresh golden sets quarterly with new examples from production logs โ€” especially failure cases users complained about.

7. Tools worth knowing

You don't have to build this from scratch:

  • LangSmith โ€” paid, polished, integrates with LangChain/LangGraph; good UI for traces + eval comparison.
  • Promptfoo โ€” open source, YAML-config evals, great for CI integration.
  • Inspect AI โ€” built by the UK AI Safety Institute, strong for capability/safety evals.
  • Phoenix โ€” Arize's open-source LLM observability + evals, runs locally.
  • Helicone, Langfuse โ€” observability-leaning; you can attach evals.

For most teams: start with golden tests in pytest + LLM-as-judge in a small script. Add a managed tool when you have multiple developers iterating on prompts.

8. Pragmatic advice

If you're starting evals from zero, do these in order:

  1. Today: collect 20 inputs that worry you. Run them on your current pipeline. Note what you see.
  2. This week: write deterministic assertions for the 5 most important failure modes. Wire them into CI.
  3. Next week: add an LLM-judge for the fuzzy ones (factuality, tone). Sanity-check 10 of its scores against your own judgement.
  4. Once you have logs: stream a sample of production conversations into the eval pipeline. The new bugs will be there.
  5. Quarterly: refresh the golden set with real failures, retire stale cases.

The single biggest mistake is waiting for the "right" eval framework. Start with pytest and a JSON fixture file. You'll learn what you actually need to measure.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Why don't traditional unit tests transfer well to LLM output?
    • LLMs are too slow for unit tests
    • There's no canonical correct output โ€” many phrasings can be acceptable, and many failure modes are subtle (tone, hallucination, partial correctness)
    • Python doesn't support testing LLMs
    • LLMs only output JSON, not text
  2. Your LLM-judge consistently rates longer answers higher than shorter ones. What is this called and how do you mitigate?
    • Position bias โ€” randomise the order of A/B comparisons
    • Length bias โ€” calibrate the judge or normalise scores for length
    • Self-preference โ€” use a judge from a different model family
    • Recency bias โ€” clear the judge's context between calls
  3. Your eval scores suddenly drop 10% with no code change. What's the most likely cause?
    • Random variance โ€” re-run and ignore
    • The model provider silently updated the underlying weights
    • Pytest version changed
    • Your golden set corrupted itself
  4. Which is a *deterministic* check you'd write in a golden test?
    • An LLM judges whether the answer is "polite"
    • Assert the output contains the correct customer ID and does not contain dollar amounts above $1,000
    • A human rater scores the answer 1โ€“5
    • The answer's embedding is close to a reference
  5. What's the recommended *first* eval move if you have none today?
    • Adopt an enterprise eval platform with full observability
    • Hire annotators for a 10,000-example dataset
    • Collect 20 worrying inputs, run them through your current pipeline, write assertions for the top failure modes
    • Switch to a more capable model and skip evals for now

Related lessons