LLM as a judge: evaluating answers
You'll build a script that compares two models (or two prompts) on your own questions: a strong judge model grades the answers, and the script sums them up in a table.
You need: a fetch_... key (create one), a balance (50 questions ≈ 10 cents), Python 3.10+, 20–100 real customer questions with references, short correct answers.
- 1Collect the questions
Take real questions from your support chat or inbox, easy and tricky ones. For each, write a reference: what the answer must contain.
- 2Pick the judge
The judge is stronger than the candidates and not one of them:
claude-sonnet-5-5orgpt-6.1-sol. - 3Check the rubric
It lives in
GRADE_SYSTEM: correctness, completeness, clarity on a 1–5 scale. If "never promise what the rules don't say" matters to your business, add it as a separate criterion. - 4Run it
Copy the code into
judge.pyand replaceRULESandDATASETwith your own. Runpip install openai pydantic, thenpython judge.py. - 5Check the judge against yourself
Open
judge_results.jsonland grade 10–20 answers yourself. If you often disagree with the judge, refine the rubric or the references instead of trusting the table.
1Code
The example compares deepseek-v4.1-flash and claude-haiku-5-5 as a store support agent. The judge is claude-sonnet-5-5.
"""
LLM as a judge: which of two models answers store customers better.
pip install openai pydantic
python judge.py
"""
import asyncio
import json
import os
import statistics
from typing import Literal
from openai import AsyncOpenAI
from pydantic import BaseModel, Field, ValidationError
You'll get a table like this (the numbers depend on the models and questions):
Model Score Corr Compl Clar Wins Losses Ties
deepseek-v4.1-flash 4.20 4.40 4.00 4.60 1 2 2
claude-haiku-5-5 4.40 4.60 4.20 4.60 2 1 2
The judge changed its mind when the answers were swapped: 1 of 52Reading the table
- The judge compares each pair twice, with the answers swapped. A win counts only if it picked the same candidate both times.
- If the judge changed its mind on more than a third of the questions, the candidates are too close or the rubric is vague.
- On 5 questions a 0.2-point gap is noise. For a decision use 30–50 questions and look at pairwise wins.
3Keep in mind
X-Zukko-Cache: true: identical requests are served from the cache for free for 7 days. Change the rubric and you pay only for the new judge requests. Need fresh candidate answers? SetCACHE = {}.- Don't set
temperature: newer Claude models may reject non-default values.
4If it doesn't work
| What you see | What to do |
|---|---|
ValidationError from the judge even after the retry | Tighten the format in the judge's prompt or use a stronger model |
| Both candidates get nearly the same scores | Add tricky questions and grow the set to 30–50 |
| The judge often changes its mind after the swap | Sharpen the criteria; look at the rubric scores |
| A re-run returns the same candidate answers | That's the cache. Need fresh ones? Set CACHE = {} |
| A made-up answer gets a high score | Complete the reference; add a "nothing made up" criterion |