Кодинг-агенты умеют быстро генерировать код, но результат часто расходится с ожиданиями. Естественный язык слишком неоднозначен для передачи точных требований, а классические unit-тесты с десятками ассертов тяжело читаемы.
Хороший подход для работы с агентами базируется на принципах BDD (Behavior-Driven Development). Нам важно не то, как реализована задача, а как ведет себя система. Лучший способ передать это поведение машине и легко проверить человеку - исполняемые спецификации.
Подход схож с такими идеями, как снепшот-тесты и Cucumber/Gherkin. Вместо написания тестового кода для каждого сценария, мы пишем тестовый раннер, а всю логику валидации выносим в файлы исполняемых спецификаций.
Файл исполняемой спецификации выступает как контракт:
Входные данные: аргументы, исходные файлы, состояние системы.
Ожидаемый результат: stdout, выходные файлы, последовательность вызовов.
Тестовый раннер читает исполняемую спецификацию, запускает код и сравнивает фактический результат с поведением, зафиксированным в файле.
При разработке утилиту outln из предыдущей статьи, я сделал формат спецификации на основе yaml. При желании можно использовать, например, json или toml (я рассматривал toml, но yaml показался более читаемым в этом сценарии). Можно, конечно, придумать и свой формат, но плюс существующего формата в том, что агент с ним уже знаком - меньше шанс запутаться в синтаксисе.
Задача утилиты — пройтись по всем исходникам в папке и вывести названия файлов и header-комментарии из них.
Одна из спецификаций:
args: # входные параметры для cli - src src/one.ts: | # файл 1 /** One summary. */ export const one = 1; src/two.ts: | # файл 2 /** Two summary. */ export const two = 2; stdout: | # результат, который мы ожидаем получить src/one.ts: One summary. src/two.ts: Two summary.
Пример со сценарием ошибки:
args: - foobar stderr: | Error: Directory foobar does not exist exit_code: 1
Получившиеся yaml-файлы и есть исполняемые спецификации. Запускаем cli с данными из args на папке описанной в файле и сравниваем то что получилось с ожидаемым результатом из stdout и stderr. Как видим:
Всё в одном месте: аргументы, входные файлы и ожидаемый вывод.
Читается и понимается за 10 секунд.
Легко расширяется: например, если нужно добавить влияние env — это просто еще одно поле.
Агенту максимально легко копировать такой формат для создания новых файлов спецификаций.
В проекте outln на данный момент 127 таких спецификаций, большинство из них созданы агентом.
Главная ценность исполняемой спецификации заключается в том, что она становится общим артефактом для совместной работы.
Естественный язык удобен человеку, но оставляет пространство для галлюцинаций агента. Написание жесткого кода тестов понятно машине, но сильно замедляет и утомляет человека. Спецификация же выступает единым источником истины, который находится ровно посередине. Человеку легко формулировать бизнес-правила в виде понятных структур, а ИИ работает с предсказуемыми форматами данных.
Помимо этого, формат дает и другие преимущества:
Стабильный контракт: Агент не спорит с формулировками. Он видит четкое правило: конкретный вход → конкретный выход.
Документация через примеры: Агент может сам генерировать новые исполняемые спецификации по вашему шаблону, опираясь на контекст задачи.
Масштабируемое ревью: Проверить глазами 10 файлов исполняемых спецификаций намного быстрее, чем вникать в 10 новых unit-тестов.
Инициализация: Задается формат исполняемых спецификаций и реализуется базовый раннер. Реализация раннера как правило это достаточно тривиальная задачи и ее можно отдать агенту.
Постановка задачи:
Можно написать спецификации самостоятельно (тогда их не придется ревьювить).
Либо поставить задачу агенту: "Добавь новые исполняемые спецификации для функционала X". В этом случае основное усилие будет направлено на ревью спецификаций, которые у него получились.
Итерация: Агент запускает новые спецификации и видит разницу между текущим поведением и ожидаемым, тем самым получая четкую цель для реализации.
Исполняемые спецификации превращают работу с кодинг-агентом из рулетки "надеюсь, оно сработает" в контролируемый процесс. Машине даются не абстрактные требования, а четкий пример поведения — общий артефакт, который одинаково легко понятен как агенту, так и человеку.
Ссылки:
Пример раннера который используется для тестов outln можно посмотреть тут: https://github.com/ptol/casefile-runner
Примеры спецификаций тут: https://github.com/ptol/outln/tree/main/tests/cases
Какие подходы используете вы, чтобы кодинг-агент четко понимал поставленную задачу?
Источник


