Most teams answer that by trying five examples and squinting at the output. That works right up until it doesn't. An eval swaps the squint for a number you can rerun tomorrow and trust. This guide covers what evals are, how people actually build them, when to run them, and which tools are worth your time right now. About 20 minutes.
You give your AI system an input. You capture what it does. You check that against what "good" looks like. Then you do it for fifty more inputs and count. That count is your eval.
That's really all it is. The word sounds academic, but if you have written a unit test you already know 80% of it. The difference is that normal code is predictable and LLMs are not. Ask the same question twice and you might get two different answers, both fine, or one fine and one quietly wrong. So instead of "does it pass", you end up asking "how often does it pass".
| Unit test | Eval | |
|---|---|---|
| Output | Same every time | Can change run to run |
| Checking | assert x == 42 | Often a judgment call: is this answer correct, polite, grounded? |
| Result | Green or red | A rate, like 34 out of 40 |
| A failure means | There's a bug | Maybe a bug. Maybe noise. You have to look. |
| Written | Once, mostly | Grows forever as you find new failures |
You'll see these across every tool and blog post. They mean simple things.
One test. An input plus some idea of what a good result is.
One attempt at a task. Since outputs vary, you often run each task a few times.
Whatever decides pass or fail. A regex, a Python function, another LLM, or a person.
The full record of a trial: messages, tool calls, reasoning, results. Gold for debugging.
A collection of tasks that measures one thing you care about.
The plumbing that runs the suite, calls the graders and records scores.
No framework needed. This is a perfectly good first eval:
# 3 cases now. 30 by next week. Real ones, from real users. cases = [ {"q": "Can I get a refund after 30 days?", "must_say": "no"}, {"q": "How do I reset my password?", "must_say": "settings"}, {"q": "What's your CEO's home address?", "must_say": "can't help"}, ] def grade(answer, case): return case["must_say"] in answer.lower() # crude, and that's fine for day one passed = sum(grade(my_bot(c["q"]), c) for c in cases) print(f"{passed}/{len(cases)} passed")
Someone says "we run LLM-as-judge evals", someone else says "we run regression evals", and a third says "we do online evals". They could all be describing the same thing. Split it into four questions and it gets clear fast.
Exact match, regex, JSON schema check, "does the SQL run", "do the unit tests pass", "was the right tool called".
Another model reads the output and decides. "Does this answer only use facts from the provided context?"
A domain expert reads the output. Still the gold standard for "is this actually good?".
Good suites use all three. Code where you can, a judge where you must, humans to keep the judge honest.
| Type | What you look at | Example check |
|---|---|---|
| Single response | One input, one output | Is the summary faithful to the source? |
| Conversation | A multi-turn chat | Did it remember the order number from turn 2? |
| Agent trajectory | Every step an agent took, plus where it ended up | Is the booking actually in the database? Did it avoid deleting anything? |
| Retrieval (RAG) | What was fetched, and what was said with it | Was the right document retrieved? Does the answer stick to it? |
| Safety / red team | Attacks and edge cases on purpose | Does a prompt injection in a PDF make it leak data? |
| Operational | Latency, cost, token usage | Under 3 seconds and under $0.01 per answer? |
"What can it do yet?" These are the hard cases you're working toward. A low score is normal and useful, it shows you where to climb.
healthy score: low, rising"Does it still do what it used to?" Cases you already solved. They should stay near 100%. Any drop means something broke.
healthy score: ~100%, flatCases graduate. Once a capability case passes reliably, move it into the regression suite so it stays fixed.
A fixed dataset you run before shipping. On your laptop, or in CI on every pull request. This is where most people start.
Graders running on a sample of real production traffic. Catches the stuff your dataset didn't imagine. No reference answer, so it's mostly judges and code checks.
MMLU, GPQA, SWE-bench, Terminal-Bench, τ-bench, Humanity's Last Exam. These are evals too, just public ones built to compare models against each other. They're useful for picking a model. They tell you almost nothing about your support bot. Many older ones are also saturated (every top model scores near the ceiling) or have leaked into training data.
A benchmark helps you pick the model. Your own evals decide everything after that.
The most common mistake is opening a framework, choosing "helpfulness" and "coherence" from a dropdown, and calling it done. You end up measuring things that don't matter while the real failures walk right past. The teams that are good at this all follow roughly the same loop.
Pull 50 to 100 real conversations or agent runs. Read them. All of them. Yes, it's boring. It's also the highest-value hour you'll spend this week.
One note per trace. "Quoted the old refund policy." "Asked for the account number twice." Don't categorise yet. Researchers call this open coding.
Similar notes become a category. Count them. Now you have a ranked list of what's actually broken. This is axial coding. An LLM can help cluster, but you decide the buckets.
Each common failure gets test cases and a grader that checks for that specific failure. Not "quality". The actual thing.
Change the prompt, the retrieval, the model. Rerun the suite. Did the failure rate drop? Did anything else break?
New bug report? New trace that looks weird? It becomes a test case. The suite grows with the product.
A clever grader on a lazy dataset gives you a confident, wrong number. Spend the effort here first.
What real users actually typed, typos and all. Nothing you invent will be this weird. If you have logs, start here.
Every one is a free test case with the failure already labelled. Turn each into a regression test.
The person who knows the refund policy cold can write the ten tricky edge cases in an afternoon.
Generated with an LLM. Great for coverage before launch, as long as you do it with structure (see below) and read what comes out.
If you just ask an LLM for "50 customer questions", you get 50 polite, similar, easy questions. Instead, pick the dimensions that make real questions different, combine them, and only then turn each combination into a question.
| Who | Wants | Situation | Generated question |
|---|---|---|---|
| First-time user | Refund | Outside policy window | "hi i bought this 6 weeks ago can i still send it back??" |
| Angry repeat customer | Refund | Already refunded once | "This is the SECOND time. I want my money back today." |
| Business account | Invoice | Missing tax ID | "Need a VAT invoice for order 88121, the one we got has no tax number" |
Go down this list and stop at the first one that can check the failure you care about. Every step down costs more money, more time and more doubt.
Valid JSON? Mentions a source? Called refund_order with the right ID? Tests pass? Under 200 words? If code can check it, code should check it.
For things only a reader can tell. "Is every claim supported by the retrieved docs?" "Did it hand off when the user asked for a human?"
For labelling the examples that calibrate the judge, spot checks, and anything high stakes.
Rating scales feel richer. In practice nobody agrees what separates a 3 from a 4, including the same person on a different day. Binary forces you to decide what "good enough" means. If you need more detail, split it into several yes/no checks instead of one fuzzy score.
# a judge prompt for exactly one failure mode You are checking a support reply for ONE problem: stating a policy detail that is not in the provided policy text. POLICY TEXT: {policy} REPLY: {reply} FAIL if the reply states any refund window, fee, or eligibility rule that is missing from or contradicts the policy text. Otherwise PASS. Examples: - "Refunds within 30 days" (policy says 30 days) -> PASS - "You can return it within 60 days" (policy says 30) -> FAIL Think it through in 2-3 sentences, then answer on the last line with exactly PASS or FAIL.
An unchecked judge is just vibes with extra steps. Have your expert label 100 or so outputs as pass or fail. Use a few as examples in the judge prompt, tune against part of the rest, and keep the last chunk untouched for a final check. Then compare the judge against the human.
Two things change once you move from single answers to agents that call tools and loop. You have more to grade, and randomness starts to matter a lot more.
If the task was "book a table for two on Friday", the question that matters is: is there a booking for two on Friday in the system? Check the end state directly. Agents are often creative about how they get there, and a grader that insists on one exact sequence of tool calls will fail good runs.
Then look at the path for things you care about regardless of outcome: did it touch something it shouldn't, loop 40 times, or burn $3 on a $0.10 task?
Say your agent succeeds 80% of the time on a task. Run it once and you'll see a pass, or a fail. Neither tells you it's 80%. So agent evals run each task several times and report one of two numbers. Which one you want depends on your product.
You changed the prompt and the score went from 78% to 82%. Before you celebrate, check how many cases that's based on. Small datasets have wide error bars.
LLM apps don't crash when they get worse. They just get a bit worse, quietly, in ways nobody notices until a customer does.
Fixing one prompt problem often creates two new ones somewhere else. A suite shows you both before users do.
New model out? Cheaper one tempting you? Run the suite and you have an answer by lunch instead of a two-week debate.
"I think v2 is better" versus "I think v1 is" goes nowhere. "v2 fixes 9 cases and breaks 2" ends the meeting.
Error analysis turns "the bot feels off" into a ranked list. You fix the thing that's broken 23 times, not the one that's broken twice.
Shorter prompts, smaller models, fewer retrieved chunks. Every saving is a quality risk. Evals tell you how much you actually gave up.
In regulated industries someone will ask "how do you know it's safe?". A versioned suite with results is a real answer.
Don't build infrastructure yet. Read outputs, keep a list of what breaks. That list becomes your first suite.
run: your eyes, a spreadsheetBuild the first real suite from error analysis, add synthetic cases for coverage, set a bar you need to clear.
run: full offline suitePrompt edit, retrieval tweak, tool change. Wire the regression suite into CI and block merges that break it.
run: regression suite in CIUpgrades, cheaper alternatives, provider changes, even the same model name with a new snapshot.
run: everything, side by sideSample live traffic and grade it with judges and code checks. Watch the trends. Send weird cases to a human queue.
run: online evals on a sampleThe bad conversation becomes a test case before the fix ships. It never comes back unnoticed.
run: new regression caseThe tooling has grown up a lot. The core habits haven't changed much. Here's what the teams getting real value out of evals tend to have in common.
Every prompt, tool call and response is logged as a trace. OpenTelemetry's GenAI conventions are now the common format, so traces move between tools. You can't evaluate what you didn't record.
A standing slot to read traces and label them. Often in a small custom review screen, which is quick to build now with a coding agent.
Regression suites run on every pull request, like tests. A drop below the bar blocks the merge, and the diff shows which cases flipped.
A sample of production traces gets graded continuously. Dashboards show failure rates per failure mode, not one vague "quality" number.
Every LLM judge has a human-labelled set behind it and known error rates. When the judge model changes, it gets rechecked.
Agents draft test cases, graders and judge prompts. Humans still decide what counts as failure and review what the agent wrote.
Prompt injection, data leaks and jailbreak attempts are just more test cases, run on schedule, not a one-time audit before launch.
Support tickets and thumbs-down feedback flow into an annotation queue. Confirmed failures are added to the dataset.
Libraries run evals from your code or CI. Platforms store traces and datasets, give you a UI to review and label, and run evals on production data. Many teams use one of each.
| Tool | Good for | Worth knowing |
|---|---|---|
| promptfoo MIT | Comparing prompts and models from a YAML config and a CLI. Strong red teaming. | Acquired by OpenAI in March 2026. OpenAI says the open-source project continues. |
| DeepEval Apache 2.0 | Evals that feel like pytest. Easy to drop into Python CI. | Ships many ready-made metrics. Use the ones that match your failure modes, skip the rest. |
| Ragas Apache 2.0 | RAG apps: retrieval quality, faithfulness, answer relevance. | Good starting metrics for RAG. Still validate them on your own data. |
| Inspect MIT | Serious agent and safety evals with sandboxes and tool use. | Built by the UK AI Security Institute. Comes with a large library of ready evals. |
| OpenAI Evals hosted | Teams already on the OpenAI platform. Includes trace grading for agents. | Convenient, but ties your eval setup to one provider. |
| Tool | Good for | Worth knowing |
|---|---|---|
| Langfuse MIT core, self-host | The popular open-source pick. Tracing, datasets, judges, annotation queues. | Joined ClickHouse in January 2026 and stays open source. |
| Braintrust commercial | An eval-first workflow with a polished UI and CI integration. | Popular with product teams who want managed and don't mind closed source. |
| LangSmith commercial | Teams on LangChain or LangGraph. | Works without LangChain too, but that's where it shines. |
| Arize Phoenix Elastic 2.0, self-host | OpenTelemetry-native tracing and evals you can run yourself. | Source-available, not OSI open source. Arize AX is the managed version. |
| Opik Apache 2.0 | Open source with a permissive licence, from Comet. | A good fit if licence terms matter to your legal team. |
| MLflow Apache 2.0 | Teams that already run classic ML on MLflow or Databricks. | Has judge alignment against human feedback and prompt optimisation built in. |
| W&B Weave Apache SDK, hosted | Teams already using Weights & Biases. | Newer than W&B's experiment tracking, still maturing. |
| What | Rule of thumb | Why |
|---|---|---|
| First suite size | 20 to 50 cases | From real failures. Grow from there. |
| Traces to read for error analysis | ~100 | Or until new failure types stop showing up. |
| Human labels to check a judge | ~100 | Split into examples, tuning set and a held-out test set. |
| Regression suite target | ~100% | Any drop means something broke. |
| Capability suite target | low, rising | If it's at 100%, add harder cases. |
| Trials per agent task | 3 to 5+ | One run can't show a success rate. |