AI/ML Deep Dive: Bias & Calibration in LLM-as-Judge

Day 54 · 2026-07-11
For: engineers with coding experience, non-AI background · Level: Advanced
Engineering counterpart → super-individual D6: Eval Engineering (building a real evaluation pipeline)

Using an LLM to score another LLM's outputs (LLM-as-Judge) is already the default infrastructure for evaluation, reward models, RLHF, and agent self-critique. But the judge is not an impartial ruler—it is systematically pulled off by surface artifacts, and it favors itself. Today we make it clear: where these biases come from, why "let the model judge right vs. wrong" still holds in principle, and how to calibrate the judge back at the mechanism level.

Position / Verbosity / Wording BiasArtifact Biases

spurious correlationevaluation
One-line analogy

Picture a code reviewer who is supposed to judge only quality, but unconsciously rubber-stamps the PR that's listed first, or the one with more lines. That's not laziness—his judgment is contaminated by surface features unrelated to quality. Closer to your world: a query planner that shouldn't depend on join order, yet switches its execution plan just because you wrote table A first—the result depends on input arrangement it should ignore. LLM judges behave exactly like this.

Problem + mechanism

You want the judge to answer "is A or B the better answer." An ideal judge looks only at quality. But Zheng et al. 2023 (the MT-Bench paper) systematically measured three biases:

  • Position bias: swap the order of the same two answers and the verdict can flip. It prefers "what it saw first"—independent of prior quality;
  • Verbosity bias: longer, wordier answers are more likely judged "better," even with no extra useful information;
  • Self-enhancement: the judge favors outputs stylistically like its own (next card).

The root is next-token prediction: models absorb many spurious correlations from training—"earlier option" and "longer text" happened to often coincide with "the chosen one" in human-labeled data, so the model mistook correlation for causation. Those signals leak into its "quality judgment."

Position bias: same pair, order flipped
Ask "A vs B" → judge: A wins
Ask "B vs A" → judge: B wins (verdict flips!)
↑ A consistent judge gives the same winner both times; a flip = position is biasing

This is why a single naive LLM score is untrustworthy: you may be measuring "who's listed first," not "who's better." The first mechanism-level fix is order symmetrization—run both orders, and only count the verdict when the two agree.

Code
from anthropic import Anthropic
client = Anthropic()  # needs ANTHROPIC_API_KEY

def judge(question, first, second):
    # return only the winner label A / B
    prompt = f"Q:{question}\n\n[Answer A]{first}\n\n[Answer B]{second}\n\nReply only A or B:"
    r = client.messages.create(model="claude-opus-4-8", max_tokens=2,
        messages=[{"role":"user","content":prompt}])
    return r.content[0].text.strip()

# Order symmetrization: ask both orders, detect position bias
v1 = judge(q, ans_x, ans_y)   # X first
v2 = judge(q, ans_y, ans_x)   # Y first (label mirrors)
winner_1 = "X" if v1=="A" else "Y"
winner_2 = "Y" if v2=="A" else "X"
if winner_1 == winner_2:
    print("Robust winner:", winner_1)
else:
    print("Position bias! Call it a tie or send to human review")  # disagree = don't trust
Pitfall + practice
"If I score 1–10 instead of picking A/B, there's no position bias"—wrong. Verbosity bias remains: longer answers tend to score higher. Scoring adds a new problem—scores clump (the model gives almost only 7/8/9), collapsing discrimination. Bias isn't removed by changing the question format; it takes mechanism-level symmetrization + calibration.
📌 BigCat scenario: when you use an LLM to compare which of two prompt-variant outputs is better, always run both orders once each. Running it once with A happening to be first, you might build "A is better" on pure positional noise—one of the most hidden error sources in an AI power-user workflow.
Takeaway + question
💡 An LLM judge may be measuring quality—or "who's first, who's longer." The bias comes from spurious correlations learned by next-token prediction, not from the model "not trying."
🤔 Your last AI-assisted "pick one of two" decision: if you swapped the two options and asked again, would the conclusion still hold?

Self-Preference & Correlated ErrorsSelf-Preference

biasfailure domain
One-line analogy

Letting the same person who wrote the code write its unit tests—they'll test exactly the cases they already thought of and miss their blind spots, because the bug and the tests share one mental model. More precisely, in distributed systems: if a primary and its replica run the same buggy firmware, their failures are correlated—the replica can't independently validate the primary, because both fail at the same place. When the "model that generated the answer" and the "model acting as judge" are the same (or same-family), that is this same failure domain.

Problem + mechanism

Zheng et al. observed GPT-4 as judge gives its own outputs about a 10% higher win rate. This isn't mere "vanity." Panickssery et al. 2024 (LLM Evaluators Recognize and Favor Their Own Generations) gives the mechanism:

  • Models can recognize which text they wrote themselves at well-above-chance accuracy (self-recognition)—style and word-distribution are a recognizable "signature";
  • More critically, they found a linear correlation between self-recognition ability and self-preference strength—the better a model recognizes itself, the more it favors itself. The preference is driven by this measurable recognition signal.

This leads directly to correlated errors: if judge and evaluatee share architecture/training data, their blind spots overlap—the judge is exactly blind to the kind of errors the generator makes, because it would make them too. The "pass" it issues is false safety: not "the answer is right," but "two same-model instances erred at the same place together." Using one model for "generate + self-judge" is essentially letting a firmware bug sign its own health certificate.

Same vs. independent failure domain

gen:Model-Xjudge:Model-X overlapping blind spots → correlated errors, self-favor
gen:Model-Xjudge:Model-Y different family → errors decorrelate, more independent
Code
# Measure self-preference: same answers, compare "self" vs "other" judging
def win_rate_for(judge_model, my_ans, other_ans, q):
    wins = 0
    for a, b, question in zip(my_ans, other_ans, q):
        v = judge_with(judge_model, question, a, b)  # order-symmetrized
        wins += (v == "mine")
    return wins / len(q)

# Judge with itself vs. a different-family model as judge
self_rate  = win_rate_for("model-X", x_ans, y_ans, q)  # X judges X's answers
neutral    = win_rate_for("model-Z", x_ans, y_ans, q)  # neutral third party
print(f"self win {self_rate:.2f} vs neutral {neutral:.2f}")
# if self_rate >> neutral → self-preference exists, don't trust self-judging
Pitfall + practice
"I generate with the strongest model X, then have the same X score it as a double check"—the opposite is true. When generation and verification share a source, verification can't catch generation's systematic errors (correlated errors), plus self-preference stacks on top. The gatekeeper model must come from a different failure domain to mean anything—just as you wouldn't validate a primary with the same firmware.
📌 BigCat scenario: when building an "AI drafts → AI reviews" pipeline, use different-family models for writing and reviewing (Claude writes, GPT reviews, or vice versa). One model self-reviewing is endorsing its own blind spots—the review step will miss exactly what it should catch.
Takeaway + question
💡 Self-preference isn't vanity—it's driven by measurable self-recognition; same-source "generate+verify" correlates errors and turns gatekeeping into a rubber stamp.
🤔 Which of your AI workflows have "one model both does the work and signs off"? Would the conclusion change with a different-family reviewer?

Generation-Verification GapGV-Gap

foundationself-improvement
One-line analogy

Given all these biases and imperfect generation, why trust the model as a judge at all? The answer hides in an intuition you know: verifying a solution is usually easier than finding it—the everyday version of P vs NP. Checking a Sudoku is instant; solving it is hard. Same in backend: writing an assert / validator is far cheaper than writing the correct implementation; a linter catches errors in correct code it could never write. "Verification is easier than generation" is the bedrock that makes LLM-as-Judge possible.

Problem + mechanism

Formalized: the Generation-Verification Gap (GV-Gap) = how much performance improves when you re-weight a batch of the model's generations by its own verification scores. Intuition: sampling one answer directly has accuracy p_gen; but generating N and using "verification ability" to pick the best raises it to p_ver. GV-Gap = p_ver − p_gen, measuring how much the model's "reviewing" precision exceeds its "writing" precision.

This gap is the engine behind Best-of-N, reward models, self-refine, and o1-style reasoning—generate a lot, then filter/rank with a verification signal. Song et al. 2024 (Mind the Gap: Examining the Self-Improvement Capabilities of LLMs, arXiv 2412.02674) proposes GV-Gap as a core metric of self-improvement, and finds a variant of it scales monotonically with pretraining compute—bigger models "review" relatively better than they "write."

Same model, two usages, accuracy
generate 1 directly p_gen
generate N + self-verify pick best p_ver
↑ the difference = GV-Gap = the value "being a judge" can provide

But the gap is not universal. For hard-enough reasoning, the model can't verify its own errors—judging its own blind spots, GV-Gap collapses to 0 or negative (this is why self-refine often "makes things worse"). When GV-Gap > 0 is exactly the boundary of "when LLM-as-Judge is trustworthy": big gap → the judge is useful; collapsed gap → it degrades into the correlated errors from the previous card.

Code
# Estimate GV-Gap: accuracy of generation vs. "self-verify pick best"
def gv_gap(model, tasks, N=8):
    p_gen, p_ver = 0, 0
    for t in tasks:
        cands = [generate(model, t.q) for _ in range(N)]
        # generation baseline: correctness of just the first
        p_gen += t.is_correct(cands[0])
        # verify-pick-best: model scores each candidate, take the top
        scores = [verify_score(model, t.q, c) for c in cands]
        best = cands[scores.index(max(scores))]
        p_ver += t.is_correct(best)
    n = len(tasks)
    return p_ver/n - p_gen/n   # >0 means "review" beats "write" → judging is meaningful
Pitfall + practice
"If the model can generate, it can surely judge whether its own generation is right"—wrong. Generation and verification are two abilities; the gap can be zero. Especially on hard problems the model can't do anyway, self-judging is nearly a coin flip—self-refine often turns right answers wrong here. Before trusting a judge, estimate GV-Gap on a labeled sample.
📌 BigCat scenario: when you ask an AI to "check it yourself before giving it to me," split by case—verification-type tasks (does this code have a syntax/logic hole, does this summary drift from the source) have a large GV-Gap, self-check is worth it; hard-solve problems (ones it couldn't answer well anyway) barely benefit—use a stronger model or external verification, not endless self-doubt.
Takeaway + question
💡 The bedrock of LLM-as-Judge is "verification easier than generation"; when the gap collapses, the judge degrades into self-endorsement.
🤔 Among tasks you hand to AI for self-check, which are "easy to verify" (worth self-checking) and which are "it can't do anyway" (self-check = waste)?

Panel Aggregation & CalibrationPanel & Calibration

consensuscalibration
One-line analogy

Don't trust a single node. The old distributed wisdom: use cross-failure-domain replica voting (quorum) to resist correlated failures—N-version programming has different teams independently implement one spec, then vote, because independent implementations won't fail at the same place. Treat "different-family LLMs" as replicas from different failure domains, have each score and then aggregate—that's canceling the prior three cards' biases with diversity. Also calibrate the "gauge": if the judge's "9" doesn't correspond to a real 90% correctness, that number is a broken scale.

Problem + mechanism

A single judge carries all of position bias, verbosity bias, self-preference, and correlated errors. Two mechanism-level fixes:

  • ① Panel of judges (PoLL): Verga et al. 2024 (Replacing Judges with Juries) uses 3 smaller models from different families voting, not one big model. The core: different families' biases are uncorrelated and cancel on aggregation (like decorrelated replica failures); the result is closer to human judgment, has less intra-model bias, and is several times cheaper than one big judge. Diversity > single-point compute;
  • ② Calibration: raw judge scores aren't probabilities—"9/10" ≠ "90% likely correct." Calibration learns a mapping from "judge score" to "true accuracy" on a batch of gold-labeled samples (with order symmetrization removing position bias). Uncalibrated scores can only be compared, not used as confidence.
Panel aggregation (different family = different failure domain)

candidate judge A (Claude) judge B (GPT) judge C (Gemini)
vote / average aggregate verdict
↑ uncorrelated biases → cancel on aggregation; a single judge amplifies them

Note diversity is the prerequisite: if the panel is all same-family models (or one model run thrice), correlated errors aren't solved—three replicas fail together, and voting just casts the same bias three times. You want decorrelation, not count.

Code
# Diverse jury: different-family models each score, aggregate by vote
JURY = ["claude-opus-4-8", "gpt-judge", "gemini-judge"]  # key: different families

def panel_verdict(q, ans_a, ans_b):
    votes = []
    for m in JURY:
        # each judge order-symmetrizes to remove position bias
        v1 = judge_with(m, q, ans_a, ans_b)
        v2 = judge_with(m, q, ans_b, ans_a)
        if v1 == v2:            # count only if both orders agree
            votes.append(v1)
    if not votes:
        return "tie"          # all position-unstable → call it a tie
    # majority vote: different-domain biases cancel here
    return max(set(votes), key=votes.count)
Pitfall + practice
"More judges voting is always more accurate"—only when the judges are independent. One model run thrice, or three same-family models, share one bias set; voting just casts the bias three times and hands you false "majority passed" confidence. What actually works is failure-domain diversity—the same principle as "three replicas must sit on different racks."
📌 BigCat scenario: for important decisions (having AI assess a plan / code / an investment thesis), don't ask just one model. Have 2–3 different-family models assess independently and see where they agree and where they diverge—the divergence points are often where the real uncertainty lives, more informative than any single "9/10."
Takeaway + question
💡 Canceling judge bias relies on "cross-failure-domain diversity voting + calibration," not a single stronger judge; diversity > count.
🤔 The "AI scores" you trust—are they calibrated confidence, or just numbers with no scale? Could you cross-validate with a different-family model next time?
📎 Engineering counterpart → super-individual D6: Eval Engineering

Further Reading

Deep Questions

1. Position bias (card 1) and correlated errors (card 2) seem unrelated, yet both are facets of "spurious correlation." Can one mechanism explain both?
Yes—both are next-token prediction mistaking correlation for causation, just with different correlation sources. Position bias: in the corpus, "earlier option" and "the chosen one" are statistically correlated (people often put the better one first), and the model learned that shortcut. Self-preference: model outputs carry a recognizable "style signature," and its familiarity with that signature correlates with the preference score it gives (Panickssery's linear relation), so "looks like mine" is mistaken for "written well." The shared cure is also aligned: break the spurious causal path—position bias via order symmetrization (eliminating the "order" variable), correlated errors via cross-family diversity (making the "style signature" ineffective across judges). Deeper: LLM judge biases fundamentally exist because the model was never cleanly trained on the causal variable "quality", but on surface features entangled with it. Debiasing means cutting those correlations at the data level, or intervening causally at inference (symmetrization, decorrelated aggregation).
2. GV-Gap (card 3) says "verification easier than generation" is the foundation; card 2 says same-source verification correlates errors. Do these two contradict?
No—the direction of "stronger" differs. GV-Gap is about tasks within the model's ability: verification (discriminative, small search space) has higher average precision than generation (generative)—consistent with P/NP intuition. Correlated errors are about the model's blind spots, where verification and generation share the same systematic error—a problem the model can't do, it also can't tell it got wrong. So GV-Gap is not constant across difficulty: positive on easy/medium tasks (self-check useful), collapsing to 0 or negative near the ability boundary (self-refine makes it worse). Engineering implication: don't ask "can the model self-judge," ask "how much GV-Gap is left at this difficulty." Give verification-type tasks to self-judging; hard problems need a stronger model or external ground truth—which is exactly why reward models and unit tests exist: they provide a verification signal independent of generation ability that the model itself can't reach.
3. The "cross-failure-domain voting" analogy to distributed consensus is neat, but distributed replicas fail via hardware randomness; how real is an LLM jury's "independence"?
Discount the analogy. N-version programming's independence comes from different teams independently implementing, with very low failure correlation. An LLM jury's "different families" are far less independent: Claude / GPT / Gemini differ in architecture, data, alignment details, but all train on heavily overlapping internet corpora, all use Transformers, all undergo similar human-preference alignment—so their biases are partly correlated, not independent. Consequence: a jury cancels family-specific bias (a model's style preference), but not industry-wide bias (everyone eats verbosity bias). That's why PoLL improves but can't "solve"—it lowers bias from "one model's big bias" down to "the industry's common bias," a residual floor. To break through, bring in truly heterogeneous signals: human labels, executable tests, retrieved facts—whose failure domain barely overlaps with LLMs'. BigCat's distributed intuition is both key and trap here: structurally isomorphic, but the independence assumption is far less clean than hardware replicas.
4. If LLM judges align to human preference, and human preference is itself biased (also eats verbosity, also has position bias), is "closer to human" a virtue or a way to bake human bias into the eval system?
This is the deepest worry. The gold standard for the whole stack is "agreement with human judgment" (Zheng reports GPT-4 >80% agreement with humans, about human-human agreement level), but it hides a dangerous assumption: human judgment = truth. The problem is human raters themselves have verbosity bias, position bias, and reward fluent-but-wrong content. When we train a judge to fit human preference, we learn not just "what a good answer is" but also faithfully replicate human systematic bias, possibly amplified (models execute a bias more consistently than people). So "closer to human" may mean "more reliably making the errors humans make." Same thread as Day 26 alignment, Day 48 calibration: the ceiling of alignment quality = the ceiling of your preference-signal quality. The way out is diversifying verification signals: for decidable tasks (code, math, facts) use objective ground truth rather than human preference; for subjective tasks, at least be aware you're optimizing "humans find it good," not "it's actually good." Eval bias also feeds back into the model via RLHF—a closed loop worth watching.
5. Putting the four cards together: when should you not use LLM-as-Judge at all, and fall back to dumber but harder evaluation?
Use a "gap × independence" 2D criterion. GV-Gap: is verification easier than generation? Independence: can you find a judge from a different failure domain? (a) High gap + independent judges ("does this code have obvious bugs," use a heterogeneous jury + order symmetrization + calibration)—the sweet spot, use freely. (b) Objective ground truth exists (math answers, does code pass tests, can facts be retrieved/checked)—don't use an LLM judge; use execution / retrieval / formal verification directly—cheaper, zero bias, reproducible; using an LLM to judge "2+2=4" is backwards. (c) Low gap (frontier problems the model can't do anyway)—self-judging is near coin-flip; fall back to human experts or admit "not measurable now." (d) Low independence and no ground truth (only same-source models judging subjective quality)—most dangerous, the conclusion has almost no information; bring in an external anchor, or explicitly label it "untrustworthy." In one line: LLM-as-Judge is a tool with a domain of validity, not a universal ruler. Judgment lies precisely in knowing when it shouldn't take the stage—the core of "AI power-individual": not outsourcing all judgment to the model, but knowing which judgments it can be trusted for and which must be backstopped by you or a harder mechanism.