
Проверка воспроизводимости ответов LLM: что именно сравнивать
Если один и тот же запрос сегодня и завтра даёт разные ответы, это не обязательно означает, что модель «сломалась». На результат влияют параметры генерации, версия модели, системная инструкция, инструменты и даже изменившийся контекст. Поэтому проверка воспроизводимости ответов LLM — это не поиск побайтно одинакового текста, а контроль того, сохранилось ли нужное поведение на заранее выбранном наборе задач.
Такой тест полезен перед обновлением модели, переносом приложения на другой API или изменением системного промпта. Он помогает ответить на практический вопрос: стало ли решение задачи хуже, лучше или просто другим?
Сначала определите, что считается успехом. Для классификатора это может быть правильная метка, для извлечения данных — валидный JSON с нужными полями, для поддержки — ответ, который не нарушает заданные правила. Если критерий не определён, сравнение сведётся к субъективному выбору «какой текст звучит убедительнее».
Почему одинаковый запрос не гарантирует одинаковый результат
Генерация текста вероятностна. Параметры вроде температуры влияют на разнообразие выборки, но не превращают API в полностью детерминированную систему. OpenAI описывает `seed` как средство сделать результаты более воспроизводимыми, одновременно предупреждая, что гарантия не абсолютна: поведение может меняться между обновлениями бэкенда. В ответах API также доступен `system_fingerprint`, который помогает заметить изменения конфигурации сервиса.
Это важное различие: фиксированный seed — способ уменьшить вариативность в контролируемом эксперименте, а не обещание вечного совпадения ответов. Повторяемость зависит от доступности конкретных параметров в выбранной модели и версии API.
На результат могут влиять и факторы за пределами семплирования:
- смена идентификатора или версии модели;
- изменение системных и пользовательских инструкций;
- другой набор переданных документов;
- обновление инструментов, схемы функций или кода приложения;
- изменение лимита токенов или формата ответа;
- скрытые обновления сервиса.
Поэтому в тесте нужно сохранять не только текст запроса, но и весь существенный контекст выполнения.
Как собрать небольшой, но полезный набор тестов
Начните с 20–50 запросов, которые отражают реальные сценарии приложения. Это не универсальная статистическая норма, а удобный стартовый размер для ручной проверки: его легко просмотреть, но он уже выявляет очевидные регрессии. Для критичных решений набор должен покрывать больше редких случаев и регулярно пополняться примерами из эксплуатации.
Включите три типа примеров:
Типичные запросы — наиболее частые задачи пользователей.
Граничные случаи — неполные данные, неоднозначные формулировки, длинный контекст.
3. Известные ошибки — запросы, на которых система раньше давала неверный формат, пропускала условие или выдумывала отсутствующие сведения.
Для каждого примера запишите ожидаемое поведение, а не обязательно единственный правильный ответ. Например: «вернуть ровно один из трёх классов» или «если источника нет, указать, что данных недостаточно». Это позволяет оценивать разные допустимые формулировки по одному критерию.
Сохраняйте тестовый набор в обычном JSONL или CSV, чтобы его можно было запускать повторно. В записи должны быть входные данные, контекст, ожидаемые свойства результата и идентификатор сценария. Не храните в наборе реальные персональные данные без надлежащего основания и защиты.
Как провести повторный запуск и отделить шум от регрессии
Зафиксируйте модель, системную инструкцию, параметры генерации, формат ответа, инструменты и версию кода, который собирает запрос. Если API поддерживает seed, задайте его и сохраните вместе с результатом. При этом повторите каждый пример несколько раз: один запуск не показывает, насколько ответ нестабилен сам по себе.
Практическая схема сравнения:
- прогоните набор на текущей конфигурации;
- выполните несколько повторов на тех же входах;
- сохраните ответы, метаданные модели и ошибки API;
- измените только один фактор — например, версию модели;
- снова запустите тот же набор и сопоставьте результаты.
Менять по одному фактору важно для диагностики. Если одновременно обновить модель, промпт и инструменты, ухудшение нельзя уверенно приписать чему-то одному.
Для каждого случая храните сырой ответ и машинно проверяемый результат. Для JSON можно проверить парсинг, обязательные ключи и типы значений; для классификации — сравнить метку с эталоном; для ответа с цитатами — проверить, что указанные фрагменты действительно есть в переданном источнике. Такие проверки надёжнее, чем сравнение текстов целиком.
Какие метрики подойдут разным задачам
Выбирайте метрику по назначению модели. Для классификации подойдут точность и матрица ошибок. Если пропуск одного класса особенно дорог, отдельно измеряйте полноту этого класса. Для извлечения структурированных данных проверяйте долю валидных ответов, заполнение обязательных полей и совпадение значений с эталоном.
Для свободного текста точное совпадение строк обычно малоинформативно. Два корректных ответа могут использовать разные слова, а похожие формулировки — содержать разную фактическую ошибку. Разбейте критерии на конкретные проверки: соблюдены ли ограничения, присутствуют ли нужные факты, не добавлены ли запрещённые утверждения, отвечает ли текст на поставленный вопрос.
Если нужна оценка человеком или отдельной моделью-судьёй, используйте фиксированную рубрику и одинаковые инструкции для всех сравниваемых версий. Автоматический судья сам может ошибаться, поэтому на выборке полезно сверять его оценки с ручной разметкой. Исследование BERTScore показывает, что семантическое сравнение может лучше учитывать близость формулировок, чем простое совпадение слов, но такая метрика не доказывает фактическую правильность ответа.
Не сводите всё к одному среднему баллу. Показывайте результаты по типам задач и сохраняйте список конкретных провалов: общее улучшение может скрыть ухудшение на редком, но важном сценарии.
Минимальный пример проверки структурированного ответа
Предположим, модель извлекает из текста номер заказа и статус. Для каждого примера задайте ожидаемый JSON, а затем проверьте формат и значения отдельно:
python
import json
def check_response(raw, expected):
try:
result = json.loads(raw)
except json.JSONDecodeError:
return {«valid_json»: False, «fields_match»: False}
required = {«order_id», «status»}
valid_json = required.issubset(result.keys())
fields_match = (
valid_json
and result[«order_id»] == expected[«order_id»]
and result[«status»] == expected[«status»]
)
return {
«valid_json»: valid_json,
«fields_match»: fields_match,
}
Этот тест не измеряет качество всей модели. Он отвечает на более узкий вопрос: выполнила ли система контракт для конкретного сценария. В реальном тесте дополнительно проверьте типы полей, лишние ключи, допустимые значения статуса и поведение при отсутствии номера заказа.
Запускайте одну и ту же функцию проверки для старой и новой конфигураций. Сохраняйте результаты построчно, чтобы увидеть, какие примеры перестали проходить. Не ограничивайтесь общим процентом: падение с 98% до 96% может скрывать два совершенно разных по важности сбоя.
Что считать изменением, а что — обычной вариативностью
При повторных запусках разделяйте три ситуации. Первая — результат меняется по форме, но проходит заданные проверки. Для задачи извлечения это обычно не проблема. Вторая — качество колеблется между запусками: тогда нужно оценить частоту ошибок и проверить, можно ли снизить вариативность настройками или более точными ограничениями. Третья — новая версия систематически нарушает критерий, который раньше выполнялся: это повод разбирать регрессию.
Установите порог до теста. Например, критичной может быть любая потеря валидного формата для интеграции, тогда как небольшое изменение стиля не заслуживает блокировки релиза. Порог зависит от последствий ошибки, поэтому его нельзя универсально вывести из документации провайдера.
При сравнении версий учитывайте размер и состав тестового набора. На маленькой выборке единичная ошибка сильно меняет процент. Для важных сценариев приводите число проверенных примеров и число сбоев, а не только округлённую долю.
Ограничения теста и что проверить перед обновлением
Регрессионный набор показывает поведение на знакомых примерах, но не гарантирует качество на всех будущих запросах. Если приложение получает новые типы документов, языки или пользовательские формулировки, тестовый корпус нужно дополнять. Не следует также считать автоматическую оценку свободного текста объективной без проверки на размеченных примерах.
Перед переключением версии проверьте:
- совпадают ли системная инструкция и входной контекст;
- не изменились ли параметры генерации и доступные инструменты;
- записаны ли версия модели и метаданные ответа;
- проверяются ли важные свойства результата, а не только стиль;
- просматривались ли ошибки отдельно по классам сценариев;
- повторялись ли тесты на критичных запросах.
Документация OpenAI по воспроизводимым результатам прямо оговаривает ограничения seed и роль отпечатка конфигурации. Материалы Anthropic по согласованности также рассматривают повторяемость как практическую цель, а не абсолютную идентичность каждого ответа. Поэтому надёжный вывод строится на совокупности: фиксированной конфигурации, повторных запусках, проверяемых критериях и сохранённых примерах ошибок.









