Перейти к содержимому
13 июл. 2025 г.·8 мин чтения

Синтетические данные для evals: где полезны, а где врут

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

Синтетические данные для evals: где полезны, а где врут

Почему тесты расходятся с продакшеном

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

Пользователь пишет на ходу. Он путает даты, теряет мысль на середине фразы, вставляет лишние детали и перескакивает с темы на тему. Вместо понятного запроса вроде "помоги вернуть платеж" в логах часто лежит что-то вроде "куда делись деньги я вчера уже писал". Если в тестах таких сообщений нет, команда получает красивую метрику и слабое поведение на реальных запросах.

Есть и вторая ловушка. Синтетика обычно распределяет случаи слишком ровно. В датасете получается по 100 примеров на каждый тип задачи, как будто мир честно делит поток по категориям. В продакшене одна группа запросов может занимать 60%, другая - 0,5%, и именно эти редкие 0,5% приносят больше всего жалоб, ручной работы и потерь.

Редкие случаи часто выпадают еще и потому, что их трудно придумать заранее. Люди странно сочетают проблемы: срочность, эмоции, неполные данные, старый контекст из прошлого диалога. Генератор синтетики обычно сглаживает такие углы. Он пишет правдоподобный текст, но не воспроизводит давление, под которым пишет человек.

В итоге команда мерит не устойчивость модели, а удобные сценарии. В набор попадает то, что легко описать: чистые FAQ, короткие инструкции, запросы без шума. А то, что ломает систему, остается за рамками. Потом модель показывает 92% на evals и неожиданно сыпется на злых, обрывочных или двусмысленных сообщениях.

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

Где синтетика помогает

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

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

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

На больших регрессиях синтетика тоже экономит время. Реальные запросы часто шумные: в них есть опечатки, лишний контекст и повторы. Это хорошо для финальной проверки, но плохо для быстрого сравнения двух версий. Когда нужен чистый тест на 500 или 1000 примеров, удобнее взять набор, где классы распределены ровно, формулировки размечены заранее, а ожидаемый результат не вызывает споров.

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

Если коротко, синтетика лучше всего помогает закрыть редкие, но рискованные сценарии, быстро проверить изменения перед релизом, прогнать регрессию без ручной чистки логов и собрать ровные пары для A/B-сравнения. Для ранней итерации это очень практичный ход. Он не заменяет реальные запросы в evals, но хорошо снимает первый слой проблем: явные ошибки, сломанный формат, пропущенные правила и слабые места нового промпта.

Где синтетика врет

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

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

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

Обычно синтетика сглаживает четыре вещи:

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

Из-за этого в датасете растет доля простых задач. Модель отвечает хорошо, метрики поднимаются, команда радуется раньше времени. А потом в проде всплывают те случаи, которых в тесте почти не было: спорные списания, неполная анкета, жалоба с длинной историей, запрос с несколькими сущностями сразу.

Отдельная проблема - ошибки маршрутизации и контекста. Это особенно заметно, если вы гоняете трафик через шлюз с несколькими моделями и провайдерами. Синтетический запрос обычно короткий и чистый, поэтому он не показывает, что одна модель хуже держит длинный диалог, другая режет важную часть истории, а третья иначе трактует system prompt или tool call.

Хороший тревожный сигнал очень простой: если ваш eval почти не заставляет модель переспрашивать, набор слишком удобный. Настоящий продакшен неудобный. В нем много грязи, недосказанности и случайных поворотов. Если тест этого не показывает, он врет.

Что брать из реальных запросов

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

Срез лучше строить сразу по нескольким осям: канал общения, продукт или сценарий, роль автора и свежесть данных. Иначе выборка быстро съедет в одну сторону. Например, вы отлично проверите чат поддержки, но почти не увидите, как ведет себя модель на письмах или после ASR, где ошибок обычно больше.

Не чините исходные реплики перед тестом. Опечатки, односложные ответы, обрывы фраз и странная пунктуация часто и ломают систему. Фраза вроде "деньги спис ались но не пришли" полезнее для evals, чем аккуратно переписанный запрос. Короткое "не понял" тоже стоит оставить: на таких репликах видно, умеет ли модель держать контекст.

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

С персональными данными лучше не ждать до разметки. Сначала маскируйте ФИО, телефоны, карты, адреса и номера договоров, потом отдавайте тексты аннотаторам. Для команд в РФ это обычная дисциплина по 152-ФЗ. Если трафик идет через RU LLM, можно использовать встроенное маскирование PII и аудит-трейлы, чтобы не разносить сырые логи по разным контурам.

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

Как смешать данные без перекоса

Проверка перед релизом
Прогоните свежий срез на нескольких моделях и посмотрите, где растет ложный отказ.

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

Часто хватает свежего окна за 2-4 недели. Из него нужно убрать дубли, замаскировать персональные данные и разложить запросы по типам задач. Для команд под 152-ФЗ это не формальность: если вы выгружаете живые диалоги, сначала чистите PII и фиксируйте, кто и когда собрал датасет.

Синтетические данные стоит добавлять только туда, где в живом потоке есть дыры. Это редкие сценарии, новые фичи, длинные контексты, сложные отказы и необычные формулировки клиента. Синтетика полезна как заплатка на пустые зоны, а не как основа набора.

В большинстве случаев живые запросы должны занимать большую часть датасета. Рабочий ориентир простой:

  • 70-85% реальных запросов;
  • 15-30% синтетики для редких случаев;
  • 0% синтетики в массовых сценариях, если живых данных уже достаточно.

Если синтетики становится слишком много, она меняет стиль всего набора. Модель начинает выглядеть лучше просто потому, что тесты написаны похожим голосом, с одинаковой длиной и слишком аккуратными формулировками.

Проверьте смесь хотя бы по трем срезам: длина запроса, тон сообщения и тип задачи. Если в продакшене половина обращений короткие и нервные, а в evals почти все спокойные и подробные, перекос уже случился. То же самое с задачами: нельзя брать много синтетики на tool calling и почти не проверять обычные вопросы пользователя.

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

Если нужен быстрый тест на здравый смысл, возьмите 100 последних живых запросов и сравните их с вашим eval-набором вручную. Разница в тоне обычно видна за десять минут.

Пример: чат банка о спорных списаниях

В чате банка спорные списания быстро ломают аккуратные тесты. Клиент редко пишет "оспорить транзакцию" или "несанкционированная операция". Чаще он пишет на эмоциях, путает даты, суммы, карту и сам не до конца понимает, что случилось.

В логах встречаются реплики вроде:

  • "у меня опять минус 399 хотя я ничего не покупал";
  • "это что за списание ночью, вы там совсем?";
  • "верните деньги, я этот сервис удалил еще в мае";
  • "платеж вроде мой, но сумма другая и магазин странный".

Если собирать eval только из живых логов, датасет быстро забьется похожими жалобами на подписки, дубликаты и забытые автоплатежи. Это полезно для проверки тона, уточняющих вопросов и маршрутизации на оператора. Но так легко пропустить редкие, зато дорогие случаи: двойное списание после офлайн-терминала, спор по токенизированной карте, операция после перевыпуска, спор по cash withdrawal reversal.

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

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

Смешанный датасет для evals работает честнее. Для такого сценария разумно держать около 60-70% реальных обращений и 30-40% синтетики для редких правил и пограничных случаев. Тогда тест проверяет сразу две вещи: знает ли модель регламент и выдерживает ли она грязный пользовательский ввод.

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

Как считать метрики честно

Не чините интеграцию заново
Смените base_url на RU LLM и прогоняйте регрессии в привычных SDK.

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

Хорошее правило простое: сравнивайте модели на одном и том же срезе. Если вчера вы проверяли 500 живых диалогов про возвраты, а сегодня взяли другой набор или обновили системный промпт, цифры уже нельзя ставить рядом как честный прогресс. Вы сравниваете не модели, а разные условия.

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

Для LLM в поддержке или финтехе обычно полезно отслеживать хотя бы четыре метрики:

  • общий score по всему eval-набору;
  • score по живым запросам отдельно;
  • долю критичных ошибок;
  • p95 по качеству или по числу провальных кейсов в батче.

Дорогие ошибки не стоит отдавать только автоматическому судье. Если ошибка ведет к лишней выплате, жалобе в ЦБ или утечке персональных данных, держите ручную проверку на отдельной выборке. Пусть она маленькая, 50-100 кейсов, но стабильная. Такой слой быстро ловит то, что автооценка пропускает.

Версии тоже дисциплинируют. Фиксируйте датасет, шаблон промпта, judge prompt, параметры модели и дату среза. Один лишний retry, новый system prompt или другое правило нормализации ответов меняют результат сильнее, чем кажется. Без версий спор о качестве обычно сводится к "у меня локально было лучше".

Если вы прогоняете один и тот же набор через разные модели, удобно держать одинаковую клиентскую обвязку. В этом смысле RU LLM полезен как единый OpenAI-совместимый эндпоинт: можно менять модель или провайдера, не переписывая остальной код, и честнее сравнивать результаты в одинаковых условиях.

Частые ошибки

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

Это особенно заметно в поддержке. Если синтетика строится по шаблону вроде "почему списали комиссию", модель быстро привыкает к одинаковой форме вопроса. А пользователь пишет иначе: "у меня опять минус после покупки, кто вернет", "чек есть, списание лишнее", "помогите, карта не у меня". Один источник генерации почти всегда сужает картину.

Вторая ошибка не менее вредная: реальные запросы чистят так сильно, что исчезает сам шум, который и ломает систему. Убирают опечатки, сокращения, обрывки фраз, следы копипаста из почты, лишние номера заказов, эмоциональные вставки. После такой уборки реальные запросы в evals перестают быть реальными.

Для оценки LLM в продакшене шум нужен. Именно он показывает, понимает ли модель фразу "деньги списали дважд ы", отличает ли номер договора от суммы и не путается ли, когда клиент пишет без знаков препинания. Если оставить только гладкий текст, вы тестируете учебный пример, а не рабочую нагрузку.

Еще одна ошибка тоньше и опаснее: команда случайно подмешивает правильный ответ в сам вопрос. Например, в датасете появляется формулировка "клиенту ошибочно списали комиссию, нужно объяснить порядок возврата". Это уже не запрос пользователя, а описание кейса с подсказкой. Модель читает вопрос и получает половину решения заранее. Потом на чистом продакшен-трафике качество внезапно падает, хотя на eval все выглядело хорошо.

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

Лучше фиксировать набор на одну серию экспериментов. Если нашли новый тип ошибки, положите его в следующий релиз eval-набора и сравнивайте заново на общей версии. Иначе вы не поймете, модель стала лучше или тест просто стал удобнее.

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

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

Быстрая проверка перед запуском

Соберите честный eval набор
Используйте реальные запросы, маскирование PII и аудит-трейлы в одном контуре.

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

Сначала возьмите свежий срез запросов. Не архив за прошлый квартал, а последние 2-4 недели, где есть текущие темы, новые формулировки и реальные перекосы по трафику. Иначе синтетика снова победит на бумаге, а в продакшене вы увидите другой профиль ошибок.

Если в логах есть персональные данные, не тащите их в evals как есть. Маскируйте имена, телефоны, номера договоров, адреса и все, по чему можно узнать человека. Для команд, которые живут в рамках 152-ФЗ, это обычная часть процесса. Без этого многие просто не смогут честно использовать реальные запросы в тестах.

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

Перед запуском стоит проверить пять вещей:

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

Порог лучше ставить именно на живых запросах. Синтетика подходит для smoke test и для покрытия дыр, но не для финального решения "катим или нет". Если модель дала 92% на синтетике и 78% на живом срезе, выпускать ее рано, даже если графики выглядят красиво.

И последнее: оставьте небольшой пакет спорных ответов на ручной разбор. Обычно хватает 30-50 примеров. За полчаса команда видит то, что теряют агрегированные метрики: опасную вежливую галлюцинацию, срыв формата ответа или неверный отказ в редком, но дорогом сценарии.

Что делать дальше

Не пытайтесь сразу построить идеальный набор на весь продукт. Обычно это съедает время и все равно не ловит реальные сбои. Лучше взять один поток, где ошибка быстро видна бизнесу: спорные списания, возвраты или ответы по лимитам.

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

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

Этого уже хватает, чтобы увидеть, где тесты врут. Если метрики растут только на синтетике, а живые запросы проседают, набор ушел слишком далеко от боевого трафика. Если на реальных данных все ровно, а синтетика падает, проверьте, не сделали ли вы искусственные примеры слишком одинаковыми или слишком сложными.

Дальше нужна дисциплина. У датасета должна быть версия, дата среза, источник реальных примеров, правило обезличивания, версия промпта, модель и способ подсчета метрик. Иначе через месяц команда увидит просадку, но не поймет, что именно изменилось.

Если вы ведете LLM через инфраструктуру внутри РФ, такой процесс проще держать под контролем. В RU LLM для этого полезны хранение логов и бэкапов в РФ, встроенное маскирование PII и аудит-трейлы на каждый запрос. Для команд, которые работают с персональными данными и живут в контуре 152-ФЗ, это снимает часть организационной боли при сборе реальных примеров для evals.

Нормальный следующий шаг простой: один сценарий, один смешанный датасет, один регресс перед релизом. Через пару недель у команды будет не абстрактный benchmark, а рабочая проверка, которая ловит поломки до выката.