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 Relevance | Retriever вернул релевантные документы | Отладка 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 gate | GPT-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-Judge | Self-hosted | Цена |
|---|---|---|---|---|
| DeepEval | Тестирование | 14+ метрик | Да (OSS) | Бесплатно |
| Ragas | RAG-оценка | Faithfulness, relevance | Да (OSS) | Бесплатно |
| Langfuse | Observability + evals | Evaluator templates | Да (OSS) | Бесплатно (self-hosted) |
| Phoenix (Arize) | Observability + evals | Hallucination, QA | Да (OSS) | Бесплатно |
| Braintrust | Evals + logging | Custom scorers | Cloud | Free 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 │
└───────────────────────────────────────────────────┘
С чего начать: пошаговый план
- Выбрать одну метрику. Для RAG: faithfulness. Для чат-бота: answer relevance.
- Собрать 20-30 примеров вручную: вопросы, ответы, оценки (хорошо/плохо). Golden dataset для калибровки.
- Написать judge-промпт, прогнать по golden dataset. Совпадение с человеческими оценками ниже 70%? Править промпт.
- Подключить DeepEval в CI для тестов при изменении промптов.
- Настроить Langfuse evaluations для production мониторинга.
От нуля до рабочего quality gate: два-три дня. Golden dataset + judge-промпт: пара часов.