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.

TermMeaning
Faithfulness / groundednessevery claim is supported by the provided context
Pointwise gradingscore one answer against the rubric
Pairwise comparisonshow two answers, ask which is better
Judge calibrationchecking 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_output

Sample output

python judge.py

Example 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.