Eval-контур для русскоязычных LLM-задач без регрессий
Покажем, как собрать eval-контур для русскоязычных LLM-задач: выбрать кейсы, ввести офлайн и онлайн проверки и ловить регрессии до релиза.

Что ломается без проверок
Пара удачных демо почти ничего не доказывает. На демо люди задают аккуратные вопросы, выбирают понятные примеры и часто сами подталкивают модель к правильному ответу. В работе все грубее: запросы обрываются на полуслове, термины путаются, один и тот же смысл формулируют десятью способами.
Без нормального контура оценки команда обычно замечает только громкие сбои. Тихие ошибки копятся незаметно: ответ звучит убедительно, но в нем неверная дата, сумма или имя; JSON перестает парситься; модель начинает отказывать там, где раньше спокойно выполняла задачу; тон внезапно становится сухим или раздраженным. На глаз такие вещи легко пропустить. В реальном потоке они ломают интеграции, раздражают клиентов и добавляют ручную работу.
С русским языком проблем еще больше. Тесты часто пишут на чистых и книжных фразах, а пользователи так не говорят. Они пишут с опечатками, смешивают русский с английским, вставляют сокращения, внутренний жаргон и куски переписки без контекста. Для человека запросы "сделай сводку по заявке" и "сводку по заявке пж, там клиент опять отвалился по kyc" почти одинаковы. Для тестового набора - не всегда.
Из-за этого офлайн-проверка легко дает ложное чувство спокойствия. На тестах все выглядит прилично, а после замены модели, промпта или параметров перед релизом система вдруг начинает спорить с пользователем, додумывать детали или отвечать слишком общо. Качество редко падает сразу в ноль. Обычно оно медленно расползается по краям. Поэтому случайные демо почти всегда выглядят лучше, чем реальная картина.
Как выбрать набор кейсов
Набор кейсов нужно брать из реальной работы. Примеры из презентации слишком чистые: короткие, понятные, с очевидным ответом. В продакшене люди пишут сбивчиво, забывают важные детали, вставляют пересланные сообщения и все равно ждут нормальный результат.
Хороший старт - выгрузить реальные запросы из логов, тикетов поддержки, CRM, поисковых строк и внутренних чатов. Если логов мало, попросите команды продаж, поддержки и аналитики собрать 30-50 типовых задач, которые модель уже решает или скоро будет решать. Это полезнее любой ручной фантазии.
Дальше разделите кейсы по риску. Ошибки стоят по-разному. Где-то модель просто раздражает пользователя, а где-то создает прямые потери. Обычно достаточно четырех групп: деньги, право, репутация и удобство. В первой важны расчеты, тарифы, лимиты и возвраты. Во второй - трактовка условий, обещания и ответы про персональные данные. В третьей - грубый тон, выдуманные факты и токсичные формулировки. В четвертой - многословие, плохая структура и промах мимо вопроса.
Такое деление снимает лишние споры. Если модель стала чуть хуже по стилю, это неприятно, но терпимо. Если она начала путать условия договора, релиз лучше остановить.
В набор обязательно стоит добавить живой русский: опечатки, разговорные сокращения, смесь русского и английского, аббревиатуры и кривую пунктуацию. Фразы вроде "скинь summary по заявке", "чо с возвратом" или "лимит по юр лицу если ИП на УСН?" часто показывают разницу между моделями лучше, чем аккуратные тестовые формулировки.
Отдельно соберите длинные сценарии. Один короткий вопрос не показывает, как модель держит контекст на 8-10 сообщениях, помнит ограничения и не противоречит себе. Для RAG нужны свои случаи: неполный документ, два похожих источника с разными цифрами, устаревшая инструкция, длинный контекст с шумом.
Если вы сравниваете несколько моделей через единый OpenAI-совместимый шлюз, один и тот же набор удобно прогонять без переписывания клиента. Например, в RU LLM можно сменить base_url на api.rullm.com и гонять одинаковые кейсы через разных провайдеров и модели с теми же SDK, кодом и промптами. Это ускоряет сравнение и убирает лишний шум в тестах.
Не оставляйте в датасете только простые запросы с очевидным ответом. Они нужны, но только как базовый слой. Хороший набор немного раздражает команду, потому что в нем есть спорные, грязные и неудобные примеры. Именно они ловят регрессии до релиза.
Как зафиксировать хороший ответ
Хороший ответ лучше описывать через пользу для человека, а не через общее впечатление. У каждого кейса должна быть понятная цель: найти причину отказа, извлечь условия договора, переписать письмо без потери смысла, ответить по базе знаний без выдумок. Если цель не записана, команда начинает спорить о стиле вместо качества.
Сразу задайте и красные линии. Браком стоит считать не только явную ошибку, но и то, что часто прячется за фразой "в целом нормально": выдуманные факты, пропуск обязательного условия, лишние обещания, подмену роли, совет вне правил компании. Один такой промах легко проходит на демо и больно бьет уже в проде.
Критерии без лишней сложности
Для одного кейса обычно хватает одного или двух критериев. Больше - не лучше. Когда критериев пять или шесть, ревьюер начинает торговаться с каждым пунктом, а judge-модель шумит.
Обычно хватает трех простых проверок:
- ответ выполнил задачу целиком;
- ответ не придумал факты и не нарушил запреты;
- ответ сохранил обязательные детали, например сумму, срок или статус.
Простой пример: пользователь просит кратко объяснить причину отклонения заявки по внутреннему тексту. Хороший ответ должен назвать причину теми же словами или близко по смыслу и не добавлять "попробуйте снова через неделю", если этого нет в источнике. Даже вежливый и гладкий текст здесь плохой, если он обещает лишнее.
Где rules, а где judge-модель
Там, где ошибка формальна, лучше работают правила. Они хорошо ловят JSON по схеме, обязательные поля, язык ответа, лимит длины, запрет на PII, наличие цитаты из источника. Такие проверки дешевле, быстрее и почти не спорят друг с другом.
Там, где нужен смысл, используйте оценку другой моделью по короткой рубрике. Не просите ее "оценить качество в целом". Лучше задать прямые вопросы: "Ответ назвал причину отказа?" и "Ответ добавил факты, которых нет во входе?" Пусть judge возвращает метку или балл 0-1. Так регрессии видны намного лучше.
Эталоны, рубрики, версии промптов и параметры запуска храните рядом. Иначе через две недели никто не вспомнит, почему кейс вчера проходил, а сегодня падает. Если команда гоняет один и тот же набор через разные модели или маршруты, сохраняйте рядом и идентификатор модели. Тогда видно, проблема в промпте, маршрутизации или в самой модели.
Как собрать офлайн-проверки шаг за шагом
Офлайн-проверка работает только тогда, когда вы сравниваете версии на одной и той же выборке и по одним правилам. На словах это звучит просто, но на практике команде нужен реальный набор запросов, понятный эталон и отчет, по которому сразу видно, где новая версия стала хуже.
Начните не с тестов, а с логов. Возьмите свежий срез запросов из продакшена за обычный период, например неделю без распродаж, аварий и других всплесков. Сразу удалите или замаскируйте персональные данные. Для российских команд это обязательная часть процесса: в тесты не должны попадать сырые ФИО, телефоны, номера договоров и адреса.
Потом разделите выборку на несколько частей. Нужен золотой набор с частыми запросами, набор крайних случаев с редкими, но дорогими ошибками, длинные и шумные входы, а также примеры, где модель должна отказаться, уточнить вопрос или честно сказать, что не знает.
Золотой набор показывает общее качество. Крайние случаи ловят сбои, которые средняя оценка обычно прячет. Для русского языка туда почти всегда стоит добавить склонения, аббревиатуры, канцелярит, опечатки и фразы из реальных писем или заявок.
Дальше прогоняйте старую и новую версию на одной и той же выборке. Не меняйте одновременно модель, системный промпт и параметры генерации. Иначе вы не поймете, что именно сломалось. Если проверяете маршрутизацию между моделями, фиксируйте и маршрут, и итоговый ответ.
Не смотрите только на одну среднюю цифру. Считайте результат по сегментам: короткие и длинные запросы, FAQ и свободный ввод, безопасные и рискованные сценарии, разные продукты или типы клиентов. Часто релиз дает плюс в среднем, но проседает на жалобах, юридических формулировках или извлечении данных из полуструктурного текста.
Все результаты складывайте в один отчет. В нем должны быть сам запрос, ответ старой версии, ответ новой версии, оценка, сегмент и причина провала. Причину лучше записывать короткими одинаковыми метками: "выдумал факт", "пропустил поле", "нарушил формат", "слишком многословно". Такой отчет экономит часы разбора и помогает быстро ответить на главный вопрос: релиз правда лучше или его пока рано выпускать.
Как ставить стоп-сигналы перед релизом
Релиз нельзя пропускать по одному общему баллу. Модель может показать хороший средний результат и при этом провалиться на маленьком, но опасном сегменте: юридические формулировки, персональные данные, токсичные ответы, чувствительные отказы. Это одна из самых частых ловушек.
Сразу задайте отдельный порог для каждой рискованной группы. Если у вас есть сценарии для банка, поддержки или поиска по документам, не смешивайте их в одну цифру. У извлечения реквизитов может быть свой минимум, у суммаризации обращений - свой, а у ответов с упоминанием ПДн порог должен быть еще строже.
Что должно блокировать релиз
Перед выпуском полезно держать короткий набор правил:
- средний балл не ниже заранее принятого порога;
- ни одна критичная группа не просела относительно прошлого релиза;
- доля плохих ответов не вышла за пределы нормы;
- спорные кейсы прошли ручной разбор.
Среднее почти всегда скрывает неприятный хвост. Поэтому смотрите не только на общую метрику, но и на худшие 5-10% ответов, число провалов по отдельным тестам и долю кейсов ниже минимального порога. Если вчера модель ошибалась в 2 запросах из 200, а сегодня в 9, это уже повод остановиться, даже если общий балл почти не изменился.
Ручной разбор нужен там, где автоматическая проверка начинает спорить сама с собой. Это нормально для открытых ответов на русском языке: два варианта могут быть разными по форме, но одинаковыми по смыслу. Обычно команда отбирает короткий список сомнительных кейсов и просматривает их перед релизом вручную. На это уходит 20 минут, а пользы часто больше, чем от еще одной красивой метрики.
Еще один полезный барьер - журнал решений. Если кто-то снимает блокировку и все же выпускает релиз, зафиксируйте имя, дату, причину и список тестов, по которым команда приняла риск. Это дисциплинирует и потом сильно упрощает разбор инцидентов.
Что смотреть в онлайне
Онлайн-метрики нужны не для красивого дашборда. Они показывают, когда модель уже ушла от качества, которое вы видели в офлайне, и когда регрессия начинает бить по пользователям, деньгам и времени ответа.
Смотрите не только на средние значения. Проблемы часто прячутся в срезах: отдельная тема, длинные запросы, смешанный язык, новый тип диалога. Обычно хватает такого набора: доля отказов и пустых ответов, средняя и p95 задержка, длина ответа, цена запроса по сценарию и доля переходов на оператора или запасной сценарий.
Переходы на оператора важно считать отдельно. Если общий процент успешных сессий держится, а эскалаций стало вдвое больше, модель уже работает хуже, просто просадку скрывает fallback-логика.
Жалобы пользователей тоже нужны, даже если их немного. Собирайте короткие сигналы вроде "ответ не по теме", "ошибка в фактах", "не понял русский запрос" и добавляйте ручные метки хотя бы на часть плохих ответов. Это помогает отделить обычное недовольство от реальной поломки.
Еще одна частая ошибка - смотреть на онлайн как на сплошной поток. Сверяйте темы, язык и длину входа с офлайн-выборкой. Если в офлайне вы проверяли короткие русские вопросы поддержки, а в онлайне пошли длинные обращения с документами, английскими вставками и юридическими терминами, старая проверка уже не отражает реальность.
Для алертов лучше брать не общий "скор качества", а фактические ошибки: резкий рост подтвержденных галлюцинаций, скачок отказов, всплеск слишком длинных ответов или заметный рост цены на один успешный диалог. Такие сигналы проще обсуждать и с продуктом, и с разработкой.
Если трафик идет через единый шлюз, полезно логировать рядом модель, провайдера и маршрут запроса. Тогда видно, проблема в промпте, в конкретной модели или в том, что один провайдер вдруг стал отвечать медленнее и дороже.
Пример на простом сценарии
Возьмем чат-ассистента интернет-магазина. У него три частые темы: где заказ, когда привезут и как оформить возврат. На таких задачах ошибки видны быстро. Модель звучит уверенно, но легко придумывает сроки, путает правила магазина и начинает спорить с раздраженным клиентом.
На старте здесь не нужен огромный датасет. Часто хватает 50-80 живых примеров, если они хорошо подобраны. В набор стоит положить не только обычные вопросы вроде "где мой заказ 58321", но и короткие сообщения типа "статус", злые реплики вроде "почему вы молчите уже третий день" и фразы с опечатками: "где зкз", "вернут дньги?".
Для такого ассистента важно проверять не красоту текста, а поведение. Если номера заказа нет, ассистент должен запросить его, а не выдумывать статус. Если в базе нет срока доставки, он не должен обещать дату от себя. Если пользователь спрашивает про возврат, ответ должен опираться на правила магазина. Если человек явно раздражен, тон должен оставаться спокойным.
Отдельно нужен жесткий сценарий на сроки и запрет выдумывать правила. Если в магазине нет правила "возврат в течение 30 дней без условий", любая модель, которая пишет такое без опоры на базу знаний, сразу проваливает тест. То же самое со сроками: если система знает только "заказ передан в службу доставки", ответ "привезем завтра до 18:00" надо считать ошибкой.
Дальше команда меняет модель перед релизом и прогоняет офлайн-набор. Все выглядит неплохо: средняя оценка не падает, тон хороший, ответы короче. Но после запуска на 10% трафика онлайн-сигнал показывает рост переводов на оператора с 12% до 19%.
Это уже не абстракция. Команда режет поток по сегментам и быстро находит проблему: новая версия чаще путается в коротких и раздраженных сообщениях, где нет номера заказа и много опечаток. Вместо уточняющего вопроса она дает общий совет про возврат или просит "ожидать уведомление" даже там, где пользователь спрашивал только статус.
После этого сегмент добавляют в офлайн-набор как отдельный срез. Следующий релиз не проходит, если на таких диалогах растет число переводов на оператора, лишних обещаний по срокам или выдуманных правил. Так контур оценки ловит регрессию до полного раската, а не после очереди из недовольных клиентов.
Где команды чаще всего ошибаются
Проблема часто не в модели, а в самом наборе. Команда смотрит на одну среднюю цифру и думает, что все в порядке. Потом выходит релиз, и внезапно падает малая, но важная группа запросов: длинные обращения, разговорный русский, смешанный русский и английский, редкие отраслевые термины.
Одна общая метрика почти всегда прячет такие провалы. Если у вас 92% по всему набору, это мало что говорит без срезов. Нужны отдельные отчеты хотя бы по типу задачи, длине запроса, источнику данных и доле рискованных кейсов.
Другая частая ошибка еще коварнее: высокий балл получает гладкий и уверенный текст, хотя модель не сделала то, что ее просили. Она могла забыть формат JSON, не вернуть нужное поле, пропустить ограничение по тону или выдумать ответ вместо честного "не знаю". Для продакшена это хуже, чем неровный стиль.
Еще одна проблема - сваливать в один набор сразу все: знание фактов, следование инструкции, безопасность, работу с RAG и формат ответа. Тогда провал видно, а причина неясна. Если модель ошиблась, команда не понимает, виноват ретривер, системный промпт, декодинг или сама модель.
Полезнее держать простую дисциплину: разделять наборы по типу проверки, считать метрики по срезам, проверять действие, а не только текст, добавлять свежие случаи из продакшена и сравнивать модели на одинаковых настройках. Последний пункт многие недооценивают. Если одна модель идет с temperature 0, а другая с 0.7, сравнение уже шумит. То же самое с max tokens, system prompt, глубиной retrieval, кэшем и постобработкой.
И еще одно: набор быстро стареет. В продакшене появляются новые формулировки, новые документы и новые попытки обойти правила. Если тесты не пополнять после инцидентов и заметных сбоев, вы начинаете мерить вчерашнюю систему.
Короткий чек-лист перед релизом
Перед выкладкой новой модели или нового маршрута полезно сделать короткую паузу и проверить не среднюю температуру по датасету, а то, что действительно ломает продукт.
- В наборе есть не только частые запросы, но и неприятные случаи: двусмысленные формулировки, длинный контекст, жаргон, опечатки, токсичные реплики и чувствительные данные, если они есть в продукте.
- Для каждого теста записано простое правило оценки. Если его нельзя объяснить в двух фразах, тест пока сырой.
- Новая версия проходит пороги не только в среднем, но и в каждом критичном сегменте.
- Онлайн-наблюдение готово до выкладки: метрики, алерты, дашборд и понятный откат.
- На первые сутки после релиза назначен конкретный дежурный.
Есть и практичная мелочь, которую часто пропускают: зафиксируйте эталон не только для модели, но и для промпта, маршрутизации и параметров генерации. Регрессия нередко приходит не из самой модели, а из нового системного промпта или смены провайдера.
Если вы работаете под требования 152-ФЗ, добавьте в чек-лист отдельную проверку на маскирование PII, логи и аудит-трейлы. Такой тест редко влияет на красивое демо, но именно он потом спасает релиз в проде.
Что делать дальше
Не пытайтесь собрать идеальный eval с первого раза. Рабочий старт проще: возьмите 50-100 живых кейсов, которые уже встречаются у пользователей или внутри команды, и прогоняйте через них каждое заметное изменение. Этого хватает, чтобы поймать грубые регрессии и быстро увидеть, где модель ломается именно на русском языке.
Потом расширяйте набор не по вдохновению, а по провалам. Если ассистент перепутал реквизиты, исказил смысл жалобы клиента или ответил слишком уверенно на слабом контексте, такой пример должен попасть в тесты в ту же неделю. Полезная привычка проста: раз в неделю разбирать логи и вручную добавлять свежие неудачные ответы.
На практике хорошо работает короткий ритм: держать базовый набор из 50-100 кейсов для каждого релиза, раз в неделю добавлять новые примеры из логов, рядом с качеством смотреть цену запроса и задержку, а также отдельно проверять, где лежат логи и как обрабатываются персональные данные.
Это важнее, чем кажется. Контур оценки почти никогда не живет отдельно. Команда меняет модель, потом маршрутизацию, потом провайдера, потом лимиты по цене, и качество уезжает сразу в нескольких местах. Поэтому проверку лучше держать рядом с выбором модели, стоимостью и latency, а не превращать в формальный отчет.
Если у вас несколько моделей и провайдеров, полезно заранее продумать и инфраструктуру сравнения. Для российских команд здесь часто важны не только качество и цена, но и хранение данных в РФ, логирование и требования 152-ФЗ. В этом месте удобен единый слой, который дает один OpenAI-совместимый эндпоинт, биллинг и аудит внутри РФ. Например, RU LLM используют именно для таких сценариев: можно маршрутизировать запросы к разным моделям через один API и при этом не менять клиентский код.
И последнее: до релиза отдельно проверьте требования к логам, ПДн и хранению в РФ. Если в eval и в проде разные правила логирования, вы получите красивый отчет и неприятный сюрприз уже после запуска.