LLM-as-Judge
Using a model to score outputs at scale. Bias, calibration, and reliability.
LLM-as-judge
LLM-as-judge uses a model to score the outputs of another model. It scales human judgement: instead of manually rating 1000 outputs, a judge model scores them. Used for faithfulness (is the answer supported by context?), relevance, helpfulness, and safety.
The judge must be DIFFERENT from the system under test (to avoid self-preference bias), prompted with clear rubrics, and calibrated against human ratings to trust its scores.
Why LLM-as-judge
Human eval doesn't scale — you can't rate every output. LLM-as-judge gives you continuous, cheap, scalable evaluation. But it has biases (self-preference, verbosity, position) and must be calibrated. Use it for triage and trending, with human review of disagreements and high-stakes cases.
Judge architecture
System output + context + rubric → judge model (different from system) → score + rationale → calibrate against human ratings → trust calibrated scores for trending, human-review disagreements.
Key design choices: judge model selection, rubric specificity, single-vs-pairwise scoring, and calibration cadence.
A faithfulness judge
from pydantic import BaseModel
class FaithfulnessScore(BaseModel):
score: float # 0.0 to 1.0
unsupported_claims: list[str]
rationale: str
JUDGE_PROMPT = """You are a strict faithfulness judge.
Given an answer and the context it was generated from, score how faithful the answer is.
Rules:
- A claim is faithful if it is directly supported by the context.
- A claim is unfaithful if it goes beyond the context (hallucination).
- Score 1.0 if every claim is supported. Score 0.0 if no claims are supported.
- List every unsupported claim.
Context:
{context}
Answer:
{answer}
Return JSON: {{score, unsupported_claims, rationale}}"""
def judge_faithfulness(answer: str, context: list[str]) -> FaithfulnessScore:
"""Use a DIFFERENT model than the system to avoid self-preference."""
ctx = "\n\n".join(context)
prompt = JUDGE_PROMPT.format(context=ctx, answer=answer)
r = client.chat.completions.create(
model="gpt-4o", # judge — different from system's gpt-4o-mini
response_format={"type": "json_object"},
messages=[{"role": "user", "content": prompt}],
temperature=0, # deterministic judging
)
return FaithfulnessScore.model_validate_json(r.choices[0].message.content)
def calibrate(judge_scores: list[float], human_scores: list[float]) -> float:
"""Correlation between judge and human — should be > 0.7 to trust."""
if len(judge_scores) != len(human_scores):
raise ValueError("Mismatched lengths")
# Pearson correlation
n = len(judge_scores)
mj = sum(judge_scores) / n
mh = sum(human_scores) / n
num = sum((j-mj)*(h-mh) for j,h in zip(judge_scores, human_scores))
den_j = (sum((j-mj)**2 for j in judge_scores) ** 0.5)
den_h = (sum((h-mh)**2 for h in human_scores) ** 0.5)
return num / (den_j * den_h) if den_j and den_h else 0.0Experiment: judge biases
See the known biases of LLM-as-judge and how to mitigate them.
What to observe
LLM-as-judge has known biases: self-preference (use a different model), verbosity (use a rubric), position (randomise + average). Mitigations are cheap and high-impact. Always calibrate against human ratings — correlation > 0.7 to trust.
Production judging
Production judge: different model from system, explicit rubric with examples, position randomisation for pairwise, temperature 0, calibration against human ratings every quarter, and human review of judge-system disagreements (where the judge and a human disagree, both learn).
Challenge
Your judge scores your system 0.92 average. Human raters score it 0.71. Diagnose the disagreement and fix the judge.
Production checklist
Production checklist
0 of 8 checked
Knowledge check
Your judge is the same model as your system. Scores are suspiciously high. What bias is this?
Complete
You can now build a calibrated LLM-as-judge. Next: online eval and regression testing in CI.
Mark this chapter as complete
Track your progress and unlock the next chapter.