METHODOLOGY · 3 июля 2026 г.
Нельзя юнит-тестить бросок кости
Разработчик встраивает LLM в систему, пишет вокруг обычный тест «прошёл/не прошёл», видит, что он флакает, и дальше либо удаляет тест, либо мокает модель до бессмысленности. Оба варианта неверны. Вероятностный компонент не сломан, когда варьируется, — но «оно варьируется» не даёт права перестать его тестировать. Просто надо тестировать распределение, а не единичный сэмпл: оценивать золотой набор с допуском, ставить порог на процент прохождений, проверять инварианты, которые обязаны держаться каждый раз, и держать жёсткую границу между стохастическим ядром и детерминированной оболочкой вокруг него.
Вот баг, который на моих глазах умные инженеры выкатывают снова и снова. Они встраивают LLM в фичу, дальше делают правильную вещь — пишут тест: подаём вход, проверяем, что на выходе ровно ожидаемая строка. На их машине проходит. В CI падает. При перезапуске снова проходит. И тогда они делают одно из двух — удаляют тест или мокают модель, чтобы та возвращала заготовленный ответ, — и теперь та часть системы, которая вероятнее всего сглючит, оказывается единственной без теста вообще.
Ошибка не в флакающем тесте. Ошибка в рамке. Ты пытаешься юнит-тестить бросок кости.
Проверка одного прогона — неправильный инструмент
Юнит-тест задаёт вопрос «да/нет» о детерминированной функции: дано x — получаю ли я ровно y? Это
правильный вопрос к парсеру и бессмысленный — к модели, которой по замыслу позволено сказать одну и
ту же верную вещь пятью разными способами. Прогони достаточно раз — и один и тот же промпт даёт тебе
распределение, а не значение. Проверять один вытянутый из этого распределения образец почти ничего не
говорит; ты просто переносишь подбрасывание монетки в свой CI.
Вот почему цифры по продуктивности ИИ-кодинга такие мутные. По ощущениям люди быстрее, но опытных разработчиков на реальных задачах замеряли как более медленных, а команды видят больше пул-реквестов, но более долгие ревью и растущий churn. Немалая часть этого — люди, пытающиеся валидировать недетерминированный вывод детерминированными привычками, по одной хрупкой проверке за раз, и тонущие в этом.
«Оно недетерминированное» — верное утверждение про модель и ленивая отмазка про систему. Кость не пришпилить. Но задать рамки, в которых ей позволено выпадать, — вполне.
Тестируй распределение, а не сэмпл
Ход в том, чтобы перестать проверять один правильный ответ и начать проверять форму поведения:
- Оценивай золотой набор, а не сверяй строку. Держи 30–100 реальных пар «вход/выход» и оценивай новые прогоны по похожести, рубрике или «модели-судье» — не по точному равенству. Ты меряешь качество, у которого есть диапазон, а не тождество, у которого его нет.
- Порог на процент прохождений, а не единственный зелёный. Прогони кейс N раз и требуй, скажем, 95% успеха. Один провал из двадцати — не сломанная сборка, а кость, ведущая себя как кость. А вот процент прохождений, ушедший за черту, — это уже регрессия, которую ты ловишь.
- Проверяй инварианты, которые обязаны держаться каждый раз. Содержимое варьируется; контракт — нет. Всегда валидный JSON. Никогда не сливает данные другого пользователя. Всегда внутри бюджета токенов. Отказывает в тех трёх промптах, в которых обязан отказать. Это детерминированно, и тестируется как всё остальное.
- Отдели стохастическое ядро от детерминированной оболочки. Роутинг, парсинг, валидация, ретраи и фолбэки вокруг модели — обычный код: юнит-тесть его жёстко, с замоканной моделью. А вероятностное тестирование прибереги для той единственной границы, которая на самом деле вероятностная.
Итог
Недетерминированность — не повод не тестировать, а повод тестировать иначе. Модель, которую не пришпилить, — ровно та часть системы, которой нужен измеряемый поводок, потому что «обычно работает» — это то, как ненадёжный софт ощущается изнутри ровно до момента, когда перестаёт.
Хватит проверять один удачный бросок. Оценивай золотой набор с допуском, ставь порог на процент прохождений, запирай инварианты и юнит-тесть детерминированную оболочку так, будто от неё зависит жизнь, — потому что в продакшне именно эта оболочка держит кости.
Комментарии
Пока нет комментариев
Войдите, чтобы участвовать в разговоре.
Будьте первым, кто оставит мысль.