Skip to main content
The eval in the quickstart only checked whether the reply contained “30 days”. A real Agent does more: it calls the right tools, remembers context across a multi-turn conversation, and edits code correctly. This page starts from the simplest eval and adds those checks step by step.

Structure of an eval

Evals live under evals/, with file names ending in .eval.ts, and default-export defineEval(...):
  • The file path decides the ID: the ID of evals/billing/refund.eval.ts is billing/refund. Only .eval.ts and .eval.tsx files are discovered.
  • description appears in results. Write it as one sentence about what this eval verifies.
  • tags are for selecting by tag, for example niceeval exp local --tag billing. The Experiment’s evals function can read them too.
  • test(t) is the eval body. t has every method for sending messages, asserting, and working with the Sandbox.
For other fields, such as judge, timeoutMs, sandbox, and metadata, see the defineEval reference.

Single-turn: send one message and check the result

This uses three kinds of checks:
  • t.succeeded(): this turn completed normally, without failing or stopping in a waiting state.
  • t.calledTool(...): it called get_weather, the city in the arguments is Brooklyn, and it called it exactly once. Tool calls are only visible when the Adapter reports the matching events; see Write send.
  • t.check(value, match): checks one specific value, here the reply text.
A failed check does not stop the checks after it. When a run ends, you see every unmet condition at once, instead of fixing one and running again. When later code really depends on a result, use await t.check(...).orStop() to stop on failure. Add .label("...") to a check, and results show your description instead of a piece of code, which is easier to read:
t.send() returns this turn’s Turn. Save it when you need to check one turn on its own:

Multi-turn: check that context carries over

Consecutive send calls on the same t form one conversation. draft.succeeded() only checks the first turn; t.calledTool checks whether the tool was called anywhere in the eval. When you need two unrelated conversations, for example to simulate two users, open a new session with t.newSession(). Multi-turn conversation requires the Adapter to carry the previous turn’s context to the application; see Write send.

Give open-ended answers to the Judge

Questions like “is the answer grounded” or “is the tone right for elementary school students” cannot be written as a regex. Hand them to the Judge model to score:
The Judge gives a score from 0 to 1, and .gate(0.7) means anything below 0.7 fails. When a deterministic rule can check something, such as which tool was called or whether a file changed, prefer the deterministic rule; save the Judge for truly open-ended quality. To configure which model the Judge uses, see Configure and Verify the Judge.

Generate many evals from one dataset

When dozens of evals share the same logic and differ only in input, you do not need dozens of files. Export an array from one file:
The generated IDs are sql/0000 and sql/0001. To read a dataset from YAML or JSON and give each row a stable ID, see Data-Driven Tests.

Prepare a workspace for a Coding Agent

When evaluating a Coding Agent, the eval is still an ordinary .eval.ts. You just put a project into the Sandbox first, then let the Agent change it:
For where the Sandbox comes from and how to score with the project’s own tests, see Fixture and Sandbox Agent.

Report progress and issues during a run

When preparation is slow, use t.progress to show what is happening in the terminal. When something is worth recording but does not affect the conclusion, record a diagnostic with t.diagnostic:
progress is only shown during the run and is not saved. diagnostic is saved with this Attempt and visible in the results afterwards, but it does not change pass or fail. Write business conclusions as assertions. When preparation fails and the run cannot continue, throw an exception directly, and this run is recorded as errored.

Prepare and clean up external resources

Some evals create temporary resources outside the Sandbox, for example a temporary repository on a shared Git service. Use setup to prepare and teardown to clean up; each runs once per Attempt:
Key points:
  • Most evals do not need teardown. Files written into the Sandbox and installed dependencies disappear when the Sandbox is destroyed. Only resources outside the Sandbox need manual cleanup.
  • Once setup has started, teardown always runs, even if setup throws, so guard against resources that were never created.
  • When teardown throws or exceeds 30 seconds, it only records a teardown-failed diagnostic and does not change the conclusion this Attempt already reached. To make a check affect the conclusion, put it in setup or test.
  • When attempts is greater than 1, multiple Attempts of the same eval run concurrently. Store handles in a WeakMap keyed by sandbox, one per Attempt, so they do not overwrite each other.
The full order of these steps in one Attempt: By default each Attempt has its own Sandbox, so “once per Sandbox” and “every Attempt” mean the same thing. They only diverge when reuse is enabled; see Reuse Sandboxes.

Next steps