Teams ship LLM features with a test suite that covers the code around the model and nothing that covers the model's behavior itself. Then a prompt tweak, a model upgrade, or a retrieval change silently regresses quality, and the first signal anyone gets is a spike in support tickets or a founder's Slack message. An eval suite is the thing standing between you and that outcome, and it's cheaper to build than most teams assume.

Start with a dataset, not a metric

The eval suite is only as good as the inputs it tests against. Pull real inputs from production logs — actual user queries, actual documents, actual edge cases — rather than inventing synthetic ones, because synthetic test cases tend to be easier than reality and give false confidence. Aim for 50-200 examples that cover: common cases, known hard cases from past incidents, and adversarial or malformed inputs. Label each with what a correct or acceptable response looks like, not necessarily an exact string.

python
eval_cases = [
    {
        "input": "cancel my subscription and refund last month",
        "checks": ["mentions cancellation confirmation", "does not promise refund without eligibility check"],
    },
    {
        "input": "",  # empty input edge case
        "checks": ["returns graceful clarification request", "does not throw or hallucinate"],
    },
]

Pick the right grading method per case, not one for the whole suite

  • Exact match / regex for structured outputs (does it return valid JSON, does it contain a required field). Cheap and deterministic — use it whenever the output has a checkable shape.
  • Rule-based checks for known constraints (no PII in output, cites a real document ID, response under N tokens).
  • LLM-as-judge for open-ended quality (is this helpful, is this grounded in the provided context) — necessary for judging free-text quality but needs its own validation before you trust it.
  • Human review as a periodic calibration check on a sample, especially to validate that the LLM judge agrees with human judgment.

Reach for LLM-as-judge last, not first — it's the most expensive and least deterministic option, and a huge fraction of what teams use it for could be a regex or a JSON schema check instead.

LLM-as-judge needs its own eval

An LLM judge is itself a prompt, and it inherits every failure mode of prompting: it can be inconsistent, biased toward longer or more confident-sounding answers, and sensitive to how the grading rubric is worded. Before trusting a judge's scores, validate it against a small human-labeled set — if the judge disagrees with human graders more than a small percentage of the time, fix the rubric before trusting the automated signal. Re-validate whenever you change the judge model, the same way you'd re-validate a fine-tune.

Wire it into the deploy path, not just the dashboard

An eval suite that runs manually before a release gets skipped under deadline pressure. Run it automatically on every prompt, retrieval config, or model change, and block or flag deploys that regress below a threshold on any category of the eval set — not just the aggregate score, since an aggregate can hide a category that dropped to zero while everything else improved slightly.

Signal Good for catching
Aggregate pass rate Broad regressions
Per-category breakdown A specific failure mode hidden by averages
Latency/cost per eval run Silent performance regressions alongside quality ones

Offline evals won't catch everything — production traffic always finds cases your dataset didn't anticipate — so pair the suite with ongoing production monitoring and a habit of feeding real failures back into the eval set. The suite should grow every time something breaks in the wild; that's what keeps it relevant instead of static.