Туториалы AI Ops

LLM-as-Judge: автоматический quality gate для LLM в production

Что такое LLM-as-Judge?

LLM-as-Judge — это паттерн, в котором одна языковая модель автоматически оценивает выходы другой по заданным критериям качества, выступая масштабируемым quality gate в production. В отличие от стандартных метрик мониторинга, он оценивает содержание ответов — обнаруживая галлюцинации, нерелевантность и токсичность — и совпадает с оценкой человека более чем в 80% случаев.

TL;DR

  • -LLM-судья совпадает с оценкой человека в 80%+ случаев — примерно так же, как два человека совпадают друг с другом (81%)
  • -HTTP 200 OK ничего не говорит о качестве ответа — модель может галлюцинировать в 15% случаев без видимых сигналов
  • -Начинать с 2–3 метрик: faithfulness + answer relevance для RAG, relevance + toxicity для чат-ботов, task completion для агентов
  • -DeepEval предоставляет готовые реализации метрик с Pydantic-схемами; Langfuse берёт на себя production-мониторинг и алертинг
  • -Judge-промпт обязан содержать рубрику с явными критериями — расплывчатые инструкции дают нестабильные оценки

LLM-as-Judge — это паттерн, в котором одна языковая модель оценивает выходы другой по заданным критериям. Автоматический quality gate: каждый ответ проходит проверку до попадания к пользователю или после — для мониторинга. Стандартные метрики production-мониторинга (200 OK, latency 340ms, rate limits в норме) бесполезны для оценки качества: модель может галлюцинировать в 15% ответов, а HTTP-статусы об этом ничего не скажут.

Ручная проверка не масштабируется. При 100 запросах в день один человек справляется. При 10 000 уже нет. А деградация качества чаще всего происходит именно на масштабе: после обновления промпта, смены модели или тихого изменения на стороне провайдера.

Эта статья — о том, как устроен LLM-as-Judge, какие метрики оценивать и как интегрировать в production pipeline.

Идея LLM-as-Judge и ограничения подхода

Модель-судья получает промпт с инструкциями, оцениваемый текст и возвращает оценку: числовой score, категорию или structured JSON. Судья не генерирует контент, а классифицирует и оценивает. Модели справляются с этим стабильнее, чем с генерацией.

Пользователь: "Порекомендуй кафе в центре Москвы"


┌──────────────────┐
│   LLM Generator  │ → "Вот 5 кафе: Кофемания на Патриарших..."
│   (GPT-4o-mini)  │
└──────────────────┘


┌──────────────────┐
│   LLM Judge      │ → { relevance: 0.9, factuality: 0.7,
│   (Claude Sonnet) │     toxicity: 0.0, completeness: 0.8 }
└──────────────────┘


   Score < threshold? → Alert / Block / Log

Исследование Zheng et al. (2023, «Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena») показало, что GPT-4 в роли судьи совпадает с человеческими оценками в 80%+ случаев. Два человека-асессора совпадают между собой примерно в 81%. Разрыв между LLM-судьёй и человеком примерно равен разрыву между двумя людьми.

Метрики качества LLM-выходов: что оценивать

Выбор метрик зависит от задачи. Основные категории ниже.

Метрики для RAG-систем

МетрикаЧто проверяетКогда нужна
FaithfulnessОтвет основан на контексте, нет выдуманных фактовВсегда для RAG
Answer RelevanceОтвет соответствует вопросуВсегда
Context RelevanceRetriever вернул релевантные документыОтладка retrieval

Метрики для генеративных задач

МетрикаЧто проверяетКогда нужна
CorrectnessФактическая точностьПри наличии reference answer
CompletenessОтвет покрывает все аспекты запросаСложные запросы
ToxicityНет оскорблений, вредного контентаUser-facing продукты
HallucinationМодель не выдумывает фактыВсегда

Метрики для агентных пайплайнов

МетрикаЧто проверяетКогда нужна
Tool Use CorrectnessПравильный инструмент с правильными аргументамиАгентные пайплайны
Task CompletionКонечный результат решает задачуВсегда для агентов

На практике достаточно начать с двух-трёх метрик. Для RAG: faithfulness + answer relevance. Для чат-бота: relevance + toxicity. Для агента: task completion. Добавлять по мере обнаружения конкретных проблем.

Структура judge-промпта для оценки

Качество оценки определяется промптом. Рабочий шаблон для faithfulness:

FAITHFULNESS_JUDGE_PROMPT = """You are an impartial judge evaluating the faithfulness
of an AI assistant's response.

Faithfulness means: every claim in the response is supported by the provided context.
Claims not found in context = unfaithful.

## Input
**User Question:** {question}
**Retrieved Context:** {context}
**AI Response:** {response}

## Task
1. Extract each factual claim from the AI Response
2. For each claim, check if it is supported by the Retrieved Context
3. A claim is SUPPORTED if the context contains evidence for it
4. A claim is UNSUPPORTED if the context does not mention it or contradicts it

## Output (JSON only)
{{
  "claims": [
    {{"claim": "...", "supported": true/false, "evidence": "..."}}
  ],
  "score": <float 0.0-1.0, ratio of supported claims to total claims>,
  "reasoning": "<one sentence summary>"
}}"""

Что здесь работает:

Конкретные критерии. «Оцени качество ответа» не работает. «Проверь, что каждый факт подтверждён контекстом» работает. Чем конкретнее инструкция, тем стабильнее оценки.

Chain-of-thought. Модель сначала извлекает claims, проверяет каждый, потом выставляет score. Без промежуточных шагов оценки нестабильны.

Structured output. JSON с фиксированной схемой, score от 0 до 1, reasoning в одно предложение. Упрощает парсинг и агрегацию.

Реализация LLM-as-Judge: три подхода

1. Python + LLM API

Минимальная реализация без фреймворков:

import json
from litellm import completion

def evaluate_faithfulness(question: str, context: str, response: str) -> dict:
    judge_response = completion(
        model="anthropic/claude-sonnet-4-20250514",
        messages=[{
            "role": "user",
            "content": FAITHFULNESS_JUDGE_PROMPT.format(
                question=question,
                context=context,
                response=response,
            )
        }],
        response_format={"type": "json_object"},
        temperature=0,
    )

    result = json.loads(judge_response.choices[0].message.content)
    return result

eval_result = evaluate_faithfulness(
    question="Какие кафе в центре Москвы?",
    context="Кофемания: Патриаршие пруды. Сёстры: Покровка 6.",
    response="Рекомендую Кофеманию на Патриарших и Пушкин на Тверском бульваре.",
)
# score: 0.5 (Кофемания подтверждена, Пушкин нет)

Плюсы: полный контроль, минимум зависимостей. Минусы: каждую метрику писать самостоятельно, нет батч-обработки. Если вы работаете с несколькими LLM-провайдерами, litellm позволяет переключаться между ними через единый интерфейс — подробнее об этом в статье о мульти-провайдерной LLM-архитектуре.

2. DeepEval

Open-source фреймворк со встроенными метриками. Работает как pytest для LLM-выходов.

from deepeval import evaluate
from deepeval.test_case import LLMTestCase
from deepeval.metrics import (
    FaithfulnessMetric,
    AnswerRelevancyMetric,
    HallucinationMetric,
)

faithfulness = FaithfulnessMetric(threshold=0.7, model="gpt-4o")
relevancy = AnswerRelevancyMetric(threshold=0.7, model="gpt-4o")
hallucination = HallucinationMetric(threshold=0.5, model="gpt-4o")

test_case = LLMTestCase(
    input="Какие кафе в центре Москвы?",
    actual_output="Рекомендую Кофеманию на Патриарших...",
    retrieval_context=["Кофемания: Патриаршие пруды. Сёстры: Покровка 6."],
)

results = evaluate([test_case], [faithfulness, relevancy, hallucination])

14+ встроенных метрик, интеграция с pytest. Тесты качества LLM запускаются вместе с юнит-тестами:

# test_llm_quality.py
from deepeval import assert_test

def test_travel_recommendations():
    test_case = LLMTestCase(
        input="Кафе в Москве",
        actual_output=run_my_pipeline("Кафе в Москве"),
        retrieval_context=get_retrieved_docs("Кафе в Москве"),
    )
    assert_test(test_case, [faithfulness, relevancy])

3. Langfuse Evaluations

Если Langfuse уже используется для трейсинга, evaluations подключаются поверх. Judge-модель запускается по каждому трейсу, score привязывается к нему. Оценки можно привязывать как ко всему трейсу, так и к отдельным observations. Если вы ещё не настроили observability-стек, начните с практического руководства по LLM observability с Langfuse.

langfuse.score(
    trace_id="trace-abc-123",
    name="faithfulness",
    value=0.85,
    comment="1 из 7 claims не подтверждён контекстом",
)

Для production-мониторинга Langfuse подходит лучше DeepEval: оценки привязаны к реальным трейсам, видны в дашборде, графики деградации качества по дням.

Интеграция LLM-as-Judge в CI/CD и production pipeline

Pre-deploy: регрессионное тестирование промптов

Изменился промпт? Прогнать dataset через judge-модель до деплоя. Score ниже порога, деплой заблокирован.

# .github/workflows/llm-quality.yml
name: LLM Quality Gate
on: [pull_request]

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install deepeval
      - run: deepeval test run test_llm_quality.py
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Runtime: gate перед ответом

Для критичных задач, оценка до отправки ответа:

async def generate_with_quality_gate(question: str) -> str:
    response = await generate_response(question)

    eval_result = await evaluate_faithfulness(
        question=question,
        context=retrieved_context,
        response=response,
    )

    if eval_result["score"] < 0.7:
        return "Извините, я не уверен в точности ответа. Попробуйте переформулировать вопрос."

    return response

Дополнительный LLM-вызов на каждый запрос. GPT-4o-mini в роли судьи стоит $0.15 за миллион входных токенов. На 10 000 запросов в день с промптом ~500 токенов: ~$0.75/день.

Post-hoc: мониторинг по выборке

Самый распространённый сценарий. Оценка запускается асинхронно:

import random

traces = langfuse.fetch_traces(limit=100)
sample = random.sample(traces.data, min(100, len(traces.data)))

scores = []
for trace in sample:
    result = evaluate_faithfulness(
        question=trace.input,
        context=trace.metadata.get("context", ""),
        response=trace.output,
    )
    scores.append(result["score"])
    langfuse.score(trace_id=trace.id, name="faithfulness", value=result["score"])

avg_score = sum(scores) / len(scores)
if avg_score < 0.75:
    send_alert(f"Faithfulness degraded: {avg_score:.2f}")

Дешевле runtime gate, зато ловит тренды. Средний faithfulness упал с 0.88 до 0.71 за неделю, значит что-то сломалось: промпт, retriever, обновление модели на стороне провайдера.

Подводные камни LLM-as-Judge

Position bias

Judge-модели систематически предпочитают ответ, стоящий первым при попарном сравнении. Zheng et al. (2023) зафиксировали сдвиг до 10-15%. Решение: запускать оценку дважды, меняя порядок, и усреднять. Или использовать pointwise scoring вместо pairwise.

Verbosity bias

Длинные ответы получают более высокие оценки, даже если короткий ответ точнее. В judge-промпте явно указать «длина ответа не влияет на оценку» и добавить пример, где короткий ответ получает высший балл.

Self-enhancement bias

GPT-4 ставит более высокие оценки текстам GPT-4. Claude предпочитает тексты Claude. Выход: judge-модель другого провайдера, чем generator. Генерация на GPT-4o, оценка на Claude Sonnet. Или наоборот. Проблема доверия к LLM-выходам в целом — тема отдельного разговора, подробнее в статье TruthGuard: когда AI-агенты врут.

Стоимость

Каждая оценка это LLM-вызов. Для runtime gate на 10 000 запросов/день получается 10 000 дополнительных вызовов. Варианты: дешёвая модель как judge (GPT-4o-mini, Claude Haiku), оценка по выборке, кеширование оценок для похожих пар.

Judge тоже галлюцинирует

Judge-модель может поставить высокий score ответу с выдуманными фактами, если галлюцинация звучит правдоподобно. Частичное решение: chain-of-thought + structured output. Полного решения нет. Это фундаментальное ограничение подхода.

Выбор модели-судьи для разных сценариев

СценарийJudge-модельПочему
Pre-deploy тестыGPT-4o или Claude Sonnet 4Точность важнее скорости
Runtime gateGPT-4o-mini или Claude HaikuДёшево и быстро
Post-hoc мониторингGPT-4o-miniМассовая обработка

Правило: judge-модель не слабее генератора. GPT-4o-mini как judge для GPT-4o-mini работает. GPT-4o-mini как judge для Claude Opus ненадёжно.

temperature=0 для judge-вызовов обязателен.

Инструменты для LLM evaluation

ИнструментФокусLLM-as-JudgeSelf-hostedЦена
DeepEvalТестирование14+ метрикДа (OSS)Бесплатно
RagasRAG-оценкаFaithfulness, relevanceДа (OSS)Бесплатно
LangfuseObservability + evalsEvaluator templatesДа (OSS)Бесплатно (self-hosted)
Phoenix (Arize)Observability + evalsHallucination, QAДа (OSS)Бесплатно
BraintrustEvals + loggingCustom scorersCloudFree tier

Для стартапа: DeepEval для pre-deploy тестов + Langfuse для production мониторинга. Два open-source инструмента закрывают весь цикл.

Production-сетап LLM-as-Judge

┌───────────────────────────────────────────────────┐
│                   CI/CD Pipeline                   │
│                                                    │
│  PR с изменением промпта                          │
│      │                                            │
│      ▼                                            │
│  DeepEval: dataset × new prompt → scores          │
│      │                                            │
│      ▼                                            │
│  Score < threshold? → Block merge                 │
└───────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────┐
│                   Production                       │
│                                                    │
│  User request → LLM → Response → User             │
│                   │                                │
│                   ▼ (async)                        │
│              Langfuse trace                        │
│                   │                                │
│                   ▼ (cron, hourly)                 │
│         Judge evaluation (sample)                  │
│                   │                                │
│                   ▼                                │
│         Score dashboard + alerts                   │
└───────────────────────────────────────────────────┘

С чего начать: пошаговый план

  1. Выбрать одну метрику. Для RAG: faithfulness. Для чат-бота: answer relevance.
  2. Собрать 20-30 примеров вручную: вопросы, ответы, оценки (хорошо/плохо). Golden dataset для калибровки.
  3. Написать judge-промпт, прогнать по golden dataset. Совпадение с человеческими оценками ниже 70%? Править промпт.
  4. Подключить DeepEval в CI для тестов при изменении промптов.
  5. Настроить Langfuse evaluations для production мониторинга.

От нуля до рабочего quality gate: два-три дня. Golden dataset + judge-промпт: пара часов.