Эксперимент Дэна Лу: тесты проходят, даже когда код содержит баг
Эксперимент Дэна Лу показал: агенты писали тесты, которые оставались зелёными даже при наличии ошибки в коде. Причина — в выборе входных данных, эталона и обходе рискованных веток.
- Дэн Лу сравнил 26 условий, включая контроль без инструкций, с Codex на GPT-5.6 Sol уровня medium и xhigh — по 80 прогонов на сочетание.
- Агенты писали декодер Zstd на Rust по RFC в контейнере без интернета и не видели скрытых тестов.
- В 10 прогонах из 160 агенты генерировали структурированные входы для фаззинга, в половине из них нашли реальные баги.

Дэн Лу провёл эксперимент, в котором ИИ-агенты писали декодер Zstd на Rust по RFC. Работали в контейнере без интернета, скрытых тестов не видели. Успехом считался прогон, прошедший все скрытые тесты. Автор сравнил 26 условий, включая контроль без дополнительных инструкций. В основном сравнении использовал Codex с GPT-5.6 Sol на уровнях medium и xhigh, по 80 прогонов на сочетание условия и уровня.
В промт добавляли указания применять TDD, фаззинг, формальные методы, аудит и другие техники; в отдельных условиях подключали скиллы. Убедительного общего выигрыша от этих указаний не получилось, многие условия выглядели хуже контроля. Автор предупреждает: разброс велик, порядок условий в таблице не стоит принимать за надёжный рейтинг методов. Кроме того, агент мог прочитать название техники и выполнить её лишь частично. Это эксперимент о поведении агентов при таких инструкциях на конкретной задаче, а не доказательство бесполезности TDD или фаззинга.
Три механизма, из-за которых тест пропускает баг
Первый механизм — вход скрывает различие. В Zstd есть режим с четырьмя потоками Хаффмана. Если реализация перепутала их порядок, тест должен это заметить. Но агенты использовали четыре одинаковых потока. Перестановка одинаковых частей ничего не меняет: правильная и неправильная реализации получают одинаковый результат. Палиндром при проверке разворота — тот же случай. Вход симметричен относительно ошибки, которую нужно обнаружить.
Второй механизм — ожидаемый ответ повторяет ошибку реализации. В условии с дифференциальным тестированием 135 прогонов из 160 сделали что-то похожее на сравнение реализаций. Однако агенты не написали две полные реализации; частичные сравнения нередко повторяли один алгоритм вместе с ошибкой. Сравнение двух результатов полезно, когда есть основания доверять независимости их получения. Если обе стороны одинаково неверно поняли RFC, совпадение ответов этого не обнаружит.
Третий механизм — проверка обходит рискованную ветку. При фаззинге агенты в основном генерировали случайные байты. Для декодера такой вход часто заканчивается ранним отказом: данные не похожи на допустимый формат. Проверка устойчивости к мусору нужна, но она может почти не затронуть сложное декодирование. В 10 прогонах из 160 агенты генерировали структурированные случайные входы; в половине этих прогонов нашли реальные баги.
Как проверить сам тест
Автор приводит учебный пример на Python. Функция должна разворачивать строку. Слабый тест проверяет вход «abba» и ожидает «abba» — он останется зелёным, даже если функция вообще ничего не делает. Полезный тест проверяет «abcd» и ожидает «dcba» — он проходит на правильной реализации и падает на намеренно сломанной. Это простейший пример мутационного тестирования: внести контролируемое изменение в код и проверить, обнаружат ли его тесты.
В обсуждении на Hacker News пользователь coder-pm описывает небольшой Bash-проект с такими мутациями: задаёт их руками под конкретные тесты, а любая выжившая мутация блокирует результат. Это опыт комментатора; для больших проектов объём и стоимость такой проверки нужно выбирать отдельно.
Для практики автор советует назвать конкретную ошибку, которую тест обязан обнаружить, выбрать вход, на котором эта ошибка изменяет результат, получить ожидаемый ответ из контракта и записать его происхождение. Затем в тестовой копии внести выбранную ошибку и запустить проверку. Если тест остался зелёным, выяснить причину.
Для профиТехнические детали: архитектура, цифры, ссылки
Эксперимент: агенты писали декодер Zstd на Rust по RFC. Работали в контейнере без интернета, скрытых тестов не видели. Успехом считался прогон, прошедший все скрытые тесты. Дэн Лу сравнил 26 условий, включая контроль без дополнительных инструкций. В основном сравнении использовал Codex с GPT-5.6 Sol на уровнях medium и xhigh, по 80 прогонов на сочетание условия и уровня. В промт добавляли указания применять TDD, фаззинг, формальные методы, аудит и другие техники; в отдельных условиях подключали скиллы.
В условии с дифференциальным тестированием 135 прогонов из 160 сделали что-то похожее на сравнение реализаций. Однако агенты не написали две полные реализации; частичные сравнения нередко повторяли один алгоритм вместе с ошибкой. Независимый аудит был у 42 агентов; эти прогоны показали худший результат, причём Дэн прямо допускает обратную связь: отдельный аудит могли выбирать в более трудных случаях. Из этих данных нельзя вывести ни пользу, ни вред отдельного агента как причинный эффект.
При фаззинге агенты в основном генерировали случайные байты. В 10 прогонах из 160 агенты генерировали структурированные случайные входы; в половине этих прогонов нашли реальные баги. У формальных методов встречалась похожая проблема: агент доказывал, что при допустимом индексе обращение остаётся в границах, тогда как ошибка была в другом свойстве.
Пример мутационного тестирования на Python:
def reverse_ok(text):
return text[::-1]
def reverse_bug(text):
return text # намеренная ошибка: разворот потерян
def weak_test(reverse):
assert reverse("abba") == "abba"
def useful_test(reverse):
assert reverse("abcd") == "dcba"
def passes(test, implementation):
try:
test(implementation)
except AssertionError:
return False
return True
for test in (weak_test, useful_test):
print(test.__name__,
passes(test, reverse_ok),
passes(test, reverse_bug))
Результат: weak_test True True; useful_test True False. Первый тест не обнаруживает выбранную ошибку. Второй проходит на правильной реализации и падает на намеренно сломанной.
Вопросы и ответы
- Сколько условий сравнил Дэн Лу?
- 26 условий, включая контроль без дополнительных инструкций. В основном сравнении использовал Codex с GPT-5.6 Sol на уровнях medium и xhigh, по 80 прогонов на сочетание.
- Какой процент прогонов с дифференциальным тестированием повторял ошибку?
- 135 прогонов из 160 сделали что-то похожее на сравнение реализаций, но не написали две полные реализации; частичные сравнения нередко повторяли один алгоритм вместе с ошибкой.
- Сколько прогонов с фаззингом нашли реальные баги?
- В 10 прогонах из 160 агенты генерировали структурированные случайные входы; в половине этих прогонов нашли реальные баги.



