Calibrate an LLM judge by comparing its labels with a consistently reviewed sample and inspecting both kinds of disagreement. Overall agreement alone can hide a judge that misses the failures your team most needs to catch.
Define the label before asking a judge
Choose a narrow criterion, such as whether a material claim is supported by supplied evidence. Give reviewers the same source material and rubric, resolve disagreements, and record uncertain cases. A general request to “score quality” mixes correctness, style, and usefulness into a label that is difficult to interpret.
Read the disagreement matrix
In the synthetic sample, one true failure is flagged, one true failure is missed, one correct answer is flagged, and one correct answer passes. The judge agrees on two of four cases. Precision and recall for failure detection are both one half, but those estimates describe only this tiny fixture.
Run a calibration cycle
- Keep calibration examples separate from the final evaluation set.
- Version the judge model, grading prompt, source context, and label definition.
- Inspect false alarms and missed failures by task category and impact.
- Recheck on held-out labels after changing the judge, and preserve unresolved reviewer disagreements.
Use calibrated scores as evidence
Decide how disagreements affect review workload and missed-risk exposure. A judge may be appropriate for triage while remaining unsuitable for automatic release approval. Calibration against one reviewer group does not establish ground truth for every domain, language, or future traffic pattern.
Run the worked example
Save the synthetic Node.js example and run node calibrate-llm-judge-with-human-review.mjs with Node.js 22 or newer. The assertions check the illustrated records and calculations; they do not test a deployed provider, SDK, or access control. No credentials or network connection are needed.
// Synthetic teaching fixture. No credentials, network calls, or production verification.
import assert from 'node:assert/strict';
const labels = [
{ humanFailure: true, judgeFailure: true },
{ humanFailure: true, judgeFailure: false },
{ humanFailure: false, judgeFailure: true },
{ humanFailure: false, judgeFailure: false },
];
const tp = labels.filter(x => x.humanFailure && x.judgeFailure).length;
const fp = labels.filter(x => !x.humanFailure && x.judgeFailure).length;
const fn = labels.filter(x => x.humanFailure && !x.judgeFailure).length;
assert.equal(tp / (tp + fp), 0.5);
assert.equal(tp / (tp + fn), 0.5);
console.log(JSON.stringify({ reviewed: labels.length, trueFlags: tp, falseFlags: fp, missedFailures: fn }));
References and further reading
Maintained by DriftRail. AI assisted the October 2026 content and template revision. Examples are illustrative; they are not measured product benchmarks. Suggest a correction.