The LLM-as-Judge Pattern
Understand how to prompt a strong LLM to score or compare outputs on criteria like correctness, helpfulness, and tone, and why this scales better than human evaluation.
The LLM-as-Judge Pattern is a free AI Engineering Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Engineering Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Automated Evaluation Is Necessary
Manually evaluating LLM outputs is slow and expensive. A team of human raters can evaluate a few hundred outputs per week, but a production system generates thousands per day. LLM-as-judge uses a powerful language model to evaluate the quality of other LLM outputs at scale, enabling automated regression testing and continuous quality monitoring without hiring an army of annotators.
The Core Idea: One LLM Evaluates Another
In LLM-as-judge, you send a system prompt defining evaluation criteria, the original question, the model's answer, and optionally a reference answer to a judge model (typically GPT-4o or Claude). The judge returns a numerical score, a label, or a ranking. This works because state-of-the-art models have sufficient understanding of quality concepts like correctness, helpfulness, and coherence.
JUDGE_SYSTEM_PROMPT = '''
You are an expert evaluator of AI-generated responses.
Given a question and an AI-generated answer, score the answer on:
- Correctness (0-5): Is the information factually accurate?
- Completeness (0-5): Does it fully address the question?
- Clarity (0-5): Is it easy to understand?
Return a JSON object with scores and a brief rationale.
'''Writing a Judge Prompt
A good judge prompt specifies explicit rubrics with score definitions rather than vague terms like 'good' or 'bad'. Define what a score of 5 vs 3 vs 1 means concretely for each criterion. Provide the question, the answer being judged, and optionally a reference answer. Ask for reasoning before the score (chain-of-thought) to reduce arbitrary scoring.
def build_judge_prompt(question: str, answer: str, reference: str = None) -> str:
ref_section = f'Reference answer:\n{reference}\n\n' if reference else ''
return f'''
Question: {question}
{ref_section}Answer to evaluate:
{answer}
Score this answer on correctness (1-5) where:
5 = Completely accurate, no factual errors
3 = Mostly accurate with minor errors
1 = Contains significant factual errors
First explain your reasoning, then provide the score as JSON:
{{"correctness": <1-5>, "rationale": "..."}}
'''Calling the Judge Model
Call the judge model with your evaluation prompt. Parse the JSON response to extract scores. Always use structured outputs or JSON mode to ensure the judge returns parseable data. Using the same model as both the system under test and the judge can introduce bias — prefer a different model or at least a different configuration for the judge.
from pydantic import BaseModel
import instructor
from openai import OpenAI
class JudgeScore(BaseModel):
correctness: int
completeness: int
clarity: int
rationale: str
judge_client = instructor.from_openai(OpenAI())
def judge(question: str, answer: str) -> JudgeScore:
return judge_client.chat.completions.create(
model='gpt-4o', # Use stronger judge than the model being tested
response_model=JudgeScore,
messages=[
{'role': 'system', 'content': JUDGE_SYSTEM_PROMPT},
{'role': 'user', 'content': build_judge_prompt(question, answer)}
]
)Reference-Free vs Reference-Based Judging
There are two modes of LLM judging. Reference-free judging asks the judge to evaluate quality without a ground truth answer — useful when ground truth does not exist, like in open-ended chat. Reference-based judging provides a gold-standard answer and asks whether the model's answer matches it — better for factual question answering where a correct answer is known. Use reference-based when you have a labeled test set.
# Reference-free: good for open-ended generation
judge_result = judge(question='What is machine learning?', answer=model_answer)
# Reference-based: better for factual QA
judge_result = judge(
question='What year was Python created?',
answer=model_answer,
reference='Python was created by Guido van Rossum and released in 1991.'
)Controlling Judge Bias
LLM judges have known biases: they prefer longer answers (verbosity bias), answers that sound confident, and answers that match the style of their training data. Mitigate verbosity bias by explicitly penalizing unnecessary length in your rubric. Reduce position bias in pairwise comparison by randomizing which answer appears first and averaging the scores from both orderings.
# Anti-verbosity note in rubric:
ANTI_VERBOSITY_CLAUSE = '''
Note: A concise, accurate answer should score higher than a long,
rambling answer that happens to contain the correct information.
Do not reward length for its own sake.
'''
# Position-debiasing for pairwise comparison:
async def debiased_pairwise(q, a, b):
score_ab = await compare(q, answer_a=a, answer_b=b)
score_ba = await compare(q, answer_a=b, answer_b=a)
# A wins if it wins in both orderings
a_wins = (score_ab == 'A' and score_ba == 'B')
return 'A' if a_wins else 'B' if (score_ab == 'B' and score_ba == 'A') else 'tie'Batching Evaluations for Speed
Run judge evaluations in parallel to evaluate large test sets quickly. With asyncio and a semaphore, you can evaluate hundreds of outputs per minute. Keep a separate rate limit budget for judge calls (they use tokens too) and consider using a smaller but still capable judge model like GPT-4o-mini for criteria that do not require deep reasoning, saving GPT-4o for the most critical criteria.
import asyncio
async def batch_judge(qa_pairs: list, concurrency: int = 20) -> list:
sem = asyncio.Semaphore(concurrency)
async def judge_one(item):
async with sem:
return await async_judge(item['question'], item['answer'])
return await asyncio.gather(
*[judge_one(item) for item in qa_pairs],
return_exceptions=True
)Aggregating Judge Scores
After evaluating a test set, aggregate scores into summary statistics: mean, median, and the percentage of responses scoring above a quality threshold (such as correctness ≥ 4). Compare these aggregates between model versions or prompt variations. A drop of more than 5% in the above-threshold rate should trigger a review before deploying the new version.
import statistics
def summarize_judge_results(scores: list) -> dict:
correctness = [s.correctness for s in scores if isinstance(s, JudgeScore)]
return {
'n': len(correctness),
'mean_correctness': round(statistics.mean(correctness), 2),
'median_correctness': statistics.median(correctness),
'pct_above_4': round(100 * sum(1 for s in correctness if s >= 4) / len(correctness), 1)
}Comparing LLM Versions with Judge
Use LLM-as-judge to compare two versions of your system — for example, before and after a prompt change. Run both versions on the same set of test questions, judge all outputs, and compute the win rate: the percentage of cases where version B outscores version A. A win rate above 55% on 100+ samples is typically statistically significant enough to justify shipping the new version.
async def ab_compare(test_questions: list, version_a, version_b) -> dict:
a_wins = b_wins = ties = 0
for q in test_questions:
answer_a = await version_a.answer(q)
answer_b = await version_b.answer(q)
winner = await debiased_pairwise(q, answer_a, answer_b)
if winner == 'A': a_wins += 1
elif winner == 'B': b_wins += 1
else: ties += 1
total = len(test_questions)
return {'a_win_rate': a_wins/total, 'b_win_rate': b_wins/total, 'tie_rate': ties/total}Calibrating Judge Scores Against Humans
Validate your judge by comparing its scores to human judgments on a calibration set of 50-200 examples. Compute Pearson correlation between judge and human scores. A correlation above 0.7 indicates the judge is reliable. If correlation is low, audit the judge prompt to see where it disagrees with humans and add examples or clarifications to the rubric. Never deploy a judge without calibration.
from scipy.stats import pearsonr
def calibrate_judge(human_scores: list, judge_scores: list) -> dict:
corr, p_value = pearsonr(human_scores, judge_scores)
mean_abs_error = sum(abs(h - j) for h, j in zip(human_scores, judge_scores)) / len(human_scores)
return {
'pearson_r': round(corr, 3),
'p_value': round(p_value, 4),
'mean_abs_error': round(mean_abs_error, 2),
'reliable': corr >= 0.7
}When Not to Use LLM-as-Judge
LLM-as-judge is not appropriate for every evaluation scenario. Avoid it when: the criterion requires domain expertise the judge model lacks (medical diagnosis accuracy, legal compliance), when you need legally defensible evaluation (human review is required), when evaluating a model stronger than the judge (the judge cannot reliably score output it could not produce), or when the evaluation budget is too tight for the additional API cost. In these cases, use human evaluation or deterministic metrics.
# Appropriate uses of LLM-as-judge:
# YES: General helpfulness, clarity, tone, factual accuracy (general knowledge)
# YES: Code correctness for common languages
# YES: Translation quality comparison
# YES: Content safety classification
#
# NOT appropriate:
# NO: Medical/legal/financial accuracy (needs domain expert)
# NO: Evaluating GPT-4o with GPT-4o (same capability ceiling)
# NO: Formal compliance audits (non-deterministic judge)
# NO: Streaming quality at individual token levelQuick Check
Test your understanding of the LLM-as-judge evaluation pattern.
Lesson Recap
In this lesson you learned: LLM-as-judge uses a strong model to evaluate quality at scale with explicit rubrics, reference-free and reference-based modes suit different evaluation scenarios, and calibration against human judgments validates that your judge is reliable before using it in production. Next up we implement pointwise and pairwise evaluation strategies.
Frequently asked questions
Is the “The LLM-as-Judge Pattern” lesson free?
Yes — the full text of “The LLM-as-Judge Pattern” is free to read here on the web, and the AI Engineering Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Engineering Academy course, upgrade to CoddyKit PRO.
What will I learn in “The LLM-as-Judge Pattern”?
Understand how to prompt a strong LLM to score or compare outputs on criteria like correctness, helpfulness, and tone, and why this scales better than human evaluation. You practise AI Engineering Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start AI Engineering Academy?
No prior experience is required. AI Engineering Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “The LLM-as-Judge Pattern” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this AI Engineering Academy lesson?
Yes. Every AI Engineering Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.