LLM as a Judge with Rubrics
Using a Model to Grade
For open-ended answers, a strong model can grade outputs against a rubric: clear criteria with pass/fail or a small 1-5 scale. Ask for a short reason before the score, and get the result as structured output.
Why does this work at all? Judging an answer is usually easier than writing one. Checking whether "every fact in this answer appears in these documents" is a reading task with all the information on the page, while answering needed retrieval and reasoning. It is like a teacher marking an exam with a marking scheme: the teacher may not have written that exact answer, but can reliably tell whether it hits the points in the scheme.
The rubric is the marking scheme. Good rubrics break quality into separate, concrete criteria (faithful, answers the question, polite), each with a definition the judge can apply. Asking for the reason first makes the judge read and compare before committing to a score, and the reason helps you debug disagreements. Structured output (a Pydantic model with messages.parse) means you get fields you can count, not text you must parse.
| Term | Meaning |
|---|---|
| Faithfulness / groundedness | every claim is supported by the provided context |
| Pointwise grading | score one answer against the rubric |
| Pairwise comparison | show two answers, ask which is better |
| Judge calibration | checking judge scores against human grades on the same cases |
A Judge with Claude
from pydantic import BaseModel
import anthropic
client = anthropic.Anthropic()
class Grade(BaseModel):
reason: str
faithful: bool # every claim is supported by the provided docs
answers_question: bool
polite: bool
score: int # 1-5
RUBRIC = """Grade the support answer.
faithful: every fact appears in <docs>. answers_question: it resolves the customer's question.
polite: friendly, no blame. score: 1 (bad) to 5 (excellent). Give the reason first."""
def judge(question, docs, answer) -> Grade:
resp = client.messages.parse(
model="claude-opus-5-5",
max_tokens=1000,
system=RUBRIC,
messages=[{"role": "user", "content":
f"<docs>{docs}</docs>\n<question>{question}</question>\n<answer>{answer}</answer>"}],
output_format=Grade,
)
return resp.parsed_outputSample output
python judge.pyExample output
Grade(reason='States the 7-day replacement from the docs but invents a free pickup charge waiver that is not in the docs.', faithful=False, answers_question=True, polite=True, score=3)
Example
Example
The aggregation side of a rubric grader. fake_judge is a stand-in for the judge model; the code turns per-criterion results into pass rates you can track over time.
CASES = [
{"id": "ret-001", "answer": "You can get a 7 day replacement for earbuds."},
{"id": "ref-003", "answer": "Refunds take 5-7 working days. Sorry for the wait!"},
{"id": "ord-010", "answer": "Your order will surely arrive tomorrow, guaranteed."},
]
def fake_judge(case): # stand-in for the judge model
a = case["answer"].lower()
return {"faithful": "guaranteed" not in a, "answers_question": True, "polite": "sorry" in a or "you" in a}
results = {c["id"]: fake_judge(c) for c in CASES}
for cid, g in results.items():
print(cid, g)
for crit in ["faithful", "answers_question", "polite"]:
rate = sum(g[crit] for g in results.values()) / len(results)
print(f"{crit:16} pass rate {rate:.0%}")Output
ret-001 {'faithful': True, 'answers_question': True, 'polite': True}
ref-003 {'faithful': True, 'answers_question': True, 'polite': True}
ord-010 {'faithful': False, 'answers_question': True, 'polite': True}
faithful pass rate 67%
answers_question pass rate 100%
polite pass rate 100%The stand-in judge fails ord-010 on faithfulness because it promises a "guaranteed" delivery date no document supports. Reporting a pass rate per criterion (67% faithful, 100% polite) tells you exactly what to fix, which a single average score would hide.
Judge Pitfalls
- Position bias: in A-vs-B comparisons, swap the order and average.
- Verbosity bias: judges like longer answers; say "length is not a quality" in the rubric.
- Calibrate: grade 50 cases by hand and check the judge agrees with humans before trusting it.
- Prefer binary criteria over a vague 1-10 score.
- Self-preference: a model may favour answers in its own style; use a different or stronger model as judge when you can, and rely on calibration.
- Summary: an LLM judge grades open-ended answers against a rubric of concrete criteria.
- Reason first, then structured scores; report pass rates per criterion.
- Judges have biases; calibrate against human grades before trusting them.
- Use string metrics where possible and judges only where judgement is needed.