The basics
expected reference data when present, the agent’s response, and — since 0.3 — the instructions the agent under test was given. A sample passes the judge when its raw score meets min (default from evals.judge.min_score, 7). The normalized score (raw / 10) feeds the sample score.
Dimensions
Require per-dimension minimums on top of the overall score:Multi-dimensional
The judge reads the agent’s instructions
Since 0.3, the agent’s system prompt is passed to the judge automatically. Nothing to configure. It matters more than it sounds. Without it the judge grades prose against a rulebook it cannot read, so the agent’s own documented behaviour reads as invention:agent: “You have 30 days from delivery to return an item, and it must be in its original condition” judge: “introduces multiple unstated policies”Every fact in that answer was in the agent’s instructions. On an 11-row suite, the same agent and the same model scored 63.7% with a criteria that assumed the judge knew the policy and 96.2% once the policy was pasted into the criteria by hand. Passing the instructions is that workaround, done once and kept in step automatically. The instructions are resolved once per evaluation, not per sample. They are skipped for a
Closure target, which has no instructions to read, and for an agent that will not resolve — a judge without context is a worse grade, not a failed run.
Some criteria are deliberately about the response alone — tone, reading age, format — where the system prompt would only bias the grade or spend tokens. In a class-based evaluation, turn it off per judge:
Inside evaluate()
->judge(...) surface always includes them; drop to the class-based form for a criteria that needs them gone.
Judges run last — and gates protect your budget
Judges execute only after all deterministic assertions, and a failed->gate() skips them entirely for that sample. An empty response never costs judge tokens. (Configurable via evals.judge.skip_on_gate_failure.)
Choosing the judge model
Per judge
evals.judge.provider / evals.judge.model (EVALS_JUDGE_PROVIDER / EVALS_JUDGE_MODEL).
Budget for the judge, not for the agent. The judge reads the criteria, the input, the response and the agent’s instructions, and it runs once per judged sample — so it routinely costs several times the thing it is grading. On one measured run, a gpt-5 judge grading a gpt-5-mini agent cost 0.0080 — 14×. Nothing is wrong when you see that; it is what judging costs. Both figures are broken out in the run summary and in the dashboard, so pick the judge model with the total in view — a smaller judge on a well-calibrated criteria is often the difference between running a suite nightly and running it monthly.
Custom judge agents
The default judge is a structured-output agent returning{score, reasoning} (+ dimensions). Supply your own for domain-specific rubrics:
app/Evals/Judges/PolicyJudge.php
Using it
Pairwise comparison
In class-based evaluations, judge one response against another — e.g. the reply your agent gave in production:Inside evaluate()
1.0, tie 0.5, loss 0.0. Only an outright win passes: prefer defaults to 'actual', so a tie fails the assertion whether or not you called ->prefer(). The 0.5 still counts toward the sample score — a draw is worth something, it just isn’t a pass.