/ Дневник курса / Урок 20

Тестирование ИИ-агента и оценка качества ответов

Зелёная сборка не говорит о качестве ответа. Как собирают золотой набор кейсов и что замеры показывают про надёжность модели-судьи.

  • evals
  • testing
  • llm-judge
  • quality-gate
Содержание
  1. Что проверяет зелёная сборка и чего она не видит
  2. Кандидатом на замену становится конфигурация целиком
  3. Что записывают в золотой кейс
  4. Двадцать кейсов не измеряют процент
  5. Кодом проверяют факты, моделью-судьёй — смысл
  6. Поверка судьи: что известно про сам прибор
  7. Правила допуска, которые не усредняют
  8. Откуда берутся новые кейсы
  9. Проверка, которая не работает, выглядит как работающая
  10. Итог
  11. FAQ
  12. Источники

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

Вопрос, на который после такой недели никто в команде не ответит, звучит просто. Агент стал лучше или хуже? Тесты говорят ровно то, что код не упал и JSON собрался. Клиент видит другое. Он получает ответ, в котором сумма комиссии верная, а срок возврата взят из позапрошлогоднего регламента. Отличить такой ответ от правильного нечем. Оба приходят двухсотым кодом, оба разбираются парсером, оба написаны одинаково уверенным тоном.

Ответ на вопрос «лучше или хуже» сам собой не появляется. Его даёт набор заранее разобранных обращений с записанными ожиданиями, набор проверок над этими ожиданиями и согласованное правило, при каком результате изменение пускают в основную ветку. Часть проверок пишется обычным кодом. Там, где правильных ответов много и все они разные, оценку выносит отдельная языковая модель, которую просят проверить ответ по узким критериям. Она и есть самое любопытное место всей конструкции: измерительный прибор, собранный из той же вероятностной модели, качество которой он меряет.

Дневник курса, урок 20. Здесь понадобится разобранное раньше: калибровка модели-судьи (урок 7) — каталог её смещений, две строки из которого придётся поправить; внешний арбитр (урок 16) — почему агент не чинит себя сам; выкатка новой версии — те же вопросы, но на живом трафике. Пост читается отдельно: все термины вводятся заново.

Сквозным примером идёт тот же чат-бот банка, что и в прошлых уроках. Он разбирает 8000 обращений в сутки, тратит около пятнадцати ходов на диалог, ходит за остатками по счетам и оформляет возвраты.

1. Что проверяет зелёная сборка и чего она не видит

Обычный тест устроен как сравнение. Подали вход, получили выход, сверили с ожидаемым. У агента такого ожидаемого выхода нет. На обращение «верните комиссию за обслуживание» существуют десятки правильных ответов, отличающихся формулировкой, порядком аргументов и уровнем вежливости, и ни один из них не записать в assert. Зато можно записать свойства, которыми любой правильный ответ обязан обладать. Отсюда и разделение, на котором держится весь урок: юнит-тесты проверяют механику, а оценка качества (evals) — поведение продукта на реальных запросах.

Разница видна на трёх обращениях, каждое из которых наш бот обрабатывает без единой технической ошибки.

обращение                    что вернул агент          что сломалось

«верните комиссию            корректный JSON,          правило по умолчанию:
 за обслуживание»            срок из регламента        возврат считается за
                             2024 года                 текущий период

«какой остаток               остаток назван,           опора на факты:
 на моём счёте?»             сумма из прошлого         число взято не из
                             диалога                   ответа инструмента

«переведите 50 000           перевод оформлен          явное ограничение:
 брату»                      без подтверждения         необратимое действие
                                                       требует эскалации

во всех трёх случаях: HTTP 200 · схема валидна · инструмент вызван без ошибки
Рисунок — три обращения, на которых механика отработала штатно: HTTP-код двухсотый, схема ответа валидна, инструмент отвечал без ошибки. Сломались при этом три разных вещи — правило по умолчанию, опора на факты из инструмента и явное ограничение на необратимое действие. Ни одну из них не различает общий балл «ответ хороший или плохой».

Классы ошибок в этой таблице разные по природе. Срок возврата проверяется сравнением с датой, и это дешёвый детерминированный код. Остаток на счёте проверяется сверкой с тем, что вернул инструмент, и это тоже код, только чуть сложнее. Пропущенная эскалация — сравнение флага с ожиданием, снова код. А вот «объяснение выдумано, хотя вывод верен» кодом не берётся никак, потому что придётся понять смысл текста. Разным ошибкам нужны разные проверки.

Пока важно другое следствие. Фраза «мы прогнали тесты, всё зелёное» после появления языковой модели в контуре означает меньше, чем раньше. Она означает, что механика цела. Про поведение продукта она молчит и будет молчать, пока для него не собран отдельный набор проверок.

2. Кандидатом на замену становится конфигурация целиком

Первый рефлекс при словах «тестируем модель» — сравнить две модели между собой. Рефлекс сбивает прицел, потому что ответ агента складывается не только из самой модели. Его определяют системный промпт, набор доступных инструментов, версия базы знаний и настройки, с которыми модель выбирает следующее слово. Смена любого из этих слоёв меняет ответы, и прежние замеры к новому агенту уже не относятся. Значит, новый промпт или новая база знаний — это тоже новый кандидат, и проверять его надо тем же прогоном.

Практически это означает, что версия агента записывается не строкой с именем модели, а набором.

базовая версия                  кандидат 1                кандидат 2

промпт      v4                  промпт      v4            промпт      v4
модель      сильная             модель      средняя       модель      эконом
база знаний v6                  база знаний v6            база знаний v6
инструменты 7                   инструменты 7             инструменты 7

              меняем одну ось за раз, остальные фиксируем
              сравниваем на одном и том же наборе кейсов
Рисунок — три конфигурации отличаются ровно одной осью, моделью, при совпадающих промпте v4, базе знаний v6 и наборе из семи инструментов. Если поменять одновременно промпт и модель, просадка на кейсах будет видна, а причина — нет.

Правило «одна ось за раз» звучит очевидно, а нарушается всякий раз, когда промпт и базу знаний правят в одном спринте. Прогон стоит времени, и обе правки уезжают одним кандидатом. Плата за это конкретная. Красный прогон не показывает, что чинить, и разбирательство начинается с бинарного поиска по собственным коммитам.

Здесь же проходит граница с выкаткой новой версии. Там кандидат сравнивается с базовой версией на живом трафике, и решение звучит как «оставить или откатить». Здесь всё происходит до этого, на фиксированном наборе, и решение звучит как «пустить изменение в основную ветку или нет». Наборы данных, статистика и цена ошибки в этих двух местах разные, так что за одно и то же их принимать не стоит.

3. Что записывают в золотой кейс

Раз эталонного текста ответа не существует, кейс хранит описание того, что должно оказаться правдой про любой годный ответ. Такой проверенный набор кейсов с записанными ожиданиями называют золотым набором (golden dataset), и полезен он ровно настолько, насколько аккуратно заполнены поля отдельного кейса.

{
    "id": "fee_refund_current_period",
    "query": "Верните комиссию за обслуживание, списали 3 сентября",
    "risk": "critical",            # решает судьбу прогона, см. раздел 7
    "slice": "refund_rules",       # срез, по которому смотрят результат
    "expect": {
        "period": "current",       # правило по умолчанию из конфига
        "amount_source": "tool",   # сумма только из ответа инструмента
        "escalate": False,
    },
    "forbidden": ["перерасчёт за прошлые периоды"],
    "reference": "Комиссия возвращается за текущий расчётный период …",
    "provenance": "support-log-masked",   # откуда взялся кейс
}

Полей здесь больше, чем кажется необходимым, и каждое отрабатывает свою функцию. По expect код выносит строгий PASS или FAIL. risk определяет, блокирует ли падение этого кейса весь прогон. slice позволяет смотреть результат по группам однородных обращений вместо общего процента. provenance отвечает на вопрос, откуда кейс взялся и кому верить, если он окажется неправ.

У правил по умолчанию своя история. Соблазн записать «период текущий» прямо в кейс велик, но тогда продуктовое правило размажется по сотне файлов, и его смену придётся вносить сотней правок. В разобранном ниже учебном репозитории такие правила вынесены в отдельный файл политики и подмешиваются в ожидания кейса уже на прогоне. Приём переносится на банк без изменений: правило «возврат считается за текущий расчётный период» живёт в одном месте, а кейсы на него ссылаются.

Теперь про reference. Обычно золотой кейс противопоставляют эталонному тексту. Свойства храним, текст не храним. Однако замер говорит, что эталон всё-таки нужен, просто не коду, а модели-судье. В работе No Free Labels 160 сложных вопросов по финансам и бизнесу составили и разметили практикующие финансовые специалисты, после чего эксперты оценили корректность 1200 ответов от разных моделей. Результат неприятный. Без эталона в контексте судья согласуется с экспертом только на тех вопросах, которые сам умеет решать верно. Там, где он ошибается, он ошибается и в оценке. Эталон, написанный человеком, эту зависимость в основном снимает, и более слабая модель с человеческим эталоном под рукой соглашается с экспертами чаще, чем сильная закрытая модель, опирающаяся на эталон, который сочинила сама.

Отсюда практическое правило, которого нет в обычном описании золотого набора. Свойства нужны коду, эталон нужен судье, и у одного кейса оказывается сразу два потребителя.

4. Двадцать кейсов не измеряют процент

Совет для старта обычно звучит мягко. Возьмите пятнадцать-двадцать ситуаций, а дальше набор дорастёт сам от реальных ошибок. Как способ начать — это разумно, потому что двадцать разобранных обращений лучше нуля. Как измерительный прибор такой набор не работает, и полезно понимать, до какой степени.

Посмотрим, что стоит за фразой «кандидат дал 90%». Если это 18 правильных ответов из 20, то доверительный интервал вокруг доли, посчитанный по методу Уилсона, растягивается почти на треть шкалы.

результат          доля      95% интервал Уилсона   ширина

18 / 20            90,0%     [69,9%; 97,2%]         27,3 п.п.
16 / 20            80,0%     [58,4%; 91,9%]         33,5 п.п.
22 / 24            91,7%     [74,2%; 97,7%]         23,5 п.п.
90 / 100           90,0%     [82,6%; 94,5%]         11,9 п.п.

размер набора, нужный, чтобы различить два уровня
(мощность 80%, α = 0,05, двусторонний тест долей)

  95% против 90%                    ~435 кейсов на вариант
  95% против 85%                    ~141
  90% против 80%                    ~199
Рисунок — интервалы посчитаны по формуле Уилсона, размеры набора — по двустороннему тесту долей; замера за этими числами не стоит. На двадцати кейсах интервал вокруг 90% занимает 27 пунктов, на сотне сжимается до 12, а чтобы уверенно отличить 95% от 90%, набор должен вырасти примерно до 435 кейсов на вариант.

Числа объясняются просто. Шаг одного кейса на наборе из двадцати равен пяти процентным пунктам, поэтому «90% против 85%» — это буквально один ответ, который в следующий раз мог выпасть иначе. Хуже то, что обычные способы посчитать погрешность на таком наборе врут в опасную сторону. Позиционная статья ICML 2025 Don't Use the CLT in LLM Evals показывает, что методы на основе центральной предельной теоремы уместны, пока в наборе тысячи примеров, а на маленьких специализированных наборах они систематически занижают неопределённость, рисуя слишком узкие доверительные интервалы. То есть команда, увидевшая «91% против 93%», получит убедительно тонкий интервал и поверит в регрессию, которой нет.

Есть и второй источник заниженной погрешности. Кейсы в наборе редко независимы: пять формулировок про одно и то же списание — это один кластер, а не пять наблюдений. Разбор Anthropic про статистику эвалов сообщает, что на популярных наборах кластеризованная стандартная ошибка бывает более чем втрое больше наивной. Наш банковский набор кластеризован по построению, потому что кейсы группируются вокруг сценариев.

Оттуда же приходит способ померить точнее, не заплатив за это ничем. Про него обычно забывают. Кандидата и базовую версию гоняют на одних и тех же кейсах, значит, сравнивать их надо попарно, кейс против кейса. Сложность вопроса при этом вычитается целиком. Anthropic измерил корреляцию оценок по вопросам между передовыми моделями и получил диапазон 0,3–0,7. Модели ошибаются на одних и тех же кейсах, поэтому разница между ними меряется точнее, чем каждая по отдельности.

И третье, про повторные прогоны. Умножение «шесть кейсов × три конфигурации × три повтора = 54 ответа» считается за секунду и звучит как готовый план. Само по себе повторение обосновано. Anthropic прямо рекомендует пересемплировать ответы модели несколько раз, когда оценка идёт по задачам с цепочкой рассуждений, и усреднять по вопросу. Рекомендуемого числа повторов нет ни в одном из прочитанных нами источников, так что тройка в такой формуле держится на соглашении команды, а не на замере. Мы вернёмся к повторам в разделе 6, где выяснится, что от одного важного класса ошибок они не спасают вовсе.

Что со всем этим делать на двадцати кейсах? Есть распространённый и правильный совет. Смотреть надо на отдельные кейсы и на срезы риска, а общий процент держать справочной строкой. Обычно его объясняют тем, что среднее прячет критическую регрессию. Настоящая причина жёстче. На таком наборе среднее не измеряет ничего, и держится решение исключительно на правиле «критических отказов ноль», которое ни в каком усреднении не участвует. Совет от этого не отменяется. Он становится единственно возможным.

5. Кодом проверяют факты, моделью-судьёй — смысл

Раз проверок нужно много и разных, встаёт вопрос распределения работы. Разделение проходит по природе ошибки, и правило простое. Всё, что можно сверить со значениями, проверяется кодом, и только там, где придётся понять смысл текста, зовут модель. Порядок именно такой, потому что проверка кодом дешёвая и воспроизводимая, а вместо балла называет точную причину провала.

def grade_amount_source(answer, tool_calls):
    """Названная сумма обязана совпасть с ответом инструмента."""
    quoted = extract_amounts(answer.text)
    returned = {c.result["amount"] for c in tool_calls if c.name == "get_fee"}
    invented = quoted - returned
    if invented:
        return Fail(f"суммы нет в ответе инструмента: {sorted(invented)}")
    return Pass()

def grade_period(answer, policy):
    want = policy.default_period
    if answer.period != want:
        return Fail(f"период {answer.period}, по политике {want}")
    return Pass()

def grade_escalation(answer, case):
    want = case.expect["escalate"]
    if answer.escalate != want:
        return Fail(f"эскалация {answer.escalate}, ожидалась {want}")
    return Pass()

Кейс считается пройденным, только если прошли все проверки этого кейса. Балл здесь не складывается. Одна упавшая проверка означает FAIL целиком, потому что «ответ на три четверти правильный» для клиента не существует.

Дальше остаётся то, что кодом не проверить. Полезно ли объяснение, честно ли агент признал, что подходящего варианта нет, не выдумал ли он причину отказа. Этим занимается модель-судья (LLM-as-a-judge). Ответ агента уходит отдельным вызовом в языковую модель, и та оценивает его по заранее записанным критериям. Ключевое слово тут «записанным». Судье задают не «оцени качество», а два-три узких вопроса с требованием привести цитату-довод по каждому. Эффект от такой формы измерен и невелик, но положителен: структурированные рубрики поднимают согласие судьи с человеком не больше чем на 6,5 п.п., правда, не одинаково для всех пар «судья — генератор».

Есть у разбиения оценки на измерения и второй эффект, менее ожидаемый. Судьи склонны выше оценивать ответы, похожие на их собственные, и на 20 распространённых моделях измерили, что структурная многомерная схема оценки снижает это предпочтение в среднем на 31,5%. Замер сделан на автоматически сконструированных парах ответов равного качества, чтобы отделить способность различать качество от склонности к перекосу. Попутно там же выяснилось, что общая сила модели с несмещённостью не связана, а местами связана отрицательно. Так что судья из модели поумнее сам по себе ничего не чинит.

Третий слой держит человек-эксперт. В каждом прогоне он не участвует, а размечает спорные случаи, по которым потом сверяют судью. Это и есть поверка судьи, в буквальном метрологическом смысле. Прибор допускают к измерениям после сверки с эталоном. Правдоподобные числа сами по себе допуском не служат.

У эксперта есть и вторая роль, которая обычно достаётся ему раньше первой. Продуктовое правило он приносит кейсом в пул-реквесте, где записаны само обращение, ожидаемые свойства ответа и ссылка на пункт политики. Инженер держит прогон, кодовый агент в редакторе помогает оформить кейс по формату набора, и правило дальше живёт в git с версией и автором. Красный статус показывает, какое именно ожидание сломалось, а исправленная сборка возвращается зелёной. Так золотой набор работает общим языком эксперта, инженера и кодового агента. Продуктовое требование записано в нём так, что исполнение проверяет машина.

Вернёмся к коду и судье. Их легко счесть двумя мнениями об одном и том же. Продольное исследование конвейера оценки на живой мультиагентной системе охватило 38 прогонов на двух десятках релизов, с калибровкой по 60 стратифицированным случаям и двумя независимыми оценщиками. Там измерили согласие между вердиктом судьи и структурным гейтом и получили каппу 0,13. По шкале, где единица означает полное совпадение, а ноль — совпадение на уровне случайности, это почти отсутствие согласия.

              что видит структурная проверка кодом
              ┌────────────────────────────────────────┐
              │ превышен порог времени ответа          │
              │ обращение ушло не в тот маршрут        │
              │ поле схемы пустое, сумма не из вызова  │
              └────────────────────────────────────────┘

              что видит модель-судья
              ┌────────────────────────────────────────┐
              │ формально верно, но клиенту бесполезно │
              │ объяснение выдумано                    │
              │ отказ дан грубо, без альтернативы      │
              └────────────────────────────────────────┘

              согласие между вердиктами: каппа = 0,13
              пересечение почти пустое — это разные приборы
Рисунок — каппа 0,13 между судьёй и структурным гейтом означает, что они ловят непересекающиеся классы отказов: нарушения времени ответа и ошибки маршрутизации невидимы в тексте ответа, а бесполезный по содержанию ответ проходит все структурные проверки.

Вывод из этого числа практический. Складывать два вердикта в один общий балл нельзя, они про разное. Держать надо оба, и решение принимать по каждому отдельно.

У того же исследования есть ещё одна находка, полезная на практике. Тяжёлые регрессии надёжнее всего отличает не доля успешных ответов, а покрытие ответов ссылками на источники. Признак структурный, дешёвый и проверяемый кодом.

6. Поверка судьи: что известно про сам прибор

Судья попал внутрь контура, чтобы мерить смысл, до которого код не дотягивается. Значит, законен встречный вопрос: а какова погрешность самого судьи? Ответ на сегодня выглядит так, что судью можно держать в конвейере, но нельзя вешать на его балл единственное решение. Крупнейший систематический замер охватил 21 судью от девяти поставщиков, три протокола, 118 прогонов и около 541 000 отдельных вердиктов на наборах MT-Bench, JudgeBench и RewardBench. Называется он «Надёжность без валидности», и название отражает главный результат точно.

Первый вывод оттуда касается самого цитируемого числа в этой области. Утверждение «судья согласен с человеком более чем в 80% случаев, то есть на уровне согласия людей между собой» пришло из работы про MT-Bench и Chatbot Arena, и оно верное. Только это доля точных совпадений, не поправленная на случайное угадывание. Если посчитать каппу Коэна, которая вычитает согласие, ожидаемое от двух независимых бросков монеты с теми же частотами ответов, от доли отваливается 33–41 процентный пункт, и на MT-Bench это универсально для всей когорты. То же исследование сообщает, что порядок судей в рейтинге сдвигается между разными наборами до 14 позиций, так что «лучший судья» — характеристика набора, а не модели.

Второе следствие ломает интуицию сильнее и бьёт прямо по повторным прогонам из раздела 4.

                        повторяемость          позиционное смещение
                        (тот же вердикт        (перестановка местами
                         при повторе)           меняет вердикт)

два судьи, работающих
в проде                    > 0,95                    > 0,10

повторный прогон           ловит                     не ловит
                           случайный разброс         систематический перекос
Рисунок — парадокс стабильности и смещения: у двух работающих в проде судей повторяемость вердикта выше 0,95 при позиционном смещении выше 0,10. Три прогона подряд согласованно подтвердят один и тот же перекос, потому что повторение меряет разброс, а не правоту.

Прибор, который врёт одинаково, точнее от этого не становится. Воспроизводимость ничего не говорит о правоте и исправно маскирует её отсутствие. Прогон, дающий один и тот же вердикт трижды, выглядит убедительно именно тогда, когда судья устойчиво ошибается. Ловится это только сверкой с человеческой разметкой, то есть той самой поверкой.

Третья находка касается потолка, которого не видно на текстовых бенчмарках. Оценка ответа в чате и оценка траектории агента, где были вызовы инструментов, ветвления и промежуточные результаты, — задачи разной сложности. Первый бенчмарк судей именно на агентных вызовах инструментов (3808 инстансов, шесть топологий графа, три уровня сложности, шесть судей от 20 миллиардов параметров до передовых моделей) даёт неприятную картину. Согласие судьи с человеком падает монотонно с ростом сложности задачи, причём без эталонного ответа в полтора раза быстрее. На сложных запросах без эталона все шесть судей сходятся в узкий коридор 77–82% независимо от масштаба модели. Потолок структурный, и деньгами он не берётся.

Там же измерены две вещи, которые принято считать лекарствами.

Цепочка рассуждений перед вердиктом даёт пренебрежимый эффект. Это прямая правка к тому, что записано у нас в контрмерах против смещений судьи (урок 7): среди процессных приёмов там стоит и требование рассуждать до вердикта. На агентных траекториях оно измерено и не работает. Эффект температуры, которая задаёт разброс при выборе следующего слова, у судьи тоже пренебрежимый. Работающими остаются рандомизация порядка в парных сравнениях и коллегия судей разных семейств, а надёжнее процессных приёмов оказывается калибровка по якорям — эталонным оценкам, которые эксперт проставил вручную.

Эталонный ответ в контексте судьи помогает не всем. Для двух передовых моделей согласие от него упало на 1,5 и 3,9 процентного пункта. Авторы объясняют это переякорением, при котором судья сверяет ответ с формулировкой эталона вместо того, чтобы оценивать его по сути. Совет из раздела 3 остаётся в силе, но действие его надо проверить на своей паре «судья — задача», прежде чем принимать на веру.

Заодно поправим ещё одну строку. В каталоге смещений урока 7 стоит verbosity bias величиной 0,5–1,5 балла: судья завышает оценку за лишний абзац-резюме. Новый замер даёт для того же явления величину меньше 0,011. Складывать эти числа в динамику «смещение почти исчезло» нельзя. Первое измерено в баллах пятибалльной шкалы, второе — в своих единицах и под единой попарной рубрикой. Это два разных измерения, а не две точки одного тренда.

Здесь замыкается виток, начатый в уроке 16. Там было измерено, что петля самоисправления агента работает только от внешнего арбитра: агент, проверяющий сам себя, ошибку не находит. Судья и есть тот внешний арбитр. Теперь известно, насколько он внешний. Внешний ровно на ту часть задач, которые сам решает верно, — и на сложной траектории без эталона его согласие с экспертом упирается в 77–82%.

Одного замера нам не хватает, и честнее сказать это прямо. Замеров того, что происходит с судьёй при смене его собственной версии, мы не нашли. Повторяемость выше 0,95 относится к одной версии и ничего не говорит о расхождении между двумя. Практика фиксировать версию судьи в конфиге и калибровать заново после её смены обоснована рассуждением, а числа за ней нет. Со стороны мониторинга живого трафика этот сюжет разобран отдельно, там для него есть набор якорей и атрибуция дрейфа. В офлайновом контуре мы берём судью исправным и меряем его погрешность в этот момент.

7. Правила допуска, которые не усредняют

К этому месту собрано всё нужное: набор кейсов с ожиданиями, дешёвые проверки кодом и судья с известной погрешностью. Осталось превратить это в решение «пускаем изменение в основную ветку или нет». Решение это не должно быть новой метрикой. Оно должно быть списком условий, согласованных до прогона и привязанных к рискам продукта, потому что порог, придуманный после того, как увидели результат, порогом уже не работает.

Рабочий набор условий выглядит примерно так:

правила допуска (согласованы до прогона)

  критических отказов          = 0
  строгие проверки             все PASS
  средний балл судьи           ≥ порога
  каждый кейс по баллу судьи   ≥ своего минимума
  кандидат против базовой      не хуже, чем на δ

реальный прогон трёх конфигураций на наборе из 24 кейсов

  конфигурация   пройдено   критических   балл судьи   цена   вердикт

  эконом         14 / 24         8            0,82      1×    BLOCKED
  средняя        22 / 24         0            0,89      3×    PASSED
  сильная        24 / 24         0            0,95      9×    PASSED
Рисунок — на этом прогоне решение принимает не доля пройденных кейсов: конфигурация «средняя» с результатом 91,7% допущена, потому что критических отказов ноль, а «эконом» с 58,3% заблокирована восемью критическими. Цена дана относительными множителями стоимости прогона, не деньгами.

В этом прогоне мы хотели заменить дорогую конфигурацию дешёвой и получили ответ на настоящий вопрос: не «какая модель умнее», а какая конфигурация достаточна продукту. Самая дешёвая проваливает восемь критических кейсов из одиннадцати. Средняя проходит все критические и уступает сильной два обычных кейса. Экономия втрое разрешена, потому что её разрешил прогон на своих кейсах.

Доля пройденных кейсов в этом решении почти не участвует. Представим четвёртую конфигурацию. Она проходит 23 кейса из 24, то есть 95,8%, и единственный провал лежит в критическом срезе. Правила допуска такую конфигурацию не пропускают, хотя пройденных кейсов у неё больше, чем у допущенной «средней» с её 91,7%. Сравнивать эти два процента между собой всё равно нет смысла, потому что на наборе из двух десятков кейсов разница в один ответ лежит внутри погрешности. Гейт держится на критическом срезе, а не на среднем, и потому он устойчив на маленьком наборе.

Что попадёт в этот срез, решает продукт, но одному кейсу место найдётся у любого агента с длинным диалогом. Разговор обрывают посередине, перезапускают процесс и смотрят, продолжит ли агент с того же места; устройство сохранённого состояния разбиралось отдельно (урок 18).

Второй разрез — по времени и стоимости. Полный прогон со всеми кейсами, повторами и судьёй занимает минуты и стоит вызовов модели, поэтому в каждый пул-реквест он не помещается.

  • В каждом пул-реквесте — проверки кодом плюс критические кейсы. Секунды и минуты, без обращений к судье.
  • Ночью и перед релизом — весь набор, повторные прогоны, судья, сравнение с базовой версией.

Разделение экономит не столько деньги, сколько внимание: быстрый контур обязан быть таким, чтобы красный статус в нём означал «чинить сейчас».

Работает ли такой гейт вообще? Продольное измерение по этой теме нашлось ровно одно, и это то же исследование конвейера на двух десятках релизов. Гейт поймал две сборки, которые пришлось бы откатывать, и обе — в первых прогонах. Долю ложных блокировок там не измеряли, и это заметный пробел: сколько хороших релизов задержал такой контур, публично неизвестно. Стоимость прогона в деньгах тоже нигде не измерена, сообщается лишь, что время растёт предсказуемо с размером набора. Так что бюджет на эвалы придётся считать самим, как считали остальные статьи расхода.

8. Откуда берутся новые кейсы

Двадцать кейсов на старте — это затравка, и раздел 4 показал, во что упирается набор такого размера. Расти он может из двух источников, и они неравноценны. Первый — подтверждённые ошибки из прода. Каждый разобранный инцидент превращается в кейс, и лучшего кандидата в набор не существует, потому что за ним стоит настоящий клиент с настоящей претензией. Второй — генерация, и вот с ней надо аккуратно.

Генерацию начинают не с просьбы «придумай сто тестовых обращений», а с карты рисков продукта. Для нашего бота она выглядит так: похожие названия продуктов, которые агент путает; форматы и единицы, в которых он ошибается; правила по умолчанию, которые он забывает применить; границы, где сумма или срок стоят ровно на пороге; противоречивые пожелания в одном обращении. Дальше генерируются вариации каждого конкретного риска. Случайная гора запросов покрытия не добавляет.

сгенерировать ──► почистить ──► проверить ──► зафиксировать
вариации          дубли и       кодом по       версия · автор ·
запросов и        мусор         данным или     происхождение
условий                         экспертом

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

Ограничение здесь принципиальное. Если ожидаемый ответ сгенерировала та же модель, что и отвечает, набор проверяет согласованность модели с самой собой. До соответствия продукту он не дотягивается. Синтетика расширяет покрытие, но истину не создаёт.

Насколько такой конвейер помогает находить настоящие дефекты — вопрос без ответа. Прямых замеров пользы синтетических тестовых кейсов именно для продукта на языковой модели найти не удалось: всё, что публикуется рядом, относится либо к обучающим данным, либо к соседним доменам вроде генерации юнит-тестов для Java. Приём разумен методологически, и это всё, что о нём можно честно сказать. Косвенный признак того же порядка даёт учебный репозиторий, который разберём в следующем разделе: в его наборе из 24 кейсов сгенерированный и проверенный ровно один. Остальные пришли от предметного эксперта, от продуктовой команды, из обезличенных логов поддержки, из текста политики и из подтверждённой регрессии.

И отдельный пункт, который легко пропустить: тесты ошибаются тоже. Проверять приходится и записанное ожидание (верно ли оно, не устарело ли правило), и судью (ловит ли он критичное или просто любит длинные ответы), и данные, на которые опираются проверки. Если в справочнике тарифов в поле «комиссия» лежит регион, никакая уверенность модели этого не исправит, а красный кейс будет обвинять невиновного.

9. Проверка, которая не работает, выглядит как работающая

Разберём контур оценки как обычный код — на публичном учебном репозитории, специально сделанном маленьким, детерминированным и запускаемым без ключей и сети. Агент там не банковский, он подбирает вино по каталогу товаров, и проверки написаны под этот каталог. Оговорка про этот код важна с самого начала. Он демонстрационный, написан, чтобы показать конструкцию на одном занятии, и свою задачу выполняет. Разделение проверок реализовано ровно так, как описано выше, проверка опоры на каталог честно ловит выдуманный товар, отчёты о прогоне пишутся в файлы и выгружаются из сборки. Ценность разбора не в списке багов учебного репозитория. Она в том, как именно ломается код такого рода.

что в коде                              что видно в отчёте

judge(case, profile, answer),           балл судьи 0,89
а в сигнатуре два параметра —           вердикт вынесен
ответ модели молча отброшен             прогон зелёный

рубрика кейса с обязательными           кейс PASS
и запрещёнными утверждениями            прогон зелёный
не читается ни одной строкой

hardFailures считается и печатается,    строка в отчёте есть
но в правила допуска не входит          прогон зелёный

сравнения кандидата с базовой           две строки в сводке
в гейте нет вовсе                       прогон зелёный

повторов нет: каждый кейс               24 × 3 × 1 = 72 ответа
исполняется ровно один раз              прогон зелёный
Рисунок — пять мест, где проверка не выполняется, и одинаковый результат во всех пяти: зелёный прогон. Судья, которому из-за лишнего аргумента не передали оцениваемый ответ, всё равно возвращает балл — он берётся из таблицы значений по идентификатору кейса.

Вот это и есть главное свойство контура оценки качества, отличающее его от остального кода. Сломанный обработчик платежей падает или отдаёт неверную сумму, и рано или поздно это увидят. Сломанная проверка возвращает зелёный результат — ровно такой же, какой возвращает работающая. Судья, физически не видящий ответа, выносит вердикт. Правило допуска, которого нет в списке проверок, ошибки не выдаёт, а молча пропускает. Наружу не выходит никакого сигнала, и обнаруживается всё это тогда, когда в прод уезжает регрессия, которую набор был обязан поймать.

Из этого следует пара практических вещей, которые стоят дешевле, чем кажется.

У каждой проверки должен быть свой негативный тест — кейс, на котором она обязана падать. Опору на факты проверяют ответом с выдуманной суммой, проверку периода — ответом с прошлым периодом, а судью — заведомо плохим ответом, на котором он обязан выдать низкий балл. Если контур на подложенной ошибке остался зелёным, проблема в контуре. Кстати, в разобранном репозитории такой тест на сам набор есть: юнит-тест требует, чтобы кейсов было не меньше двадцати.

Артефакты прогона хранятся вместе с вердиктом. Отчёт в машинном формате, читаемая человеком сводка и результаты в формате, который понимает система сборки, складываются рядом с прогоном и выгружаются даже при падении. Сюда же ложится трейс прогона со сквозным идентификатором, и тогда по красному кейсу открывается ещё и последовательность шагов, которая к нему привела. Смысл в том, что красный статус обязан объяснять, какие ожидания сломались и на каких кейсах. Иначе разбирательство начинается с воспроизведения прогона руками, а ровно ради избавления от этой работы контур и строился. Формулировка на память простая. Конвейер хранит вердикт вместе с доказательствами, по которым он вынесен.

Итог

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

Свести всё это можно одной фразой. Контур оценки качества — измерительный прибор, наполовину собранный из той же вероятностной модели, которую он меряет. Отсюда следует и устройство контура, и способ им пользоваться: решение о допуске вешают на то, что проверяется кодом и записано в кейсе заранее, а показания судьи читают как показания прибора с известной погрешностью и регулярной поверкой.

  • Юнит-тесты проверяют механику, оценка качества — поведение продукта. Зелёная сборка после появления модели в контуре означает только то, что код цел.
  • Кандидат — это конфигурация целиком. Промпт, база знаний и набор инструментов тоже меняют ответы, поэтому ось меняют по одной.
  • На двадцати кейсах общий процент статистически пуст. Интервал вокруг 90% занимает 27 пунктов, и держится решение на критическом срезе, который не усредняется.
  • У судьи измеренная погрешность. После поправки на случайное угадывание от согласия с человеком отваливается 33–41 пункт, на агентных траекториях без эталона потолок 77–82%, а повторяемость выше 0,95 спокойно уживается с систематическим перекосом.
  • Сломанная проверка выглядит как работающая. Спасает от этого негативный тест на каждую проверку и хранение доказательств рядом с вердиктом.

Наш агент поддержки после этого урока не попадает в основную ветку, пока не отчитается по критическому срезу и не покажет, что именно сломалось. Чего он всё ещё не умеет — заметить, что качество просело уже на живом трафике, после выкатки.

FAQ

Чем оценка качества отличается от юнит-тестов?

Юнит-тест сравнивает выход функции с записанным ожидаемым значением и проверяет механику: собрался ли JSON, вызвался ли инструмент, не упал ли код. У ответа языковой модели единственного правильного текста нет, поэтому оценка качества проверяет не текст, а свойства ответа: применено ли правило по умолчанию, взяты ли числа из ответа инструмента, не выдумано ли объяснение. Оба вида проверок нужны одновременно и отвечают на разные вопросы.

Сколько кейсов нужно в золотом наборе?

Пятнадцать-двадцать кейсов годятся как способ начать, но измерять долю успешных ответов на таком наборе нельзя: доверительный интервал вокруг 90% растягивается примерно на 27 процентных пунктов, а обычные способы посчитать погрешность на малых наборах её ещё и занижают. Чтобы уверенно отличить 95% от 90%, нужно около 435 кейсов на вариант. Поэтому на маленьком наборе решение принимают по критическим кейсам и срезам риска, а среднее в нём почти не участвует.

Можно ли доверять модели-судье?

Как единственному основанию для решения — нет. Самое цитируемое «более 80% согласия с человеком» — это доля точных совпадений без поправки на случайное угадывание; после поправки от неё отваливается 33–41 процентный пункт. На агентных траекториях без эталонного ответа все проверенные судьи упираются в потолок 77–82% независимо от размера модели. При этом судья остаётся полезен там, где кодом смысл не проверить, при условии, что его сверили с человеческой разметкой и держат рядом со структурными проверками.

Помогают ли повторные прогоны против ошибок судьи?

Против случайного разброса помогают, против систематического перекоса нет. У двух работающих в проде судей повторяемость вердикта выше 0,95 сочетается с позиционным смещением выше 0,10, то есть три прогона подряд согласованно подтвердят один и тот же перекос. Систематическое смещение ловится сверкой с человеческой разметкой и рандомизацией порядка ответов в парных сравнениях.

Что должно быть в правилах допуска перед слиянием в основную ветку?

Минимальный рабочий набор: ноль критических отказов, все строгие проверки пройдены, балл судьи не ниже порога и результат кандидата не хуже базовой версии на согласованную дельту. Условия согласуют до прогона и привязывают к рискам продукта, иначе порог подгоняется под результат. Доля пройденных кейсов при этом почти не влияет на решение: конфигурация с более высоким процентом и одной критической ошибкой блокируется, а с более низким и нулём критических проходит.

Можно ли генерировать тестовые кейсы моделью?

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

Источники

Доверительные интервалы и требуемые размеры набора в разделе 4 посчитаны по формулам (интервал Уилсона, двусторонний тест долей при мощности 80% и α = 0,05); это арифметика, и отдельного замера за ней не стоит. Остальные числовые ориентиры сняты на конкретных наборах, моделях и профилях нагрузки. Достаточно сменить корпус кейсов или пару «судья — генератор», и они могут оказаться другими, так что переносить их на свой профиль без собственного замера не стоит.