Judge calibration — no uncalibrated judge may gate¶
An LLM judge decides which model reaches production. If nobody has measured that judge, the promotion gate is a confident guess wearing the clothes of a measurement.
ExaMLOps therefore refuses to let an unmeasured judge gate anything. A judge must first pass the
Minimum Viable Validation Protocol (MVVP); until it does, exa eval gate,
exa pipeline promote and exa autopilot all refuse and name the failed check.
This is deliberately disruptive on first rollout — see Migration.
Why¶
The largest systematic study of LLM judges to date (21 judges, 9 providers, 3 benchmarks, 118 runs, ≈541 000 judgments) found:
| Finding | Consequence for a gate |
|---|---|
| Raw agreement overstates chance-corrected κ by 33.8–41.3 pp | A "90 % agreement" judge can be a κ ≈ 0.6 judge |
| 11 of 21 judges shift ≥4 rank positions across benchmarks (one moves 15) | A judge validated on one benchmark family tells you nothing about another |
| Test–retest 0.992 with position bias 0.192 | A judge can be almost perfectly reproducible and almost perfectly wrong |
| Position bias spans 0.002 → 0.192 (~100×) | Which judge you picked matters more than the model you are gating |
The last row is the one that motivates the hard failure: reproducibility is the property people instinctively check, and on its own it is not evidence of anything.
The protocol¶
A calibration measures five things. All must pass for the judge to be gate-eligible.
| Check | Rule | Requirement |
|---|---|---|
| Chance-corrected agreement | Cohen's κ with a 95 % interval — never raw agreement | G7.1 |
| Position bias | Paired AB + BA presentation; \|P(first position wins) − 0.5\| ≤ 0.10 |
G7.2 |
| Test–retest | ≥3 replications, temperature 0, response caching off | G7.2 |
| Benchmark coverage | ≥2 families, spanning preference-based and correctness-based labels | G7.2 |
| Consistency–bias paradox | test–retest > 0.95 and position bias > 0.10 ⇒ hard failure | G7.2 |
Two further rules apply to everything downstream:
- Provenance (G7.3). Every evaluation result carries a
calibration_idresolving to the judge's κ and bias at the time of that evaluation. Re-measuring a judge never rewrites the provenance of an evaluation that already ran. - Uncertainty (G7.4). Proportion scores are stored with a Wilson interval
(
score_lo/score_hi), not as point values.
Replications must run with caching off: three replications served from a cache measure the cache, not the judge.
Calibrating a judge¶
Collect the judgments into a JSON file — a batch job, reviewable and committable:
{
"benchmarks": [
{
"name": "mt-bench-sample",
"family": "preference",
"items": [
{"human_label": 1, "judge_scores": [1, 1, 1], "ab_first_wins": [true, false]},
{"human_label": 0, "judge_scores": [0, 0, 0], "ab_first_wins": [false, true]}
]
},
{
"name": "gsm8k-sample",
"family": "correctness",
"items": [{"human_label": 1, "judge_scores": [1, 1, 1]}]
}
]
}
judge_scores— one score per replication, in[0, 1]. The recorded replication count is the minimum across items: an item scored once cannot support a three-replication claim.ab_first_wins— outcomes of the paired AB + BA probe: did the option in the first slot win? An order-blind judge produces onetrueand onefalseper pair.human_label— ground truth, binarized at 0.5.
Then measure and record it:
exa eval calibrate gpt-judge --from ./eval/judge-calibration.json
exa eval calibrate gpt-judge --from ./eval/judge-calibration.json --require-eligible # CI gate
exa eval calibration show gpt-judge
exa eval calibration list
Recording a failing calibration is not an error — the measurement is the point. Use
--require-eligible when a CI job should fail on a judge that may not gate.
To calibrate against a live judge from Python, use the same seam LLMJudge uses:
from examlops.evaluation.calibration import CalibrationBenchmark, CalibrationItem, calibrate
from examlops.data.evaluation import record_judge_calibration
cal = calibrate(judge_fn, benchmarks, judge="gpt-judge", replications=3)
record_judge_calibration(cal)
What refuses, and what it looks like¶
| Surface | Behaviour when the judge is not eligible |
|---|---|
exa eval gate run |
Gate fails; a judge_calibration verdict names the failed checks |
exa pipeline promote |
Alias is not moved; blocked promotion is audited (--force still overrides, and is audited) |
exa autopilot run |
Promotion is blocked and an autopilot_promote_blocked audit event is written |
A judge nobody has measured fails with no_calibration. Absence of calibration is not
eligibility — that is the specific failure mode this exists to prevent.
The refusal applies in warn mode too. warn says metric regressions are advisory; it was
never a licence to let an unmeasured instrument decide what reaches production.
A suite made only of deterministic evaluators (exact match, regex, JSON validity) has no judge and is unaffected.
Correcting a score for judge error¶
A judge's raw pass-rate is an apparent rate measured with an imperfect instrument. The Rogan–Gladen correction converts it into an estimate of the true rate using the judge's own sensitivity and specificity, both recorded by the calibration:
from examlops.evaluation.calibration import rogan_gladen
rogan_gladen(apparent=0.80, sensitivity=0.90, specificity=0.90) # -> 0.875
It returns None when sensitivity + specificity ≤ 1: a judge no better than chance carries no
information, and a corrected number there would be an invention.
Migration¶
Gates that pass today will start refusing as soon as their suite uses a judge. That is the intended behaviour. The path forward is:
exa eval calibration list— see which judges are measured (empty means none may gate).- Assemble a labelled benchmark for each judge in use, spanning both families.
exa eval calibrate <judge> --from <file>and read the failed checks.- Fix the judge (prompt, model, or presentation order) and re-measure.
There is no flag to skip the requirement. If calibration cannot run because no labelled benchmark exists, the judge is not gate-eligible — a deliberate hard failure, not an oversight.
Reference¶
- ADR 0111 — No uncalibrated judge may gate (requirements G7.1–G7.4)
- Module:
examlops.evaluation.calibration· gate wiring:examlops.evaluation.gate - Table:
judge_calibrations; provenance columnseval_suite_results.calibration_id,score_lo,score_hi - Related: Evaluation & regression gates