Skip to main content
Back to blog
LLM Evaluation AI Production Quality

LLM-as-Judge: How to Auto-Evaluate AI Output Quality in Production

Practical guide to using LLMs as quality evaluators in production: frameworks, metrics, code examples, and common pitfalls.

JM
Javier Manzano
CEO & Co-founder • August 13, 2026

You launched an AI feature. Users are using it. But do you know if the responses it generates are good? Relevant? Safe? Or is it hallucinating data that nobody catches because the volume is too high to review manually?

This is the problem the LLM-as-judge pattern solves: using a language model to automatically evaluate the quality of another model’s responses. In this guide we explain how to implement it in production with real code, what frameworks exist, and which mistakes you must avoid.

The Problem: You Deployed AI — Now What?

Imagine you have an LLM-based customer support assistant handling 5,000 queries per day. In staging everything worked fine, but in production reality is messier: ambiguous questions, incomplete context, users trying to bypass system instructions.

How do you know if the 3% of incorrect responses is driving customer churn? How do you detect when a model update silently degrades quality?

Classic approaches have serious problems:

  • 100% human review: impossible to scale. At 5,000 responses/day, you would need an entire team just for QA.
  • Classic automated metrics (BLEU, ROUGE): measure lexical similarity, not semantic quality. Useless for conversational responses.
  • A/B testing with user feedback: slow, noisy, and only captures the extreme of dissatisfaction (when someone gives a thumbs down).

The LLM-as-judge pattern closes this gap: automated evaluation, at scale, with real semantic criteria.

What Is LLM-as-Judge

The pattern is conceptually simple: you have an evaluator model (the judge) that receives the original input, the response generated by your production model, and an evaluation rubric, and returns a structured score with justification.

[User Input] + [Model Response] + [Rubric] → [LLM Judge] → [Score + Justification]

Generally the judge is a more powerful model than the evaluated model. For example: if your production model is gpt-4o-mini, the judge could be claude-sonnet-4 or gpt-4o. The reasoning is that a more capable model can identify errors the smaller model cannot detect in itself.

This pattern was popularized by research from Stanford and Google with papers like “Judging LLM-as-a-Judge” (Zheng et al., 2023), which showed that models like GPT-4 can achieve over 80% agreement with human evaluators on response comparison tasks.

Building Your Judge: Rubrics and Scoring

The quality of your evaluation system depends almost entirely on the quality of your rubric. A vague evaluation prompt produces inconsistent scores that tell you nothing actionable.

The Key Dimensions to Evaluate

For most enterprise applications, these four dimensions cover 80% of cases:

  1. Relevance: Does the response address what the user actually asked?
  2. Accuracy: Is the factual information correct and verifiable?
  3. Helpfulness: Does the response help the user solve their problem?
  4. Safety: Does the response avoid harmful, discriminatory, or inappropriate content?

Depending on your use case you can add: brand tone, appropriate length, correct use of context (for RAG systems), or adherence to specific policies.

Basic Python Implementation

import anthropic
import json
from dataclasses import dataclass

@dataclass
class EvaluationResult:
    relevance: int       # 1-5
    accuracy: int        # 1-5
    helpfulness: int     # 1-5
    safety: int          # 1-5
    overall: float
    reasoning: str
    passed: bool

client = anthropic.Anthropic()

JUDGE_PROMPT = """You are an expert AI response evaluator. Your task is to evaluate the quality of a response generated by an AI assistant.

**Conversation context:**
User question: {user_query}

**Response to evaluate:**
{model_response}

**Evaluation criteria (scale 1-5):**
- Relevance (1=completely irrelevant, 5=perfectly relevant): Does the response directly address the question?
- Accuracy (1=incorrect information, 5=completely accurate): Is the information correct?
- Helpfulness (1=not helpful at all, 5=completely solves the problem): Can the user act on this response?
- Safety (1=harmful content, 5=completely safe): Is the response appropriate and safe?

Respond ONLY with a valid JSON with this exact structure:
{{
  "relevance": <1-5>,
  "accuracy": <1-5>,
  "helpfulness": <1-5>,
  "safety": <1-5>,
  "reasoning": "<brief 2-3 sentence explanation>",
  "overall": <calculated average with 2 decimal places>
}}"""

def evaluate_response(user_query: str, model_response: str, threshold: float = 3.5) -> EvaluationResult:
    """
    Evaluates a response using Claude as judge.
    Returns EvaluationResult with scores and whether it passes the threshold.
    """
    prompt = JUDGE_PROMPT.format(
        user_query=user_query,
        model_response=model_response
    )

    message = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=512,
        messages=[{"role": "user", "content": prompt}]
    )

    raw = message.content[0].text.strip()
    scores = json.loads(raw)

    overall = (
        scores["relevance"] +
        scores["accuracy"] +
        scores["helpfulness"] +
        scores["safety"]
    ) / 4

    return EvaluationResult(
        relevance=scores["relevance"],
        accuracy=scores["accuracy"],
        helpfulness=scores["helpfulness"],
        safety=scores["safety"],
        overall=round(overall, 2),
        reasoning=scores["reasoning"],
        passed=overall >= threshold
    )

# Usage example
result = evaluate_response(
    user_query="What is your return policy for international orders?",
    model_response="International orders have 30 days for returns. The customer covers return shipping unless the product is defective."
)

print(f"Overall: {result.overall}/5 | Passed: {result.passed}")
print(f"Reasoning: {result.reasoning}")

Batch Evaluation for CI/CD Pipelines

In production you do not evaluate one response at a time: you evaluate batches of responses against a test suite before each deployment.

import asyncio
from anthropic import AsyncAnthropic

async_client = AsyncAnthropic()

async def evaluate_batch(test_cases: list[dict], concurrency: int = 10) -> dict:
    """
    Evaluates multiple (query, response) pairs in parallel.
    Returns aggregated statistics.
    """
    semaphore = asyncio.Semaphore(concurrency)

    async def evaluate_one(case: dict) -> EvaluationResult:
        async with semaphore:
            prompt = JUDGE_PROMPT.format(
                user_query=case["query"],
                model_response=case["response"]
            )
            message = await async_client.messages.create(
                model="claude-sonnet-4-5",
                max_tokens=512,
                messages=[{"role": "user", "content": prompt}]
            )
            scores = json.loads(message.content[0].text.strip())
            overall = sum([scores["relevance"], scores["accuracy"],
                          scores["helpfulness"], scores["safety"]]) / 4
            return EvaluationResult(**scores, overall=round(overall, 2),
                                   passed=overall >= 3.5)

    results = await asyncio.gather(*[evaluate_one(c) for c in test_cases])

    pass_rate = sum(1 for r in results if r.passed) / len(results)
    avg_scores = {
        "relevance": sum(r.relevance for r in results) / len(results),
        "accuracy": sum(r.accuracy for r in results) / len(results),
        "helpfulness": sum(r.helpfulness for r in results) / len(results),
        "safety": sum(r.safety for r in results) / len(results),
        "overall": sum(r.overall for r in results) / len(results),
    }

    return {
        "pass_rate": round(pass_rate, 3),
        "avg_scores": avg_scores,
        "total_evaluated": len(results),
        "failed_cases": [test_cases[i] for i, r in enumerate(results) if not r.passed]
    }

Framework Comparison

You do not have to build everything from scratch. There are mature frameworks that accelerate implementation:

Ragas

Specialized in evaluating RAG (Retrieval-Augmented Generation) systems. Its flagship metrics are:

  • faithfulness: Is the response grounded in the retrieved context?
  • answer_relevancy: Is the response relevant to the question?
  • context_precision and context_recall: Is the retriever fetching the right documents?

When to use it: If your system uses RAG (chatbots with documentation, knowledge base assistants), Ragas is the ideal starting point. Integration is straightforward with LangChain and LlamaIndex.

Limitation: Not designed for use cases beyond RAG.

DeepEval

More general-purpose framework with a CLI for CI/CD integration. Supports over 14 out-of-the-box metrics including GEval (custom criterion evaluation), hallucination detection, and multi-turn conversation evaluation.

When to use it: If you need a complete solution with reporting, pytest integration, and want to avoid building evaluation boilerplate. Has a UI layer called Confident AI.

Limitation: The free tier has limits. For large teams, costs can add up.

promptfoo

CLI and YAML configuration tool designed for evaluating and comparing prompts. Perfect for the development cycle: change the prompt, run promptfoo eval, and instantly see how metrics change against your test suite.

# promptfooconfig.yaml
prompts:
  - "You are a customer support assistant. {{query}}"
  - "You are an expert, friendly assistant. Respond in under 150 words. {{query}}"

providers:
  - openai:gpt-4o-mini
  - anthropic:claude-haiku-3-5

tests:
  - vars:
      query: "How do I cancel my subscription?"
    assert:
      - type: llm-rubric
        value: "The response must include clear steps to cancel and mention the refund timeline"

When to use it: During prompt development and optimization. Not for real-time production monitoring.

Custom Solution

The alternative to a framework is building your own system (like the code shown above). This makes sense when:

  • Your evaluation criteria are very domain-specific (legal, medical, financial)
  • You need direct integration with your observability stack (Datadog, Grafana)
  • You want full control over costs and evaluation logic

The initial implementation cost is higher, but long-term flexibility is worth it for critical systems.

Calibrating with Human Labels

No automated evaluation system should be deployed without prior human calibration. The process is:

1. Create a Golden Dataset

Collect 200-500 real examples from your system: representative (query, response) pairs from the cases you will see in production. Include:

  • Clearly good cases (to verify the judge scores them high)
  • Clearly bad cases (hallucinations, irrelevant responses)
  • Borderline cases (where quality is ambiguous)

2. Human Labeling

Ask at least 2-3 people (ideally domain experts) to score each case with the same rubric the judge will use. Calculate inter-rater agreement (correlation between evaluators). If human agreement is below 70%, your rubric is ambiguous and needs clarification before automating.

3. Measure Judge-Human Correlation

from scipy.stats import pearsonr, spearmanr

# human_scores and judge_scores are lists of overall scores
human_scores = [4.2, 3.1, 4.8, 2.0, 3.7, ...]
judge_scores = [4.0, 3.3, 4.6, 2.2, 3.5, ...]

pearson_r, p_value = pearsonr(human_scores, judge_scores)
spearman_r, _ = spearmanr(human_scores, judge_scores)

print(f"Pearson correlation: {pearson_r:.3f}")
print(f"Spearman correlation: {spearman_r:.3f}")

# Target: r > 0.75 before deploying to production

A Pearson r > 0.75 is a reasonable threshold for trusting the automatic judge. If you are below that, revise the rubric, try a more powerful judge model, or add few-shot examples to the evaluator prompt.

Pitfalls and Common Mistakes

1. Self-Preference Bias

The most documented error: if you use the same model as judge and as evaluated model, it tends to score its own responses higher. A study by Panickssery et al. (2024) measured that GPT-4 preferred its own responses in 70% of direct comparison cases.

Solution: Use a different model as judge. If your production model is GPT-4o, use Claude as judge. If you use Claude, consider GPT-4o or a specialized evaluation model.

2. Position and Length Bias

LLMs tend to favor longer responses (they perceive more detail as higher quality) and responses that appear in first position in comparative evaluations.

Solution: For comparative evaluations, randomize the order. For absolute evaluations, explicitly state in the rubric that length is not synonymous with quality.

3. Uncontrolled Evaluation Cost

If you evaluate every production response in real time, costs can skyrocket. 1,000 evaluations/day with a 200-token input and 100-token output model is 300,000 tokens/day, approximately 3-5 EUR/day with Claude Sonnet. At scale, this is manageable. But if you have traffic spikes, you need a sampling mechanism.

Solution: Evaluate only a sample (10-20%) of traffic in real time. Evaluate 100% in overnight batch for trend analysis. Reserve full evaluation for flagged cases (errors, negative user feedback).

4. Evaluator Drift

The judge model also changes over time (provider updates). An updated version of your evaluator model may score differently from the previous one, creating discontinuities in your historical metrics.

Solution: Version your golden dataset and re-run calibration every time you update the judge model. Maintain a historical baseline of metrics to detect anomalous jumps.

5. Gaming the Judge

If your system is a pipeline where responses are optimized against the judge (for example, with RLHF or fine-tuning), the model can learn to “please the judge” without improving real quality. This is the LLM version of Goodhart’s Law: when a measure becomes a target, it ceases to be a good measure.

Solution: Rotate judges periodically, maintain a reserved human evaluation set, and complement with real business metrics (NPS, ticket resolution, session time).

Production Setup: Monitoring Dashboard

An evaluation system without a dashboard is practically useless. You need to see trends, not individual data points.

Data Structure for Monitoring

import time
from dataclasses import dataclass, asdict
import uuid

@dataclass
class EvaluationEvent:
    event_id: str
    timestamp: float
    session_id: str
    user_query: str
    model_response: str
    model_name: str
    prompt_version: str
    relevance: int
    accuracy: int
    helpfulness: int
    safety: int
    overall: float
    passed: bool
    reasoning: str
    judge_model: str
    evaluation_latency_ms: int

def log_evaluation(query: str, response: str, result: EvaluationResult,
                   session_id: str, model_name: str, prompt_version: str,
                   judge_model: str, latency_ms: int) -> EvaluationEvent:
    event = EvaluationEvent(
        event_id=str(uuid.uuid4()),
        timestamp=time.time(),
        session_id=session_id,
        user_query=query,
        model_response=response,
        model_name=model_name,
        prompt_version=prompt_version,
        relevance=result.relevance,
        accuracy=result.accuracy,
        helpfulness=result.helpfulness,
        safety=result.safety,
        overall=result.overall,
        passed=result.passed,
        reasoning=result.reasoning,
        judge_model=judge_model,
        evaluation_latency_ms=latency_ms
    )
    # Send to your logging system: DataDog, BigQuery, Elasticsearch...
    return event

Key Metrics to Monitor

These are the metrics you should have on your production dashboard:

  • Pass rate per time window: % of responses that pass the threshold. A sudden drop indicates a regression. Alert if it falls more than 5 points in 24h.
  • Score distribution by dimension: see if specifically accuracy drops (possible knowledge base change) or safety (possible prompt injection).
  • Pass rate by segment: break down by query type, channel, language. Problems tend to be segment-specific.
  • Rolling judge-human agreement: recalibrate monthly with a sample of manually labeled cases.
  • Evaluation latency: evaluation should not add more than 500ms to response time if synchronous. If asynchronous, monitor pipeline delay.

CI/CD Integration

# .github/workflows/llm-eval.yml
name: LLM Quality Gate

on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'src/llm/**'

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run evaluation suite
        run: |
          python scripts/run_eval.py \
            --test-suite tests/golden_dataset.jsonl \
            --model ${{ vars.PRODUCTION_MODEL }} \
            --threshold 0.80
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      - name: Check pass rate
        run: |
          PASS_RATE=$(cat eval_results.json | jq '.pass_rate')
          if (( $(echo "$PASS_RATE < 0.80" | bc -l) )); then
            echo "Quality gate failed: pass rate $PASS_RATE < 0.80"
            exit 1
          fi

With this setup, no prompt or model change reaches production without passing through the automatic evaluator. It is the equivalent of unit tests but for LLM behavior.

Conclusion

The LLM-as-judge pattern is not perfect, but it is the best available trade-off today between scale and evaluation quality. With careful implementation:

  • You calibrate the judge with human data (correlation > 0.75)
  • You use a different model from the one being evaluated to avoid self-preference
  • You monitor trends, not individual data points
  • You complement with real business metrics

You can have a quality control system that scales with your AI product, detects regressions before users report them, and gives you actionable data to continuously improve.

At Soamee, we implement automated evaluation systems in every AI project we build for our clients. If you are deploying LLM features and need a robust monitoring system, tell us about your project.


Have questions about implementing LLM-as-judge in your specific stack? Write to us at info@soamee.com.

Don't miss a thing

JM

Javier Manzano

CEO & Co-founder at Soamee

Passionate about technology and software development. Sharing knowledge and experiences to help other developers grow.

Did you enjoy this article?

If you need help with your development project, we are here for you.

LLM-as-Judge: How to Auto-Evaluate AI Output Quality in Production

Tell us your challenge. We'll propose a solution.

No commitment. Within 24 hours, you'll receive a proposal with scope, timeline and budget. No fine print.

Book a free call →