Назад к заметкам

Gopnik-Driven Development

Агент пишет код, сам его проверяет и сообщает: «Всё работает». Почему этого недостаточно?

Иллюстрация Gopnik: разработчик в чёрной одежде, белый кот и ноутбук
Перед словом «готово» кто-то должен попытаться доказать обратное.

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

Агенту слишком легко поверить самому себе

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

Исследования показывают: языковые модели склонны соглашаться с пользователем, даже когда он ошибается. В научных работах для этого используют термин sycophancy, то есть склонность поддакивать пользователю. Одна из возможных причин - дообучение по оценкам людей, которые сравнивают несколько ответов модели. Если более высокую оценку получает вариант, поддерживающий позицию пользователя, модель может научиться воспроизводить это поведение.1

Мы, разработчики, добавляем к этому свою привычку. Я и сам не раз писал тесты так, чтобы подтвердить собственный замысел: подал ожидаемые данные, получил ожидаемый ответ, поставил галочку. Намного реже тест начинается с вопроса: «На чём это решение должно сломаться?»

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

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

Тест должен искать ошибку, а не подтверждение

Эдсгер Дейкстра ещё в 1969 году объяснил, чего тесты доказать не могут:

Тестирование программ может показать наличие ошибок, но никогда - их отсутствие.

Эдсгер Дейкстра, Notes on Structured Programming, EWD249

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

Через десять лет Гленфорд Майерс сделал из этой асимметрии практическое правило. Тестирование - это выполнение программы с намерением найти ошибку. Хороший тест - тот, у которого высок шанс обнаружить ещё неизвестный дефект.3

Отсюда меняется не отдельная проверка, а принцип работы. Написанный код не уходит сразу в «готово». Сначала кто-то честно пытается его сломать так, чтобы результат можно было повторить. Если получилось, автор получает не мнение проверяющего, а контрпример.

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

Как устроен Gopnik

Эту роль выполняет Gopnik - набор из трёх скиллов.

онбординг

gopnik-setup

Знакомит Gopnik с проектом: находит и запускает проектные проверки, выясняет, где используют готовый результат, и сохраняет воспроизводимые маршруты для следующих прогонов.

утверждения

gopnik-critic

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

готовность

gopnik

Ищет, что может сломаться, атакует код и готовый результат, а затем выдаёт вердикт с доказательствами и границами проверки.

gopnik-setup выполняется при подключении к проекту и не повторяется в каждой задаче. Дальше скиллы можно использовать по отдельности. В нашем рабочем цикле роли распределены так:

Один из рабочих циклов

  1. Задача из трекеракоманда
  2. Критика постановки и решенияgopnik-critic
  3. Реализацияосновной агент
  4. Попытка сломать изменениеgopnik
  5. Вердикт и доказательстваgopnik

Последний шаг - вердикт. Gopnik не ограничивается перечнем выполненных команд, а отвечает, готово изменение или нет, и прикладывает доказательства.

NOT READY
Сломано: повторная установка оставляет устаревший файл.
Ревизия: 6eafa7d - повторная проверка дала тот же результат.
Сокращено: команда, путь к файлу и полный вывод здесь опущены.

Контрпример означает NOT READY. READY появляется, когда обязательные проверки пройдены, блокеров нет, а последний раунд проверки покрывает точную ревизию. Непроверенные границы Gopnik называет отдельно.

Что получилось на практике

На крупном внутреннем проекте, где агенты активно участвовали в разработке, одна из первых проверок с Gopnik выявила четыре класса дефектов. Ошибки исправили, а найденные случаи закрепили тестами.

После этого посмотрели на динамику в истории Git.

Число коммитов с пометками регрессий и хотфиксов снизилось с 8,9 до 3,9 на 100 коммитов без слияний
На 100 коммитов без слияний; два последовательных окна примерно по 28 суток. Источник - локальная история основной ветки.

После этого мы пересмотрели сам критерий готовности.

Что теперь означает «готово»

Gopnik не доказывает, что в коде больше нет ошибок. Он меняет право произнести слово «готово». Теперь одного зелёного прогона мало: агент должен показать, как пытался сломать изменение, какую версию проверил и что осталось за границей проверки.

Когда агент в следующий раз скажет, что всё работает, не просите его проверить ещё раз. Спросите, как именно он пытался взять свою реализацию на понт.

Gopnik распространяется по лицензии MIT. В репозитории есть инструкции по установке для Claude Code и Codex, описание трёх скиллов и пример конфигурации проекта.

Посмотреть Gopnik на GitHub

Источники

  1. Towards Understanding Sycophancy in Language Models, Sharma et al., 2023.
  2. Large Language Models Cannot Self-Correct Reasoning Yet, Huang et al., 2023.
  3. The Art of Software Testing, Glenford J. Myers, 1979.
  4. Notes on Structured Programming, EWD249, Эдсгер Дейкстра, 1969.