defineAgent constructs an Adapter that directly calls a function, SDK, or service endpoint. Its send receives input, drives the Agent, and returns the result of that turn.
This tutorial first gets a minimal integration working with an Adapter, Experiment, and eval, then explains how parameters travel from the Experiment to the Adapter and application under test. Event streams, multi-turn interaction, HITL, and tracing are optional capabilities; the matching tutorials are listed at the end.
Choose an integration path by system under test
AI SDK App
A Vercel AI SDK app can use the built-in Adapter to connect an existing HTTP interface.
Agent
To evaluate a standalone Agent such as Claude Code, Codex, or bub, use a built-in Sandbox Agent.
Other AI Agent
Your own Agent needs Write Send. If the app already has OTel instrumentation, connect OTel.
Minimal integration example
The following steps assume the project has already runnpx niceeval init and contains niceeval.config.ts and an evals/ directory. Each of the three files has one responsibility: the Adapter connects the system under test, the Experiment fixes run configuration, and the eval defines interactions and Assertions.
1. Write the Adapter. The minimal integration fills only status and events, putting the Agent reply in one message event.
send contract—every field in TurnInput, AgentContext, and Turn.
When the system under test is an independent HTTP service, call the HTTP interface that users actually use even if the service source and eval live in the same repository. Do not replace this example’s fetch with an in-process function call, because:
- An in-process call is not the path users take. It bypasses HTTP, serialization, middleware, and streaming. A passing eval does not prove production behavior is correct.
- The Adapter cannot be reused in another deployment environment. An HTTP Adapter connects to local, staging, or production by changing
baseUrl(see the two Experiment files below). An in-process call depends on the current repository.
send. Keep the same input, cancellation signal, and returned Turn shape. Do not create a service that does not exist merely to fit the HTTP example.
2. Experiment
includes("30 days"). Wording varies on every reply, and one exact phrase makes an open-ended answer fail even if the system answered correctly. For open wording, use a regular expression that accepts equivalent phrasing, a shape Assertion such as includesUrl() / hasSections(), or a Judge Match registered through check. Use exact Assertions only for deterministic results such as a concrete value or ID. See Evaluation Kinds and Assertions for the complete trade-off.
pnpm exec niceeval view --run <runId> to inspect every eval’s per-turn input, events, and scoring detail.
When it does not run, triage by where the error appears:
fetchthrows directly (such as connection refused): the application is not running, or the URL insendis wrong. First usecurlto send the same request to that interface and confirm it.t.succeeded()fails and the turn’s Verdict is failed: the request was sent, but the application returned afailedTurn. Map protocol failures toTurn.statusor a standarderrorevent. Preserve bounded extra context withctx.diagnostic(...), not by printing the full response body.- Only a content Assertion fails: the integration itself works. Compare the actual
t.replyvalue inview, then adjust the Assertion or application.
Experiment Flags
Configuration has exactly two channels. Keep them separate and the integration stays clear:- Static configuration goes through the Adapter factory. Write environment-level configuration such as URL, authentication, and protocol details as factory parameters in the Experiment file. The
agentfield indefineExperimentreceives an already-configured instance. - Dynamic values per turn go through
ctx.modelandflagsdeclared by the Experiment travel unchanged tosendthroughctxon every turn. The Adapter does not interpret their meaning; it only forwards them with the request to the application.
my-agent from step 1 into a factory that accepts configuration. Change the default export to a function returning defineAgent(...), and read factory parameters in send. The Experiment’s model and flags reach every turn through ctx, then send forwards them with the request:
options the same way. The Experiment needs two small changes: import the factory by name and change agent from “refer to an instance” to “call the factory”:
ctx fields a turn may use and how the Adapter consumes them:
Progress, diagnostics, and fatal errors in an Adapter
progress is short-lived status that later updates replace. diagnostic is a bounded record available after the run ends. Neither can choose a phase or output stream, and neither changes Turn.status or the Attempt Verdict automatically. Throw infrastructure errors that cannot continue, such as connection failure or unparseable input. Express a normal failure received from the Agent under test with Turn.status: "failed".
The terminal displays only a one-layer error summary and Attempt identity. Full code, message, cause, stack, and diagnostics live in Attempt-owned channels; inspect them with niceeval show @<attempt-locator> or a fixed query operation. An OTel trace only adds call relationships and timing; it is not a prerequisite for error data.
To evaluate local and production environments separately, create two Experiment files with different factory parameters:
attempts, budget, concurrency, and sandbox.
Add optional capabilities
After minimal integration, extend the Adapter as needed. Existing evals do not need to change:
See Tier for the integration tier and scope corresponding to each capability.
Reference implementations
examples/zh/tier1 provides five runnable non-intrusive integration examples—ai-sdk-v7, claude-sdk, codex-sdk, pi-sdk, and langgraph. They cover event-stream Assertions, multi-turn isolation, HITL approval and rejection, and the trace waterfall. Hand-writing send requires only a transport and mapping table. ctx.session provides session continuation and HITL pause/resume; for frame-by-frame driving, use a built-in implementation. See Built-in Agent Capabilities.
Related reading
- Official Adapter Overview: Sandbox and non-Sandbox Adapters and their options.
- Write Send: the complete seven-step hand-written Adapter tutorial, from sending one message to HITL, OTel, and flags.
- Adapter: the
sendcontract, field by field forTurnInput,AgentContext, andTurn. - OTel Integration: send application spans to NiceEval too in exchange for the
niceeval viewcall waterfall. - Tier: requirements and capabilities for the three integration tiers.
- Write Experiments: the complete
defineExperimentfields.