Before you begin
Before you start, make sure you have:- Working eval: An
Evalcall that runs, as in Run your first eval. - Expected values: Cases whose
expectedvalue has the shape your scorer reads. See Create eval datasets.
1
Write scorer function
Declare only the arguments you use.
- Arguments: Python matches parameters by keyword from
input,output,expected, andmetadata, so accept**_for the rest. TypeScript passes one object withinput,output,expected,metadata,tags, andid.metadatais the inline case’smetadata, or the dataset record’sextra. - Optional
expectedin TypeScript:expectedis optional on every case. Type itexpected?:and returnnullwhen it is missing; a requiredexpectedfailstsc --strict. - Passing scorers to
Eval: Python accepts bare functions, named after the function, or{"name": ..., "scorer": fn}mappings. Always wrap a lambda in a mapping with a name. TypeScript accepts{ name, scorer }objects only.
2
Return score
Return one of these shapes:
A non-finite number raises. Two scorers that emit the same score name fail the run: every score name belongs to exactly one scorer. If a scorer raises, its message is appended to that case’s
error and the other scorers still run. If the task raises, the case records the error and its scorers are skipped.Scores gate well when they follow these rules:- One behaviour per score. A score that mixes three rules cannot tell you which one regressed.
- Partial credit where it carries information. “3 of 4 facts” is more useful than pass or fail.
- Abstain rather than pass. Return
Nonewhen a rule does not apply. A free1.0hides regressions in the mean. - Stable names. The CI gate and trend views key on the score name. Renaming a score starts a new series.
3
Copy scorer from catalog
Each entry says what it measures and the case shape it needs. Import
ScoreValue from atlanai. To score which tools an agent called, in what order, and with which arguments, see Score agent behaviour.Exact match
Exact match
Measures whether the output equals the expected value, after normalising case and whitespace. Needs
expected as a string.Required facts
Required facts
Measures the share of required phrases that appear in the output. Partial credit shows how much was missed. Needs
expected as {"must_include": [...]}.Valid JSON with required keys
Valid JSON with required keys
Measures whether the output parses as JSON and carries every required key. Needs
expected as {"keys": [...]}.Numeric within tolerance
Numeric within tolerance
Measures whether a numeric answer is within a relative tolerance set on the case. Needs
expected as a number, and extra.tolerance on the record or metadata.tolerance inline.Pattern and refusal checks
Pattern and refusal checks
Measures a format rule or a forbidden pattern, such as a leaked internal ID, or a refusal where an answer was required. Needs only the output.
Rule for some cases only
Rule for some cases only
Measures a rule that only some cases exercise. Return
None or null elsewhere, so the mean covers only the cases the rule applies to. Needs a category on the case.LLM judge in your own code
LLM judge in your own code
Measures a quality that code cannot decide, such as tone or faithfulness, with your own model client. Needs a model you are approved to send case data to.Hosted Keep judge scorers few and deterministic: set
llm_judge scorers run over recorded sessions, not inside Eval. When a CI run needs a judge, call your model from a code scorer, parse a constrained answer, and keep the rationale:temperature=0, constrain the answer to fixed labels, and delimit the case content as data. Changing the rubric records a new scorer version, so compare runs only when the same version scored them.4
Name and version scorer
Eval registers each scorer in the workspace as <eval-name>-<scorer-name>, lowercased with _ turned into -: scorer exact_match in eval support-agent becomes support-agent-exact-match. Score names on results keep the function’s own name, exact_match. Eval stores a digest of the function’s source; when the source changes, the next run records a new scorer version, and each score cites the version that produced it.- Keep the
Evalname stable. Renaming the eval creates new scorers, so a baseline and a candidate with different eval names are not scored by the same scorer. The CI action also matches baselines on this name. - Share one scorer across evals by passing
registry_name(registryNamein TypeScript) in the scorer mapping. - Pin an existing scorer with
scorer_idandscorer_version(scorerId,scorerVersion).Evalthen writes nothing to the scorer. - Version behaviour changes yourself. The digest covers the function’s source text only. A changed constant it closes over, or a helper it calls, does not create a version. Put thresholds inside the function, or bump
registry_namewhen behaviour changes.
5
Verify scores in run
Run the eval and check its result:
- Check summary:
run.summary["scores"](run.summary.scores) has one entry per score name, each with acountequal to the cases the score applies to.run.summary["scorers"]names the scorer ID and version behind each score. - Check a case: Each case in
run.resultshas the score under its name, and its trace has ascore.<name>span.
None or raised on them; a raise appears in that case’s error. See Eval errors.Troubleshooting
Scorer failures include two scorers that emit the same name, a scorer name that collides with another scorer in the workspace, and an empty summary because every task raised. See Eval errors.Next steps
- Score agent behaviour: Score tool choice, arguments, and trajectory, not only the final answer.
See also
- Write hosted judges: Judges that score recorded production sessions.
- Eval options and errors: The summary shape and scorer limits.
- Eval object fields: Scorer fields and versions.