AI coding

From Verdicts to a Queue: A Schema for Judge Scores and Reasons

Antonio Evans8 min read

A single wide spreadsheet row on the left: columns for claim, passage, verdict, score, and reason, with one row highlighted in red.
Key points

Key points

  1. 1Use one row per checked item, with the claim text, the exact passage the judge read and its source id, a categorical verdict, a numeric score and a short reason.
  2. 2Add judge confidence and a routing decision as columns so the table itself tells a reviewer or a second judge what to do next.
  3. 3Route on two variables, not one: fail plus low confidence goes to a human reviewer, and borderline scores go to a second judge, so the expensive paths only receive the cases that need them.
  4. 4Keep the judge prompt, model and rubric version in each row, because a score is only interpretable next to the instrument that produced it.

The problem with a verdict-only table

A judge that returns {"pass": false, "reason": "..."} is easy to wire up and nearly useless three weeks later. The verdict is a single bit, it loses the passage the judge actually read, and it gives a reviewer nothing to check. The moment someone asks "why did this batch fail?", you are reading judge logs instead of querying a table.

The fix is to treat the judge as a measuring instrument rather than an oracle. The Datadog overview of LLM-as-a-judge makes the point directly: a judge requires calibration against human labels before its scores can be trusted, and its output is a structured verdict plus a written rationale. If the rationale is not stored next to the claim and the passage, calibration is guesswork.

There is a second reason to store more than a verdict. Structured rubrics with anchored score levels raise judge-vs-human agreement substantially: one glossary reports Cohen's kappa rising from roughly 0.4 with a one-line rubric to roughly 0.78 with anchored 1-5 levels, as summarized in Future AGI's rubric glossary. Variance that large has to live somewhere you can slice it, and a single boolean column cannot carry it.

A third reason is attribution. A claim is only checkable when you can point at the passage behind it, including the population, outcome and figures the passage states, as described in this guide to checking source passages. If the table stores the claim but not the passage, you have logged an opinion.

Seven columns that carry a verdict

The schema below is the whole design. One table, one row per checked item, no joins required to route work.

ColumnTypePurposeExample
claim_idtext (primary key)Stable id for the checked itemrpt42-c017
claim_texttextThe exact sentence or span under judgment, uneditedChurn fell 12% after onboarding change.
passagetextThe verbatim passage the judge was shownRetention improved 3 points...
source_idtextId of the document, page or chunk the passage came fromdoc-881#p7
verdictenumpass, fail or unclearfail
scorenumericFixed range, documented per rubric0.20
reasontextOne or two sentences, no restating the claimPassage reports points, not percent.
confidencenumericJudge's own confidence, same fixed range0.55
judge_idtextJudge model plus prompt and rubric versionjudge-a/r7/p3
routeenumdone, human_review, second_judgehuman_review

Three of these deserve comment because they are the ones teams skip.

passage must be what the judge read, not what you meant to send. If a retrieval bug hands the judge the wrong chunk, the stored passage is the only evidence that explains a strange verdict. Papers on citation evaluation show why this matters at scale: a framework that parses Markdown reports and retrieves the actual cited content to judge each claim against its source closes a loop that claim-in-isolation checks cannot, as described in this source attribution evaluation. And even strong frontier models with link validity above 94% reached only 39 to 77 percent factual accuracy in that benchmark, which is exactly the gap you cannot see from a pass/fail column.

verdict stays categorical even though score exists. Named categories carry more meaning than arbitrary numbers. Evidently's judge FAQ recommends binary or a few clearly defined classes and warns that a similar response may get a 7 in one run and a 9 in another on a 1-10 scale (Evidently AI). The numeric score is still useful for thresholding and trend charts, but the categorical verdict is what the routing rule and human reviewers read.

judge_id is not optional. Judge upgrades are not interchangeable: one audit across four judgment datasets found that scaling a judge from 1.7B to 32B parameters produced a robust adjacent gain while adjacent API releases did not, and that stronger judges reduce but do not remove position and verbosity bias (auditing LLM-as-judge reliability). If you cannot group rows by judge, you cannot tell a quality regression from an instrument change.

The claim_text and passage split also matches how claim-level benchmarks are built. PARCEL labels 3,396 legal parenthetical claims as Supported, Refuted or Not Found, and finds that missing support is harder for models to detect than direct contradiction (PARCEL benchmark). A three-way verdict enum is the practical consequence: you need unclear as a first-class outcome, not an error state.

Getting structured output you can store

Promptfoo's llm-rubric assertion documents the shape most judges converge on: a JSON object with reason, score between 0.0 and 1.0, and pass as a boolean (promptfoo docs). That is a good starting point, but it lacks the two fields routing needs. Ask for confidence and a null-safe verdict explicitly.

{
  "verdict": "pass | fail | unclear",
  "score": 0.0,
  "confidence": 0.0,
  "reason": "one or two sentences citing the passage's wording",
  "passage_quote": "the exact span relied on, or null"
}

Then validate before insert, and treat validation failure as unclear with confidence 0.0 so it lands in the human queue. A parser that accepts true for one call and "true" for the next silently breaks your routing rule; the audit paper on judge reliability notes that shifts produced by deliberation protocols cannot be attributed without parser and fallback logs (the same audit).

Keep the score range fixed per rubric and record it. Mixing a 0-1 faithfulness judge with a 0-100 quality judge in one column makes thresholds meaningless. The rubric anchors are what turn "1-5" into something reproducible across runs and across judge models, per Future AGI's description of anchors.

The routing rule, stated as code

Routing is a pure function of two numbers and one label. Write it down once so reviewers, dashboards and backfills agree.

PASS_SCORE = 0.80
FLOOR = 0.60
BORDER = 0.45

def route(row):
    if row["verdict"] == "pass" and row["score"] >= PASS_SCORE and row["confidence"] >= FLOOR:
        return "done"
    if row["verdict"] == "fail" and row["confidence"] < FLOOR:
        return "human_review"
    if BORDER <= row["score"] < PASS_SCORE:
        return "second_judge"
    return "human_review"

The four thresholds are not arbitrary but they are not universal either. A confidence threshold is a business decision layered on a probability estimate, and the default 0.5 is one of the most common production mistakes (LlamaIndex on confidence thresholds). Their table of domain ranges gives a starting calibration: document extraction runs 0.85 to 0.95, medical diagnosis 0.90 to 0.98, spam filtering 0.60 to 0.80. A faithfulness judge on marketing copy sits nearer the spam end; a legal or medical claim judge sits near the top.

Here is the decision rule in plain words, which is also the rule to paste into your runbook:

ConditionRouteWhy
pass, score at or above pass threshold, confidence at or above floordoneThe judge is sure and positive
fail, confidence below floorhuman_reviewA confident-sounding wrong failure burns trust fastest
score in the borderline bandsecond_judgeCheap disambiguation before a person is paid for
anything else (unclear, parse failure, missing passage)human_reviewUnknown is not a pass

Two properties matter. First, done requires both a categorical pass and a numeric one, so a judge that says "pass" with a 0.5 score lands in the borderline band instead of disappearing. Second, low-confidence failures go to humans rather than a second judge, because a false accusation is more expensive than a missed one in most review workflows, and a second model sharing the first model's bias will not catch it. Repeated-sample juries add little when errors are correlated, per the reliability audit.

A worked batch of 1,000 claims

Suppose a report generator emits 1,000 claims in a nightly batch. The first judge scores every row. Assume the distribution lands like this (illustrative numbers you should replace with your own after two weeks of logging):

BucketRowsRouteCost per row
pass, score 0.86, confidence 0.82610doneone judge call
fail, confidence 0.4195human_reviewone judge call plus 3 minutes
score 0.62, confidence 0.71180second_judgetwo judge calls
unclear or parse failure25human_reviewone judge call plus 4 minutes
fail, high confidence90human_review (spot sample 10%)one judge call plus 3 minutes

The routing saves the interesting number. Without the rule, all 380 non-pass rows reach a person at roughly 3 minutes each, about 19 hours of review. With the rule, 180 rows go to a second judge for a second API call, 90 confident failures get a 10 percent spot sample of 9 rows, and the human queue is about 120 rows, under 7 hours. You have cut reviewer load by roughly two thirds and, more importantly, every row that reaches a person arrives with a passage and a reason attached.

The 10 percent sample on confident failures is the calibration loop. When a sampled row shows the judge was wrong, you have a labeled example, and those examples are what Galileo's calibration guide argues should feed back into the judge's prompt, anchors and rubric continuously, because provider-side model updates shift score distributions without warning. The guide cites mean score shifts of +0.022 to +0.072 on HelpSteer2 when re-judging with an updated model version, and a documented case where a model update caused a 9.6 percent accuracy drop for one prompt and a 5.1 percent increase for another on the same task.

That is the argument for judge_id in one sentence: the same fixed candidates can score differently because the instrument changed.

Audit queries that earn their keep

The table exists partly so that four questions are one query each.

-- 1. Which sources produce the most unsupported claims?
SELECT source_id, count(*) AS fails
FROM checks WHERE verdict = 'fail'
GROUP BY source_id ORDER BY fails DESC LIMIT 20;

-- 2. Is the current judge drifting against human labels?
SELECT judge_id,
       avg(CASE WHEN verdict = 'fail' AND score >= 0.80 THEN 1.0 ELSE 0 END) AS contradiction_rate,
       avg(confidence) AS mean_confidence
FROM checks GROUP BY judge_id;

-- 3. How much work is the second judge absorbing?
SELECT date_trunc('day', created_at) AS day,
       count(*) FILTER (WHERE route = 'second_judge') AS to_second_judge,
       count(*) FILTER (WHERE route = 'human_review') AS to_human
FROM checks GROUP BY 1 ORDER BY 1;

-- 4. Are reviews changing verdicts?
SELECT c.judge_id,
       avg(CASE WHEN r.final_verdict <> c.verdict THEN 1.0 ELSE 0 END) AS overturn_rate
FROM checks c JOIN reviews r USING (claim_id)
GROUP BY c.judge_id;

Query 2 is the contradiction check: a row that says fail with a high score means the rubric anchors or the prompt are internally inconsistent, and those rows should be rare. Query 4 is the cheapest calibration signal you can build, because it needs no extra labeling work, only that reviewers record a final verdict. Cohen's kappa is the metric to report rather than raw accuracy, since accuracy inflates on imbalanced labels, per Galileo's calibration guide.

One caution on aggregation. Multi-judge ensembles are supported in research frameworks with majority, weighted, unanimous and any-vote aggregation, along with a CANNOT_ASSESS verdict for explicit uncertainty handling (Autorubric). If you add a second judge to the borderline band, store its verdict in the same row with its own judge_id suffix, and define the tie rule before you run the batch. Otherwise the second judge becomes a second opinion nobody can act on.

Where this fits in a pipeline

Keep the table boring and the routing explicit. Any eval harness can write these rows; the classification work is the judge call itself. If you are running judges across text, image and audio inputs, routing them through one endpoint with an OpenAI-compatible interface keeps the client code identical and the judge_id honest, which is what Quanta Inference is for. If the work is better handed off than wired up, Quanta Experts read your context and return the finished rows in this shape.

The design survives the two changes that break naive eval tables: a new judge model and a new rubric. Both change judge_id, both are visible in query 2, and neither requires a migration.

FAQ

FAQ

Do I need both a categorical verdict and a numeric score?

Yes. The verdict is what routing and reviewers read, and named categories are more stable than arbitrary numbers, since a similar response can score 7 in one run and 9 in another on a loose 1-10 scale (Evidently AI). The numeric score earns its column by enabling thresholds, borderline bands and trend charts that a boolean cannot express.

Should `unclear` be a verdict or an error?

Make it a verdict. Claim-level benchmarks treat "not found" as a third label alongside supported and refuted, precisely because missing support is harder to detect than direct contradiction (PARCEL). Frameworks have converged on the same idea with an explicit cannot-assess outcome for uncertainty (Autorubric). Rows that fail schema validation should also become unclear so they reach the human queue instead of vanishing.

How do I set the confidence floor?

Start from the cost of being wrong, not from 0.5. Domain ranges for confidence thresholds run from 0.60 to 0.80 for spam filtering up to 0.90 to 0.98 for medical diagnosis (LlamaIndex). Then run two weeks of rows, compare human overturns by confidence band, and move the floor to whichever value keeps the overturn rate acceptable for the volume your reviewers can absorb.

Why send low-confidence failures to a human instead of a second judge?

Because correlated errors survive voting. The reliability audit found that juries add little when errors are correlated and that stronger judges reduce but do not remove bias (the audit). A second judge drawn from the same family will often reproduce the first judge's mistake with the same confidence. Save the second judge for borderline scores, where a genuine disagreement exists to resolve.

How often should I re-check the judge itself?

Continuously, on rolling production samples, not once against a golden set. Golden datasets capture a snapshot while production distributions keep moving, and provider-side changes can shift scoring distributions silently (Galileo). A 10 percent sample of confident failures plus the overturn-rate query gives you a standing early warning without a labeling project.

Sources

Sources

  1. What Is LLM-as-a-Judge?
  2. What Is a Rubric in LLM Evaluation?
  3. How to Fact-Check AI Answers: Check Source Passages
  4. Parsing and Evaluating Source Attribution in LLM Deep ...
  5. F.A.Q on LLM judges: 7 questions we often get
  6. Auditing LLM-as-Judge Reliability
  7. When Citations Mislead? A Claim-Level Benchmark for ...
  8. LLM Rubric
  9. Understanding Confidence Threshold in AI Systems
  10. How to Calibrate Your LLM Judge With Human Annotations
  11. A Unified Framework for Rubric-Based LLM Evaluation
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