LLM evaluation is the systematic testing of how well a large language model performs a specific task. An individual test is called an eval. The catch that makes LLM evaluation its own discipline is that the thing you are testing can return a different answer every time you run it. Traditional tests assume a fixed output for a fixed input. Evals do not get that luxury.
We run evals against v0 almost daily, and that practice has shaped how we think about quality for every AI feature we ship. I'll cover what evals are, how we grade v0 internally, how to wire them into CI, and why most off-the-shelf eval approaches buy you less confidence than they appear to.
Copy link to headingWhat an LLM eval actually is
Start with the reframe, because it changes everything downstream. The developers who get the most out of evals are the ones who stop treating them as a report card for the model and start treating them as a regression test for behavior. A report card asks whether the model is good. A regression test asks whether the thing you just shipped got worse than the version before it. The second question is the one that wakes you up at night, and it is the one evals are built to answer.
The reason you need them at all is non-determinism. The same prompt can produce different results across runs. A small wording change in your system prompt can cascade into regressions you will never catch by clicking through the UI. And what works cleanly in development can fall apart on real user inputs in production. I've watched teams ship a one-word change to a system prompt, smoke-test it by hand, and learn a week later that it quietly broke structured output for a whole class of inputs nobody thought to check. That failure mode is invisible to manual review by construction, because manual review only covers the paths you remember to walk.
An eval makes that coverage explicit and repeatable. You write down the inputs that matter, you write down what counts as a pass, and you run them on every change. That is the entire idea, and it sounds trivial until you try to do it for a system whose outputs you cannot predict.
Copy link to headingMost teams are still eyeballing outputs
Our State of AI survey (656 respondents, Q4 2024 through Q1 2025) put numbers on something I'd been seeing anecdotally for a year. Seventy percent of developers still evaluate model outputs by manual testing. Only 34% run automated evals. Fifteen percent have no formal evaluation at all. The tooling to do better has existed the whole time. The gap is workflow integration, not capability.
Manual review works right up until it doesn't, and the point where it stops working is not obvious from the inside. Clicking through the UI catches the failures you go looking for. It is structurally blind to the ones you don't, and with a non-deterministic system the failures you didn't anticipate are most of them. A reviewer who approves ten good generations has learned nothing about the eleventh, because the model is free to behave differently on it.
The trap is that manual testing feels like diligence. You looked at the output. It looked fine. But "I looked and it was fine" is a claim about one run of a system that does not repeat itself, and teams that lean on it are quietly accumulating regressions they have no way to see. By the time a manual-only team notices a quality problem, a user usually found it first.
Copy link to headingThree ways to grade an output, and the order that matters
When we built eval-driven development for v0, the most important decision was not which grading method to use. It was the order we reached for them. We start with the cheapest, fastest check that can catch a given failure, and we only escalate to a more expensive method when the cheaper one structurally can't see the problem. Get that ordering wrong and your eval suite becomes too slow and too costly to run on every change, which means it stops running on every change, which means it stops mattering.
There are three methods, and they are not interchangeable. Each one catches a class of failure the others miss.
Code-based grading runs first. We use regex to check whether v0 generates inline styles instead of Tailwind classes, to validate that code blocks parse, to confirm imports resolve, to verify multi-file usage, and to check the ratio of comments to code as a signal of model laziness. These checks cost nothing in tokens and run in milliseconds. The rule of thumb: anything you can express as an assertion, express as an assertion. It costs nothing and it never flakes.
Human grading handles the subjective layer. A domain expert looks at the generated code and gives a thumbs-up or thumbs-down. It is slow and expensive, and it catches what no automated system can: whether the UI looks good, whether the interaction pattern makes sense, whether the code reads as idiomatic. We treat it as the expensive method it is. You don't spend an expert's afternoon on something a regex could have caught. You spend it on the questions where taste is the actual metric.
LLM-based grading fills the middle. We use another model to evaluate output on dimensions like coherence and correctness, the qualities a rule can't express but that don't need a person either. The cost is real, since LLM-based grading costs 1.5x to 2x more than code-based grading, so we reserve it for exactly what the first two layers cannot reach, and not one check more.
Copy link to headingHow this runs in CI
An automated script runs the entire suite and reports pass/fail rates, regressions, and improvements. Braintrust logs everything for manual review. Every GitHub pull request that touches the output pipeline carries its eval results with it, so the question of whether a change made v0 worse gets answered before the merge, not after. We hold a 100% pass rate on refusal and safety evals, and we treat any failing prompt we find as a new permanent member of the set. The metric we optimize for is blunt on purpose: the percentage of generations that produce a working website in v0's preview instead of an error or a blank screen.
One distinction is worth drawing before we go further, because it shapes the pipeline. Evals split into two timing modes. Offline evaluation runs before you ship, against a fixed test set, and answers whether a version is safe to release. Online evaluation runs against live traffic and answers whether the version you already shipped is still behaving. You need both, and the second feeds the first.
Copy link to headingWhat to measure
The hardest part of evals is not running them. It is choosing what they check. This is where most suites go wrong, because the instinct is to reach for a general quality score, and a general quality score is almost always measuring something other than what you care about.
We don't run a generic "code quality" number on v0. We check whether imports resolve, whether Tailwind classes are used instead of inline styles, whether the generated output renders. These are binary, and they are grounded in failure modes we've watched happen in production. A check that returns pass or fail on a concrete behavior tells you something you can act on. A 7.3 out of 10 on "quality" tells you nothing.
Good metrics for an LLM feature tend to share a few traits:
They are binary or close to it. Pass or fail beats a float, because a float invites you to argue with it instead of fixing the problem.
They map to a failure you have actually seen, not a quality you imagine matters.
They are read as a delta from your own baseline, not against an absolute threshold.
That last trait is the one teams skip. If your factual accuracy has been running at 0.92 and it drops to 0.87, that is a real signal worth blocking a release over, even though 0.87 looks perfectly respectable in isolation. The absolute number is close to meaningless. The movement is the whole story. An eval that only checks against a fixed bar will wave a regression through as long as the regressed version still clears the bar.
It helps to know the metric families before you pick one. At the model level the classic measures are statistical: perplexity, BLEU and ROUGE for text overlap, BERTScore for semantic similarity, F1 for classification. These describe a model in the abstract. At the application level the metrics that predict whether your feature actually works are different: hallucination rate, response relevance, coherence, latency, task accuracy. The model-level numbers are what benchmarks report. The application-level ones are what your users feel, and they are the ones your evals should optimize for.
Copy link to headingGeneric evals manufacture confidence you haven't earned
Here is the position I'll defend: a generic eval is worse than no eval. No eval at least leaves you appropriately nervous. A prefab eval hands you a number that feels like safety while measuring abstract qualities that have nothing to do with your use case. The nervousness was the honest state. The number is the dangerous one.
This is not just our view. Hamel Husain puts it bluntly: "All you get from using these prefab evals is you don't know what they actually do and in the best case they waste your time and in the worst case they create an illusion of confidence that is unjustified." Public benchmarks have the same problem one layer up. A benchmark is a standardized public test set, like MMLU, HellaSwag, or GLUE, that scores a model across many tasks at once. It tells you something about the model in the abstract and close to nothing about your use case. Andrej Karpathy noted in his 2025 review that "training on the test set is a new art form." Nicole van der Hoeven documents benchmark errors and contamination concerns around MMLU. And Sebastian Raschka names the asymmetry cleanly: a low benchmark score tells you a model is weak, but a high score does not tell you it is meaningfully better than another model that also scored high.
So build your eval suite against your own production data. Start with the inputs your users actually send, the edge cases you've already tripped over, and the specific failures you want to guarantee never ship again. In my experience the teams that do this are calmer than the benchmark-chasers, because their green checkmark corresponds to something real. Twenty well-chosen test cases that cover your core use cases are worth more than 200 examples scraped at random. The 200 will give you a more impressive-looking dashboard and tell you less.
Copy link to headingWiring evals into your pipeline
An eval suite that runs when someone remembers to run it is a documentation project, not an engineering control. The value comes from automation, from the suite running on every change whether or not anyone asked it to. Across teams shipping AI features on Vercel, three patterns do most of the work.
Copy link to headingLayer the checks, cheapest first
The ordering logic from grading applies to the whole pipeline. Latitude's CI/CD guide frames it in three layers, and it lines up with how we think about cost. Layer one is deterministic: JSON schema validation, regex assertions, format checks. Zero token cost, sub-second runs. Layer two is heuristic: pattern matching and rule-based scoring that still needs no model call. Layer three is LLM-as-judge, reserved for subjective dimensions like tone, helpfulness, and reasoning coherence, and run at temperature 0 so the scoring itself stays consistent. You only spend tokens on the checks that genuinely require a model, which keeps the suite fast enough to run on everything.
Copy link to headingGate at three checkpoints
Run a small, fast smoke eval on every pull request. It should finish in minutes and catch obvious breakage. Before a release, run the full suite and block the deploy if metrics fall below versioned thresholds. After release, compare canary traffic against the baseline and alert on drift. The thing to track at every gate is regression from your own baseline rather than an absolute floor, for the reason above: a version can clear the bar and still be meaningfully worse than what it replaced.
Copy link to headingMock in CI, run real models in staging
LLM non-determinism is exactly what you want from an eval and exactly what you do not want from a unit test. The AI SDK's MockLanguageModelV3 and simulateReadableStream let you pin model output for deterministic unit tests without ever calling a provider. Use mocks for integration testing and user-flow verification, where you need the same result every run. Reserve real model calls for the eval suite running against preview deployments, where non-determinism is the point and you want to see how the model behaves on inputs you didn't hand-pick.
The tooling to enforce this is mature. Braintrust ships a native GitHub Action that posts score summaries as PR comments and blocks merges when scores drop below thresholds. Promptfoo documents its CI/CD command as npx promptfoo@latest eval -c promptfooconfig.yaml, runnable in any CI environment with Node.js 20 or higher. Both slot directly into the three-checkpoint pattern.
Copy link to headingThe AI SDK is already an eval harness
If you're building with the AI SDK, you already have the primitives for a production eval system. Most teams haven't recognized them as eval tooling.
MockLanguageModelV3 and MockEmbeddingModelV3 give you repeatable unit tests with no provider call. Output.object({ schema }) validates structured responses against a Zod schema, which quietly turns every structured generation into a type-checked assertion. Output.choice({ options }) constrains output to a fixed set of strings, which is exactly what LLM-as-judge scoring needs: force the judge to return "pass" or "fail" instead of a paragraph of hedging you then have to parse.
import { generateText, Output } from 'ai';// Force the judge to return a verdict, not a paragraph of hedgingconst { output } = await generateText({ model: yourModel, output: Output.choice({ options: ['pass', 'fail'] as const }), prompt: `Does this response correctly answer the question?\n\n${candidate}`,});// output is 'pass' | 'fail'Agent evals need more than input-output checks, and the tool-calling lifecycle gives you the hooks. onStepFinish exposes the step number, the tool calls, the tool results, the finish reason, and token usage. experimental_onToolCallFinish adds per-tool timing through durationMs. Together, these are instrumentation hooks for trace-level evaluation, the data you need to ask whether the agent called the right tools in the right order and whether each step produced what it should have.
The model-agnostic design ties it together. A good eval runner is model-agnostic, which means swapping GPT-4 for Claude, or changing a prompt, doesn't force a rewrite of your evaluation logic. Because generateText and streamText share a common model interface, switching modelId to compare providers takes zero changes to the harness. We use this internally with v0, where feature flags and the AI SDK let us evaluate providers and make model and prompt decisions from data instead of debate.
Copy link to headingAgent evals are the next gap to close
The shape of the workload is shifting under everyone's feet. Tool-call requests on our AI Gateway grew from 11.4% to 22.2% of all requests between October 2025 and April 2026. Measured by tokens the shift is starker: 58.9% of all tokens now flow through tool-call requests, up from 31.6% six months earlier, because tool-using requests run roughly 2.6x more token-heavy than the rest. Agents are no longer a niche of the traffic. They are becoming the traffic.
That exposes the gap. Single-turn prompt-response evals say nothing about trace-level correctness, tool sequencing, or whether a multi-step workflow finished. This is where the tooling still feels immature, and it's where I'd expect the next year of eval work to concentrate.
We hit it ourselves. When we built a durable AI code agent on Vercel, the workflow ran code generation, test generation, and sandbox execution inside retry loops of up to three iterations. The eval question changed from whether the model produced a good response to whether the workflow succeeded end to end. Those are different questions with different failure modes. An error in an early step corrupts everything downstream, so agent evals have to grade each step on its own and the full trace as a single unit.
The payoff when you get it right is concrete. Our AI support agent reduced human-handled tickets by 31%, and that number came from measuring the right thing: not whether each answer was correct, but whether the workflow resolved the user's problem without escalation. The eval methodology followed the workflow, not the individual response.
Copy link to headingEvals are version control for behavior
Strip away the tooling and the patterns and one idea is left. Evals are the testing discipline AI features have been missing, and the teams that get this don't treat them as a quality report they read after the fact. They treat them the way they treat CI: the thing that decides whether code ships.
The mechanics are settled. Deterministic checks first, heuristic scoring second, LLM-as-judge only for what the first two can't cover. Wire the suite into your pull requests so it runs without being asked, which, in our experience with v0, is what turns evals from a review habit into a control on what reaches production. Start with 20 test cases drawn from real user inputs and grow from there. The loop closes on itself: every production trace is a future test case, and every test case you add tightens the behavior of the next release. That is version control for a system that won't hold still, and once you have it you stop guessing.
Copy link to headingFAQ
Copy link to headingHow many test cases do I need to start?
Quality beats quantity, and it isn't close. Twenty well-chosen cases covering your core use cases are worth more than 200 random examples. Start there and grow the dataset from your production logs as you discover new failure patterns. The cases that matter most are usually the ones a real user surfaced, not the ones you invented at your desk.
Copy link to headingHow much do LLM evals cost to run?
Deterministic and heuristic checks cost nothing in tokens. LLM-as-judge grading runs 1.5x to 2x the cost of code-based grading per evaluation. That cost structure is the whole argument for layering: run the free checks first and reserve model-based scoring for the subjective dimensions the cheaper layers can't reach.
Copy link to headingShould I eval in production or only before deployment?
Both, and they feed each other. Offline evals catch regressions before code ships. For production, Braintrust recommends asynchronous online scoring with sampling rates between 1% and 10% for high-volume applications. The low-scoring production traces then flow back into your offline test set, which is how the suite gets smarter over time instead of going stale.
Copy link to headingWhat tools work best with TypeScript and the AI SDK?
Braintrust offers TypeScript-first evaluation with a native Vercel Marketplace integration and automatic AI SDK tracing. Promptfoo runs via npx in any Node.js 20+ environment. And the AI SDK's built-in mock providers handle unit testing with no external dependency at all, which is the cheapest place to start.