16 сентября 2026 г.

26 ИИ-агентов прошли все тесты - и ни один не починил баг

Андрей Присекин
Автор

Андрей Присекин

Создаю AI-агентов, автоматизирую бизнес-процессы

«Все тесты зелёные» от ИИ-агента не доказывает, что баг починен. В свежем эксперименте 26 разных агентов - от крошечной 7B-модели до Opus 5 - получили один и тот же баг с готовыми тестами. Все прошли эти тесты. И все одинаково провалили скрытую проверку, которую не видели. Тесты, что ты написал, - это и есть ТЗ, а агент подгоняет решение ровно под них.

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

Что за эксперимент

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

Через стенд прогнали 26 агентов: Claude Code, Codex CLI, Haiku 4.5, Sonnet 5, Opus 5 и квантованную Qwen2.5-Coder 7B по нескольку прогонов. Разброс мощности огромный - от 4-битной модели на 7 миллиардов параметров до флагманского Opus 5. Логика проверки простая. Если агент реально понял баг, он пройдёт и скрытые тесты тоже. Если он просто подогнался под видимые - скрытые вскроют халтуру.

Все прошли видимые. И одинаково легли на скрытом

Самый показательный из пяти багов - вырезание HTML-тегов жадной регуляркой. Код такой: re.sub(r"<.+>", "", html). Кусок .+ жадный, он хватает максимум, что может: от первого символа < до последнего > в строке. На нормальной разметке это работает и все видимые тесты проходят.

А теперь дай строке a < b and c > d, где < и > - обычные знаки сравнения, а не теги. Жадная регулярка видит первый <, последний > и сжирает всё между ними, оставляя обрубок вроде a d. Правильное решение должно трогать только настоящие теги: <, за которым идёт имя тега, а не пробел.

На этой задаче все 26 агентов прошли видимые тесты. И все провалили скрытый. Варианты регулярки у них были разные - три-четыре разных выражения, но результат один и тот же неверный. Не случайный разброс, а одинаковая ошибка и у 4-битной 7B, и у Opus 5.

Один и тот же патч: видимый тест проходит, скрытый падает

Модель дороже - ответ тот же

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

В том же прогоне Haiku стоила 28 центов за задачу, Opus - 1 доллар 53 цента. В 5,4 раза дороже. Баллы на обоих наборах тестов у них вышли одинаковые: тот же зелёный видимый тест, тот же провал на скрытом. Переплата за флагман здесь не купила ничего. И «перезапусти ещё раз» тоже мимо: формулировка регулярки от прогона к прогону гуляет, а итог всё равно сходится к той же дыре.

Haiku за 0,28 доллара и Opus за 1,53 доллара дали одинаковый результат

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

Почему так: тест - это твоё ТЗ

Механизм под всем этим простой. Агент оптимизирует ровно под то, что записано в тестах. Тесты в его глазах - это спецификация задачи, а не «примеры на удачу». Написал в тесте только «нормальный HTML вырезается» - агент ровно это и обеспечит. Про строку a < b в тесте не сказано ни слова, значит для агента этого случая нет.

Лучшее доказательство - обратный ход. Когда тому же агенту скормили скрытый тест уже как видимую спеку, он решил задачу с первого раза. И сам объяснил: <, за которым не идёт буква, это обычный текст, трогать его нельзя. Знание у модели было всё это время. Не было его в задании.

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

Это не единичный курьёз

Тот же разрыв меряют и в исследованиях. Есть понятие «reward hacking gap» - зазор между баллом на видимых тестах и на скрытых. Чем он больше, тем сильнее агент набрал очки на прокси, не выполнив саму задачу. Под это уже собирают отдельные бенчмарки, где к каждой задаче идут две проверки: одна для агента, вторая спрятана.

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

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

Что с этим делать

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

  • Пиши крайние случаи в тест сам, до того как просишь чинить. Пустая строка, ноль, странный символ, граница диапазона. Один такой тест, добавленный руками, стоит десяти автосгенерированных - потому что он про тот самый край, где живёт баг.
  • Не давай агенту писать и фикс, и тесты к нему в один заход. Он подгонит их друг под друга: тест будет проверять ровно то, что делает код, и оба сойдутся на одной ошибке. Сначала свой падающий тест, потом фикс под него.
  • Смотри в диф, а не в галочку. «Все тесты прошли» читай как «прошли те тесты, что были». Пробеги патч глазами: что он на самом деле меняет и не подменил ли он логику заглушкой.
  • Старшая модель - не страховка. Если дыра в ТЗ, Opus провалится так же, как Haiku, просто дороже. Сначала полное задание, потом уже разговор про модель.

Хорошая новость в том, что рычаг у тебя в руках. Не нужен более умный ИИ, чтобы закрыть эту дыру, - нужно чуть более полное ТЗ. Пара крайних случаев, вписанных в тест до того, как ты позвал агента, вытаскивают из той же дешёвой модели верный код. А привычка читать диф вместо галочки отделяет тех, кто правит код с ИИ, от тех, кто копит скрытые баги под зелёными тестами. Если ещё не читал, как решать, где агенту вообще место, а где хватит простого сценария - это ровно про ту же дисциплину.

Источники

← Все статьи