Calibrating an LLM Judge You Can Trust
LLM judge calibration is the process of measuring how well an LLM's quality scores agree with human judgment, then adjusting the judge prompt, rubric, or scoring scale until that agreement is high and stable. If you skip this step, your "eval score went from 82 to 88" chart is decoration, not evidence. This article walks through why uncalibrated judges drift, how to build a gold set, which agreement metrics to trust, and how to keep a judge honest as your product and models change underneath it.
Most teams adopt an LLM-as-judge pattern because manual review does not scale. You have thousands of chatbot transcripts, RAG answers, or generated summaries, and no budget to have humans read all of them every time you ship a prompt change. So you write a judge prompt: "Rate this response 1-5 for helpfulness and accuracy." It runs, it produces numbers, and those numbers start showing up in dashboards and pull request descriptions. The problem is that a judge prompt written in an afternoon has no guarantee of measuring what you think it measures. Calibration is the discipline that turns "a model that outputs numbers" into "a measurement instrument."
Why uncalibrated judges fail silently
An uncalibrated judge does not throw errors. It just produces plausible-looking scores that do not track the thing you actually care about. There are a few recurring failure patterns worth naming because they are easy to reproduce.
Length bias. Judges (and humans, honestly) tend to rate longer answers as more thorough, even when the extra length is padding. If your candidate model got more verbose between versions, your judge score can go up for reasons that have nothing to do with quality.
Position bias in pairwise comparisons. When a judge picks between "Response A" and "Response B," many models favor whichever response appears first in the prompt, sometimes by a wide margin. If you do not randomize or swap positions, you are measuring an artifact of your prompt template.
Self-preference. A judge built on the same model family as the system under test tends to score that family's outputs more favorably. This matters a lot if you use, say, a model from one lab to judge outputs from the same lab's other models.
Leniency drift over a session. Judges scoring a long batch in one context window sometimes drift toward the middle of the scale, or get more lenient as they process more "acceptable" examples in a row. This shows up as score compression: everything clusters around a 4 out of 5 regardless of real quality differences.
Rubric ambiguity. If two humans reading your rubric would disagree about what a "3" means, the judge will produce inconsistent scores too, just with more confidence than the disagreement warrants.
None of these are visible from the judge's output alone. You need a reference point, which is why calibration starts with humans, not with the judge.
Step 1: build a gold set with real disagreement
The gold set is a sample of inputs and outputs, scored by humans, that you treat as ground truth for calibration. Two mistakes ruin gold sets before they are used.
The first is sampling only easy cases. If every example in your gold set is either obviously great or obviously broken, any judge will hit high agreement, including a broken one, because the task is trivial. Deliberately oversample the boundary: responses that are almost right but have a subtle factual error, responses that are correct but poorly formatted, responses that are correct but miss part of a multi-part question. This is where judges actually fail, so this is where you need signal.
The second mistake is using a single annotator. One person's scores encode one person's idiosyncrasies. Use at least two independent annotators per example, and compute inter-annotator agreement before you even look at the judge. If your humans disagree with each other 40% of the time, no judge can beat that ceiling, and you need to fix the rubric first, not the judge.
A practical gold set size for a first calibration pass is 100-150 examples, stratified across the failure modes you care about (factual errors, tone problems, incomplete answers, refusals, formatting breaks). You can grow it over time, but do not skip calibration waiting to hand-label a thousand examples. Start smaller and iterate.
gold_set_schema = {
"id": "uuid",
"input": "the prompt or user query",
"candidate_output": "the response being judged",
"context": "retrieved docs, system prompt, or other grounding, if any",
"human_scores": [
{"annotator": "a1", "score": 4, "notes": "correct but verbose"},
{"annotator": "a2", "score": 3, "notes": "verbose hurts clarity"}
],
"consensus_score": 3.5,
"category": "factual_qa" # tag the failure mode being tested
}Keep raw per-annotator scores, not just a consensus number. You will need the spread later to know how much disagreement is "normal" for a given example.
Step 2: pick the right agreement metric
Once you have a gold set, run the judge over the same inputs and compare judge scores to human scores. The metric you choose changes what "good calibration" means, so pick deliberately.
Percent exact agreement is intuitive but brittle on ordinal scales. If your rubric is 1-5 and the judge is off by one point on every example, exact agreement looks terrible even though the judge is nearly always in the right neighborhood.
Cohen's kappa or weighted kappa corrects for agreement you'd expect by chance and, in the weighted version, penalizes large misses more than small ones. This is usually the right first metric for an ordinal rubric.
Spearman or Pearson correlation is useful when you care about rank ordering more than absolute score values, for example if the judge feeds an A/B comparison between two model versions rather than an absolute quality bar.
Pairwise agreement rate applies when your judge does A/B comparisons instead of absolute scoring. Here you check how often the judge picks the same winner a human would pick, and you should also check this separately with A and B swapped, to catch position bias directly.
A minimal calibration script:
from scipy.stats import spearmanr
from sklearn.metrics import cohen_kappa_score
def calibration_report(gold_set, judge_fn):
human_scores = []
judge_scores = []
for example in gold_set:
human_scores.append(round(example["consensus_score"]))
judge_result = judge_fn(
input=example["input"],
output=example["candidate_output"],
context=example.get("context"),
)
judge_scores.append(judge_result["score"])
kappa = cohen_kappa_score(human_scores, judge_scores, weights="linear")
rho, _ = spearmanr(human_scores, judge_scores)
exact = sum(
h == j for h, j in zip(human_scores, judge_scores)
) / len(gold_set)
return {
"weighted_kappa": kappa,
"spearman_rho": rho,
"exact_agreement": exact,
"n": len(gold_set),
}There is no universal "good" threshold, but as a working rule of thumb: weighted kappa below 0.4 means the judge is not usable, 0.4-0.6 needs rubric work before you trust it for anything but rough triage, and above 0.6 is generally workable for tracking trends, with above 0.75 needed if you plan to gate deploys on the judge score alone.
Step 3: diagnose before you tune
When agreement is low, resist the urge to immediately reword the judge prompt. First find out where the disagreement concentrates.
Break the gold set into categories (as tagged in the schema above) and compute agreement per category. It is common to find that a judge is excellent at catching factual errors but nearly useless at judging tone or formatting, because those are underspecified in the rubric. That tells you exactly which part of the prompt to fix, instead of guessing.
Also look at the direction of disagreement, not just its magnitude. A judge that is consistently more lenient than humans (systematic positive bias) needs a different fix than a judge with high variance but no consistent direction (needs a tighter rubric), which needs a different fix than a judge that disagrees only on long outputs (needs an explicit anti-length-bias instruction).
from collections import defaultdict
def agreement_by_category(gold_set, judge_fn):
buckets = defaultdict(list)
for example in gold_set:
judge_result = judge_fn(
input=example["input"],
output=example["candidate_output"],
context=example.get("context"),
)
diff = judge_result["score"] - example["consensus_score"]
buckets[example["category"]].append(diff)
for category, diffs in buckets.items():
avg_diff = sum(diffs) / len(diffs)
print(f"{category}: mean_diff={avg_diff:.2f}, n={len(diffs)}")A positive mean_diff means the judge scores that category higher than humans on average; negative means lower. Large absolute values point you to the categories that need rubric attention first.
Step 4: fix the rubric, not just the prompt wording
The highest-leverage calibration fixes are usually structural, not stylistic.
Move from a single holistic score to decomposed criteria. Instead of asking a judge to output one number for "quality," ask it to score accuracy, completeness, and clarity separately, each with a short definition and 1-2 examples of what a low score looks like. Decomposed scoring reduces the chance that a judge lets one strong dimension (like fluent writing) paper over a weak one (like a wrong fact).
Add few-shot anchors pulled from your gold set. Include one example each of a clearly low, medium, and high score, taken from real disagreement cases, with a one-line justification. This does more to reduce variance than almost any amount of instruction wording.
Require the judge to cite evidence before scoring. Ask for a short quote or span from the output that justifies the score, written before the numeric score itself. This forces the model to ground its judgment in the text rather than pattern-matching on surface features like length or tone.
Neutralize position bias explicitly. For pairwise judges, run every comparison twice with positions swapped, and only accept a preference if both runs agree. Discard or flag ties.
Control for length directly. If your diagnosis shows length bias, add an explicit instruction such as "a shorter, correct answer should score at least as well as a longer answer with the same content" and re-test the length-bias category specifically.
After each rubric change, rerun the calibration report on the full gold set, not just the category you were fixing. Rubric changes have side effects; a fix for tone judging can quietly shift how the judge scores completeness.
Step 5: separate the judge model from the model under test
If your judge and your production model come from the same family, self-preference bias is a real risk, and it is worth testing for directly. Run the same gold set through two different judge models (for example, one from each of two different labs) and compare their agreement with humans and with each other. If judge A consistently favors outputs written in judge A's own house style, and that style correlates with your production model's outputs, your scores are inflated in a way that will not show up until you swap production models and the "quality" score mysteriously drops.
A practical mitigation, if budget allows, is an ensemble: score with two different judge models and take the average or require agreement within one point before trusting the result. This roughly doubles judge cost but meaningfully reduces single-model bias, and it is often worth it for scores that gate a release.
Step 6: monitor for drift, do not calibrate once and forget
Calibration is not a one-time gate before launch. Three things break a previously-good judge over time.
Model updates behind an API change the judge's behavior even if your prompt is untouched. If your judge provider updates the underlying model, rerun your calibration report immediately, before trusting the next batch of scores.
Your product's output distribution shifts as you ship features. A judge calibrated on chatbot answers will not automatically transfer to code generation or structured extraction outputs from the same product. Recalibrate, or at minimum re-run the gold set, whenever the task type materially changes.
Gold sets go stale. As your product evolves, "current" failure modes change: a judge calibrated against last year's most common error type may have a large blind spot for this year's most common error type. Rotate 10-20% of the gold set periodically with freshly annotated examples pulled from recent production traffic, focused on cases where the judge and a spot-check human disagree.
A lightweight way to catch drift without a full recalibration cycle: keep a small "canary" set of 15-20 fixed examples with known human scores, and run it automatically every time the judge prompt or underlying model changes. If canary agreement drops below your threshold, that is your signal to run the full gold-set calibration again before trusting anything downstream.
def canary_check(canary_set, judge_fn, kappa_threshold=0.5):
report = calibration_report(canary_set, judge_fn)
if report["weighted_kappa"] < kappa_threshold:
raise RuntimeError(
f"Judge canary failed: kappa={report['weighted_kappa']:.2f} "
f"below threshold {kappa_threshold}. Do not trust new scores "
f"until full recalibration."
)
return reportWire this into whatever pipeline runs your eval suite, so a silently degraded judge cannot quietly poison a week of deploy decisions.
Putting it together
A working calibration loop looks like this in practice: build a stratified gold set with multiple human annotators, measure baseline judge agreement with weighted kappa and category breakdowns, fix the rubric based on where disagreement concentrates (decomposed criteria, few-shot anchors, evidence citation, position-swap for pairwise, explicit length control), check for self-preference if judge and production model share a lineage, and then keep a canary set running continuously so drift gets caught before it corrupts your metrics. None of this is exotic machine learning, it is closer to building a survey instrument: define what you are measuring precisely enough that two humans would agree, then check whether your automated proxy agrees with them too.
The teams that get burned by LLM judges are almost never the ones whose judge scores are low. They are the ones whose judge scores look great and are quietly measuring something else, like response length or the judge's own stylistic preferences, while the actual thing they shipped to change (accuracy, helpfulness, safety) moved in the opposite direction. Calibration is the only thing standing between those two situations, and it is worth doing before your eval numbers show up in a launch decision.
FAQ
How big does my gold set need to be to start calibrating? 100-150 examples is enough for a first, meaningful calibration pass, provided you stratify across failure categories and oversample boundary cases rather than obvious ones. Grow it over time as you find new failure modes in production, but do not wait for a large dataset before starting.
Can I use the same model to judge and to generate, or does that always bias results? It is not automatically disqualifying, but it is a real risk you have to test for, not assume away. Run a same-family judge against a different-family judge on your gold set; if agreement with humans is comparable and neither shows a directional bias toward its own family's outputs, same-family judging can be acceptable. If you see a gap, use a different model family for judging, or ensemble two judges.
What agreement score counts as "good enough" to ship a judge? There is no universal number, but weighted kappa above roughly 0.6 is a reasonable bar for tracking trends over time, and above roughly 0.75 is a better bar if the judge score alone gates a deploy decision. Below 0.4, treat the judge as not yet usable and go back to rubric diagnosis.
How often should I recalibrate? Recalibrate fully whenever the underlying judge model changes, whenever your product's output distribution changes meaningfully (new task type, new feature), or on a fixed cadence such as quarterly. Between full recalibrations, run a small canary set on every judge prompt change to catch obvious regressions immediately.
Should I use absolute scoring (1-5) or pairwise comparison (A vs B)? Pairwise comparison generally has higher human-judge agreement because "which is better" is an easier judgment than "how good is this in isolation," and it maps naturally onto A/B testing between model versions. Absolute scoring is more useful when you need a stable metric to track over time or a fixed bar to gate releases against. Many mature eval setups use both: pairwise for model comparison, absolute decomposed scoring for ongoing quality monitoring.
What is the fastest fix if my judge shows length bias? Add an explicit rubric instruction that a shorter, equally correct answer should score at least as well as a longer one, then re-run your category-level calibration report specifically on the length-biased examples to confirm the fix worked before trusting it on the rest of the gold set.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.