In This Guide
Key Takeaways
- LLM evaluation is fundamentally statistical, not deterministic — golden datasets and judge models replace hard-coded expected values
- The three layers — offline benchmarks, app-level evals, online production monitoring — each answer a different question and none replaces the others
- Reference-based metrics like BLEU and ROUGE are useful for translation and summarization but break down on open-ended generation
- LLM-as-judge is the most practical scoring method for app-level evals, but it has known biases (position, length, self-preference) that must be controlled for
- RAGAS is the standard open-source framework for RAG eval; its four metrics — faithfulness, answer relevancy, context precision, context recall — cover the failure modes that matter
- The teams winning in production treat evals like tests — versioned golden sets, CI gates on every prompt change, and a feedback loop from prod back into the eval suite
Why LLM Evaluation Is Different
Traditional software testing assumes a deterministic contract: given input X, the function returns output Y, and a unit test asserts equality. LLM applications break that contract on the first day. The same input can produce a different output on the next call. There is rarely a single correct answer to compare against. The output space is open-ended natural language, not a finite set of return values. And the model itself is a moving target — providers update models, you swap prompts, retrieval indexes change, and yesterday's behavior is not today's behavior.
This is the eval crisis. Every team building serious LLM applications hits it. The first month, the demo works and everyone is excited. The second month, a prompt tweak breaks something nobody noticed for a week. The third month, the model provider deprecates a checkpoint and the whole pipeline drifts overnight. By the sixth month, the team realizes that without a real evaluation discipline, they are flying blind — shipping changes they cannot measure into a system whose behavior they cannot characterize.
The four properties that make LLM eval different from traditional testing:
- Non-determinism. Even at temperature zero, identical inputs do not always produce identical outputs across runs, especially with batched inference, sampling differences, or multi-turn context.
- No ground truth. For most real applications — chat, summarization, agent planning — there is no single correct answer. Multiple outputs can be equally good, or differently good in different ways.
- Drift. Models change, prompts change, retrieval corpora change, user inputs distribution shifts. A passing eval today does not mean passing eval tomorrow.
- Expensive and slow. Each eval call costs API tokens and takes seconds, not microseconds. A 10,000-case unit test suite is normal in software; a 10,000-case LLM eval suite costs real money and real time.
The teams that figure this out early ship faster, ship safer, and iterate with confidence. The teams that punt on evaluation ship slower over time as the cost of every change grows because nobody can measure whether it helped.
The Three Layers of Eval
Production LLM systems require three distinct layers of evaluation. Each answers a different question. None of them is optional, and none of them substitutes for the others.
Layer 1: Offline Benchmarks
Standard public benchmarks — MMLU for general knowledge, HumanEval for code, GSM8K for math, MT-Bench for chat, HELM for a holistic battery — measure raw model capability. These are the numbers vendors publish to compare models. They are useful for picking which base model to start with, and largely useless for predicting how your specific application will perform. A model that scores 88 on MMLU may still hallucinate constantly on your domain-specific corpus. Use offline benchmarks for model selection; do not use them to ship.
Layer 2: Application-Level Evals
This is where most of the work lives. App-level evals measure your specific pipeline — prompt template, retrieval setup, model choice, tool definitions, output parser — against your golden dataset of representative inputs. The metric set is task-specific: faithfulness for RAG, trajectory correctness for agents, helpfulness and tone for chat. The eval suite runs on every PR that touches the pipeline, and the team gates merges on it. This is the layer where eval-driven development happens.
Layer 3: Online Production Monitoring
Once deployed, real users send real inputs your golden set never anticipated. Online monitoring samples production traffic, runs scoring (cheap heuristics in real time, more expensive LLM-as-judge async on a sample), and watches for regressions. The patterns that matter at this layer: latency, cost-per-call, content safety violations, drift in input distribution, drift in output quality, and any prompt injection attempts. The output of this layer feeds back into Layer 2 — every interesting prod failure becomes a new test case in the golden set.
Pro Tip: Golden Datasets vs. Synthetic
The first version of a golden dataset should be hand-curated — 50 to 200 carefully chosen real examples that reflect the breadth of your actual users and the failure modes you most want to catch. Synthetic eval data (LLM-generated test cases) is useful for scaling later, but synthetic-only golden sets become self-confirming: the same model that generated the cases easily passes them. Always seed with real user data, and always review synthetic additions by hand before they enter the gate set.
Reference-Based Metrics
Reference-based metrics compare a model output against a known reference answer. They were the dominant eval method before LLM-as-judge took over, and they remain useful in narrow situations.
BLEU
Bilingual Evaluation Understudy was developed for machine translation. It measures n-gram overlap between candidate and reference. BLEU is precision-oriented and works well when there is a tight expected translation — but it punishes valid paraphrases that use different surface words, and it correlates poorly with human judgment on open-ended generation.
ROUGE
Recall-Oriented Understudy for Gisting Evaluation comes from the summarization world. ROUGE-N measures n-gram recall between candidate and reference; ROUGE-L uses longest common subsequence. It is more recall-focused than BLEU and remains the default reported metric for summarization benchmarks. Same caveat as BLEU: it rewards surface overlap, not meaning.
BERTScore
BERTScore replaces n-gram matching with contextual embedding similarity. It computes cosine similarity between BERT embeddings of tokens in the candidate and reference and aggregates to a single score. It correlates with human judgment far better than BLEU or ROUGE because it captures semantic similarity rather than surface string overlap. For tasks with a clear reference output, BERTScore is the modern choice.
What These Metrics Fail At
All three break down when there is no single reference answer. For a chatbot's response to "explain transformer attention," fifteen excellent answers exist with little surface overlap. BLEU and ROUGE will score them all low against any one reference. BERTScore does better but still misses the question of whether the explanation is correct, helpful, well-structured, and appropriate for the user's level. For most LLM applications in 2026, reference-based metrics are a tool in the box — useful when applicable, insufficient on their own.
LLM-as-Judge
LLM-as-judge is the workhorse of modern application-level evaluation. The idea is simple: use a strong language model to score the output of your application, either by comparing two candidates pairwise or by scoring against a rubric. It works because frontier models are surprisingly good at judging quality, even of outputs from other frontier models — though they have biases that must be controlled for.
Pairwise Preference
Show the judge two candidate outputs (often "A" and "B") for the same input and ask which is better. Pairwise judgments are easier and more reliable than absolute scoring because the judge does not need an internal scale — it only needs to discriminate. This is how Chatbot Arena ranks models, and it is the default mode for A/B testing prompts.
Rubric Scoring
Give the judge a single output and a rubric — for example, "score 1 to 5 on faithfulness, helpfulness, and tone, with these definitions" — and ask for a numerical score per dimension plus a brief explanation. Rubric scoring produces an absolute number per example that can be averaged across the eval set. It requires more careful prompt engineering of the judge to avoid drift.
The Calibration Problem
Judges have known biases. Position bias: in pairwise mode, judges tend to prefer whichever option is presented first (or sometimes second, depending on model). The fix is to randomize order and average. Length bias: judges often prefer longer answers regardless of quality. Self-preference bias: a judge from the same model family as the candidate sometimes rates that candidate higher. The fix is to use a different model family as judge than the one being evaluated, or to use multiple judges and ensemble. Calibration also drifts as judge model versions change; pin your judge model version in the eval config.
# LLM-as-judge with pairwise preference + position-bias mitigation import random from anthropic import Anthropic client = Anthropic() JUDGE_MODEL = "claude-sonnet-4-6" # pin the version JUDGE_PROMPT = """You are an expert evaluator. Compare two responses to the same user question and decide which is better on accuracy, helpfulness, and clarity. Question: {question} Response A: {a} Response B: {b} Reply with exactly one of: A, B, or TIE. Then on a new line briefly explain.""" def judge_pair(question, candidate_1, candidate_2): # Randomize position to control for position bias flipped = random.random() < 0.5 a, b = (candidate_2, candidate_1) if flipped else (candidate_1, candidate_2) msg = client.messages.create( model=JUDGE_MODEL, max_tokens=300, messages=[{"role": "user", "content": JUDGE_PROMPT.format(question=question, a=a, b=b)}], ) raw = msg.content[0].text.strip().split("\n")[0] # Map judge label back to original candidate if raw == "A": winner = 2 if flipped else 1 elif raw == "B": winner = 1 if flipped else 2 else: winner = 0 # tie return winner
Building an Eval Harness
Most teams do not build their eval harness from scratch. Five mature platforms cover the space, and the right one depends on whether you are already in the LangChain ecosystem, want a pure observability angle, or prefer open-source you can self-host.
| Tool | Origin | Best For | Hosted / OSS | Strengths |
|---|---|---|---|---|
| LangSmith | LangChain | Teams already on LangChain / LangGraph | Hosted (with limited self-host) | Tight integration with LangChain tracing, dataset management, prompt playground, CI hooks |
| Braintrust | Standalone | Framework-agnostic eval-first teams | Hosted | Built around evals from day one, strong scoring SDK, fast UI for diff review |
| Arize Phoenix | Arize AI | Teams that want OSS observability + eval | Open source | Self-hostable, OpenTelemetry-native traces, eval templates, runs locally for free |
| Helicone | Standalone | Logging and prod monitoring with eval bolt-on | Hosted + OSS | Drop-in proxy for any provider, cost tracking, prompt versioning, on-the-fly eval |
| OpenAI Evals | OpenAI | Benchmark-style evals on OpenAI models | Open source | Public registry of eval templates, simple YAML config, good for capability testing |
A useful default in 2026: pick LangSmith if you are already on LangChain, pick Braintrust if you want the cleanest eval-first developer experience, and pick Arize Phoenix if open-source and self-hosting matter. DeepEval is also worth a look as a pytest-style open-source library that lets you write evals as test files in your repo and run them with the rest of your suite.
Code Sample: Building Your Own Eval
Before reaching for a platform, every engineer should build the simplest possible eval loop from scratch once. It clarifies what the platforms automate and what the irreducible parts are. Below is the full pattern: golden dataset → judge prompt → score aggregation → CI integration.
# Minimal eval harness: golden set + LLM judge + aggregate + CI gate import json, sys, statistics from pathlib import Path from anthropic import Anthropic client = Anthropic() APP_MODEL = "claude-sonnet-4-6" JUDGE_MODEL = "claude-sonnet-4-6" # 1. Load golden dataset (versioned in repo: tests/golden/qa.jsonl) def load_golden(path): return [json.loads(l) for l in Path(path).read_text().splitlines()] # 2. Run the application under test def app_answer(question): msg = client.messages.create( model=APP_MODEL, max_tokens=800, system="You are a precise customer-support assistant.", messages=[{"role": "user", "content": question}], ) return msg.content[0].text # 3. Rubric judge: score 1-5 on faithfulness to the reference RUBRIC = """Score the candidate answer 1-5 for faithfulness to the reference. 5 = fully faithful, 3 = partially correct, 1 = contradicts reference. Question: {q} Reference: {ref} Candidate: {cand} Reply with one integer only.""" def judge(question, reference, candidate): msg = client.messages.create( model=JUDGE_MODEL, max_tokens=10, messages=[{"role": "user", "content": RUBRIC.format(q=question, ref=reference, cand=candidate)}], ) try: return int(msg.content[0].text.strip()[:1]) except: return 0 # 4. Run suite and aggregate def run_suite(threshold=4.0): cases = load_golden("tests/golden/qa.jsonl") scores = [] for c in cases: ans = app_answer(c["question"]) s = judge(c["question"], c["reference"], ans) scores.append(s) print(f"{s}/5 {c['id']}") mean = statistics.mean(scores) print(f"\nmean={mean:.2f} threshold={threshold}") return mean if __name__ == "__main__": mean = run_suite() # 5. CI gate: non-zero exit if mean drops below threshold sys.exit(0 if mean >= 4.0 else 1)
This is roughly 50 lines, runs in CI, and gates merges on a quality threshold. Wire it into a GitHub Action that runs on every PR touching prompts/ or tests/golden/, and you have eval-driven development. From here you scale by adding metrics, breaking the suite by feature area, parallelizing the judge calls, and graduating to a hosted platform when the dataset and reporting needs grow beyond a JSONL file.
RAG-Specific Evals
Retrieval-augmented generation has its own failure modes that generic chat evals do not capture. RAGAS is the open-source framework that emerged as the standard, and its four headline metrics map cleanly onto the failure modes that matter.
The Four RAGAS Metrics
- Faithfulness. Does the answer stay grounded in the retrieved context, or does the model hallucinate beyond what the documents support? Computed by extracting claims from the answer and checking each against the retrieved passages.
- Answer Relevancy. Does the answer actually address the question that was asked? RAGAS computes this by generating questions from the answer and measuring similarity to the original question.
- Context Precision. Among the retrieved chunks, are the most relevant ones ranked highest? This catches retrievers that surface useful chunks deep in the result list where the model may ignore them.
- Context Recall. Was all the information needed to answer the question actually retrieved? This requires a ground-truth answer to compare retrieved context against, and catches retrievers that miss critical chunks.
Faithfulness and answer relevancy are reference-free — they only need the question, the retrieved context, and the answer. Context recall requires a ground-truth answer. In practice, teams run faithfulness and answer relevancy on every retrieval call in production (sampled), and run the full four-metric suite against a versioned golden set in CI.
# RAGAS evaluation on a small RAG pipeline from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall from datasets import Dataset # Each row: question, retrieved contexts list, model answer, ground-truth answer data = { "question": ["What is our refund window?", "How do I reset my password?"], "contexts": [["Refunds are accepted within 30 days..."], ["Click 'Forgot password' on the login page..."]], "answer": ["You can request a refund within 30 days of purchase.", "Click 'Forgot password' on the sign-in screen."], "ground_truth": ["30-day refund window from purchase date.", "Use 'Forgot password' link on login page."], } ds = Dataset.from_dict(data) result = evaluate( dataset=ds, metrics=[faithfulness, answer_relevancy, context_precision, context_recall], ) print(result) # {'faithfulness': 0.92, 'answer_relevancy': 0.96, # 'context_precision': 1.0, 'context_recall': 0.88}
If a deeper walk-through of the retrieval side helps, the RAG tutorial in Python for 2026 covers the index, retriever, and prompt assembly that this eval scores.
Agent-Specific Evals
Agents add a dimension that pure RAG and chat do not have: a sequence of decisions. The model picks tools, calls them, observes results, and decides what to do next. Evaluating only the final answer misses most of what can go wrong.
Trajectory Evaluation
Trajectory eval scores the full sequence of tool calls and intermediate states, not just the end. The two questions trajectory eval answers: did the agent take a reasonable path, and did it terminate when it should have? A trajectory that takes 12 tool calls to answer a question solvable in 2 is a regression even if the final answer is correct, because it is a cost and latency disaster in production.
Tool-Call Accuracy
For each step, did the agent pick the right tool, and did it pass valid arguments? This is the most actionable agent metric because tool-selection errors cascade. Score per-call as a binary correct/incorrect (against an expected tool plus argument schema check) and aggregate to a tool-call accuracy rate. This is where most agent regressions show up first when a prompt or tool description changes.
End-Task Completion
Did the agent actually finish the task? End-task completion is the binary outcome metric — for a task with a clear definition of done (booking made, ticket filed, file produced), did it complete or not. Combined with trajectory length, this gives you a productivity-per-task signal that maps directly to what users care about.
The practical pattern: define agent eval cases with three fields — input task, expected trajectory (or expected tool sequence), and expected end state. Score each run on (1) end-task completion as binary, (2) tool-call accuracy as a rate, and (3) trajectory cost in tool calls. Track all three over time. A pure end-state eval will let bad trajectories pass; trajectory and tool-call accuracy together catch the underlying degradation before it becomes a wrong answer.
Production Patterns
The patterns that separate teams with mature LLM eval practice from teams still firefighting:
Regression Tests on Every PR
The eval suite runs on every pull request that touches prompts, model code, or chains. The CI gate fails the build if mean score drops more than a small tolerance below baseline, with a per-example diff so the author can see exactly which cases regressed. This is non-negotiable for serious teams. Without it, every prompt edit is a silent gamble.
Online Sampling for Human Review
In production, sample a small percentage of real interactions (1–5%) and route them to a human-review queue. Reviewers tag good and bad outcomes. The bad ones become new test cases in the golden set. This is how the eval suite stays honest as the input distribution drifts away from what was anticipated at launch.
Drift Monitoring
Track three time-series in production: input-distribution drift (are users asking different things than last month?), output-quality drift (are LLM-as-judge scores trending down?), and cost-per-call drift (are calls getting longer?). Alert on meaningful changes. Drift detection catches the silent failures that traditional alerting misses.
The Eval-Driven Development Workflow
The mature loop looks like this: a real-world failure shows up in production monitoring → it gets added as a new case to the golden dataset → the eval suite now fails for that case → the engineer iterates on the prompt or pipeline until the case passes → the change is merged with a green eval suite → the failure mode is permanently fixed and protected against regression. This is test-driven development, applied to LLM systems. The teams that internalize it ship faster with fewer surprises.
Frequently Asked Questions
What is LLM evaluation and why is it different from traditional software testing?
LLM evaluation measures whether a language-model application produces correct, useful, and safe output. It differs from traditional testing because outputs are non-deterministic, there is rarely a single ground-truth answer, behavior drifts as models and prompts change, and exhaustive test cases are impractical because the input space is open-ended natural language. Eval requires statistical thinking, golden datasets, and judge models — not unit tests with hard-coded expected values.
What is LLM-as-judge?
LLM-as-judge is a technique where one language model scores the output of another. Two common patterns are pairwise preference (the judge picks the better of two answers) and rubric scoring (the judge scores a single answer 1–5 against criteria). Judges work well for fluency, helpfulness, and rubric adherence. They struggle with factual correctness in specialized domains and have known biases — position bias, length bias, and self-preference bias when judging output from the same model family.
What is RAGAS?
RAGAS is an open-source Python framework for evaluating retrieval-augmented generation pipelines. Its four headline metrics are faithfulness, answer relevancy, context precision, and context recall. RAGAS uses LLM-as-judge under the hood to score these on a per-example basis, and it is the most widely adopted RAG eval framework in production today.
How do I integrate LLM evals into CI?
Maintain a versioned golden dataset of inputs and reference outputs. Run the eval suite on every pull request that touches prompts or model code. Fail the build if scores drop below threshold, and surface a per-example diff showing which cases regressed. Eval harnesses like LangSmith, Braintrust, and Arize Phoenix all provide CI integrations. The discipline is treating prompts and chains as code under test — every change runs through the suite before merge.
Sources: RAGAS Documentation, LangSmith Docs, Braintrust Docs, Arize Phoenix Docs, OpenAI Evals, DeepEval Docs. Pricing and feature notes reflect public documentation as of April 2026.
Explore More Guides
Eval is the most undervalued skill in applied AI engineering, and the org that evals well wins.
Most teams learning LLM development spend ninety percent of their first year on prompts, retrieval, and agent design, and ten percent on evaluation. The teams that ship and stay shipped invert that ratio over time. The reason is simple: prompts and pipelines are easy to change. The hard part is knowing whether a change made things better or worse, on real data, across the failure modes you actually care about. Without an evaluation discipline, every change is a vibe check. With one, every change is a measurement.
The pattern we see in 2026 is converging fast. The teams winning at applied LLMs all look similar from the inside: they have a versioned golden dataset checked into the repo, they have a CI gate that runs LLM-as-judge on every PR touching prompts, they sample real production traffic into a human-review queue, and they treat every interesting prod failure as a new test case to add to the golden set. None of these practices are exotic. All of them are still rare. The rarity is the opportunity.
Our honest recommendation: if you are building anything serious with LLMs and you do not have evals, stop building features for two weeks and build the eval suite. It will feel slower for those two weeks. It will be dramatically faster for every week after. The teams that figure this out ship four times as many improvements per quarter as the teams that do not, because every change is gated, measured, and retained instead of guessed at, hoped for, and forgotten. Eval is the moat. The work is unglamorous. The compounding is enormous.