Evals на русском: как ловить регрессии без ручных проверок
Evals на русском помогают заметить поломки до релиза: разберем golden set, метрики, judge-модель и проверки в CI без ручной рутины.

Почему регрессии проходят незаметно
У LLM поломка редко выглядит как явная ошибка. Модель по-прежнему отвечает связно, вежливо и иногда даже быстрее, чем раньше. Но меняется поведение в мелочах, а именно они потом бьют по продукту.
Обычный сценарий выглядит так: новый промпт чинит один кейс и тихо ломает соседний. Бот поддержки интернет-магазина стал лучше объяснять возврат, но начал путаться в обмене товара, если пользователь пишет не по шаблону: "хочу другой размер, чек потерял, что делать?" На демо-примерах все выглядит нормально, команда выпускает изменение и замечает сбой только после жалоб.
Средний балл тоже легко вводит в заблуждение. Общая оценка может вырасти, потому что ответы стали короче, чище или вежливее. При этом нужный навык проседает: модель хуже следует условиям, пропускает ограничения или менее точно отвечает на русский запрос с несколькими деталями.
Ручная проверка пары ответов почти всегда дает ложное спокойствие. Люди смотрят на понятные примеры и редко воспроизводят настоящий поток запросов. А проблемы обычно живут в хвосте: в репликах с опечатками, в длинных сообщениях с лишним контекстом, в смешении разговорного и официального стиля, в запросах с двумя или тремя условиями сразу.
С русским языком это заметно сильнее. Шаблонные тесты часто пишут на "чистом" русском, а пользователи говорят иначе: сокращают слова, путают падежи, вставляют англицизмы, пересылают кусок переписки, задают вопрос намеками. Модель может пройти аккуратный тест и провалиться на живой формулировке вроде "спишите бонусы, но промокод не трогайте".
Еще одна ловушка возникает, когда команда меняет модель, провайдера или роутинг через единый API. Код приложения не меняется, поэтому риск кажется низким. На деле ответы уже другие: тон тот же, структура похожа, а точность в нескольких русских сценариях хуже.
Именно поэтому регрессии редко видно "на глаз". Если в проверках нет живых русских формулировок и отдельных метрик по навыкам, проблема всплывает не в CI, а в поддержке после релиза.
Что именно вы хотите поймать
Команды часто проверяют модель по общему впечатлению: ответ звучит нормально, значит все хорошо. Такой подход пропускает регрессии. Для evals лучше заранее решить, какие сбои вы считаете неприемлемыми, и проверять именно их.
Обычно достаточно пяти групп проблем. Первая - фактические ошибки: модель путает факты, додумывает детали или уверенно отвечает там, где данных нет. Для бота магазина это выглядит просто: он обещает доставку "завтра", хотя в данных такого срока нет.
Вторая группа - поломка формата. Вместо нужного JSON модель пишет текст, пропускает поле, меняет тип значения или растягивает ответ в три раза длиннее лимита.
Третья - неверные отказы. Иногда модель зря пишет "я не могу помочь", хотя вопрос обычный и безопасный. Иногда все наоборот: она дает инструкцию там, где должна остановиться.
Четвертая - рискованные формулировки в темах права, денег и ответственности. Фразы вроде "это точно законно" или "вам подходит этот финансовый продукт" без оговорок - плохой сигнал.
Пятая - деградация по эксплуатации. Если качество осталось тем же, а цена и задержка выросли, это тоже регрессия.
Как формулировать критерии
Лучше писать не абстрактные правила, а наблюдаемые признаки. Не "ответ должен быть хорошим", а "ответ должен вернуть JSON с полями status и reason", "не должен придумывать срок доставки", "должен отказаться на запрос о персональных данных клиента".
Так команда меньше спорит и больше автоматизирует. Judge-модель, строковые проверки и простые метрики начинают работать только тогда, когда у вас есть ясное определение ошибки.
Если вы меняете модель, промпт или провайдера через единый API-шлюз, риски тоже стоит разделить. Один набор тестов ловит смысловые ошибки, другой следит за форматом, третий - за ценой и latency. На практике это полезнее, чем один общий "балл качества".
Evals на русском начинают работать именно в этот момент: когда вы перестаете мерить все сразу и начинаете ловить конкретные поломки, которые реально бьют по продукту, поддержке и бюджету.
Как собрать golden set с нуля
Хороший golden set редко пишут с чистого листа. Его собирают из живых диалогов, тикетов поддержки, поисковых запросов и тех мест, где модель уже ошибалась. Для русского это особенно важно: реальные формулировки почти всегда грязнее, короче и двусмысленнее, чем тестовые примеры из головы.
Если взять только "красивые" запросы, набор быстро станет декоративным. Он покажет высокий балл, но пропустит падение качества на обычных фразах вроде "не списалась оплата, но заказ есть?" или "где закрывающие документы за март". Продакшен пишет тесты честнее любой команды.
Что брать в набор
Сначала выгрузите 200-500 запросов за последний период. Не пытайтесь сразу сделать идеальный датасет. На первом проходе важнее покрыть живой язык, чем отполировать каждую запись.
Потом уберите повторы и почти одинаковые формулировки. Если у вас десять вариантов вопроса про возврат, оставьте два или три: короткий, подробный и с опечатками. Иначе набор перекосит метрики, и один частый сценарий начнет весить слишком много.
После чистки разложите кейсы по трем группам: простые, спорные и редкие. Простые быстро ловят грубые поломки. Спорные показывают, где judge-модель или ручная проверка должны смотреть на контекст. Редкие случаи часто ловят самые дорогие ошибки, хотя встречаются не каждый день.
Что хранить для каждого кейса
У каждого примера должна быть одна и та же структура. Обычно хватает пяти полей: входной запрос, ожидаемый ответ или критерии ответа, причина добавления кейса, теги вроде сценария и риска, а также версия промпта или маршрута, с которой кейс появился.
Поле с причиной многие пропускают зря. Через месяц никто не вспомнит, почему фраза "счет есть, акта нет" считалась сложной. Короткая пометка вроде "путает бухгалтерские документы" заметно ускоряет разбор регрессии.
Храните golden set рядом с промптами, кодом и конфигами. Если команда меняет системный промпт, base_url, модель или схему маршрутизации, тестовый набор должен меняться в том же репозитории и в той же истории коммитов. Это особенно удобно в инфраструктуре вроде RU LLM, где можно быстро переключать модели и провайдеров через один OpenAI-совместимый эндпоинт: без версионирования потом трудно понять, что именно сломало результат.
Хороший первый набор не обязан быть большим. Даже 80-120 аккуратно отобранных кейсов уже ловят больше регрессий, чем разовые ручные прогоны перед релизом.
Какие метрики выбрать
Одна метрика почти всегда врет. Модель может отвечать быстро и дешево, но путать факты. Или писать по делу, но ломать нужный JSON. Поэтому метрики лучше разложить на несколько групп и смотреть на них отдельно.
Сначала отделите формат от смысла. Формат показывает, соблюдает ли модель структуру ответа: JSON валиден, нужные поля на месте, язык ответа правильный, длина не вышла за лимит. Смысл отвечает на другой вопрос: модель решила задачу или нет. Без такого разделения команда часто спорит о качестве, хотя проблема вообще не в качестве, а в сломанном формате.
Безопасность тоже считайте отдельно. Если бот иногда выдает персональные данные, токсичный текст или опасные советы, средний балл по качеству вас не спасет. Такие ошибки редки, но именно они ломают релиз.
Смотрите на метрики по слоям
Удобно держать рядом четыре группы метрик:
- формат: валидный JSON, нужные поля, соблюдение инструкции
- смысл: точность ответа, полнота, полезность для пользователя
- безопасность: утечки PII, запрещенные ответы, рискованные формулировки
- эксплуатация: цена запроса и задержка
Не сводите все к одному числу. Общий скор полезен для дашборда, но он прячет причину провала. Если качество выросло на 2 процента, а задержка стала вдвое выше, разговор уже другой. Для команд, которые гоняют запросы через несколько моделей и провайдеров, цена и latency часто меняются вместе с качеством. Их лучше смотреть в одной таблице.
Для суммаризации полезно проверять две вещи: полноту и лишние факты. Хорошее резюме не теряет важные пункты и не придумывает то, чего не было в тексте. Полноту можно оценить по чек-листу фактов. Лишние факты лучше ловит judge-модель или ручная разметка на небольшом наборе.
Для RAG метрика должна проверять опору на контекст. Модель может звучать уверенно и все равно отвечать мимо. Считайте, использовала ли она факты из переданных документов, сослалась ли на нужный фрагмент и не добавила ли сведения из своей "памяти", которых в контексте нет.
Хороший набор метрик отвечает на простой вопрос: что именно ухудшилось после смены модели, промпта или маршрута. Если вы видите это сразу, регрессии перестают прятаться в общих оценках.
Как настроить judge-модель
Judge-модель нужна не для "умной оценки вообще", а для одного простого дела: стабильно отличать хороший ответ от плохого по вашим правилам. Если правила расплывчатые, судья начнет колебаться и пропускать регрессии. Поэтому сначала задайте короткую рубрику с явными штрафами.
Хорошая рубрика помещается в несколько пунктов. Например: ответил по вопросу, не придумал факты, соблюдал тон, не пропустил обязательный шаг, не выдал лишние персональные данные. За каждый промах задайте понятный минус. Тогда judge-модель не рассуждает в пустоту, а проверяет конкретные условия.
Что давать судье
Судье мало одного ответа. Ему нужен полный контекст:
- запрос пользователя
- ответ модели
- эталонные признаки хорошего ответа
- список ошибок, которые надо штрафовать
- формат вердикта, например score и краткая причина
Эталонные признаки полезнее длинного идеального ответа. Если в поддержке магазина клиент спрашивает про возврат, судья может проверять три вещи: есть срок возврата, есть следующий шаг, нет выдуманных условий. Такой шаблон живет дольше, чем один "правильный" текст.
Лучше сравнивать два ответа вслепую, без имени модели. Дайте судье вариант A и вариант B, а затем попросите выбрать лучший и объяснить выбор по рубрике. Слепое сравнение снижает шум. Когда вы меняете промпт, модель или base_url, такой тест часто ловит деградацию раньше, чем обычный score.
Не ставьте ту же модель и участником, и судьей. Она чаще прощает собственный стиль и собственные ошибки. Для русского это особенно заметно на вежливых, но пустых ответах: модель-судья может завысить оценку просто потому, что текст звучит гладко. Лучше брать другую модель, а для спорных кейсов держать небольшой ручной контроль.
Раз в неделю просматривайте часть вердиктов руками. Хватает 20-30 примеров из свежих прогонов. Если судья начал часто хвалить ответы с фактическими промахами или, наоборот, ругать нормальные короткие ответы, правьте рубрику, а не только модель.
Практичный формат вывода простой: числовая оценка, победитель A/B, список найденных нарушений и одна короткая причина. Чем строже формат, тем проще встроить judge-модель в CI и не разбирать результаты вручную.
Пример: бот поддержки интернет-магазина
У бота поддержки обычно повторяются одни и те же темы: возврат, доставка, статус заказа. Поэтому тестовый набор можно собрать быстро, если брать не абстрактные промпты, а живые обращения, которые уже были в чате или почте.
Хороший минимум для такого бота - короткие и длинные диалоги. Короткие ловят простые сбои: бот стал резким, отвечает слишком сухо или путает шаги возврата. Длинные проверяют другое: держит ли он контекст, не меняет ли тон на пятом сообщении и не теряет ли формат ответа.
Для русского такой набор может включать несколько типовых кейсов. Клиент пишет: "Хочу вернуть куртку, получила вчера". Бот должен спокойно объяснить порядок возврата и не спорить с пользователем. Другой клиент спрашивает: "Когда точно привезут заказ?" Если в данных нет даты, бот не должен придумывать срок вроде "завтра до 18:00".
Еще один обязательный сценарий - запрос про чужие данные: "Скажите статус заказа моей жены, номер такой-то". Здесь бот должен отказать и не раскрывать чужую информацию. Полезен и длинный диалог, где клиент сначала спрашивает про доставку, а потом переключается на возврат. Бот должен сохранить вежливый тон и не смешать два сценария в один ответ.
Проверки удобно разделять по типу. Один тест ловит грубость или раздраженный тон. Это часто делает judge-модель, если у нее есть четкие критерии: вежливость, ясность, отсутствие давления. Другой тест лучше сделать жестким правилом: если система не знает срок доставки, в ответе не должно появляться точное время или дата.
Кейс про чужие данные нужен почти всегда. Он быстро показывает, умеет ли бот держать границу, когда собеседник давит, торопит или пытается выглядеть "уполномоченным". Для российских команд это особенно важно, если в диалогах есть персональные данные и отказ должен быть строгим.
После смены модели такой набор сразу показывает, что именно просело. Иногда новая модель отвечает точнее, но становится холоднее. Иногда тон нормальный, зато ломается формат: раньше бот писал короткое резюме и следующий шаг, а теперь выдает длинный абзац без структуры. Люди замечают такие изменения не сразу, а тесты ловят их в первый же прогон.
Как встроить проверки в CI
CI не должен гонять весь eval-набор на каждый коммит. Если проверка идет 40 минут, ее быстро начнут пропускать. Лучше разделить набор на два слоя: короткий smoke и полный прогон.
Smoke-набор запускают на каждом PR. Обычно хватает 15-30 примеров, которые ловят самые дорогие поломки: пустой ответ, сломанный JSON, переход на английский вместо русского, опасный совет, отказ там, где ответ нужен. Такой прогон должен укладываться в несколько минут.
Полный набор держат отдельно. Его удобно ставить на ночь, по расписанию или перед релизом модели, промпта и маршрутизации. Там уже проверяют длинный хвост: редкие формулировки, сложные диалоги, спорные кейсы и пограничные ответы judge-модели.
Сборку стоит останавливать не при любом отклонении, а при явном падении по рубрике. Например, если точность по фактам ушла ниже порога, доля ответов в нужном формате просела или выросло число нарушений по безопасности. Один странный пример не должен ломать весь PR. Системное ухудшение должно.
Полезно сохранять артефакты каждого прогона:
- старый и новый ответ на один и тот же тест
- оценку judge-модели и причину
- задержку по каждому кейсу и среднее время ответа
- цену прогона и расход токенов
- commit SHA, версию промпта и имя модели
Без этого команда видит только красный статус и тратит время на догадки. С диффом ответов сразу понятно, что именно изменилось: смысл, формат, цена или скорость.
Если вы уже работаете через OpenAI-совместимый эндпоинт, CI можно держать почти таким же, как прод. Для команд, которые используют RU LLM, это удобно: тот же SDK, тот же base_url и те же маршруты моделей можно проверять в автоматическом прогоне без отдельной обвязки.
Простое правило такое: быстрый набор защищает pull request, полный набор защищает релиз. Так CI не тормозит разработку и при этом ловит регрессии до того, как их заметит пользователь.
Где команды чаще ошибаются
В evals на русском чаще ломается не код, а сам способ проверки. Первая ошибка скучная, но очень частая: команда берет 30 "красивых" примеров, на которых система и так отвечает прилично. Такой golden set почти не ловит регрессии. Он пропускает опечатки, длинный контекст, редкие формулировки и спорные запросы, где модель начинает фантазировать.
Еще одна путаница появляется, когда смешивают тесты промпта и тесты модели. Это разные вещи. Если вы в одном релизе меняете инструкцию, judge-модель и провайдера, потом никто не понимает, что именно дало просадку. Отладка превращается в угадайку.
Средний балл тоже часто успокаивает зря. Бот может стать лучше на простых FAQ, но хуже работать с возвратами, отменой заказа и жалобами. Среднее число выглядит нормально, а пользователи уже пишут в поддержку. Смотрите не только на общий score, но и на срезы по типам задач, на худшие кейсы и на долю явных промахов.
С русским языком многие экономят, и это почти всегда плохая идея. Английский набор не покажет, как модель ведет себя на падежах, сокращениях, смешанной речи и бытовых формулировках вроде "где мой заказ", "оформите возврат", "верните refund за подписку". Добавьте кейсы с ФИО, адресами, номерами заказов, разговорным тоном и ошибками в тексте. Для продукта на рынке РФ это обычный трафик, а не экзотика.
Отдельная ошибка связана с judge-моделью: команды меняют rubric вместе с релизом. Так делать не стоит. В этот момент вы меняете и экзамен, и ученика. Если результат вырос, вы не знаете, стало ли лучше качество ответа или судья просто стал мягче. Rubric лучше держать неизменной несколько релизов подряд и обновлять отдельно, с переоценкой старого baseline.
Полезный минимум выглядит так:
- отдельные наборы для промпта, модели и retrieval
- срезы по русским кейсам, а не один общий пул
- метрики по категориям, а не только общий средний балл
- неизменная rubric judge-модели на период сравнения
Сначала такой подход кажется более медленным. На практике он убирает ручную рутину и быстро показывает, где именно пошел откат.
Быстрый чек-лист перед релизом
Перед выкладкой не смотрите только на средний балл. Один удачный прогон легко прячет поломку в важном сценарии: ответ стал длиннее, пропустил ограничение или начал придумывать детали, которых нет в источнике.
Для русского обычно хватает пяти проверок. Если хотя бы одна из них не проходит, релиз лучше задержать на день, чем потом разбирать жалобы вручную.
- В наборе есть реальные запросы пользователей, а не только придуманные примеры.
- У каждого теста есть ясный критерий провала.
- Judge-модель проверяет хотя бы один срез, где обычные правила слабы.
- CI показывает не только итоговый score, но и diff.
- Команда заранее решает, кто разбирает спорные кейсы.
Есть простой признак здорового процесса: новый участник команды за 10 минут понимает, почему тест упал и кто принимает решение. Если для этого нужно звать автора пайплайна, система уже слишком хрупкая.
Это особенно заметно, когда команда часто меняет модель или маршрут. Если вы гоняете запросы через единый OpenAI-совместимый шлюз вроде RU LLM, diff между старым и новым прогоном помогает поймать не только падение качества, но и сдвиг в стиле, длине ответа и цене одного запроса.
Перед релизом полезно отдельно просмотреть самые болезненные 20-30 кейсов. Средняя метрика почти всегда выглядит спокойнее, чем реальные ошибки в проде.
Что делать дальше
Не стройте большой стенд с первого дня. Для начала хватит набора из 50-100 запросов, который бьет по самым болезненным местам: факты, формат ответа, отказ от лишних домыслов, соблюдение инструкций. Такой набор легко прогонять и на каждом коммите, и по расписанию ночью.
Если тесты занимают час и требуют ручного разбора, команда быстро перестает им доверять. Лучше маленький набор, который ловит большую часть типовых поломок, чем идеальная схема, которую никто не запускает.
Хорошая привычка проста: после каждого инцидента добавляйте в golden set еще один кейс. Бот перепутал статус заказа, ассистент выдал лишние персональные данные, judge-модель пропустила слабый ответ - значит, у вас появился новый постоянный тест. Через пару месяцев такой набор начинает отражать реальную историю ошибок, а не фантазии на этапе планирования.
С логами тоже лучше определиться сразу. Если у вас есть требования по 152-ФЗ, заранее решите, где вы храните тестовые запросы, ответы, judge-оценки и аудит запуска. Частая ошибка - собирать evals в одном месте, а логи складывать в сторонний контур без понятных правил доступа, маскирования и срока хранения.
Для рабочей базы обычно хватает такого порядка:
- держать короткий набор на 50-100 запросов для частых прогонов
- после каждого сбоя добавлять кейс, который его воспроизводит
- хранить логи и аудит там, где это не ломает ваши правила по персональным данным
- сравнивать модели в одном и том же контуре, чтобы результаты не плавали из-за разной инфраструктуры
Если вы сравниваете много моделей через один OpenAI-совместимый эндпоинт внутри РФ, такой контур удобно держать на RU LLM. Команда меняет base_url, продолжает использовать те же SDK и промпты без изменений, а логи, биллинг и аудит остаются в российском контуре. Это особенно удобно, когда нужно быстро прогнать один и тот же golden set по нескольким провайдерам и не собирать для каждого отдельную обвязку.
Польза от evals на русском появляется не тогда, когда у вас сложная таблица метрик. Она появляется тогда, когда любой спорный ответ можно превратить в новый тест в тот же день.
Часто задаваемые вопросы
Что вообще считать регрессией у LLM?
Считайте регрессией любое ухудшение, которое бьет по продукту. Это не только явная ошибка, но и выдуманные факты, сломанный JSON, лишний отказ, рискованная фраза, рост задержки или цены.
Хорошее правило простое: если после смены промпта, модели или маршрута бот хуже решает задачу пользователя, это регрессия, даже если ответ звучит вежливо и общий балл не упал.
Сколько тестов нужно для первого golden set?
На старте обычно хватает 50–100 запросов. Такой набор уже ловит типовые поломки и не тормозит команду.
Если у вас есть живой трафик, потом расширьте набор до 80–120 самых полезных кейсов. Не гонитесь за размером: маленький, но честный набор лучше большого декоративного.
Откуда брать хорошие примеры для тестов?
Берите реальные диалоги из поддержки, поиска, тикетов и прошлых инцидентов. Именно там есть опечатки, сокращения, намеки и смешанный стиль, на которых модель чаще ошибается.
Не пишите набор только "из головы". Красивые примеры почти всегда дают слишком спокойную картину и пропускают сбои на живом русском.
Что нужно хранить у каждого кейса?
Для каждого примера храните сам запрос, ожидаемый результат или ясные критерии проверки, теги сценария и короткую причину, зачем кейс попал в набор.
Добавьте версию промпта или маршрута, с которой кейс появился. Потом вы быстро поймете, что именно сломалось и почему этот пример вообще считался сложным.
Какие метрики смотреть в первую очередь?
Обычно достаточно четырех групп метрик: формат, смысл, безопасность и эксплуатация. Формат проверяет JSON, поля, язык и длину ответа. Смысл показывает, решил ли бот задачу. Безопасность ловит утечки и опасные советы. Эксплуатация считает цену и задержку.
Не сводите все в одно число. Когда метрики лежат рядом, вы сразу видите, что просело: точность, структура ответа или время отклика.
Как правильно настроить judge-модель?
Сначала задайте судье короткую рубрику с явными ошибками. Ему проще стабильно оценивать ответ, когда он проверяет понятные вещи: не придумал факт, не пропустил обязательный шаг, не раскрыл чужие данные.
Давайте судье запрос, ответ модели, признаки хорошего ответа и строгий формат вердикта. Еще лучше сравнивать вариант A и B вслепую, без имени модели.
Можно ли ориентироваться на один общий score?
Нет, среднему баллу доверять нельзя. Он часто растет из-за более коротких или гладких ответов, хотя нужный навык уже просел.
Смотрите на срезы по сценариям и на явные промахи. Если бот стал хуже на возвратах, статусе заказа или длинных русских запросах, среднее число это легко спрячет.
Как встроить evals в CI и не замедлить разработку?
Разделите проверки на быстрый smoke-набор и полный прогон. Smoke запускайте на каждом PR, а полный набор гоняйте по расписанию или перед релизом.
Останавливайте сборку только при понятном падении по рубрике, а не из-за одного странного ответа. Сохраняйте diff ответов, оценку судьи, задержку, цену, commit и версию промпта.
Что делать после найденной ошибки или инцидента?
Сразу добавьте этот случай в golden set. Если бот однажды перепутал статус заказа или раскрыл лишние данные, такой кейс должен стать постоянной проверкой.
Именно так набор начинает отражать реальные риски продукта, а не догадки команды. Через несколько месяцев новые прогоны ловят повторение старых сбоев еще до релиза.
Как проводить evals и не нарушить требования по персональным данным?
Если в тестах есть персональные данные, заранее решите, где вы храните запросы, ответы, оценки судьи и аудит запусков. Маскируйте PII и держите единые правила доступа и срока хранения.
Не разносите evals и логи по случайным контурам. Когда команда сравнивает модели в одном российском контуре, ей проще соблюдать требования 152-ФЗ и разбирать инциденты без путаницы.