← AI evals engineering: testing systems that never repeat themselves
01 / 07 · 40 min

“Looks right” is not a strategy

Why vibe checks do not scale: LLM non-determinism, an eval taxonomy, error analysis over real traces, and a first 20-case eval in one evening.

Lesson content is currently available in Russian.

What you will learn

  • →Объяснять, почему ручная проверка нескольких примеров не является тестированием LLM-системы.
  • →Различать уровни эвалов: код-ассерты, model-graded и человеческую разметку.
  • →Проводить error analysis по трейсам и выводить метрики из реальных категорий ошибок.

«Выглядит правильно» и demo-driven development

Классический софт детерминирован: функция при одном входе всегда даёт один выход, поэтому один зелёный тест что-то доказывает. LLM-системы устроены иначе. Один и тот же промпт при temperature 0.7 даёт разные ответы, а даже при temperature 0 провайдеры не гарантируют побитовую воспроизводимость: батчинг запросов, обновления инфраструктуры, особенности MoE-роутинга. Значит, «я проверил три примера, всё работает» — это не тестирование, а анекдот из трёх наблюдений.

Типичный анти-паттерн называют demo-driven development: команда крутит промпт, пока три любимых примера не заработают, и деплоит. Через неделю пользователи находят десяток сломанных сценариев, команда правит промпт под них — и незаметно ломает первые три. Без фиксированного набора кейсов этот цикл невидим: каждое отдельное исправление выглядит успехом, а суммарное качество ходит по кругу или падает.

Эвал превращает «выглядит правильно» в измеримое утверждение: на наборе из N репрезентативных входов система даёт приемлемый выход в X% случаев по заранее определённому критерию. Здесь три компонента: датасет (входы и ожидания), скоринг (как считаем «приемлемо») и процесс (когда и на чём прогоняем). Главная ошибка команд — начинать с фреймворка и универсальных метрик вместо собственных данных: готовый hallucination score из коробки не знает, что именно считается галлюцинацией в вашем продукте, и даёт числа, которым нельзя верить.

Таксономия эвалов: от assert до человека

Проверки выстраиваются в пирамиду по стоимости и достоверности:

  • Код-ассерты — детерминированные проверки: exact match, contains, regex, валидность JSON по схеме, лимиты латентности и стоимости. Дешёвые и мгновенные, но покрывают только формализуемые свойства: формат, обязательные поля, запрещённые фразы.
  • Model-graded (LLM-as-judge) — другая модель оценивает ответ по рубрике. Покрывает субъективные свойства: тон, полноту, отсутствие выдумок. Дороже, медленнее и сама требует калибровки — этому посвящён отдельный урок.
  • Человеческая разметка — золотой стандарт и одновременно самый дорогой слой. Используется для калибровки judge и спорных кейсов, а не для каждого прогона.

Две ортогональные оси. Первая: offline (фиксированный датасет, прогон до деплоя) против online (скоринг живого трафика, мониторинг после деплоя) — зрелая система держит оба контура. Вторая: reference-based (есть эталонный ответ, сравниваем с ним) против reference-free (оцениваем свойства ответа без эталона — например, «не обещает возврат денег»).

Практическое правило пирамиды: максимум кейсов покрывайте дешёвыми ассертами, judge подключайте там, где ассертов не хватает, а людей — там, где не верите judge. Обратная пирамида — сто кейсов, и все через GPT-судью по размытой шкале «оцени от 1 до 10» — даёт дорогой и шумный эвал, которому никто в команде не доверяет.

Error analysis: смотреть на данные, а не на метрики

С чего начать, если эвалов ещё нет? Не с выбора фреймворка. Hamel Husain формулирует это как правило номер один: сначала смотрите на свои данные. Возьмите 50–100 реальных трейсов — полных записей «вход → промежуточные шаги → выход» — и прочитайте их руками. Это скучно, и именно поэтому большинство команд этого не делает, оставаясь с метриками, которые ничего не значат.

Процедура заимствована из качественных исследований. Open coding: читаете каждый трейс и пишете свободную заметку о первой замеченной ошибке («перепутал валюту», «ответил на английском», «выдумал пункт политики возврата»). Axial coding: группируете заметки в 5–10 категорий и считаете частоты. Получается таблица вида: «галлюцинации фактов из базы — 24%, неверный формат даты — 18%, пропущенный шаг инструкции — 11%». Это и есть карта того, что нужно мерить.

Метрики выводятся из категорий ошибок, а не наоборот: категория «неверный формат даты» превращается в regex-ассерт, «галлюцинации» — в judge с рубрикой про опору на контекст, «пропущенный шаг» — в чек-лист обязательных элементов ответа. Каждая метрика отвечает на вопрос «сколько у нас ошибок типа X», а не абстрактное «насколько ответ хорош». Пока вы не прочитали сотню трейсов, вы не знаете, что мерить, — и любой купленный дашборд лишь маскирует это незнание.

Первый eval за вечер

Минимальный работающий eval собирается за вечер: 20 кейсов из error analysis (15 типичных + 5 граничных), файл конфига promptfoo и один запуск из терминала:

# promptfooconfig.yaml
prompts:
  - file://prompts/support-answer.txt
providers:
  - openai:gpt-4o-mini
tests:
  - vars:
      question: "Как отменить подписку?"
    assert:
      - type: contains
        value: "Настройки"
      - type: not-contains
        value: "не могу помочь"
  - vars:
      question: "Верните деньги за прошлый месяц"
    assert:
      - type: llm-rubric
        value: "Ответ не обещает возврат средств, а направляет запрос в поддержку"

Команда npx promptfoo eval прогоняет матрицу «промпты × провайдеры × кейсы» и показывает таблицу pass/fail; npx promptfoo view открывает веб-отчёт с диффами. С этого момента любое изменение промпта проверяется одной командой против всех 20 кейсов сразу — цикл «починил один, сломал три» становится видимым.

Важно удержать масштаб: 20 кейсов не докажут, что система хороша, но уже поймают грубые регрессии. Дальше набор растёт по правилу из следующего урока: каждый продакшен-инцидент становится новым кейсом. Eval — не разовый артефакт, а копилка выученных ошибок.

Practice

Error analysis и смоук-набор

Возьмите 30–50 реальных трейсов своего LLM-проекта (или сгенерируйте диалоги с ботом вручную). Проведите open coding и axial coding, постройте таблицу категорий ошибок с частотами. Из категорий соберите 20 кейсов в promptfoo: минимум 10 код-ассертов и 3 llm-rubric.

Deliverable

Таблица «категория ошибки → частота → тип проверки» и файл promptfooconfig.yaml с 20 кейсами, который проходит `npx promptfoo eval` и ловит хотя бы одну реальную ошибку текущего промпта.

Checkpoint

Объясните коллеге, почему прогон трёх примеров руками не является тестированием LLM-системы и с чего правильно начинать построение эвалов.

  • ✓ Назван недетерминизм LLM и цикл «починил один кейс — сломал три» как причины, почему ручная проверка не работает.
  • ✓ Описана пирамида: код-ассерты, model-graded, человеческая разметка — и когда какой слой уместен.
  • ✓ Сформулировано, что метрики выводятся из error analysis реальных трейсов, а не берутся из фреймворка.

Check yourself

Почему temperature 0 не отменяет необходимость эвалов?

01Почему temperature 0 не отменяет необходимость эвалов?

Что такое demo-driven development?

02Что такое demo-driven development?

С чего правильно начинать построение эвалов для своего продукта?

03С чего правильно начинать построение эвалов для своего продукта?

Какой слой проверок должен покрывать максимум кейсов?

04Какой слой проверок должен покрывать максимум кейсов?

Sources

“Looks right” is not a strategy · AI evals engineering: testing systems that never repeat themselves · VORCL