A model grading another model is a measurement instrument with systematic biases — calibrate it against humans, or you are optimizing noise.
An LLM judge is a measurement instrument, not an oracle. You prompt a model to score or compare outputs against a rubric, and you get a cheap, flexible signal that correlates with human judgment better than n-gram metrics like BLEU — but only after you treat it like an instrument with a known error profile. It has three systematic, reproducible biases: it prefers options at certain rubric positions (position bias), it prefers longer answers (verbosity bias), and it prefers its own family's outputs (self-enhancement bias). The single number that tells you whether a judge is usable is its agreement with human labels on a held-out set; everything else — pairwise vs pointwise, balanced permutation, ensembling — exists to push that number up. And the moment a task has a checkable ground truth (does the code compile, does the number match, is the JSON valid), you should prefer a verifiable reward over a judge, because a deterministic check has zero bias and zero per-call cost.
The words first.
Step by step.
Remember this: a judge you have not calibrated against humans is a number generator, not a measurement.
Reference-based metrics break on open-ended text. BLEU and ROUGE measure n-gram overlap against a reference answer, so a correct paraphrase that shares no surface tokens scores near zero. Embedding metrics like BERTScore fix the paraphrase problem — it computes token-level cosine similarities between candidate and reference contextual embeddings and aggregates a greedy-matched precision/recall into an F1, correlating with human judgment around 59% versus roughly 47% for BLEU — but it still needs a reference and still can't tell you whether an answer is faithful, helpful, or safe. Those are properties of meaning, not overlap.
An LLM judge needs no reference for many criteria. You describe the quality dimension in the prompt and let the model reason about it. On faithfulness specifically, LLM judges show the highest correlation with human judgments among automatic metrics — they catch subtle entailment failures that token-overlap metrics miss. The price: the judge is non-deterministic, costs an API call per evaluation, and carries the biases below. Everything in this lesson is about paying that price intelligently.
Pointwise (absolute scoring): the judge sees one response and emits a scalar, e.g. "rate faithfulness 1–5." Simple to prompt, simple to aggregate (average the scores). But absolute scores have high calibration variance — what the judge means by "4" drifts across calls, across prompt phrasings, and across model versions. Two runs of the same judge on the same answer can disagree by a point.
Pairwise: the judge sees two responses and says which is better. This reduces calibration variance relative to absolute scoring because the judge only has to make a relative decision — "is A better than B" is an easier, more stable judgment than "is A worth exactly 4." You convert pairwise verdicts into a ranking (Elo, Bradley-Terry, or simple win-rate). The cost is combinatorial: ranking n candidates pairwise is O(n²) comparisons in the worst case, versus n for pointwise. For a leaderboard of 5 models you can afford all 10 pairs; for scoring 10,000 production traces you cannot, so you fall back to pointwise (often comparing each output against a single fixed baseline).
Rule of thumb: pairwise for model selection and A/B decisions (few candidates, you want a trustworthy ranking); pointwise for monitoring and CI gates (many items, you want a stable absolute threshold like faithfulness > 0.85).
Position bias. Rubric-based and pairwise evaluation both exhibit systematic position bias: the judge prefers options at specific positions independent of content. In a pairwise setup it may favor whichever answer is presented first; in a rubric with a numbered scale it may favor score options at the start or end of the list. This is an artifact of the implicit multiple-choice structure, not a content judgment. It is large enough to flip verdicts.
Verbosity bias. The judge systematically prefers longer responses, mistaking length for quality. Longer text gives the judge more phrases that look like supporting evidence, so it rationalizes a higher score. A wordy, padded answer beats a crisp correct one.
Self-enhancement bias. A judge disproportionately favors responses generated by itself or its own model family — an in-group preference. If you evaluate GPT-family and Claude-family candidates with a Claude-family judge, the judge tilts toward the Claude outputs for reasons unrelated to quality. This is the most dangerous bias in competitive evaluation because it silently corrupts cross-vendor comparisons.
The symbols. Say you compare answer A against answer B with a pairwise judge. p_first = the judge's probability of picking whichever answer is shown first, regardless of content. A perfectly fair judge has p_first = 0.5.
The measurement. Run the same A-vs-B comparison twice, swapping order. Suppose across 100 pairs of genuinely-equal answers the judge picks the first-shown one 64 times. Then p_first = 0.64 — a 14-point position bias.
The fix (swap-and-average). For each real comparison, query both orders:
Without swapping you would have recorded "A wins" and baked the position artifact straight into your leaderboard. With swapping, a verdict only counts as a win if it survives order reversal. For rubric scoring, the analogous move is balanced permutation: distribute each score option evenly across all list positions and average the scores across permutations.
What it did to the data. It converted a contaminated 64% win-rate into an honest tie, at the cost of 2x the judge calls. That doubled cost is the price of an unbiased number.
A judge is useful exactly to the degree it agrees with humans. So the calibration loop is:
The mitigations stack on top of calibration: balanced permutation for position bias, explicit anti-length instructions plus reference-based penalties for verbosity, and ensembling judges from different model families (and anonymizing the generation source) for self-enhancement. Ensembling also dampens the idiosyncratic variance of any single judge.
G-Eval is the standard way to turn "score this for coherence" into a reproducible procedure. It (1) states the task and criteria, (2) has the LLM generate explicit chain-of-thought evaluation steps from those criteria, (3) uses a form-filling paradigm where the model answers yes/no/unsure per step, and (4) weights the final score by token-level log-probabilities so the score is a smooth expected value rather than a single sampled integer. It is reference-free and customizable for any dimension (coherence, fluency, relevance, safety), which is why frameworks like DeepEval ship it as a core metric. The log-prob weighting is the clever part: instead of the model emitting "4" (high variance), you read the probability mass over each score and compute the expectation, which is more stable across calls.
A judge is the right tool when correctness is a matter of meaning with no closed-form check. It is the wrong tool when a deterministic verifier exists. If you can check the answer with a compiler, a unit test, a JSON-schema validator, an exact-match against a known key, a regex, or a calculator, do that instead. A verifier has:
The boundary is rarely all-or-nothing. For a product with mostly open-ended outputs, the move is to carve verifiable sub-claims out of the open-ended whole: in a RAG answer, faithfulness of individual factual claims can be checked by NLI entailment against the retrieved context (a classifier, near-deterministic) even though "is this answer helpful" stays a judge call. RAGAS scores faithfulness exactly this way — extract claims, verify each against context — turning part of a fuzzy judgment into a near-verifiable one. Push as much of the evaluation onto verifiers as the task allows, and reserve the judge for the irreducibly subjective remainder. This matters doubly when the judge feeds a training loop (RLAIF, reward modeling): a biased or hackable judge becomes a reward the policy will learn to exploit, so verifiable rewards are strongly preferred wherever the task admits them.
A production-shaped pairwise judge with swap-and-average position-bias correction, robust parsing, and a calibration harness. It uses Claude (Opus 4.8) as the judge.
import json
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY
JUDGE_MODEL = "claude-opus-4-8"
RUBRIC = """You are comparing two answers to the same question for FAITHFULNESS
to the provided context. An answer is faithful only if every factual claim it
makes is supported by the context. Penalize unsupported claims. Do NOT reward
length: a shorter answer that is fully supported beats a longer one that adds
unsupported claims.
Question: {question}
Context: {context}
Answer A: {a}
Answer B: {b}
Respond with ONLY a JSON object: {{"winner": "A" | "B" | "tie", "reason": "<one sentence>"}}"""
def _judge_once(question, context, ans_first, ans_second):
"""One pairwise call. Returns 'A', 'B', or 'tie' relative to (first, second)."""
prompt = RUBRIC.format(question=question, context=context,
a=ans_first, b=ans_second)
resp = client.messages.create(
model=JUDGE_MODEL,
max_tokens=512,
thinking={"type": "adaptive"}, # let the judge reason before deciding
output_config={ # constrain output to valid JSON
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"winner": {"type": "string", "enum": ["A", "B", "tie"]},
"reason": {"type": "string"},
},
"required": ["winner", "reason"],
"additionalProperties": False,
},
}
},
messages=[{"role": "user", "content": prompt}],
)
text = next(b.text for b in resp.content if b.type == "text")
return json.loads(text)["winner"]
def judge_pairwise(question, context, answer_x, answer_y):
"""Position-bias-corrected verdict for X vs Y. Runs both orders."""
# Order 1: X is shown first (as 'A'), Y second (as 'B')
v1 = _judge_once(question, context, answer_x, answer_y)
# Order 2: Y is shown first (as 'A'), X second (as 'B')
v2 = _judge_once(question, context, answer_y, answer_x)
# Translate each verdict into a vote for X.
def x_vote(verdict, x_is_a):
if verdict == "tie":
return 0.5
x_label = "A" if x_is_a else "B"
return 1.0 if verdict == x_label else 0.0
x_score = (x_vote(v1, x_is_a=True) + x_vote(v2, x_is_a=False)) / 2
# A verdict only "wins" if it survives order reversal.
if x_score > 0.5:
return "X"
if x_score < 0.5:
return "Y"
return "tie"
def cohens_kappa(judge_labels, human_labels):
"""Chance-corrected agreement. The number that decides if the judge is usable."""
n = len(judge_labels)
classes = set(judge_labels) | set(human_labels)
po = sum(j == h for j, h in zip(judge_labels, human_labels)) / n
pe = sum(
(judge_labels.count(c) / n) * (human_labels.count(c) / n)
for c in classes
)
return (po - pe) / (1 - pe) if pe < 1 else 1.0
if __name__ == "__main__":
# Calibration set: items YOU labeled by hand, ideally from production failures.
golden = [
{"question": "When was the contract signed?",
"context": "The agreement was executed on March 3, 2024.",
"answer_x": "It was signed on March 3, 2024.",
"answer_y": "It was signed in early 2024, likely after lengthy negotiation.",
"human": "X"}, # Y adds an unsupported claim about negotiation
# ... dozens more, sourced from real failure cases
]
judge = [judge_pairwise(g["question"], g["context"], g["answer_x"], g["answer_y"])
for g in golden]
human = [g["human"] for g in golden]
print(f"judge-human kappa = {cohens_kappa(judge, human):.3f}")The load-bearing parts: output_config.format forces schema-valid JSON so parsing never fails on a stray preamble; running both orders and counting a win only if it survives reversal kills position bias; and the cohens_kappa call is the gate — if that number is low, nothing else in your eval pipeline is trustworthy and you fix the rubric before shipping the judge. thinking: {type: "adaptive"} lets the judge reason (the G-Eval insight) without you hand-tuning a budget.
| Approach | Cost / item | Latency | Quality (human agreement) | Main failure mode |
|---|---|---|---|---|
| BLEU / ROUGE | ~0 | ~0 | Low (~47% corr.) | Blind to paraphrase; needs reference |
| BERTScore | ~0 (local model) | Low | Moderate (~59% corr.) | Still needs reference; no faithfulness signal |
| Verifiable reward (test/regex/exact) | ~0 | ~0 | Perfect where it applies | Only covers checkable claims |
| Pointwise LLM judge | 1 call | 1 call | Moderate-high after calibration | Calibration drift; verbosity bias |
| Pairwise LLM judge (swap-corrected) | 2 calls | 2 calls (parallelizable) | Higher, lower variance | O(n²) at scale; position bias if uncorrected |
| Ensemble of judges (3 families) | 3-6 calls | max of calls | Highest; dampens self-enhancement | Cost; needs aggregation logic |
Cost. At Opus 4.8 pricing ($5 / 1M input, $25 / 1M output), a judge call on a ~2K-token rubric+context plus a ~200-token verdict is roughly $0.015. Swap-correction doubles it; a 3-judge ensemble with swap is ~6x. For 10,000 daily production traces, a single pointwise pass is ~$150/day; a swap-corrected 3-judge ensemble is ~$900/day. Drive this down by routing the bulk of grading to a cheaper model (Haiku 4.5 at $1/$5 is ~5x cheaper) and reserving the expensive judge for disagreements or borderline scores, by caching the stable rubric/context prefix, and by batching (50% off, non-latency-sensitive).
Latency. A judge in a CI gate adds seconds per item; swap and ensemble add parallel calls, so wall-clock is the slowest single call if you fan out. A judge in the serving path (online guardrail) is usually a non-starter at the per-request budget — push it to async monitoring instead.
Quality. Calibrated pairwise > calibrated pointwise > uncalibrated anything > n-gram metrics. The biggest quality lever is not the method but the calibration: an uncalibrated state-of-the-art judge can be worse than a calibrated cheap one.
What changes at scale. Three things bite. (1) Cost scales linearly with traffic — you cannot afford pairwise-everything on millions of items, so pointwise-against-baseline plus sampling becomes mandatory. (2) The judge becomes a moving target — provider version bumps silently shift its behavior, so you need scheduled re-calibration against fresh human labels, treated as a recurring cost. (3) Reward hacking — if the judge gates training or prompt-optimization loops, the system under test learns to exploit the judge's biases (pad for the verbosity bias, phrase for the self-enhancement bias). At scale, prefer verifiable rewards wherever the task admits them, and hold out a human-labeled set the optimizer never sees to detect when your judge score and real quality have decoupled.
Flashcard. What single number decides whether an LLM judge is usable, and what makes it lie? Its chance-corrected agreement (Cohen's kappa) with human labels on a held-out set — raw accuracy lies on imbalanced data, so always correct for chance.
Next: Classification metrics — precision, recall, F1, and why accuracy lies on imbalanced data.