Vol. 32 · Evaluation & Improvement
LLM-as-Judge Meta-Evaluation
A separate volume is not about eval in general, but about the validation of the judge itself: agreement with people, metrics for binary and ordinal verdicts, resistance to repeated runs, confidence calibration, bias audits, pairwise ranking and minimum reporting standard. Agreement → classifier view → stability → calibration → bias → reporting.
Frame: Judge should also be evaluated.
LLM-as-judge is convenient to use as a quick proxy for human eval, but that doesn’t make it a true metric. If you start to optimize the system for the judge without checking the judge itself, the model quickly learns to “like the appraiser” rather than really improve.
Source-complete noteswhen it is particularly important
when it is particularly important
- leaderboard
- production eval loop
- reward shaping for the judge score
- consider the single judge score to be true
Before you connect the judge to the pipeline, you need to go through a short, but mandatory validation loop. The idea is to first understand the ceiling of consent between people, then compare the judge not with one annotator, but with consensus or distribution of human labels.
Source-complete notesminimum · practice
text1. 100-200 examples: good/bad/borderline
2. Mark 2-3 people in the same category
3. The Inter-Human Agreement
4. Run the judge on the same set
5. Agreement Judge vs Human
6. Check stability, calibration and bias probes
If inter-human κ = 0.45 and judge-human κ = 0.43
The judge is close to the human ceiling.
If inter-human κ = 0.75 and judge-human κ = 0.38
Judge is weak, the problem is not the task, but the judgepractice
- compared to the human ceiling
- borderline cases
- Validate the judge on too easy examples
Agreement: How much does the judge match people
If the judge's verdict is binary, 'Cohen's κ' is almost always better than accuracy: it takes into account how much of the coincidences may have come about by chance. This is a good default for tasks like grounded/not grounded, safe/unsafe, pass/fail.
Source-complete noteshow to count · when to use
sqlfrom sklearn.metrics import cohen_kappa_score
human = [1, 0, 1, ...]
judge = [1, 1, 1, ...]
kappa = cohen_kappa_score(human, judge)
#human labels: from 2-3 experts or majority vote
# judge labels: from the same eval set, in the same orderwhen to use
- binary rubric
- judge vs consensus label
- Look at the confusion matrix
- Does not replace calibration and bias probes
For ordinal labels, the usual 'κ' is too rough: the error '5→4' and the error '5→1' are considered the same. 'Weighted κ' gives a partial penalty and usually better reflects the real quality of the judge on scales.
Source-complete notesformula · use · when appropriate
sqlWe need two ordinal ratings:
human = [5, 3, 4, ...]
judge = [4, 3, 5, ...]
We construct observed matrix O(i,j) and expected matrix E(i,j):
O = pair frequencies (human=i, judge=j)
E = expected frequencies from marginals
κ_w = 1 − (ΣᵢΣⱼ wᵢⱼ Oᵢⱼ) / (ΣᵢΣⱼ wᵢⱼ Eᵢⱼ)
Linear weights:
wᵢⱼ = |i−j| / (K−1)
Quadratic weights:
wᵢⱼ = (i−j)² / (K−1)²
Where to get the values:
K = number of scale levels
O and E = from confusion matrix by ratingssqlfrom sklearn.metrics import cohen_kappa_score
k_linear = cohen_kappa_score(human, judge, weights="linear")
k_quad = cohen_kappa_score(human, judge, weights="quadratic")
Practically:
if the adjacent levels are almost uniform
quadratic if long-range misses are particularly painfulwhen appropriate
- 1-4, 1-5, 1-10
- Only with clear anchor examples
- Not to use as a substitute for binary rubric
Once in the 3+ evaluator problem, 'Cohen's κ' no longer fits. 'Fleiss' κ' gives a chance-corrected agreement at the level of the entire annotator pool and shows well whether there is consensus at all.
Source-complete notesformula · wherein · scenario
texSay:
N = number of examples
n = number of appraisers for example
k = number of classes
nij = how many appraisers gave example i class j
Agreement within Example i:
1
Pᵢ = ------- · Σⱼ nᵢⱼ(nᵢⱼ−1)
n(n−1)
Average observed agreement:
P̄ = (1/N) · Σᵢ Pᵢ
Share of class j over the pool:
1
pⱼ = -------- · Σᵢ nᵢⱼ
N · n
Chance agreement:
P̄ₑ = Σⱼ pⱼ²
Fleiss κ = (P̄ − P̄ₑ) / (1 − P̄ₑ)textI need a table.
rows = examples
columns = raters
values = class label
Example:
tc_001 [PASS, PASS, FAIL]
tc_002 [FAIL, FAIL, FAIL]
tc_003 [PASS, PASS, PASS]
It is considered nij for each example.scenario
- 3+ human raters
- Comparison of several judge models
- I don't like messy missing labels.
'Krippendorff's α' is useful where the actual markup is not ideal: different types of scales, omissions, not all examples have the same number of annotators. In practical eval, it is often a better long-term choice than a zoo of different κ variants.
Source-complete notesformula · wherein · when
sqlα = 1 − D_observed / D_expected
Where:
D observed = actual disagreement between annotators
D expected = Disagreement expected by chance
Ordinal labels are usually defined as distance:
δ(c, c′) = (c − c′)²
Then great discrepancies are fined more.
as in quadratic weighted κ
If α → 1: almost complete consensus
If α → 0: no better than chancesqlWe need an item × rater matrix:
[
[1, 1, None, 0],
[2, 2, 2, 2],
[4, 3, 4, None],
]
from krippendorff import alpha
alpha_value = alpha(reliability_data=data, level_of_measurement="ordinal")when
- messy annotation pipeline
- partial
- more difficult to explain to the team from scratch
When a judge returns a score or a rank, there are three types of connection. For most LLM-eval tasks, ‘Spearman ρ’ is better than default. 'Kendall τ' is useful for the stability of order. Pearson r only makes sense if the score is close to the interval and you are interested in linear communication.
Source-complete notesHow to count and what to provide as input · table · conclusion
sqlfrom scipy.stats import pearsonr, spearmanr, kendalltau
pearson_r, _ = pearsonr(human_scores, judge_scores)
spearman_rho, _ = spearmanr(human_scores, judge_scores)
kendall_tau, _ = kendalltau(human_scores, judge_scores)
Human scores: average human score or rank for each example
Judge scores: judge score for the same examples
For the leaderboard:
human rank = rank of the system
judge rank = rank of the same system| Metrica | What meter | When you're good. | When dangerous |
|---|---|---|---|
| Pearson r | Linear communication | interval | ordinal scales 1-5 |
| Spearman rho | Monotonic rank | default for judge scores | distanceless |
| Kendall tau | Pairwise agreement | rank stability, leaderboard | less familiar |
conclusion
- Spearman as first choice
- Kendall for System Order
- Pearson only if there is a reason
Judge as a classifier
MCC is especially useful when positive class is rare: hallucination, unsafe answer, policy violation. Unlike accuracy and even F1, it takes into account all the confusion matrix and gives an honest picture of the quality of the judge in unbalanced tasks.
Source-complete noteswherein · particularly useful
sqlFirst you fix the positive class:
1 = unsafe, 0 = safe
Then on the same eval set:
y_true = human / gold labels
y_pred = hard verdict judge
from sklearn.metrics import matthews_corrcoef
mcc = matthews_corrcoef(y_true, y_pred)
If the judge gives you a probability:
y_pred = 1[p_i ≥ threshold]particularly useful
- safety eval
- hallucination detection
- rare negative cases
- Read with the Reference Dangerous Class
A judge who always says ‘PASS’ can look strong on a dataset where 90% of the examples are true pass. Precision can almost never be read alone.
Source-complete notesformulae · code · red flags
sqlaccuracy = (TP + TN) / N
precision_pos = TP / (TP + FP)
recall_pos = TP / (TP + FN)
specificity = TN / (TN + FP)
F1 = 2 · precision · recall / (precision + recall)
balanced_accuracy = (recall_pos + specificity) / 2
Where to get the values:
TP, TN, FP, FN = from judge verdict vs gold label
An example of accuracy cheating:
unsafe = 5%, judge always says SAFE
accuracy = 95%
recall_unsafe = 0%
balanced_accuracy = 50%sqlfrom sklearn.metrics import accuracy_score, precision_recall_fscore_support
from sklearn.metrics import balanced_accuracy_score
acc = accuracy_score(y_true, y_pred)
prec, rec, f1, _ = precision_recall_fscore_support(y_true, y_pred, average="binary")
bacc = balanced_accuracy_score(y_true, y_pred)red flags
- Accuracy without class balance
- No breakdown by FP/FN errors
- classify
If the judge returns the probability, you can rate it as a ranker. “ROC-AUC” shows overall separability, but in rare positive classes, it is often too optimistic. PR-AUC better reflects how the judge actually finds rare violations.
Source-complete notesreckon · table · rule
sqlfrom sklearn.metrics import roc_auc_score, average_precision_score
roc_auc = roc_auc_score(y_true, p_judge)
pr_auc = average_precision_score(y_true, p_judge)
Important:
Probabilities / confidences are provided here.
Not hard labels PASS/FAIL| Metrica | What shows | Best. |
|---|---|---|
| ROC-AUC | tradeoff TPR/FPR | class |
| PR-AUC | precision/recall positive | Rare problems, safety, hallucinations |
rule
- Rarely positive to watch PR-AUC
- ROC-AUC as secondary
Judge's not judging in a vacuum. Sometimes the false ‘PASS’ is the most dangerous, sometimes the false ‘FAIL’ turns the system into an over-refusal machine. Therefore, confusion matrix should be associated with the cost of error, not just a beautiful final metric.
Source-complete noteshow to build it · Example of cost-aware reading · do
textIf the judge gives confidence p:
y_pred = 1[p ≥ t]
Then you think:
judge=1 judge=0
human=1 TP FN
human=0 FP TN
The threshold t is not chosen for beauty.
a for the cost of errors FP and FNtextFaithfulness eval:
FN judge = missed hallucination
FP judge = punished with correct answer
Moderation eval:
FN judge = missed unsafe content
FP judge = blocked harmless response
Same accuracy.
may be acceptable in one case.
and unacceptable elsewheredo
- fix the target operating point
- Discuss FP and FN separately
- Optimize only the average metric
Stability & uncertainty
Even with ‘temperature=0’ and one model, the judge may be unstable due to long reasoning paths, backend sampling, or subtle prompt shifts. If the same example receives different verdicts, the point score becomes meaningless.
Source-complete notesHow to measure binary and score verdicts · wherein · signal
textFor each example i run judge K times:
vᵢ = [vᵢ₁, vᵢ₂, ..., vᵢₖ]
If verdict binary:
c pass = number of PASS
c fail = number of FAIL
self_agreementᵢ = max(c_pass, c_fail) / K
If the verdict numeric:
μᵢ = mean(vᵢ)
σᵢ = std(vᵢ)
MADᵢ = mean(|vᵢⱼ − μᵢ|)
Outcome on dataset:
mean_self_agreement = mean(self_agreementᵢ)
mean_sigma = mean(σᵢ)textSame thing:
prompt
context
answer
rubric
You run Ks with the same judge.
And you only keep your verdict/confidence.
dataset-freesignal
- Frequent flips on borderline cases
- You need abstain or uncertainty
- Average a few runs only if it is conscious
Convenient Instability Metric: How often a judge changes his mind when repeating the same task. In production, this is easier to communicate than abstract variance: “The judge contradicts itself by 14% of the examples.”
Source-complete notesformula · example · useful
texFor example i:
unstablei = 1, if unique(vi1 ... vik) > 1
= 0, otherwise
1
flip_rate = ----- · Σᵢ unstableᵢ
N
If the verdict numeric:
First, transfer it to the label.
e.g. PASS if score ≥ 4texttc_014 → PASS, PASS, FAIL, PASS, FAIL
unstable_014 = 1
tc_015 → FAIL, FAIL, FAIL, FAIL, FAIL
unstable_015 = 0
If 14 out of 100 examples are unstable,
flip_rate = 0.14useful
- Comparison of judge prompts
- comparison
- It is not a substitute for people.
If two systems judged on the same set of examples, you need to compare pairs. 'Paired bootstrap' gives a confidence interval for the difference in metrics and helps distinguish real improvements from sample noise.
Source-complete noteswherein · where must-have
textEach example requires an outcome for both systems:
example_id | score_A | score_B
tc_001 | 1 | 0
tc_002 | 1 | 1
tc_003 | 0 | 1
metric may be:
accuracy
pass_rate
mean judge score
win_rate
It's not tokens or answers that are being reassembled.
(a) Example indexeswhere must-have
- A/B on same eval set
- leaderboard deltas
- Compare point estimates without CI
Forced choice gives a beautiful appearance of certainty, but breaks meta-eval on really ambiguous examples. Sometimes the best judge is not the one who always answers, but the one who can send the case to the human review.
Source-complete notestwo useful formulae · three modes · whenever
texLet the judge give confidence p . [0.1].
coverage(τ) =
#(pᵢ ≥ τ) / N
selective_risk(τ) =
errors in examples with pi ≥ τ / #(pi ≥ τ)
Meaning:
increase the threshold τ
Coverage is falling
Selective risk should also fall.textHigh confidence:
auto-score
Medium confidence:
Consider, but mark as weak evidence
Low confidence / tie / unsure:
send out
This is especially important for rating indeterminacy.
borderline exampleswhenever
- ambiguous rubric
- pairwise ties
- Downstream Unsure Processing Logic
Calibration: Can you trust the judge? and
If a judge gives a probability of 'PASS', 'SAFE' or 'A wins', 'Brier Score' is almost always the first. This is a quadratic error of probabilistic prediction: both calibration and sharpness in one digit.
Source-complete noteswhere to get p and y · Good friend
sqlFor each example i:
pi = confidence judge that label = 1
yi = real binary label from human/gold
Example:
p = [0.91, 0.72, 0.10, ...]
y = [1, 0, 0, ...]
from sklearn.metrics import brier_score_loss
brier = brier_score_loss(y, p)
If the judge gives confidence 0-100,
split by 100 firstGood friend
- reliability diagram
- ECE
- You need the correct probability output
ECE compares confidence to its actual success rate in the basket of probability. If the judge says ‘90% confident’ and is right only 65% of the time, that’s overconfidence.
Source-complete notesfile · crucial
textŷᵢ = predicted judge label
Pi = confidence in this particular label
yᵢ = gold / human label
Usually:
M = 10 bins
or equal-width,
or equal-mass binningcrucial
- count
- point out
- Read more about ECE without a reliable plot
One number hides the form of the error. Reliability diagram shows where the judge overestimates himself: on high confidence, on medium or only in a narrow range. This is often more useful than any summary metric.
Source-complete notesconstruct · how to read · useful
text1. Break predictions by confidence bins
2. For each bin count:
mean_confidence
empirical_accuracy
3. Draw dots:
x = mean_confidence
y = empirical_accuracy
4. Add diagonal y=x as idealtextPerfect line:
confidence = empirical accuracy
Curve below diagonal:
judge overconfident
The curve above the diagonal:
judge underconfident
Separately useful to watch:
coverage by bins
borderline subset
hard cases onlyuseful
- threshold tuning
- abstain / human review policy
- show the team without a statistical background
Bias audits: where the judge is systematically distorted
In a pairwise score, the judge may prefer the first or second answer simply because of the position. If you do not do A/B swap, you can get a false leaderboard even with a strong judge model.
Source-complete notesreckon · wherein · protection
textjudge([A, B]) → verdict₁
judge([B, A]) → verdict₂
winrate(A first) = wins_A_when_first / pairs_with_A_first
winrate(A second) = wins_A_when_second / pairs_with_A_second
position_gap = |winrate(A first) − winrate(A second)|
swap_robust_winrate(A) =
0.5 · (winrate(A first) + winrate(A second))textFor each pair of answers, you need two launches:
first [A, B]
then [B, A]
Save:
winner
confidence
explanation
Large position gap
It depends on order, not just quality.protection
- Make sure to order swap
- Read Delta after swap
- One pairwise run without rotation
Judge often confuses length with quality: a detailed but empty answer begins to systematically defeat a short but accurate answer. This is especially dangerous in QA and enterprise copilots, where accuracy is more important than text impression.
Source-complete notestwo practical metrics · pairing · when it pops up
texFor pair i:
len_deltaᵢ = tokens(longer) − tokens(shorter)
pref longi = 1 if the judge chooses a longer answer
= 0, otherwise
long_win_rate =
Σ pref_longᵢ / N_pairs
You can still count:
corr(length_delta, judge_score_delta)
Perfect:
at matched-quality pairs
long_win_rate ≈ 0.5textBest audit set:
short and long answers,
comparable in human quality
Then if the judge consistently chooses a long one,
It's a verbosity bias.
Not a real difference in quality.when it pops up
- QA and support
- essay-style tasks
- consider verbosity bias to be “natural” behavior
A single-provider judge may be softer on the responses of models of the same ecosystem: the style, length, reasoning structure, and formulation template seem “naturally good.” This is particularly insidious in intermodel comparisons.
Source-complete notesbias score · How to experiment · practice
textLet's have a balanced pair:
answer_X_family vs answer_Y_family
Then for Judge X:
pref_X = wins_X_family / decisive_pairs
For cross-provider judge:
pref_X_cross = wins_X_family_cross / decisive_pairs
self_enhancement_gap =
pref_X − pref_X_cross
Big positive gap
Judge X overstates his family of modelssqlNeed:
matched prompts
A balanced set of responses from different families
at least two judges from different providers
It's not just absolute wins.
Change of leader when changing judgepractice
- cross-provider judging
- ensemble of judges
- Evaluate the closed model only its answers
If you rewrite the rubric or output format slightly enough, and the final scores are noticeably floating, then the judge is too sensitive to the prompt surface form. Such fragility then turns into erratic experiments and false regressions.
Source-complete notesreckon · wherein · helping
sqlThere are P equivalent prompt variants:
prompt_1 ... prompt_P
score_drift =
std(metric(prompt_1), ..., metric(prompt_P))
rank_drift =
max_rank_shift across prompts
prompt_flip_rate =
Examples where the verdict changes
when changing the wording of rubrictextSame dataset,
The same judge model,
Only word rubric changes
If the score drift is big,
Regression after prompt editing
Could be an artifact judge- andhelping
- fix the prompt version
- rubric examples
- Use robust prompt templates
Pairwise ranking & reporting
When two systems are compared on a single prompt, a pairwise verdict is often more stable than an absolute score on a scale. But it is important not to throw away draws: ties and "unsure" carry information about the indistinguishability of systems and the complexity of the case.
Source-complete notesformulas · wherein · practice
texSay:
W_A = wins(A)
W_B = wins(B)
T = ties
U = unsure
N = W_A + W_B + T + U
overall_win_share(A) = W_A / N
decisive_win_rate(A) = W_A / (W_A + W_B)
tie_rate = T / N
unsure_rate = U / N
If you want a significance test,
H₀: decisive_win_rate(A) = 0.5
binomtest(W_A, W_A + W_B, 0.5)textEach prompt judge returns one of:
A_WINS
B_WINS
TIE
UNSURE
Good practice:
Keeping raw verdict and confidence
Not just the final win rate.practice
- swap order for each pair
- tie off
- Keep an explanation for hard cases
As soon as there are many models, it is more convenient to switch from raw pairwise wins to a general power model. “Bradley-Terry” and “Elo” allow you to aggregate a grid of pairwise results, but without confidence intervals, such a rating is easy to interpret.
Source-complete notestwo working formulas · What to show with the rating · useful
texBradley-Terry:
exp(β_A)
P(A>B) = ----------------------
exp(β_A) + exp(β_B)
β A, β B = hidden forces of systems
Which are matched by pairwise wins
Elo:
E_A = 1 / (1 + 10^((R_B−R_A)/400))
R′_A = R_A + K · (S_A − E_A)
S A = 1 on win, 0.5 on tie, 0 on losstextranking score
bootstrap CI
Number of pairwise matches played
tie rate
position-bias audit
If the intervals overlap strongly,
The leaderboard is visually accurate.
what he really isuseful
- arena-style eval
- Many systems and few pairs per system
- Publish Elo without uncertainty
A properly documented judge-report should allow the other person to understand exactly what was measured, on what data, on what prompt, against what human ceiling, and with what limitations. Everything else quickly turns into an unreplicable number.
Source-complete notesresult
result
- replicability
- fair comparison of judges
- Update the report when changing the judge model
- Post only "correlates with humans"
No dead end
Keep moving through the map.
Continue in sequence, switch to a related guide, or return to the seven-track learning map.