Writing Metrics
A metric scores one trial. Metrics are attached to each task (not once per run), so a suite can grade heterogeneous work — a Q&A task and a coding task can carry different scorers. Every metric, however simple or elaborate, implements the same small protocol.
The protocol
No base class — a metric is any object with these three members (a structural Metric protocol).
What compute_scores receives
input.candidate is the trial under evaluation:
input.row.data is a dict describing the task and trial:
So an outcome metric reads candidate.output_text and row.data["reference"]; a trajectory
metric reads candidate.evidence.
Declaring outputs
A metric declares its outputs up front; the runtime validates that compute_scores returns exactly
those names, each coercible to the declared type (a missing or undeclared output raises). Build specs
with the MetricOutputSpec factories:
A metric may emit several outputs — for example an efficiency metric returning both a boolean and a count:
Prefer continuous_score when you want a numeric mean in the run summary. A boolean output
reports per-trial pass/fail but does not aggregate to a numeric mean on its own (though it still
contributes as 0/1 to a view).
Returning a result
Return a MetricResult whose outputs match output_spec by name:
Reading evidence
candidate.evidence (a CandidateEvidence) holds named descriptors, each exposed through a typed
handle. Always guard first — a trial may not carry a given descriptor:
The Score by Component guide has a
complete, runnable trajectory metric; the SDK’s example_metrics.py (under
examples/run_agent_eval/) shows filesystem and trace metrics.
How outputs are reported
Each output aggregates in result.summary under the key <metric.type>.<output> (mean / min / max /
std-dev). To roll several outputs into one reported score, define a view on the task — see
Score by Component.