Skip to main content
Agent scoring checks both the final answer and the trajectory: which tools the agent called, with which arguments, in how many steps. Use it when an agent can reach the right answer by the wrong path, or the wrong answer by a reasonable one. For answer-only checks, the scorers in Write code scorers are enough.

Before you begin

Before you start, make sure you have:
  • Working eval: An Eval call that runs, as in Run your first eval.
  • Agent entry point: A function that takes a user message and returns the agent’s reply, with its tools callable from your code.
  • Side effects listed: The tools that send email, issue refunds, write to production, or change anything else.
1

Wrap agent as task

Scorers see the case and, of the run, only what the task returns. Give the agent a thin adapter that returns the answer plus the tool calls it made.Record tool calls at the tool boundary, where they execute, not from what the agent says it did: a model’s own account can differ from what ran. Here each tool is traced and records its calls into a per-case list. The list is a context variable, so concurrent cases do not mix their calls.
Pass the logger to Eval as trace_logger in Python or logger in TypeScript, so your agent’s spans nest under each case. Model calls, tool spans, tokens, cost, and latency then land on the case’s trace without the task returning them.Model and framework spans appear when their instrumentation is installed. In Python, auto_instrument() enables installed OpenTelemetry instrumentors for providers such as OpenAI and Anthropic, and frameworks such as LangChain, CrewAI, and the OpenAI Agents SDK. In TypeScript, wrap the Vercel AI SDK with wrapAISDK, or set ATLAN_INSTRUMENT=pi to trace a pi agent (see Use pi with Atlan). See Agent frameworks. Every eval case is traced in full: Eval refuses a logger that samples or drops content.
2

Stub tools with side effects

An eval runs your agent for real. A tool that sends email, issues a refund, or writes to production does that on every case of every run.
Give the agent a stand-in for each such tool. It records the call, so scorers still see it, and returns a plausible result without acting.
Pass the stand-ins where your agent looks up its tools, such as its tool registry or constructor. Keep each stand-in’s name and signature identical to the real tool’s, so the tool schema the model sees does not change.
3

Score tool calls

Put what a good trajectory is in each case’s expected: the expected tools and their arguments. These scorers read output["tool_calls"] (output.toolCalls in TypeScript) and expected["tools"] as lists of {name, args}. Both languages read the same expected keys, so one dataset serves both.
In TypeScript, declare expected optional and return null when it is missing; a required expected fails tsc --strict. Compare structured values with same, not raw JSON.stringify: expected read from a dataset comes back with its object keys in a different order, and a raw comparison scores 0 on every case. tool_arguments matches each expected call to at most one actual call, so one call cannot satisfy two expected calls to the same tool.Pick the scorers for the questions you need answered:Compare arguments on the fields that matter. Normalise or drop volatile ones, such as timestamps and request IDs, or the score measures noise.
4

Run eval on agent

Run the agent, the scorers, and Eval from one file. Set subject_kind and subject_id so the run appears on the agent’s page in the app, and so you can list its history.
subject_kind is agent, harness, or skill, and subject_id must be an artifact of that kind you can read.The experiment does not record which version of the agent ran. Put the agent version and prompt revision in config, or pin them with a context manifest. In the CI action, Python Eval records the commit, branch, and pull request in config.git, and the action’s model input in config.model, unless your config already has a git or model key; a TypeScript eval records them only through the results-file shim.If the task raises, that case records an error and its scorers do not run; the run still returns. If no case produces a score, Eval completes the experiment and then raises, with the experiment ID on the exception. Pass require_scores=False (requireScores: false) to get the result back instead.
5

Verify trajectory and cost

  • Check scores: No entry in run.results has an error, and run.summary["scores"] has one entry per scorer.
  • Check trace: Each case’s trace has this shape:
  • Check cost and tokens: They live on the traces, not in the summary. Query them per experiment with trace stats. A scalar query returns one number per measure; a table query grouped by score_name returns the mean of each score across the run’s score spans.
    Token and cost values come from model spans that report usage; an agent without instrumented model calls shows 0. To enforce a budget, compare these numbers in your CI script. The CI action reports them but never fails a check on them.
Agents vary run to run, and Eval runs each case once. To measure variance, run the same eval several times with a trial field in config and compare the means:
Set model temperature to 0 where your agent allows it. A gate threshold narrower than the run-to-run spread fails at random: widen it, or gate on more cases.
6

Score multi-turn conversation (optional)

Put the user turns in input as a list. The task replays them against your agent with the history so far, and returns the final reply as answer plus every tool call across the turns. It keeps the single-turn output shape, so the scorers above work on both.
Scorers can judge the final turn, every turn, or the whole trajectory. In a dataset, store the turns under the input_key, for example {"question": ["...", "..."]}. To serve single-turn and multi-turn records from one dataset, normalise first: turns = question if isinstance(question, list) else [question].

Troubleshooting

Agent eval failures include a task that raises on every case, a TypeScript comparison that scores 0 because of key order, and a logger that samples or drops content. See Eval errors.

Next steps

See also