LLM-as-a-Judge

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Written on 2026-10-01 against openevals 0.2.0 and langsmith 0.14.2, MLflow 3.16.1, Spring AI 2.0.1 on Spring Boot 4.1.x with Java 21, and LangChain 1.4.3 on Python 3.12; the full list is the version baseline on the landing page. None of the snippets on this page was run for this page: the openevals and MLflow snippets were written from their official documentation and source docstrings, and the Spring AI snippet follows the reference page and the API already exercised on Spring AI evaluation. Two details could not be confirmed from official pages that were reachable on that date, and the text says so where they occur (the MLflow alignment workflow and the exact judge model names you would choose). No benchmark numbers or vendor prices are quoted.

An LLM judge is a model asked to grade another model’s output against a rubric. It is the only practical way to score open-ended text at the volume a CI gate or a live-traffic sample needs, and it is also a measuring instrument with systematic errors. This page treats it as one: write it down precisely, measure its bias, calibrate it against humans, and report its agreement alongside every score it produces. The reasoning about why open-ended answers need judges at all is on Evaluation Methodology; pairwise rankings at scale are on Comparative Evaluation and Arenas. The framing here (judge as a measurement to be validated, the bias catalogue, and agreement statistics) follows the treatment of AI-as-a-judge in Chip Huyen’s AI Engineering, and the empirical findings on bias come from the MT-Bench paper listed in the References; the explanations and examples are our own.

Why a judge works, and when it does not

Judging is easier than generating. A judge does not have to compose an answer; it compares one with a rubric and a context. That asymmetry is why a modest model can grade a stronger one on narrow questions, and why a judge is cheaper than a human. It fails in predictable ways:

  • The rubric is ambiguous, so two runs read it differently ("is the answer helpful?" has no stable meaning).

  • The judge shares a blind spot with the model it grades, for instance the same factual error learned from the same data.

  • The judge prefers certain surface features regardless of quality (position, length, style, its own style).

  • The task needs ground truth the judge does not have (current facts, private documents, arithmetic), so the judge guesses.

The first rule follows: where a deterministic check exists, use it. A citation either points at a retrieved chunk or it does not; a JSON answer either parses or it does not; a code answer either compiles or it does not. Spend judge calls on the remainder.

Anatomy of a judge prompt

A reliable judge prompt has six parts. The order is not decoration: criteria and scale come before the material to grade, so the model reads the rubric first, and the verdict format comes last.

Part What it contains

Role and task

One sentence: what is being graded and from whose point of view ("You grade answers given by a documentation assistant").

Criterion

One property per judge. Correctness and tone are two judges, not one prompt; combined criteria produce scores nobody can interpret.

Scale and anchors

A small scale (binary, or 1 to 4) with a written description of every level. Anchors are what make two runs, or two people, agree.

Inputs

The question, the retrieved context if the criterion is about grounding, the answer, and the reference answer if one exists. Delimit each with tags so the model cannot confuse them.

Reasoning instruction

Ask for a short justification before the verdict, so the verdict is conditioned on it. Whether to ask for the reasoning first or last is a design choice you should measure on your own data rather than assume.

Output format

A fixed, parseable shape: a single token, or a small JSON object enforced with structured output.

A rubric for the DocsAssistant

The DocsAssistant (see the running scenario) answers questions about the documentation and cites passages. Its quality splits into criteria that need different inputs, so each becomes its own judge:

Criterion Inputs the judge needs Scale and anchors

Groundedness

Question, retrieved context, answer

Binary. Pass only if every factual claim in the answer is supported by the context; any unsupported claim fails.

Correctness

Question, answer, reference answer

1 to 4. 4: same facts as the reference; 3: correct but omits a minor point; 2: partly wrong or missing a key point; 1: contradicts the reference.

Refusal quality

Question, context, answer

Binary. Pass if, when the context lacks the answer, the assistant says so instead of guessing, and if it answers when the context does contain it.

Tone and format

Answer only

Binary. Pass if the answer follows the style guide (short paragraphs, code in fenced blocks, no marketing language).

Prefer binary or very small scales. A model asked for a 1 to 10 score clusters around a few favourite values, and the resulting numbers hide more noise than signal; a 1 to 4 scale with anchors keeps the information that is actually reproducible. The same applies to humans, which is why the calibration section below measures agreement on exactly the scale you ship.

Pointwise and pairwise judging

There are two shapes of judgement, and they answer different questions.

Shape Question it answers Strengths and weaknesses

Pointwise (single answer grading)

"Does this answer meet the rubric?" Output: a label or a score per answer.

Scales linearly with the number of answers and gives an absolute number to threshold in CI. Scores drift when the judge or its prompt changes, so absolute numbers are comparable only within one judge version.

Pairwise (comparison)

"Which of these two answers is better for this question?" Output: A, B or tie.

Relative judgements are easier and more stable than absolute ones, which suits choosing between two prompts or two models. Cost grows with the number of pairs, it needs both candidates to exist, and it exposes position bias directly.

Use pointwise judges for gates on a single candidate (is groundedness above the threshold?) and for monitoring live traffic, where there is no second candidate. Use pairwise judges to choose between two versions on the same inputs, and turn the outcomes into ratings with the methods on Comparative Evaluation and Arenas. A third shape, reference-guided grading, is pointwise with a reference answer in the prompt; it is the most reliable of the three when a trustworthy reference exists, because the judge compares two texts rather than recalling facts.

The MT-Bench paper by Zheng and colleagues names these modes (single-answer grading, pairwise comparison and reference-guided grading) and quantifies how strongly a strong judge model agrees with human preferences on its benchmark; read it for the numbers instead of trusting a rule of thumb copied from a blog.

Known biases and how to counter them

The same paper catalogues the systematic errors of LLM judges, and later work confirms and extends them. Treat each as a hypothesis to test on your own judge, not a constant of nature.

Bias Symptom Mitigation

Position bias

In a pairwise prompt the judge favours the answer shown first (or last), whichever it is.

Run each pair twice with the order swapped and count the pair only if both runs agree; otherwise record a tie or send it to a human. Report the flip rate (the share of pairs whose verdict changes with order).

Verbosity bias

Longer answers win regardless of quality.

Say in the rubric that length is not a virtue and that extra unrequested detail counts against the answer; compare lengths of winners and losers on your data; add length-controlled examples to a validation set.

Self-preference bias

A judge scores outputs from its own model family higher.

Judge with a different model family from the one that generated the answers; when comparing two vendors' models, use a judge from a third, or use more than one judge and report disagreement.

Leniency and scale compression

Most scores land in the top bucket; the metric cannot detect regressions.

Use binary criteria with strict anchors ("any unsupported claim fails"); include known-bad examples in the validation set.

Style and authority bias

Confident, formatted or citation-heavy answers win even when wrong.

Judge groundedness against the context, not against how it sounds; give the judge the context and the reference, not just the answer.

Judge non-determinism

The same input gets different scores across runs.

Temperature zero and a fixed seed where the provider supports it; repeat borderline items; cache judge calls by hash of (prompt version, inputs) so that unchanged items are not re-scored.

The position-swap protocol is easy to implement and worth making a default. For a pair and a judge that returns the preferred answer, the swapped verdict is . The pair has a consistent winner only when both orders pick the same underlying answer, and the flip rate over pairs is

where the superscript means the verdict is mapped back to the original labels before comparing. A flip rate near zero means the judge’s preference is driven by content; a high one means the judge cannot discriminate between the candidates, or has a strong position bias, and its verdicts on those pairs are noise.

Judges with openevals

openevals is the LangChain team’s package of prebuilt evaluators. Its create_llm_as_judge function builds a judge from a prompt, a model, and a feedback key, and returns a callable that takes inputs, outputs, reference_outputs and any further variables your prompt names. The documented parameters include prompt, feedback_key, model (a string of the form provider:model_name) or judge (a LangChain chat model or an OpenAI client), continuous (a float score between 0 and 1 instead of a boolean), choices (a list of allowed numeric scores, exclusive with continuous), use_reasoning and few_shot_examples. The result is a dictionary with key, score and comment. Prebuilt prompts such as CORRECTNESS_PROMPT live in openevals.prompts. How these evaluators plug into LangSmith datasets, evaluate and the pytest plugin is on LangChain evaluation; this section only shows the judge itself.

The DocsAssistant correctness judge, with the rubric from the table above as a custom prompt and a discrete 1 to 4 scale:

from openevals.llm import create_llm_as_judge

CORRECTNESS_RUBRIC = """You grade answers given by a documentation assistant.
Compare the answer with the reference answer. Judge facts only; ignore length and style.

Scale:
4 - same facts as the reference
3 - correct, but omits a minor point
2 - partly wrong, or misses a key point
1 - contradicts the reference

<question>
{inputs}
</question>

<answer>
{outputs}
</answer>

<reference_answer>
{reference_outputs}
</reference_answer>

Explain briefly, then give the score."""

correctness_judge = create_llm_as_judge(
    prompt=CORRECTNESS_RUBRIC,
    feedback_key="correctness",
    choices=[1.0, 2.0, 3.0, 4.0],
    model="ollama:llama3.1:8b",   # a model family different from the one that wrote the answer
)

result = correctness_judge(
    inputs="How do I invert a matrix?",
    outputs="Use MatrixInverse on a square, non-singular matrix.",
    reference_outputs="Call MatrixInverse; the matrix must be square and invertible.",
)
print(result["key"], result["score"], result["comment"])

Source: openevals README (LLM-as-judge). The model string ollama:llama3.1:8b assumes the provider integration for Ollama is installed; the README documents the provider:model form and its OpenAI example, and a LangChain chat model can be passed through judge instead if you prefer to construct it yourself.

A pairwise judge is the same function with a prompt that has two answers and a categorical outcome. The README documents that extra prompt variables are supplied at call time, and that choices restricts the numeric score; encoding "A wins" and "B wins" as two scores keeps the verdict machine-readable. Running the pair in both orders implements the position-swap protocol:

from openevals.llm import create_llm_as_judge

PAIRWISE = """Decide which answer is better for the question, using only the criterion given.
Criterion: factual agreement with the reference. Ignore length, style and order of presentation.
Reply 1.0 if answer A is better, 0.0 if answer B is better.

<question>{inputs}</question>
<reference_answer>{reference_outputs}</reference_answer>
<answer_a>{answer_a}</answer_a>
<answer_b>{answer_b}</answer_b>"""

pairwise = create_llm_as_judge(
    prompt=PAIRWISE,
    feedback_key="a_beats_b",
    choices=[0.0, 1.0],
    model="ollama:llama3.1:8b",
)


def compare(question, reference, first, second):
    """Return 'first', 'second' or 'tie' after judging in both orders."""
    forward = pairwise(inputs=question, reference_outputs=reference, answer_a=first, answer_b=second)
    swapped = pairwise(inputs=question, reference_outputs=reference, answer_a=second, answer_b=first)
    first_wins_forward = forward["score"] == 1.0
    first_wins_swapped = swapped["score"] == 0.0     # 'first' is answer B in the swapped run
    if first_wins_forward and first_wins_swapped:
        return "first"
    if not first_wins_forward and not first_wins_swapped:
        return "second"
    return "tie"                                       # the verdict flipped with the order

Source: openevals README (LLM-as-judge, custom prompts and extra variables). Passing custom variables such as answer_a follows the README’s statement that additional prompt variables are provided when the evaluator is called; the repository’s other prompt helpers (for example prebuilt correctness or hallucination prompts) are single-answer prompts, so the pairwise prompt above is ours, not a prebuilt one.

Judges with MLflow 3

MLflow 3 treats a judge as a scorer: an object that mlflow.genai.evaluate runs over a dataset, recording the verdicts and the judge’s rationale next to traces and runs, so judge scores sit in the same experiment as the rest of the evaluation. The make_judge function builds a judge from natural-language instructions. Its signature in the 3.x source takes name, instructions, model (a URI of the form openai:/gpt-4), feedback_value_type, and optional description, inference_params, base_url, extra_headers and generate_rationale_first. The instructions must contain at least one of the template variables {{ inputs }}, {{ outputs }}, {{ expectations }}, {{ conversation }} or {{ trace }}, and feedback_value_type accepts primitive types, Literal enums and some containers (Pydantic models are not supported). MLflow also ships built-in scorers such as Correctness.

from typing import Literal

import mlflow
from mlflow.genai.judges import make_judge

grounded = make_judge(
    name="grounded_in_docs",
    instructions=(
        "You grade answers given by a documentation assistant. The answer in {{ outputs }} "
        "responds to the question in {{ inputs }}. The answer is grounded only if every factual "
        "claim in it is supported by the reference material in {{ expectations }}. "
        "Any unsupported claim means 'no'."
    ),
    model="ollama:/llama3.1:8b",       # URI form: provider:/model; check that your MLflow version lists the provider
    feedback_value_type=Literal["yes", "no"],
)

dataset = [
    {
        "inputs": {"question": "How do I invert a matrix?"},
        "outputs": "Use MatrixInverse on a square, non-singular matrix.",
        "expectations": {"expected_response": "Call MatrixInverse; the matrix must be square and invertible."},
    },
]

results = mlflow.genai.evaluate(data=dataset, scorers=[grounded])
print(results.metrics)          # summary across rows
print(results.result_df)        # per-row verdicts and rationales

Source: MLflow GenAI documentation; the signatures of make_judge and mlflow.genai.evaluate (data, scorers, predict_fn, model_id) were read from the mlflow source on GitHub because the documentation site answered automated fetches with HTTP 403 on 2026-10-01. The ollama:/ model URI is an assumption by analogy with the documented openai:/gpt-4 form; confirm the provider prefix for your version before relying on it. With predict_fn set, mlflow.genai.evaluate calls your application on each input and traces it, and the dataset then needs no outputs column.

MLflow documents a workflow for aligning a judge with human feedback: Judge.align(traces, optimizer) takes traces that carry human assessments and returns a new judge whose instructions have been adjusted, defaulting to an optimizer named MemAlign in the source. I did not find a reachable official page that states the full workflow, so this page does not show code for it; treat alignment as an automated version of the prompt revision loop in the calibration section below, and validate any aligned judge on held-out labelled data before trusting it.

Judges in Spring AI

Spring AI ships two model-based evaluators in org.springframework.ai.chat.evaluation that implement the Evaluator interface (one method, evaluate(EvaluationRequest), returning an EvaluationResponse with isPass(), a score and feedback). RelevancyEvaluator asks whether the answer is in line with the context for the query, and FactCheckingEvaluator asks whether a document supports a claim, which makes it a groundedness check for the DocsAssistant. Both are pointwise, binary judges whose prompts you can replace; the full API, the strictness of the yes parsing and the custom evaluators are on Spring AI evaluation, so only the judge-specific parts are shown here.

import java.util.List;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.evaluation.FactCheckingEvaluator;
import org.springframework.ai.chat.evaluation.RelevancyEvaluator;
import org.springframework.ai.document.Document;
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.EvaluationResponse;

class DocsAssistantJudges {

    private final RelevancyEvaluator relevancy;
    private final FactCheckingEvaluator groundedness;

    // the builder must wrap a model different from the one that generates answers
    DocsAssistantJudges(ChatClient.Builder judgeClientBuilder) {
        this.relevancy = RelevancyEvaluator.builder().chatClientBuilder(judgeClientBuilder).build();
        this.groundedness = FactCheckingEvaluator.builder(judgeClientBuilder).build();
    }

    boolean judge(String question, List<Document> retrieved, String answer) {
        EvaluationRequest request = new EvaluationRequest(question, retrieved, answer);
        EvaluationResponse relevant = this.relevancy.evaluate(request);
        EvaluationResponse grounded = this.groundedness.evaluate(request);
        return relevant.isPass() && grounded.isPass();
    }
}

Source: Spring AI: Model Evaluation. The builder form of FactCheckingEvaluator (builder(clientBuilder).build()) follows the 2.0.1 API as exercised on the sibling page, where the constructor shown in the reference page is not accessible.

Spring AI’s own LLM-as-a-judge guide describes the same two patterns as this page, direct assessment (point-wise) and pairwise comparison, and recommends a separate evaluation model to avoid self-evaluation (narcissistic) bias; its worked example is a recursive advisor that rates a response on a 1 to 4 scale and retries with the judge’s feedback, which is the runtime self-refinement use of a judge and is covered on the sibling page. For fact checking, the evaluator has a forBespokeMinicheck mode, which leads to the next topic.

Specialised judge models

A general chat model can judge, but a model trained for the job is often cheaper and more consistent on that job. Two families exist:

  • Fact-checking models. Bespoke-Minicheck, which Spring AI’s FactCheckingEvaluator supports through forBespokeMinicheck, is a small model that answers one question: does this document support this claim? It suits the groundedness criterion and nothing else.

  • Rubric-following judge models. Open models fine-tuned on judgement data take a rubric and a pair or a single answer and return a score with feedback. Spring AI’s guide names avcodes/flowaicom-flow-judge:q4 as an example for the Ollama runtime, and the Prometheus family of papers describes a similar approach. Whether any specific one fits the DocsAssistant is an empirical question that the calibration below answers; I have not checked the current state of these model releases.

Three considerations decide between a specialised judge and a large general model. A small local judge keeps documentation text and user questions inside your network (see Running LLMs Locally for hosting it), costs little per call, and can be run on every pull request. A large model reads long contexts and subtle rubrics better. And any fine-tuned judge inherits the biases of its training data, so it needs the same bias tests and kappa measurement as a general model. A practical pattern is a two-tier setup: a cheap judge scores everything, and a stronger judge re-scores the items where the cheap judge is uncertain or where two cheap judges disagree.

G-Eval (Liu et al.) is the related idea of asking the judge to first generate evaluation steps from the criterion and then score, optionally weighting the score by token probabilities; libraries such as DeepEval implement it, and it improves granularity at the cost of extra calls.

Calibrating a judge against human labels

A judge score is a claim about quality; calibration is how you earn the right to use it. The procedure:

  1. Label a validation set. Take 100 to 300 DocsAssistant (question, context, answer) triples, stratified over your intents and including known failures. Two people label them with the same rubric the judge uses. How to build and version such sets is on Building Eval Sets.

  2. Measure human agreement first. If two humans disagree, the rubric is the problem and no judge can fix it; revise the anchors until they agree.

  3. Run the judge with the production prompt, model and settings.

  4. Measure judge-versus-human agreement with a chance-corrected statistic, the confusion matrix, and the per-class rates.

  5. Read the disagreements, revise the rubric or add few-shot examples, and repeat. Freeze the validated prompt as a version.

Raw accuracy misleads whenever one label dominates: a judge that always says "grounded" scores 90% accurate if nine answers in ten are grounded. Cohen’s kappa corrects for the agreement expected by chance. With two raters (the judge and a human) labelling the same items into categories, let be the observed fraction of items on which they agree, and , the fraction of items each rater assigned to category . Then

where is the agreement expected if both raters labelled independently with their own base rates. is perfect agreement, is no better than chance, and negative values are worse than chance. A worked case: 100 answers, judge and human both say "grounded" on 80, both say "ungrounded" on 8, and disagree on 12 (judge says grounded 6 times when the human says not, and the reverse 6 times). Then , the judge says grounded on 86% and the human on 86%, so and . An 88% raw accuracy hides a kappa of about one half, because the prevalence of one class inflates chance agreement.

Common verbal scales (Landis and Koch’s) call 0.41 to 0.60 moderate and 0.61 to 0.80 substantial agreement, but the threshold that matters is the one your decision needs. A judge that gates releases should be near the kappa between two trained humans on the same rubric; a judge that only triages items for human review can be lower.

For ordered scales such as 1 to 4, a disagreement of one level is less serious than three. Weighted kappa replaces exact matching with disagreement weights (linear or quadratic in the distance between levels):

where is the observed count of items the judge put in level and the human in , and is the count expected by chance from the marginals. In Python, scikit-learn implements both:

from sklearn.metrics import cohen_kappa_score, confusion_matrix

human = ["yes", "yes", "no", "yes", "no", "yes", "yes", "no"]   # the validation labels
judge = ["yes", "yes", "no", "no", "no", "yes", "yes", "yes"]   # the judge's verdicts on the same items

kappa = cohen_kappa_score(human, judge)
print(f"kappa = {kappa:.2f}")
print(confusion_matrix(human, judge, labels=["yes", "no"]))

# ordered 1-4 scores: penalise far disagreements more than near ones
human_scores = [4, 3, 2, 4, 1, 3]
judge_scores = [4, 4, 2, 3, 1, 2]
print(cohen_kappa_score(human_scores, judge_scores, weights="quadratic"))

Source: scikit-learn: cohen_kappa_score. Eight items are far too few for a real estimate; the sample-size reasoning is on Building Eval Sets, and a kappa computed on 100 items has a wide confidence interval that you should report.

Correcting a prevalence estimate for judge error

Kappa tells you whether to trust the judge; it does not remove the judge’s errors from an aggregate such as "what share of production answers are grounded". If the judge has true-positive rate and true-negative rate measured on the validation set, and it reports a fraction of live answers as grounded, the corrected estimate of the true grounded share is the classical misclassification correction (known in epidemiology as the Rogan-Gladen estimator)

where the denominator must be positive, which only holds when the judge is better than a coin flip. If the judge catches 90% of grounded answers ( ) and 80% of ungrounded ones ( ) and flags 70% of live traffic as grounded, then . The correction widens the uncertainty, and it assumes the validation set resembles live traffic, so re-measure when the traffic changes. Using the corrected figure is how sampled online judging stays honest about its own error.

Putting judges in the pipeline

A validated judge becomes part of the suite described on the neighbouring pages:

  • Gates. Judge scores on the golden set enter the CI gate on Evaluation in CI. Pin the judge model version, the prompt version and the temperature, and cache judge calls by hash so only changed items are re-scored.

  • Retrieval-augmented answers. The reference-free RAG judges (groundedness, answer relevance, context relevance) and the agent judges are covered on RAG and Agent Evaluation; the retrieval metrics and RAGAS basics are on Evaluating RAG Systems, and hallucination detection as a runtime step is on Hallucination Detection and Correction.

  • Production sampling. Run the same judge, asynchronously, over a sample of live traces (Online Evaluation and Feedback).

  • Drift. A judge is a dependency. When the judge model is a hosted model that a vendor updates, its scores can shift without any change in your application; the periodic re-run of the validation set described on Drift and Regression Monitoring catches that.

  • Evaluation at scale for RAG. Batch strategies, sampling and cost control for large judged sets are described on Evaluation at Scale.

Judge prompts are versioned artefacts like any other (see Prompt and Model Management). Security testing of judges themselves, for example attempts to embed instructions in an answer that tell the judge to pass it, belongs to AI Red Teaming in the AI Security & Responsible AI sub-section; a minimal defence is to delimit the graded text with tags, tell the judge that the content inside them is data to be graded and not instructions, and include injection attempts in the validation set.

Judge checklist

  1. One criterion per judge, binary or a small anchored scale; deterministic checks used wherever they exist.

  2. Rubric, scale anchors, inputs and output format written down and versioned; temperature zero and a seed.

  3. Judge model from a different family from the generator; pairwise runs done in both orders, flip rate reported.

  4. Two human labellers agree on the validation set before the judge is measured against them.

  5. Kappa (weighted for ordered scales), confusion matrix and per-class rates reported with their sample size.

  6. Aggregates over live traffic corrected for the judge’s measured error.

  7. Judge, prompt and model versions pinned; validation set re-run on a schedule and on any judge change.

References