Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Reference-grade guide to evaluating LLM and RAG systems — golden/regression/adversarial eval sets, LLM-as-judge and its biases, retrieval metrics (recall@k, MRR, nDCG), grounding/faithfulness/attribution, RAGAS-style scoring, eval-set construction from production traces, and the CI gates that stop silent regressions.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 180% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 262% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 230% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 225% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 231% | 0% |
Evals are the core discipline of AI engineering. A model is a stochastic component you do not control: a prompt tweak, a temperature change, a model version bump, or a vendor silently re-routing your traffic can move quality in any direction, and none of it shows up in a stack trace. The only way to know whether a change helped or hurt is to measure task performance on a fixed set of inputs you trust. "It looks better" is a hypothesis, not a result. Vibes don't ship — a passing eval suite does.
Evals are to LLM systems what tests are to ordinary software, with one twist: the output is non-deterministic and often open-ended, so the grader is itself a system you must build and validate. This guide covers what to measure (eval types, generation metrics, retrieval metrics, grounding), how to grade (exact match, embeddings, LLM-as-judge, humans), how to build and maintain an eval set, and how to wire evals into your delivery process so regressions get caught before users do.
The mental model: inputs → system → outputs → grader → score → decision. Every section below is one link in that chain. If any link is weak — a stale eval set, a biased judge, a metric that doesn't track the task — the score lies, and you ship blind.
Capability evals ≠ product evals. Public benchmarks (MMLU, GSM8K, HumanEval, MT-Bench) measure a model's general capability and help you pick a base model. They tell you almost nothing about your task on your data — a model can top a leaderboard and fail your extraction schema. They are also heavily contaminated (leaked into training sets), so a high public score is partly memorization. Build product evals on your own inputs; treat benchmarks only as a coarse shortlist for which models to even try.
> Rule of thumb: if you cannot state the metric and the threshold a change must clear, you are not ready to make the change.
| Type | What it is | Catches | Cadence | |---|---|---|---| | Golden / reference set | Curated input → expected output pairs, hand-blessed | Core correctness on representative cases | Every change | | Regression set | Frozen suite of previously-passing cases | Quality drops on deploy / model swap / prompt change | CI, blocking | | Adversarial / red-team | Edge cases, injections, jailbreaks, refusal probes | Safety + robustness failures | Pre-release, periodic | | Unit eval | One component in isolation (a tool call, a router, a parser) | Localized defects, fast feedback | Per-commit | | End-to-end eval | Full pipeline, real user inputs | Integration + emergent failures | Pre-deploy, canary |
Golden sets are the backbone: small, high-quality, human-verified. Each item carries the input, the expected output (or a rubric/checklist when output is open-ended), and metadata (category, difficulty, source). Quality over quantity — 80 well-chosen cases beat 5,000 noisy ones.
Regression sets exist to fail loudly. They are golden cases promoted to a CI gate: if a change drops the pass rate below threshold, the build is red. No regression gate = a prompt tweak can quietly break prod and nobody knows until a customer complains.
Adversarial sets probe the unhappy path on purpose: prompt injection ("ignore previous instructions…"), jailbreaks, PII exfiltration attempts, out-of-scope requests that should trigger refusal, ambiguous or contradictory inputs, and inputs in the wrong language/format. Track refusal correctness in both directions — over-refusal (declining valid requests) is as much a failure as under-refusal (complying with harmful ones).
Unit vs end-to-end. Decompose the pipeline (retriever, reranker, prompt assembly, generation, post-parse) and eval each unit so failures are attributable, then run an end-to-end eval so you catch interaction effects. A retriever can score 95% recall and the end-to-end answer still be wrong because the generator ignored the context. You need both.
Use when the output space is closed: classification labels, extracted fields, JSON conforming to a schema, SQL that must run, a number. Grade with ==, schema validation (Zod/Pydantic/JSON-Schema), or execution (does the SQL return the right rows?). Cheap, deterministic, zero judge bias. Prefer this whenever the task can be made structured — it's the gold standard when it applies.
For classification/extraction tasks, report the confusion-matrix family, not bare accuracy: precision = TP/(TP+FP), recall = TP/(TP+FN), F1 = 2·P·R/(P+R). Accuracy lies on imbalanced data (99% "not-spam" accuracy by always answering "not-spam"). Use macro-F1 when every class matters equally, micro-F1 when frequent classes should dominate. For a router/classifier, the per-class confusion matrix tells you which class it confuses, which an aggregate score hides.
N-gram overlap against a reference answer. Weak for generation. They reward surface lexical overlap, not meaning: a correct paraphrase with different words scores low; a fluent wrong answer that reuses input words scores high. Acceptable as a cheap signal for translation/summarization with tight references; near-useless for open-ended Q&A, chat, or agentic output. Do not gate releases on ROUGE alone.
Cosine similarity between embeddings of output and reference. Captures meaning better than n-grams, tolerates paraphrase. But it's a blunt scalar — it can't tell you why something is wrong, conflates topical similarity with correctness, and a confidently-wrong answer on the right topic scores high. Use as a coarse filter or a regression tripwire, not a final verdict.
A strong model grades the output against a rubric. Two modes:
Use pairwise to choose between candidates; use pointwise/rubric to gate and trend.
LLM-as-judge gotchas — every one of these is a known, measured bias:
| Bias | What happens | Mitigation | |---|---|---| | Position bias | Judge favors the first (or last) option in pairwise | Randomize order; run both orders and average; require consistency | | Verbosity / length bias | Longer answers rated higher regardless of quality | Rubric penalizing fluff; control for length; pairwise on equal-length | | Self-preference | Judge prefers outputs from its own model family | Use a different model family as judge than as generator | | Sycophancy / leniency | Judge agrees with assertive or confident phrasing | Concrete rubric, require evidence/quote citations in the verdict | | Miscalibration | "7/10" means nothing stable across runs | Use few discrete levels (pass/fail, 1–3), anchor each level with an example | | Format/style halo | Well-formatted markdown rated as more correct | Separate "format" and "correctness" criteria in the rubric |
A usable rubric looks like a graded checklist, not a vibe scale. Example for a support-answer faithfulness judge:
For each claim in the ANSWER, label it against the CONTEXT:
SUPPORTED — context entails the claim
UNSUPPORTED — context neither entails nor contradicts (no basis)
CONTRADICTED— context contradicts the claim
First list claims with labels and a quoted supporting span; THEN output:
PASS = all claims SUPPORTED
FAIL = any claim UNSUPPORTED or CONTRADICTED
Output JSON: { "claims": [...], "verdict": "PASS"|"FAIL" }This binds the judge to evidence (quoted spans), forces reasoning before the verdict, and yields a binary gateable result instead of an uncalibrated 7/10.
Non-negotiables for LLM-as-judge:
Required when: defining ground truth for a new task, validating an LLM judge, evaluating subjective quality (tone, helpfulness, brand voice), safety-critical decisions, or when automated metrics disagree with intuition. Make it rigorous:
| Output shape | Use | Avoid | |---|---|---| | Closed labels / schema'd JSON / runnable code | Exact + structural match, F1 | LLM-judge (overkill, noisier) | | Short factual answer with a reference | Exact / embedding similarity, LLM-judge as backstop | BLEU/ROUGE alone | | Long open-ended generation, chat, summaries | LLM-as-judge (rubric) + targeted human sample | BLEU/ROUGE/perplexity as a gate | | Choosing between two systems/prompts | Pairwise LLM-judge or human preference | Pointwise absolute scores (higher variance for ranking) | | Subjective quality, safety-critical, new task | Human eval (defines ground truth) | Automated metric as sole signal |
Default ladder: make it structured → exact match; if you can't → LLM-judge with a rubric; validate the judge with humans; reserve full human eval for ground-truth and safety.
Retrieval quality caps everything downstream: the generator cannot answer from context it never saw. Eval the retriever separately from the generator. You need a labeled set mapping queries → relevant documents/chunks (binary relevant, or graded relevance for nDCG).
| Metric | Formula / definition | Answers | |---|---|---| | Recall@k | (# relevant in top-k) / (total relevant) | Did we fetch the right chunks at all? | | Precision@k | (# relevant in top-k) / k | How much of what we fetched is noise? | | Hit-rate@k | fraction of queries with ≥1 relevant in top-k | Did we get anything useful? | | MRR | mean of 1/rank of the first relevant result | How high is the first good hit? | | MAP | mean of average-precision per query | Ranking quality across all relevant items | | nDCG@k | DCG/IDCG, DCG = Σ relᵢ/log₂(i+1) | Graded relevance + position-discounted ranking |
MRR = (1/N) Σ 1/rankᵢ — only the first relevant hit matters, good for "one right answer" lookups. nDCG rewards putting more relevant items higher with graded labels, the right metric when many chunks are partially relevant.
Context-level RAG metrics (RAGAS-style):
Recall vs precision tradeoff at k: raising k lifts recall but dilutes precision and bloats the prompt (cost + lost-in-the-middle errors). Tune k against the end-to-end answer metric, not recall in isolation.
What retrieval evals diagnose. A low recall@k is rarely "the embedding model is bad" — it's usually upstream: chunks too large (relevant fact buried with noise, embedding washed out) or too small (fact split across chunks); the wrong embedding model for the domain; no hybrid (dense + BM25/lexical) so exact-match terms like IDs and error codes are missed; or no reranker so the relevant chunk sits at rank 18. Ablate each: hold the eval set fixed and vary chunk size, embedding model, hybrid on/off, reranker on/off — the metric delta attributes the gain. This is why the labeled query→chunk set is worth building once and reusing for every retrieval change.
A RAG answer can retrieve perfectly and still hallucinate. These metrics check the generation against the retrieved context:
Faithfulness = supported claims / total claims. An unsupported-but-true claim still fails grounding — it means the model is generating from parametric memory, which you cannot audit.The RAG triad — measure all three or you can't localize a failure: context relevance (retriever), faithfulness (generator grounding), answer relevance (generator on-task). Low context relevance → fix retrieval. High context relevance + low faithfulness → fix the prompt/generator. High on both + low answer relevance → fix instruction-following.
The eval set is the asset. The model is rented; the labeled eval set is owned IP and the moat.
Wire evals into delivery so measurement is automatic, not heroic.
Aggregation and significance. A pass rate is an estimate with error bars. Two systems at 84% and 86% on 100 cases are not distinguishable — the 95% CI on a proportion is roughly ±10pts at n=100, ±3pts at n=1000. Before declaring a winner: report the confidence interval (Wilson interval for proportions), and for paired comparisons on the same inputs use a paired test (McNemar's for pass/fail, bootstrap for continuous scores). Run non-deterministic systems k times per input (k≥3) and average to separate true change from sampling noise. "It went up 2 points" on a tiny set is the single most common way teams ship a regression while celebrating.
Quality is not the only axis. Every eval run should also report cost (tokens/$ per task) and latency (p50/p95 end-to-end). A change that lifts faithfulness 1pt while doubling cost or pushing p95 past your budget is often a net loss — surface all three so the decision is honest. The cheaper model that's "98% as good" is frequently the right call, and only a multi-axis eval makes that visible.
Tooling. You can hand-roll evals (a JSONL set + a scoring script + a CI job — start here, it's a day of work). Mature options: RAGAS (RAG faithfulness/relevance/context metrics), promptfoo and DeepEval (declarative eval suites + CI), Braintrust / LangSmith / Langfuse (eval + tracing + dashboards), OpenAI Evals / inspect-ai (framework-grade harnesses). Pick one once you've outgrown the script; don't let tool selection delay having any eval.
> Every metric should map to a decision. If a number can't change what you ship, stop computing it.
Do
Don't
If you do nothing else, do this, in order:
input → expected/rubric cases from production traces.That loop — measure, gate, observe, mine failures, grow the set — is the entire discipline. Everything else is refinement.
Other measured skills in the registry, with their headline benchmark lift.