Почему Coding Agent мало одного репозитория: 3 прогона — 3 разные цены
Разбор эксперимента: Coding Agent получил задачу и репозиторий и 3 раза написал аккуратный код с зелёными тестами — и каждый раз сам придумал цену, поля API, флаг выкатки и правило возврата. Почему это не ошибка кода и чего агенту не хватает.
Владимир Павлунин · опубликовано
Короткий ответ
Почему Coding Agent, которому дали только задачу и репозиторий, пишет правильный код, но неправильное решение?
Репозиторий показывает, как система устроена, но почти не объясняет, почему, и совсем не содержит решений, которые бизнес принял вне кода: цену, контракт с клиентскими приложениями, правила выкатки, историю инцидентов. Когда этих знаний нет, агент не останавливается — он заполняет пробел правдоподобной догадкой и закрепляет её тестами. В моём эксперименте 3 прогона на одной задаче дали 3 разные цены самовывоза (199, 0 и 150 ₽ вместо 99 ₽), ни в одном не было выключенного флага выкатки и верного правила возврата, при этом все тесты были зелёными. Это не дефект кода и не слабость модели, а отсутствие инженерного контекста.
Главное
- Coding Agent с одним репозиторием пишет код хорошего уровня: соблюдает слои, повторяет стиль проекта, находит готовую таблицу в миграциях и не ходит в сеть.
- Бизнес-решения, которых нет в коде, агент придумывает: 3 прогона — 3 цены (199, 0 и 150 ₽), ни одна не совпала с утверждённой 99 ₽ и порогом бесплатной доставки от 3000 ₽.
- Правила, которые живут в ADR, контракте и разборе инцидента, в условии A не выполнены ни разу: флаг выкатки выключен — 0 из 3, правило возврата верное — 0 из 3, общий лимит 15 кг — 0 из 3.
- Там, где в коде есть образец, агент сходится; там, где образца нет, он угадывает — и угадывает по-разному от запуска к запуску.
- Зелёные тесты не спасают: агент пишет тесты по своему пониманию задачи и закрепляет в них собственные догадки.
- Проблема не в агенте и не в ревьюере, а в том, где живут знания. Если их нет в контексте, их не получит ни агент, ни новый сотрудник.
Coding Agent получил обычную бизнес-задачу и репозиторий. За 4–7 минут он написал аккуратный код, прогнал тесты — все зелёные. Я повторил это 3 раза. И 3 раза агент сам назначил цену новой услуги: 199 рублей, бесплатно и 150 рублей. Бизнес утвердил 99 рублей с бесплатной доставкой от 3000.
Ниже — подробный разбор условия A из моего эксперимента для выпуска «От Coding Agent к AI Development Platform»: что агент сделал хорошо, что он придумал сам и почему это не ошибка кода. Общая картина — в основной статье о платформе, здесь только условие «задача + репозиторий».
Короткий ответ
Репозиторий — это не вся система. В нём лежат код, тесты, зависимости и конфиги. Но решения, по которым этот код должен быть написан, живут в других местах: требования у продакта, контракт API в соседнем репозитории, решение о выкатке в ADR, урок из инцидента — в разборе инцидента или просто в чьей-то памяти.
Когда этих знаний у агента нет, он не останавливается. Он заполняет пробел правдоподобной догадкой, реализует её чисто и закрепляет тестом. Получается правильный код, который решает не ту задачу.
Условия эксперимента
Чтобы спорить не на уровне мнений, я собрал небольшой синтетический сервис доставки интернет-магазина — shipline. Ничего корпоративного: код, 9 тестов, слои api → application → domain, адаптеры, миграции, модуль возвратов — всё создано специально для выпуска.
Задача — одна, в стиле реального тикета:
PROD-2291 · Самовывоз из пунктов выдачи
Добавить новый способ доставки — самовывоз из пунктов выдачи партнёра.
Клиент при оформлении заказа должен видеть этот вариант, выбрать пункт
выдачи и увидеть стоимость и срок.
Это нормальная задача: коротко и про ценность для клиента. В ней нет цены, срока, ограничений по весу, названий полей API, правил выкатки и правил возврата. Всё это в компании известно, но живёт не в тикете.
Два условия:
- A — задача и репозиторий. Так чаще всего и работают с агентами.
- B — то же самое плюс 8 коротких документов инженерного контекста: архитектура, 2 ADR, контракт API, требования, инженерные правила, политика безопасности и разбор инцидента. Всего 522 слова.
Модель одна — claude-sonnet-5, агент — Claude Code в headless-режиме с одинаковым списком разрешённых команд, стартовое состояние кода одинаковое. Каждое условие я прогнал 3 раза. Проверки написал заранее, до прогонов. Про условие B подробно — в статье о context engineering; здесь только A.
Что агент сделал хорошо
Сначала честно: код хороший. Если бы я получил такой pull request от человека, я бы не стал его разворачивать с порога.
В 2 прогонах из 3 агент начинал с того, что запускал вспомогательного агента на разведку: тот обходил репозиторий и возвращал короткую карту проекта. Потом основной агент перечитывал ключевые файлы, формулировал план и только после этого писал код.
Все 3 раза он:
- нашёл миграцию
004_partner_pickup_points.sqlс таблицей пунктов выдачи, которая нигде не использовалась, и подключил её, а не изобрёл своё хранилище; - добавил способ доставки классом в домене, репозиторий пунктов — в адаптерах (в памяти для тестов и поверх таблицы из миграции), сборку — в точке сборки приложения;
- не нарушил слои: домен не импортирует адаптеры, API и слой приложения;
- не добавил ни одного сетевого вызова;
- посчитал деньги в копейках через
Money, как принято в проекте; - добавил новый способ в словарь правил возврата и тест на возврат — без этой записи
refund_amountпадал бы сKeyError, и в прогоне A2 агент прямо объясняет это в отчёте.
Последний пункт особенно показателен: не каждый разработчик вспомнил бы о возвратах. Прогон A2 занял 262 секунды, 46 ходов и стоил 0,80 доллара.
Итог по собственным тестам агента:
прогон тестов зелёные время стоимость
условие A a1 22 да 346 c $1,13
a2 20 да 262 c $0,80
a3 19 да 434 c $0,88
Вот здесь обычно заканчиваются демонстрации: код написан, тесты прошли. Но эксперимент только начинается.
Что агент придумал сам
Цена: 3 прогона — 3 цены
a1 PRICE = Money.rub(199) всегда, без порога
a2 PRICE = Money(0) бесплатно всегда
a3 PRICE = Money.rub(150) всегда, без порога
PRD 99 ₽, бесплатно при сумме товаров от 3000 ₽
Ни одна цена не совпала с утверждённой. Интересно, что образец порога в коде был: курьер стоит 350 рублей и бесплатен от 5000. Но какая цена у самовывоза и есть ли у него порог, из кода не следует никак. В отчёте прогона A2 агент прямо пишет, что способ даёт «free/2-day delivery», — то есть он принял бизнес-решение и честно его описал.
Агент не ошибся в коде. Он принял решение, которое не имел права принимать, просто потому, что информации не было.
Контракт API: имена, удобные агенту
Контракт delivery-options.openapi.yaml, согласованный с командами web и mobile, требует:
code: pickup_point
список пунктов: pickup_points[] { pickup_point_id, address, hours }
выбор пункта: pickup_point_id
ошибки 422: pickup_point_required, pickup_point_unavailable
Что получилось в условии A:
a1 code = partner_pickup; список "points" с ключом "id"
a2 code = pickup_point; список "points" с ключом "id"; выбор по "point_id"
a3 code = pickup_point; список "pickup_points", но ключ "id"
Контрактных кодов ошибок pickup_point_required и pickup_point_unavailable нет ни в одном прогоне: вместо них общий delivery_not_available, а в A1 выбор без пункта вообще проходил. Различать «пункт не передан» и «пункт недоступен» агент не стал, потому что не знал, что клиенты это различают.
Любопытная деталь из отчёта прогона A2: агент пишет, что обработчики API менять не пришлось — они и так передают дополнительные поля как есть, поэтому список пунктов появится в ответе автоматически. Инженерно это правда. Но именно поэтому поле называется так, как было удобно внутри, — points, а не так, как договорились с клиентами. Код переиспользован идеально, контракт нарушен незаметно. Мобильное приложение ждёт pickup_points и pickup_point_id, получает points и point_id — и клиент видит способ доставки, но не может выбрать пункт.
Флаг выкатки: нет или включён сразу
В репозитории есть флаги функциональности, и экспресс-доставка стоит за флагом express_delivery, который включён. Почему флаги вообще появились, в коде не написано. А в ADR-011 написано: после запуска экспресса каждый новый способ доставки выходит за флагом delivery_<code>, по умолчанию выключенным, и включается по регионам после проверки на стенде.
a1 FLAGS["partner_pickup"] = True
a2 флага нет, способ включён всегда
a3 FLAGS["pickup_point_delivery"] = True
ADR FLAGS["delivery_pickup_point"] = False
Агент увидел образец (express_delivery: True) и повторил его. В итоге выкатка происходит в момент мержа, сразу на всех: без стенда, без поэтапного включения, поддержка ничего не знает.
Возвраты: «возвращать» во всех 3 прогонах
Агент молодец, что вспомнил о словаре DELIVERY_FEE_REFUNDABLE. Но по требованиям стоимость доставки при возврате из пункта выдачи не возвращается. Во всех 3 прогонах агент поставил True — «возвращать». Это деньги компании: каждый невыкупленный заказ превращается в возврат стоимости доставки, который компания возвращать не собиралась.
Правило «у каждого нового способа должно быть правило возврата» пришло из инцидента INC-2025-031: экспресс запустили без записи в этом словаре, и возвраты падали двое суток. Агент повторил правильное действие из этого урока, но значение выбрал сам.
15 килограммов: расхождение, которое я нашёл позже
В требованиях есть общий лимит — 15 килограммов на заказ, и не больше лимита конкретного пункта. Во всех 3 прогонах A агент проверяет только лимит пункта max_weight_g из таблицы. Если у пункта лимит 25 килограммов, заказ на 20 уйдёт в самовывоз. Независимый валидатор в прогоне A2 проверил ровно этот сценарий: заказ 20 кг при пункте с лимитом 25 кг получил вариант самовывоза.
Этот пример мне нравится больше всего, потому что я сам его сначала не заметил. Лимит пункта агент нашёл в миграции и учёл аккуратно. Общего лимита в коде нет — значит, его нет и в решении.
Итог проверок условия A
Я заранее записал 7 детерминированных проверок того, что следует из контракта, ADR, политики безопасности и истории инцидентов, но чего нет в тексте задачи:
проверка a1 a2 a3 итог
тесты зелёные да да да 3 из 3
слои не нарушены (ADR-003) да да да 3 из 3
нет сети в пути запроса да да да 3 из 3
code = pickup_point (контракт) нет да да 2 из 3
поле pickup_point_id (контракт) да нет да 2 из 3
флаг выключен по умолчанию (ADR-011) нет нет нет 0 из 3
доставка не возвращается (PRD п.6) нет нет нет 0 из 3
всего 4/7 4/7 5/7
Отдельный агент-валидатор, который не видел, как писался код, написал приёмочные тесты по требованиям, контракту и ADR. Из 10 его проверок прогоны A прошли от 2 до 4. Во всех 3 прогонах провалены цена, общий лимит веса, правило возврата, формат списка пунктов, коды ошибок и флаг выкатки. Подробно про эту часть — в статье о том, почему зелёные тесты ещё не доказательство.
Картина складывается простая:
ЕСТЬ ОБРАЗЕЦ В КОДЕ НЕТ ОБРАЗЦА В КОДЕ
слои, порты, адаптеры ✓ 3/3 цена и порог ✗ 0/3
деньги в копейках ✓ 3/3 флаг выключен ✗ 0/3
без сетевых вызовов ✓ 3/3 доставка не возвращ. ✗ 0/3
общий лимит 15 кг ✗ 0/3
поля по контракту ~ то да, то нет
Там, где у агента есть образец, он сходится. Там, где образца нет, он угадывает — и каждый раз по-разному. Срок 2 дня агент, кстати, угадал во всех 3 прогонах, хотя его нет ни в задаче, ни в коде. Это удачное совпадение, а не решение: на него так же нельзя опереться, как на цену.
Почему это не ошибка кода
Важно разделить два вида дефектов.
Дефект кода — это когда код не делает того, что задумано: падает, считает неверно, нарушает инварианты. Такие вещи находят тесты, линтеры и ревью, и в условии A их почти нет. Одно честное исключение: в прогоне A1 выбор самовывоза без пункта выдачи возвращал 200 и оформлял доставку. Это следует из самой задачи («выбрать пункт»), и тут агент действительно недоработал — его собственные тесты проверяли только несуществующий пункт, но не отсутствующий.
Дефект решения — это когда код безупречно делает то, что никто не решал. Цена 0 рублей реализована корректно. Флаг partner_pickup: True работает. Правило возврата True работает. Всё это исполнено идеально — просто не то.
Второй вид дефектов нельзя исправить «лучшей моделью» или «более внимательным агентом». Цены 99 рублей нет в репозитории, её нельзя вычислить из кода. Более сильная модель угадает убедительнее, но утверждённое число не появится из интеллекта.
И это не про ИИ. Очень похоже на очень быстрого и очень старательного новичка в команде. Он отлично пишет код и читает проект. Но он не был на встрече, где договорились о цене, не читал решение про флаги и не знает, что было с экспрессом.
РЕПОЗИТОРИЙ ИНЖЕНЕРНЫЙ КОНТЕКСТ
код требования и бизнес-правила
тесты контракты API с другими командами
зависимости ADR: почему устроено именно так
конфиги политика безопасности
инциденты и выводы из них
стандарты команды
как система устроена почему и по каким правилам
Почему ревью это пропустит
Справедливый вопрос: а ревьюер-человек это поймал бы? Если у ревьюера тот же контекст, что у агента, — скорее всего, нет. Код чистый, тесты есть, стиль соблюдён. Такой pull request одобряют быстро.
Поймает тот, кто помнит договорённость о цене, знает контракт с мобильной командой и был на разборе инцидента. То есть проблема не в агенте и не в ревьюере, а в том, где живут знания. Если они в головах, их не получит ни агент, ни новый сотрудник, ни ревьюер из соседней команды.
Для проверки я дал отдельному агенту-ревьюеру diff прогона A2 и тот же пакет контекста, без права что-либо менять. За 81 секунду и 25 центов он нашёл 3 блокера (цена, флаг, поля контракта), 3 серьёзных замечания и вынес вердикт «мержить нельзя». Разница между ним и автором — не в модели, а в том, что ревьюер видел требования.
Stack Overflow в опросе 2025 года фиксирует похожее ощущение у разработчиков: 66% называют главной проблемой ИИ-инструментов решения, которые «почти правильные, но не совсем». Эксперимент показывает один из механизмов этого «почти»: код правильный, а решение нет.
Что изменилось в условии B
Коротко, чтобы замкнуть историю. Та же задача, тот же репозиторий, та же модель плюс 8 документов на 522 слова. Все 3 прогона B поставили цену 99 рублей с порогом 3000, лимит 15 кг, флаг delivery_pickup_point выключенным, правило «доставка не возвращается» — и прошли 7 из 7 проверок по контексту и 10 из 10 приёмочных. Быстрее агент при этом не стал: прогоны B заняли от 275 до 547 секунд и стоили от 1,03 до 1,85 доллара. Контекст меняет не скорость написания кода, а то, насколько код соответствует задаче.
Ограничения
Чтобы не выдавать это за исследование:
- 6 прогонов (3 в условии A и 3 в условии B), один синтетический репозиторий, одна модель;
- проверки писал я сам и зафиксировал до прогонов; приёмочные тесты писал отдельный агент, по одной проверке на пункт требований;
- задача специально содержит решения, которых нет в коде, — в реальной задаче их может быть больше или меньше;
- агент работал в headless-режиме и не мог задать вопрос человеку.
Это демонстрация механизма, а не бенчмарк. Но механизм воспроизводился в каждом прогоне: если знания нет в контексте, агент его придумает — уверенно и правдоподобно.
Что делать
- Не считать тикет полной постановкой. Перед тем как отдать задачу агенту, спросите: какие решения по ней уже приняты вне кода? Цена, контракт, выкатка, возвраты, лимиты — всё это кандидаты в контекст.
- Собрать пакет контекста под задачу. Не всю вики, а требования, контракт, релевантные ADR и связанный инцидент. Для самовывоза это меньше 2 страниц. Как отбирать — в статье о context engineering.
- Держать постоянные правила рядом с кодом. Правила проекта в CLAUDE.md или AGENTS.md — полезный первый шаг, но это общий файл для всех задач, а не контекст конкретной задачи. Где он заканчивается — в разборе ограничений CLAUDE.md.
- Проверять независимо. Приёмочные тесты из требований и ревью с контекстом ловят дефекты решения, которые тесты автора закрепляют.
- Думать о масштабе. Пакет для эксперимента я собрал руками. Для тысяч задач нужна система, которая делает это сама, — context platform.
Если хотите разобрать, где у вас живут знания, которые агенты сейчас угадывают, — это как раз то, с чего я начинаю оценку AI-разработки в команде.
Источники
- Эксперимент автора «От Coding Agent к AI Development Platform», 6 прогонов Claude Code (claude-sonnet-5) на синтетическом репозитории shipline, 2026.
- 2025 Stack Overflow Developer Survey — AI. Stack Overflow, 2025.
Вопросы и ответы
Может быть, агент просто плохо написал код?
Нет. Код во всех 3 прогонах аккуратный: домен не импортирует инфраструктуру, деньги считаются в копейках, данные о пунктах берутся из существующей таблицы, сетевых вызовов нет, тесты проходят. Расхождения — в решениях, которые из кода вывести нельзя: цена, названия полей по контракту, флаг выкатки, правило возврата, общий лимит веса.
Почему агент не спросил, какая должна быть цена?
В headless-режиме ему некого спросить, но и в интерактивном режиме модель чаще заполняет пробел, чем останавливается: в задаче есть всё, чтобы написать работающий код, и ничто не говорит, что цена где-то уже утверждена. Агент не знает, чего он не знает. Поэтому вопрос «есть ли решение по цене» должен закрывать контекст задачи, а не надежда на вопрос от модели.
Более сильная модель решит эту проблему?
Не решит. Цены 99 ₽ нет в репозитории, её нельзя вычислить из кода. Более сильная модель напишет ещё более аккуратный код и угадает ещё убедительнее, но утверждённое бизнесом число не появится из интеллекта.
Поймал бы это ревьюер-человек?
Если у ревьюера тот же контекст, что у агента, — скорее всего, нет: код чистый, тесты есть, стиль соблюдён. Поймает тот, кто помнит договорённость о цене, знает контракт с мобильной командой и был на разборе инцидента. Отдельный агент-ревьюер, которому дали diff и пакет контекста, нашёл 3 блокера за 81 секунду и 25 центов.
Насколько этому эксперименту можно доверять?
Это демонстрация механизма, а не бенчмарк: 6 прогонов (3 без контекста и 3 с контекстом), один синтетический репозиторий, одна модель, проверки писал я сам и зафиксировал до прогонов. Но механизм воспроизводился в каждом прогоне: если знания нет в контексте, агент его придумывает.
Что нужно дать агенту, чтобы получить правильное решение?
Не всю вики, а небольшой пакет под задачу: требования, контракт API, релевантные ADR, инженерные правила, политику безопасности и разбор связанного инцидента. В эксперименте это 8 документов на 522 слова, и с ними все 3 прогона прошли 7 из 7 проверок по контексту и 10 из 10 независимых приёмочных проверок.