Development

Scoring an Automated Judge on the Golden Set First

Antonio Evans8 min read

A workbench with a stack of human-labeled index cards on the left and a judging machine on the right, joined by a comparison table showing per-intent agreement and a three-way release gate marked judge alone.
Key points

Key points

  1. 1An automated judge is only as trustworthy as its agreement with human labels on a frozen golden set, measured per intent rather than in aggregate.
  2. 2Raw percent agreement inflates when one label dominates, so the release gate should use Cohen's kappa, which subtracts chance agreement.
  3. 3A workable gate is kappa of 0.7 or higher on at least 30 human-labeled examples per intent before the judge may grade that intent alone.
  4. 4The gate must be re-run whenever the judge prompt, model, or output schema changes, because provider-side updates shift scoring distributions silently.

Build the golden set so it can serve as ground truth

A judge cannot be calibrated against random production traffic, because random traffic is skewed toward easy, repetitive tickets. What you need is a golden set: a curated, versioned collection of tickets with fixed human labels that you trust as the answer key. The common framing across evaluation guides is that a golden dataset is a labeled record of what should have happened, deliberately weighted toward grey-area cases rather than the most common ones (Musubi). That bias is intentional. If your golden set accuracy is 75% while live accuracy looks like 92%, that gap is a feature, not a defect.

The backbone should be real failures, not invented examples. Every escalation, thumbs-down, or reopened ticket is a free, high-value test case that exposed a genuine gap (qaskills.sh). Capture the ticket text, the metadata the judge will see, and what a correct outcome was.

Tag every case with an intent, because the whole release decision is per intent. A judge that is excellent at password resets and terrible at billing disputes needs different treatment for each, and an aggregate number hides exactly that.

DecisionRecommended choiceWhy
Source of casesReal tickets plus known failures, some synthetic edge casesSynthetic alone encodes your assumptions, not user behavior
Minimum per intent30 labeled cases before any autonomous gradingBelow roughly 20 to 30, kappa confidence intervals are too wide to act on (GEDD)
Mix per intentClear passes, clear fails, boundary casesBoundary cases are where judges break
Label authorityOne named gold owner with final sayPrevents label churn across reviewers
VersioningFreeze v1.0, create new versions on policy changeKeeps trend lines comparable
SeparationNever reuse golden cases in prompt tuningTuning on the test set silently inflates scores

The last row matters more than people expect. If you paste golden examples into the judge prompt as few-shot demonstrations, you have trained on the test set and the kappa you compute afterwards is meaningless. Keep a small tuning bank of examples separate from the frozen golden set.

One practical sourcing note: sample the golden set from the same workflows the judge will score, including rare high-risk cases and cases sitting right on the rubric boundary, then keep it isolated from anything you use to write the judge prompt (OneUptime). Store each row with a case identifier, the ticket input, the human label, a short human reason, and the rubric version. The reason field is what lets you debug a judge later without re-reading every ticket.

Write the rubric and a scoring scale the judge can actually apply

Before you write a single judge prompt, the humans need a rubric. If two experienced support leads cannot apply your criterion consistently to the same twenty tickets, no model will. The advice from calibration practitioners is blunt: if human reviewers cannot apply the rubric consistently, an LLM will not repair the underlying ambiguity (OneUptime).

Write the rubric as explicit label definitions plus boundary examples. For a support judge scoring whether an agent response correctly resolved the customer's stated problem, a three-point scale is usually enough:

{
  "labels": ["pass", "partial", "fail"],
  "definitions": {
    "pass": "Response states the correct resolution or correct next action, and every factual claim about policy, price or timing matches the knowledge base.",
    "partial": "Response is directionally correct but omits a required detail, or asks for information already present in the ticket.",
    "fail": "Response states an incorrect policy, promises something the knowledge base forbids, or does not address the stated problem."
  },
  "boundary_examples": [
    {"label": "partial", "why": "Correct refund window but no instruction on how to start the return."},
    {"label": "fail", "why": "Quotes a 60-day window when policy says 30."}
  ],
  "output_schema": {"label": "pass|partial|fail", "reason": "string, max 40 words"}
}

Three labels rather than five is a deliberate trade. Nominal scales with many levels scatter human labels and depress kappa even when the judge is reasonable. If the underlying property is genuinely ordered (severity, satisfaction), use weighted kappa so near-misses count as near-misses (OneUptime). If the labels are nominal categories such as pass and fail, use unweighted kappa.

Two design rules pay off immediately. First, constrain the output to a schema and treat anything outside it as an explicit error outcome, not as a missing value. Second, cap the reason field. A judge that writes three paragraphs is harder to audit and slower to run than one that writes one sentence.

Teams that skip the rubric step often discover the real problem was never the model. The labels disagreed because the policy itself was ambiguous, and the judge was faithfully reproducing the ambiguity.

Pick chance-corrected agreement metrics and set the threshold

Accuracy is the wrong metric for this job, and the math shows why. If 80% of your agent responses are correct and your judge answers "pass" every time, it scores 80% accuracy while contributing nothing. The same judge's kappa is 0.00, because it agrees with humans no better than chance on the cases that matter (GEDD). Kappa is computed as observed agreement minus chance agreement, divided by one minus chance agreement. A worked example makes it concrete: 100 items, two raters, agreement matrix with 40 true agreements and 35 true disagreements, gives observed agreement of 0.75 and expected chance agreement of 0.50, so kappa is 0.50, or moderate (Galileo).

The research literature confirms the practical point. In a study of nine judge models, judges with high percent agreement still assigned wildly different scores, and the authors explicitly rediscovered the importance of Cohen's kappa over simple percent agreement (arXiv). A judge can look aligned and rank systems poorly.

Kappa is not a universal quality number, though. It must be read alongside the confusion matrix, class prevalence, and the actual disagreements, because a kappa of 0.7 on an intent where 95% of tickets are passes says something different from the same kappa on a balanced intent (OneUptime).

For a release gate, the widely used interpretation bands from Landis and Koch give you defensible cutoffs. Substantial agreement (0.61 to 0.79) is acceptable for low-stakes automation, and 0.80 and above is the band adopted by much of the NLP evaluation community for inter-annotator agreement (GEDD). Support ticket grading with customer-visible consequences is not low stakes, so set the autonomous-grading bar at 0.7 and require human sign-off above that only if the intent has real financial or legal exposure.

Also track a second, simpler number: coverage. Calibration is a trade between precision (agreement with humans) and coverage (the share of cases scored without abstention) (Galileo). A judge that abstains on half of a tricky intent and gets the rest right has not earned the right to grade that intent alone.

Run the judge on the frozen set and read agreement one intent at a time

Pin everything before you run. Model version, full prompt, few-shot examples, output schema, decoding settings, and any reference documents supplied to the judge all belong in the configuration record. Then run the judge on the untouched golden set and normalize only harmless formatting, mapping anything unexpected to an explicit judge error label (OneUptime). Do not drop errors from the denominator. A judge that labels 90% of cases and errors on the remaining 10% has, at best, 81% end-to-end agreement.

Here is a compact scoring script that computes per-intent kappa and the release decision:

from sklearn.metrics import cohen_kappa_score, confusion_matrix
import pandas as pd

MIN_N = 30
KAPPA_BAR = 0.70
LABELS = ["pass", "partial", "fail", "judge_error"]

def gate(rows: pd.DataFrame) -> pd.DataFrame:
    out = []
    for intent, group in rows.groupby("intent"):
        human = group["human_label"].tolist()
        judge = group["judge_label"].tolist()
        n = len(group)
        raw = sum(a == b for a, b in zip(human, judge)) / n
        kappa = cohen_kappa_score(human, judge, labels=LABELS)
        # one-sided conservative read: treat negative kappa as zero
        kappa_adj = max(kappa, 0.0)
        decision = "autonomous" if (n >= MIN_N and kappa_adj >= KAPPA_BAR) else "human_review"
        out.append({
            "intent": intent, "n": n, "raw_agreement": round(raw, 3),
            "kappa": round(kappa, 3), "decision": decision
        })
    return pd.DataFrame(out).sort_values("kappa", ascending=False)

A worked example with numbers shows why the per-intent read matters. Suppose you have four intents in the golden set:

IntentLabelled casesRaw agreementKappaDecision under the gate
Password reset480.940.86Autonomous grading
Delivery status360.890.74Autonomous grading
Refund eligibility410.830.58Human review
Billing dispute220.910.62Human review (also under 30 cases)

The aggregate across all 147 cases would land near 0.87 raw agreement, an impressive-looking number that hides a failing intent. Refund eligibility has the same kind of tickets that generate the costliest mistakes, and its kappa says the judge is only moderately aligned. Billing dispute looks better on kappa but has too few cases, so the threshold cannot be trusted. This is the decision rule in miniature: two intents earn autonomy, two do not, and the judge still adds value by pre-labeling the human queue for the intents it cannot own.

Keep errors visible in that table even where they are rare. If judge errors cluster on one intent, that is usually a signal about ticket format or length rather than about the rubric.

Set the release gate and define what "human review" means

The gate is a simple conjunction: at least 30 human-labeled cases for that intent, and kappa at or above 0.7, and judge error rate under a small ceiling you choose (for example 2%). Fail any condition and the intent stays with humans plus a sampled spot-check.

The spot-check is not a formality. For intents below the bar, let the judge draft a label and route it to a human for confirmation, then measure the human override rate weekly. If overrides on Refund eligibility fall below, say, 5% for several weeks, that is evidence to expand the golden set for that intent and re-run the gate.

This is also the point where the business case for automation becomes concrete. Escalated tickets cost substantially more than first-level resolutions, with benchmark figures putting Tier 3 handling at roughly three times the cost of Tier 1 (Maven AGI). A judge that reliably grades an easy, high-volume intent frees senior reviewers for exactly the intents it cannot handle. The gate protects the savings from being eaten by quietly misgraded disputes.

Write the gate into your pipeline as code, not a document. The judge run should emit the per-intent table above, and your CI job should fail the release if any intent currently marked autonomous drops below the bar. That converts calibration from a one-off study into a regression test.

Re-run the gate whenever the judge or the world changes

A one-time calibration captures judge behavior at a single moment, while production distributions keep moving (Galileo). Three things break a passing gate.

First, provider-side model updates. Re-judging with an updated model version produced uniformly more lenient scores, with mean score gaps shifting in the range of a few hundredths of a point across two datasets (Galileo). Small shifts matter when your gate sits at 0.70. Freeze the judge model version where the API allows it, and re-run the gate before adopting a newer one.

Second, your own prompt edits. Changing one line in the judge prompt can move kappa by more than a model swap. Treat the prompt as versioned code with the golden run attached.

Third, drift in the input distribution. If a new returns tool changes how agents phrase confirmations, the judge, calibrated on conversational answers, may start scoring valid flows as low quality. Historical drift analysis is a useful way to set monitoring triggers: run drift checks on past data at different thresholds and windows to see how much change is normal before you pick an alert level (Evidently AI). For a support judge, the practical version is simpler: compare the intent mix of live traffic to the golden set mix monthly, and re-run the gate on any intent whose live volume or phrasing has clearly shifted.

The operational loop is: any change to judge model, prompt, schema, knowledge base, or support policy triggers a full golden-set run; the per-intent table is attached to the change; and any intent that fails drops back to human review automatically.

If you are running several judges (correctness, tone, escalation fit) across multiple model providers and want one place to route prompts and keep model versions pinned, Quanta Inference gives you text, image, and embedding models through a single OpenAI-compatible API key, so swapping or freezing a judge model is a config change rather than an integration project. You can see the model lineup at Quanta Inference.

FAQ

FAQ

How many labeled examples do I need per intent before trusting kappa?

Thirty is a reasonable working floor. Below roughly twenty, the confidence interval on kappa is wider than plus or minus 0.30, which makes the number statistically meaningless for a deployment decision, while twenty to thirty is enough to act on (GEDD). Higher-stakes intents deserve more, and you should expand the set whenever a failing intent gets close to the bar.

Why not just use accuracy or percent agreement?

Because a judge that always predicts the majority label scores high accuracy while learning nothing. In a concrete example, a judge that always answers "correct" on a set that is 80% correct scores 80% accuracy and kappa of 0.00 (GEDD). Kappa subtracts the agreement you would get from the label distributions alone, so it reflects real discrimination (Galileo).

Should I use one judge for all intents or a separate judge per intent?

Start with one judge and one rubric, then split by intent only where the gate fails for a specific intent. Separate prompts per intent make the calibration table easier to reason about and let you fix Refund eligibility without disturbing Password reset, but they multiply the prompt-maintenance and re-run burden. Split when the failure is clearly about intent-specific policy, not when it is a general rubric or schema problem.

What do I do with judge errors and refusals?

Keep them as an explicit outcome in the denominator. A judge that labels 90% of cases and errors on 10% has at best 81% end-to-end agreement, so excluding errors inflates your numbers (OneUptime). Set a ceiling on error rate as part of the gate, and investigate errors that cluster on a single intent, since those usually point at input format rather than rubric ambiguity.

Can I reuse golden-set examples in the judge prompt?

No. Contaminating the evaluation set with examples used for prompt tuning produces inflated scores that will not hold in production, which is why evaluation guides insist on decontamination between test and training material (Maxim AI). Keep a separate tuning bank for few-shot examples, and never edit golden labels after seeing the judge's output on them.

Sources

Sources

  1. How to Build Golden Datasets for Content Moderation - Musubi
  2. How to Build a Golden Dataset for LLM Evaluation
  3. cohens-kappa-for-llm-judges.md - sample-GEDD
  4. How to Calibrate an LLM-as-a-Judge Against Human ...
  5. Enhancing AI Evaluation with Cohen's Kappa Metric
  6. Evaluating Alignment and Vulnerabilities in LLMs-as-Judges
  7. How to Calibrate Your LLM Judge With Human Annotations
  8. 15 Cost-Per-Ticket Statistics for Customer Support
  9. How to detect, evaluate and visualize historical drifts in the ...
  10. Building a “Golden Dataset” for AI Evaluation: A Step-by- ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles