Новости ИИ

Как ловят галлюцинации LLM в коде через CI-проверки

Два монитора с кодом и логами сборки рядом с распечатками PR и кабелями на рабочем месте разработчиков

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

Почему нейросетевой код легко обманывает ревью

История началась с пулл-реквеста, где в цикле обновления частиц оказался вызов несуществующей функции FrameAllocator::TryReclaimTail(). Он был оформлен в правильном стиле, с нужным префиксом и даже с комментарием, поэтому на ревью выглядел вполне правдоподобно.

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

Что команда поменяла в сборке

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

Первое и самое важное правило — каждый PR теперь собирается сразу всеми тулчейнами проекта: MSVC для десктопа и редактора, clang для двух консолей и gcc для серверной части. Все это идет с включенными предупреждениями как ошибками. Да, это дороже по времени и ресурсам, зато закрывает дыры, которые раньше проявлялись только ночью или уже после мерджа.

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

Какие проверки ловят ошибки LLM лучше всего

Отдельно в пайплайн добавили анализ новых идентификаторов из diff. Бот вытаскивает все имена из добавленных строк и проверяет, встречались ли они в репозитории или в сторонних заголовках. Если имя впервые появляется в том же PR и при этом нигде не объявлено, на запрос ставится метка и прилетает комментарий.

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

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

Частые вопросы

Зачем вообще проверять код от LLM отдельно от обычной сборки?
Потому что нейросеть может написать правдоподобный, но несуществующий код, который не ловится сразу на компиляции. Такие ошибки особенно опасны в старых проектах и на платформах, где часть кода не исполняется на каждом билде.
Почему одной сборки с тестами оказалось недостаточно?
Потому что проблема может прятаться в макросах, условной компиляции или шаблонах. На одной платформе все выглядит нормально, а на другой ошибка всплывает только позже.
Что дает проверка новых идентификаторов в diff?
Она помогает заметить имена, которых раньше не было в проекте и которые модель могла просто выдумать. Это полезно, когда компилятор пока не может увидеть проблему из-за структуры кода.
Стоит ли запрещать команде пользоваться ИИ в разработке?
В описанном подходе команда не запрещает LLM, а ставит вокруг нее защитные барьеры. Это практичнее: люди все равно будут пользоваться ИИ, а задача команды — сделать так, чтобы выдумки не доезжали до релиза.

Читайте также

Попробовать Lord GPT бесплатно →