При этом агент может не врать: по его собственной проверке всё и правда сходится. Только код написал он, тесты придумал он же, и окончательное «готово» сказал тоже он.
Агенту слишком легко поверить самому себе
В основе агента лежит языковая модель. Она генерирует текст, последовательно предсказывая следующий токен. Инструкции, история работы и результаты команд задают контекст для каждого нового шага. Так агент может написать код, запустить тесты и решить, что всё готово. Но связная последовательность действий ещё не гарантирует верный результат.
Исследования показывают: языковые модели склонны соглашаться с пользователем, даже когда он ошибается. В научных работах для этого используют термин sycophancy, то есть склонность поддакивать пользователю. Одна из возможных причин - дообучение по оценкам людей, которые сравнивают несколько ответов модели. Если более высокую оценку получает вариант, поддерживающий позицию пользователя, модель может научиться воспроизводить это поведение.1
Мы, разработчики, добавляем к этому свою привычку. Я и сам не раз писал тесты так, чтобы подтвердить собственный замысел: подал ожидаемые данные, получил ожидаемый ответ, поставил галочку. Намного реже тест начинается с вопроса: «На чём это решение должно сломаться?»
Когда агент делает так же, одна ошибка проходит через всю работу. Сначала она попадает в код. Затем - в тест, потому что тест построен на том же предположении. Тесты проходят, агент получает подтверждение и пишет: «Готово». На деле он просто трижды согласился сам с собой.
Команда «проверь себя ещё раз» иногда помогает, иногда не меняет ответ, а иногда портит правильное решение. Исследования внутренней самокоррекции рассуждений показывают то же ограничение: без внешнего сигнала повторная проверка тем же автором не становится независимым доказательством.2
Тест должен искать ошибку, а не подтверждение
Эдсгер Дейкстра ещё в 1969 году объяснил, чего тесты доказать не могут:
Тестирование программ может показать наличие ошибок, но никогда - их отсутствие.
Эдсгер Дейкстра, Notes on Structured Programming, EWD249
Красный результат предъявляет конкретный провал: вот вход, вот фактическое поведение, вот то, что требовалось. Зелёный результат говорит только о выбранных условиях. На этом входе и в этой сборке проверка не нашла сбоя. Про остальные входы она молчит.
Через десять лет Гленфорд Майерс сделал из этой асимметрии практическое правило. Тестирование - это выполнение программы с намерением найти ошибку. Хороший тест - тот, у которого высок шанс обнаружить ещё неизвестный дефект.3
Отсюда меняется не отдельная проверка, а принцип работы. Написанный код не уходит сразу в «готово». Сначала кто-то честно пытается его сломать так, чтобы результат можно было повторить. Если получилось, автор получает не мнение проверяющего, а контрпример.
В разработке с агентами этот принцип особенно важен. Один и тот же исполнитель пишет реализацию, выбирает проверки, читает их результат и формулирует вывод. Ошибка в исходном предположении может пережить все четыре шага. Поэтому проверку нужно вынести в отдельную роль: агент перестаёт защищать сделанное и ищет основания не признавать изменение готовым.
Как устроен Gopnik
Эту роль выполняет Gopnik - набор из трёх скиллов.
gopnik-setup
Знакомит Gopnik с проектом: находит и запускает проектные проверки, выясняет, где используют готовый результат, и сохраняет воспроизводимые маршруты для следующих прогонов.
gopnik-critic
Пытается опровергнуть утверждение до публикации: сверяет его с первичными источниками, а затем отдаёт на независимую проверку с задачей доказать обратное.
gopnik
Ищет, что может сломаться, атакует код и готовый результат, а затем выдаёт вердикт с доказательствами и границами проверки.
gopnik-setup выполняется при подключении к проекту и не повторяется в каждой задаче. Дальше скиллы можно использовать по отдельности. В нашем рабочем цикле роли распределены так:
Один из рабочих циклов
- Задача из трекеракоманда
- Критика постановки и решенияgopnik-critic
- Реализацияосновной агент
- Попытка сломать изменениеgopnik
- Вердикт и доказательстваgopnik
Последний шаг - вердикт. Gopnik не ограничивается перечнем выполненных команд, а отвечает, готово изменение или нет, и прикладывает доказательства.
NOT READY
Сломано: повторная установка оставляет устаревший файл.
Ревизия: 6eafa7d - повторная проверка дала тот же результат.
Сокращено: команда, путь к файлу и полный вывод здесь опущены.
Контрпример означает NOT READY. READY появляется, когда обязательные проверки пройдены, блокеров нет, а последний раунд проверки покрывает точную ревизию. Непроверенные границы Gopnik называет отдельно.
Что получилось на практике
На крупном внутреннем проекте, где агенты активно участвовали в разработке, одна из первых проверок с Gopnik выявила четыре класса дефектов. Ошибки исправили, а найденные случаи закрепили тестами.
После этого посмотрели на динамику в истории Git.
После этого мы пересмотрели сам критерий готовности.
Что теперь означает «готово»
Gopnik не доказывает, что в коде больше нет ошибок. Он меняет право произнести слово «готово». Теперь одного зелёного прогона мало: агент должен показать, как пытался сломать изменение, какую версию проверил и что осталось за границей проверки.
Когда агент в следующий раз скажет, что всё работает, не просите его проверить ещё раз. Спросите, как именно он пытался взять свою реализацию на понт.
Gopnik распространяется по лицензии MIT. В репозитории есть инструкции по установке для Claude Code и Codex, описание трёх скиллов и пример конфигурации проекта.
Источники
- Towards Understanding Sycophancy in Language Models, Sharma et al., 2023.
- Large Language Models Cannot Self-Correct Reasoning Yet, Huang et al., 2023.
- The Art of Software Testing, Glenford J. Myers, 1979.
- Notes on Structured Programming, EWD249, Эдсгер Дейкстра, 1969.