Вторая статья из шести про инженерный процесс для разработки с ИИ-агентами. Автор — Андрей Юмашев — много лет руководил разработкой и инфраструктурой, полтора года назад отдал весь код агентам. Первая статья была про путь от первых проектов до стандарта SENAR. Эта про то, чем именно агент отличается от программиста и что из этого следует для процесса.
Начну со случая, после которого исчезли последние иллюзии.

Случай, после которого всё стало понятно
Легаси-система на Java. Пятнадцать лет разработки, семнадцать отдельных модулей в одном репозитории, ни одной строки актуальной документации. Команды, которая её писала, в компании больше нет. Задача досталась мне как внешнему эксперту: оценить, во что обойдётся передача системы новой команде.
Первым делом поставил агенту задачу собрать архитектурную документацию: описание модулей, их зависимостей и интерфейсов взаимодействия. На входе весь исходный код, на выходе внятная карта того, что есть.
Аудит шёл несколько дней. Агент работал по модулям, объём нарастал постепенно: аннотации к каждому модулю, диаграммы зависимостей, описание интерфейсов. Результат превзошёл ожидания. Документ выглядел как полноценная внутренняя спецификация зрелой команды: структурированно, последовательно, вплоть до описаний отдельных компонентов. Работа, на которую у живой команды ушли бы недели.
Я показал результат инженеру, который десять лет назад этот код писал. Вышли на созвон, он читал документ вслух и комментировал каждый раздел. Подавляющая часть описания оказалась точной. Но в нескольких местах агент наткнулся на костыли, которые выглядели как нормальный код, и не распознал их.
Конкретный пример: между двумя сервисами стоит прокси-слой (дополнительная прослойка, скрывающая детали взаимодействия), который в коде выглядит как обычный HTTP-вызов. Это был костыль, оставшийся после миграции на очередь сообщений два года назад. Агент принял его за рабочую связь и описал как штатную архитектуру. Придумал логику, которой не было: зачем эта связь нужна, как она работает, почему так сделано. Уверенно, детально, неправильно.
В этом суть. Агент отлично справился с тем, что видно в коде. Но там, где код содержал исторический костыль, агент не распознал его как костыль, а достроил вокруг него логичное объяснение. Без оговорки «это предположение», без пометки «требует проверки».
Человек-программист на месте агента, скорее всего, остановился бы: «тут странная конструкция, нужно уточнить у того, кто писал». Агент этого шага не делает. Для него нет разницы между фактом из кода и собственной реконструкцией.
Работа в целом была сделана отлично. Но именно это и вскрыло неочевидную проблему: чем лучше агент справляется в среднем, тем труднее заметить места, где он ошибся.
Другой исполнитель: пять наблюдений
Разница между программистом и агентом качественная. Пять типов рабочих ситуаций, которые накопились за полтора года, это показывают.
У агента нет прошлого проекта
Проект полевого учёта из первой статьи, веб-приложение, которое работает без интернета: бригадиры заполняют отчёты прямо в поле. Каждая новая задача ложится в контекст, который копится месяцами: почему синхронизация работает именно так, почему конфликты разрешаются в пользу сервера, почему у бригадира нет права редактировать закрытый отчёт.
Программист, который работает на проекте, живёт в этом контексте. Он не помнит все миграции наизусть, но помнит, что «в марте чинили баг с дублями при синхронизации, из-за этого добавили уникальный индекс». Когда он видит новую задачу по синхронизации, этот факт всплывает автоматически.
Легаси-система из начала статьи — тот же случай. Костыль, который агент не распознал, существовал только в голове конкретного инженера. В коде разницы между костылём и штатной архитектурой не видно. Разница — в памяти человека, который знает, зачем конструкция была добавлена.
Агент работает с тем, что получил прямо сейчас. Дал миграцию, знает миграцию. Историю решений не передали — значит, её не было.
Свойство фундаментальное, общее для всех моделей.
На знакомой кодовой базе фильтрация результата происходит автоматически: я знаю проект и ловлю ошибки через собственную модель. Проблема начинается, когда агент работает с частью системы, которую я знаю хуже. Или когда прихожу на новый проект и пытаюсь разобраться с помощью агента. Тогда доверие к результату оказывается выше, чем оно того заслуживает. Собственного фильтра нет.
На практике это означает одно: контекстные документы. Это единственный способ передать агенту то, что программист впитывает за месяцы работы на проекте. Чем сложнее домен, тем больше контекста требуется. На проекте кадастровых утилит (обработка данных о земельных участках в форматах ГКН и ЕГРН) первая неделя ушла на совместное исследование предметной области. Агент копал в источники, собирал информацию по форматам, выдавал промежуточные результаты. Осмысленное он генерировал уже сразу. Но полезное для проекта — только после того, как мы вместе разобрали детали: где формат ГКН расходится с ЕГРН, какие поля обязательны, какие исторические. Агент в этом разборе очень помог, но направление давал человек.
Агент исполняет задачу буквально
Задача на проекте полевого учёта: «При конфликте синхронизации показывать пользователю диалог выбора версии». Конфликт возникает, когда бригадир заполнил отчёт без связи, а на сервере тот же отчёт уже изменили.
Программист, знающий предметную область, спросит: «А если бригадир изменил и дату, и объём, и комментарий — это один конфликт с тремя изменёнными полями. Показывать общий диалог или по каждому полю отдельно? А если связи нет в момент конфликта?»
Агент молча напишет компонент диалога на один конфликт в одном поле. Аккуратный React-компонент (готовый блок интерфейса), чистый код, типы на месте. Работает. Проходит тесты, если тесты написаны только на этот случай.
В production (боевой среде) ломается на первом же реальном конфликте. Бригадир изменил дату, объём и комментарий одновременно. Компонент показывает конфликт только по первому полю, остальные два молча теряет.
Программист замечает неоднозначность, потому что у него есть модель реального мира. У агента такой модели нет, есть только текст задачи.
Можно написать в системном промпте «при неоднозначности всегда спрашивай». Агент прочитает. И в половине случаев спросит. В другой половине решит, что всё однозначно, и сделает по-своему. Узнаёшь об этом на проверке.
Поэтому я стал требовать от себя: задача не берётся в работу, пока в ней нет цели, критериев приёмки и хотя бы одного негативного сценария. В SENAR это оформлено как шлюз качества на входе. Звучит бюрократично. На деле это способ закрыть пространство для додумывания до того, как агент начал писать код. Конфликт в трёх полях, бригадир без сети, неизвестный формат данных: всё это должно быть в спецификации, а не в голове того, кто потом проверяет результат.
На практике достижимо для большинства задач, если спецификация хорошая. Оставшаяся часть требует ручного разбора.

Буквальное исполнение. Фрагмент безупречен. Всё, что не было в задаче, для агента не существует.
Одна ошибка агента — это несколько ошибок сразу
На проекте генерации кадастровых отчётов одна неправильная формула пересчёта координат давала ошибку в несколько сантиметров. Для межевания, где допустимые отклонения измеряются десятками сантиметров, это половина бюджета точности.
Программист пишет медленно. За день он допустит такую ошибку максимум в одном месте. Код-ревью с высокой вероятностью её поймает.
Агент за час сгенерировал одиннадцать утилит. Ошибка оказалась в трёх из них. Одно неверное предположение, размноженное по всему набору. Высокая скорость генерации работает как мультипликатор, и в обе стороны.
У человека ошибка локализована: он допустил её в том файле, который писал. У агента ошибка распространяется: он копирует неверный паттерн во все файлы, где тот применим.
Вывод: когда агент генерирует серию похожих модулей, ошибка в одном почти гарантированно повторена в остальных. Проверять один и считать, что остальные такие же — ловушка. Лучше выборочно проверить несколько из разных мест набора.
Агент не видит архитектуры за горизонтом задачи
На проекте Сортула (личный сервис для сохранения ссылок с поиском по смыслу) стоял выбор: делать поиск прямым запросом из обработчика или через отдельный сервисный слой. Сервисный слой — промежуточная прослойка, куда потом удобно добавлять новую логику. Прямой запрос проще, быстрее, меньше кода.
Я выбрал сервисный слой. Не из книги. Из опыта: системы, где запрос из обработчика идёт прямо в хранилище, через три-четыре месяца превращаются в кашу. Через месяц потребуется кэширование, через два переранжирование результатов, через три сравнение моделей на живом трафике. Без промежуточного слоя каждая новая функция ложится прямо в обработчик, и код разбухает.
Агенту была поставлена задача «реализовать поиск». Он сделал прямой запрос из обработчика. Для одноразового скрипта это было бы правильное решение. Для проекта, который я планировал развивать, — нет. Но агент не знал моих планов, потому что я их не описал.
Позже в контекст проекта добавилась секция с архитектурными решениями: какой подход выбран и почему. С тех пор на задачах поиска агент стабильно использует сервисный слой. Но инициатива этого решения человеческая.
Агент не живёт с последствиями
На проекте полевого учёта агент генерировал миграции базы данных (скрипты, которые изменяют структуру таблиц). В одной из миграций каскадное удаление (автоматическое удаление всех связанных записей при удалении основной) было настроено так, что удаление пользователя-бригадира вычищало все его отчёты и все связанные фотографии. Ни один тест это не поймал: тесты были написаны на ту же схему, и в них каскад не проверялся отдельно.
Проблему заметили до того, как она выстрелила в production: при подготовке к релизу я вручную проверял миграции и увидел каскад. Но увидел только потому, что специально искал. Без этой проверки миграция ушла бы в бой и вычистила бы реальные данные. Агент к тому моменту уже давно «забыл», что писал эту миграцию. Для него каждая новая задача — чистый лист.
Программист живёт с кодом. Он понимает: то, что он сейчас закоммитит (отправит в репозиторий), ляжет ему на плечи через месяц, когда всплывёт баг. Агент не живёт, он проходит мимо. Код на выходе и код на входе следующей задачи для него никак не связаны, даже если это один файл. Без внешних ограничений долг копится быстрее, чем успеваю замечать.
Отсюда простое следствие: ответственность за результат остаётся на человеке целиком. На том, кто ведёт разработку. Это приходится держать в голове на каждой задаче.

Когнитивный долг: код работает, но никто не знает как
Каждое из пяти наблюдений по отдельности знакомо любому, кто работал с ИИ-агентами хотя бы месяц. Вместе они складываются в одну общую проблему, для которой нужен системный ответ. Человек теряет понимание того, что построено.
Технический долг известен: код работает, но менять его дорого. Когнитивный долг1 устроен иначе: код работает, но человек не понимает как. Код при этом может быть хорошим. Проблема в том, что человек его не писал, не читал построчно, не принимал решений о его структуре.
При классической разработке когнитивный долг накапливается медленно: человек уволился, документацию не написал, через год никто не помнит, зачем модуль устроен именно так. При работе с ИИ-агентами он возникает сразу. Агент выдал несколько сотен строк. Код работает, тесты проходят, линтер молчит. А ментальной модели нет ни у кого — потому что решения принимал не человек. Когда модуль написал коллега, я могу спросить, почему он сделал так. Когда агент — спрашивать некого.
Когда программист пишет модуль, он проходит через десятки микрорешений: как назвать переменную, куда вынести логику, что обработать здесь, что делегировать дальше. Каждое решение оставляет след в памяти. Когда код генерирует агент, у меня этих следов нет. Есть только результат.
Пример из практики: агент сгенерировал модуль кэширования (временного хранения данных для ускорения повторных запросов) размером около пятисот строк. Через две недели потребовалось добавить сброс устаревших данных. При открытии модуля выяснилось, что непонятно, какая стратегия кэширования выбрана и почему. Ответа не было, потому что решения принимал не человек.
Итог: вместо часа на доработку — полдня на понимание существующего кода.
Когнитивный долг опаснее технического: я не могу оценить, сколько займёт изменение, потому что не понимаю, что затронется. Для планирования это приговор. Сроки плывут в разы.
С тех пор я требую от агента на каждую задачу с архитектурным решением краткий комментарий: что выбрано и почему. Две-три минуты к каждой задаче экономят часы на горизонте месяцев.
Полностью избежать когнитивного долга при работе с агентами мне не удалось. Можно снизить: спецификации, фиксация решений, проверка с пониманием. Но какая-то доля остаётся всегда.

Из этих наблюдений и вырос стандарт SENAR. Из повторяющихся провалов, каждый из которых требовал системного ответа. Три следствия ниже — первые такие ответы. Они не убирают когнитивный долг полностью. Они делают его управляемым.
Следствие первое: считать ручные правки отдельно
Каждый раз, когда агент выдал результат, а я открываю файл и правлю код руками, это сигнал. Где-то в системе управления дыра.
Рассуждение простое: система это задача + контекст + агент. Если эта система не выдала приемлемый результат, значит что-то в ней не так. Задача плохо сформулирована? Контекст неполный? Архитектурные границы не описаны? Модель не справляется с этим типом работ?
Без ответа система не улучшится. Поэтому ручные правки считаются отдельной метрикой.
В методологии SENAR, которую мы разрабатываем вместе с Вадимом Соглаевым, это оформлено как MIR (Manual Intervention Rate) — доля задач, в которых потребовалась ручная правка кода после работы агента. MIR измеряет другое, чем FPSR (доля задач, решённых с первой попытки) из первой статьи. FPSR показывает, решена ли задача с первой попытки. MIR — потребовалось ли лезть в код руками.
Как это работает на практике: формулируется спецификация, собирается контекст, запускается агент. Результат проверяется.
Если проверка пройдена, задача закрыта, MIR по ней ноль.
Если не пройдена, дальше два пути. Переформулировать задачу и запустить агента снова. Или открыть файл, поправить три строчки руками и пойти дальше. Второй путь быстрее, и это ловушка. Быстрая ручная правка — потерянная обратная связь.
Учёт ведётся просто: пометка в описании задачи и короткая запись, что именно пришлось править и почему. Раз в две недели я просматриваю распределение целиком.
MIR не должен быть нулём. Нулевой MIR — либо враньё, либо работа только с тривиальными задачами. На зрелых проектах, где архитектурный контекст уже собран, MIR держится в диапазоне 5–15% в зависимости от сложности домена (по моему рабочему журналу, без независимой проверки). На старте нового проекта выше: на Сортуле за первый месяц MIR был около 20%, пока не накопился архитектурный контекст.
Пример. На той же Сортуле разбор трёх последних случаев с пометкой MIR: yes, context показал одну причину: агент некорректно обрабатывал формат пагинации (разбивки результатов на страницы) в API. Все три спецификации формата пагинации не содержали. Решение заняло пять минут: в архитектурный контекст проекта добавилась секция «API conventions». После этого MIR по задачам API упал до 5%.
Отличие от баг-трекера: баг-трекер покажет, что баг был. MIR покажет, что три последних бага выросли из одной и той же дыры в контексте, и закрыть её можно за пять минут. Это не метрика для отчётов руководству. Это инструмент для человека, который ведёт процесс.

Следствие второе: роль человека сдвигается
Учёт ручных правок показал, куда на самом деле уходит время. Я прикинул распределение за типичный рабочий день:
60% — формулировка задач, сбор контекста, архитектурные решения
30% — проверка результатов
10% — настройка инструментов, метрики, сопровождение процесса
Ноль процентов на написание кода. Самая впечатляющая часть процесса — когда агент пишет код — занимает наименьшую долю времени.
В начале карьеры я писал код. Потом архитектура, потом управление командами и инфраструктурой. Код перестал занимать моё рабочее время много лет назад. Полтора года назад на его место встали формулировка задач и проверка результата агента. Ближайшая аналогия: инженер, который определяет допуски и проверяет качество, а не стоит у станка.
На проекте полевого учёта одна задача «сделать синхронизацию» раскладывалась на двенадцать подзадач. Каждую нужно было сформулировать так, чтобы агент не мог додумать в неправильном направлении. Потом проверить результат. Потом понять, как изменение в одном модуле повлияет на соседние. Всё это навыки, которыми я пользовался, когда руководил командами. Разница в том, что теперь они заполняют весь день целиком.

Одно наблюдение, которое меня удивило: проверять чужой код восемь часов тяжелее, чем писать свой. Когда пишешь — создаёшь, решаешь, строишь. Когда проверяешь — ищешь дефекты в коде, который выглядит правильным. После четырёх-пяти часов я замечаю, что начинаю пропускать. Формируется шаблон «посмотрел, ок, следующий». Мой рецепт — сессии по девяносто минут с перерывами. Проверка это узкое место всей системы. Ни один линтер не поймает логическую ошибку в бизнес-правиле. Поймает только человек.
Подробнее о том, как устроена эта новая роль, чем именно занят человек на каждом этапе и почему ручная правка кода маскирует проблемы вместо того, чтобы их решать, — в следующей статье серии.
Следствие третье: что не записано, для агента не существует
На Сортуле я столкнулся с этим напрямую. Проект вырос, модулей стало больше десяти. У каждого своя зона ответственности: один отвечает за поиск, другой за хранение, третий за взаимодействие с внешними сервисами. Границы между ними были у меня в голове. В коде модули технически могли обращаться друг к другу как угодно.
При очередном рефакторинге агент начал использовать внутренние функции одного модуля из другого. Не из злого умысла — они были доступны, импорт работал, тесты проходили. Но это ломало архитектуру: модули, которые не должны знать друг о друге, оказывались связаны. Я заметил это на проверке, потому что знал, как задумано. Со стороны код выглядел нормально.
После этого я описал границы модулей явно: что от чего зависит, что с чем не должно пересекаться и почему. Не общими словами вроде «придерживайтесь разделения ответственности», а конкретно: такой-то модуль не использует код из такого-то. Рядом с каждым ограничением — причина. Когда агент знает, почему нельзя, он реже нарушает правило.
Со временем такая документация накапливалась: частично я писал её сам, частично агент фиксировал решения в памяти проекта по ходу работы — это одна из практик, заложенных в SENAR. Когда я показал Сортулу другому разработчику, первое, что он сказал: «Наконец-то проект, где не надо три дня разбираться в архитектуре. Всё написано». Документация, которая создавалась для агента, пригодилась и живому человеку.
О людях и границах
Три следствия выше меняют процесс. Но за ними стоит вопрос, который я слышу каждый раз: а зачем тогда программисты?
Затем, что разложить сложную задачу на части может только тот, кто уже видел, как такие задачи разваливаются. Заметить гонку данных в сгенерированном коде может только тот, кто встречал её в работающей системе. Предвидеть, что сегодняшнее архитектурное решение через полгода станет проблемой, может только тот, кому уже приходилось такие проблемы разгребать. Хорошая спецификация сложнее кода, потому что в ней нужно предусмотреть то, что может пойти не так. Профессия не упрощается. Меняется то, на что уходит время.
И тут я должен быть честен. Всё, что описано выше, — опыт одного человека. Полтора года, тридцать с лишним проектов, разные языки и предметные области. Это не научный эксперимент. Описанный процесс опирается на мой инженерный опыт: я вижу гонку данных, ловлю неоднозначность в задаче, предвижу архитектурные тупики. Это годы работы. У человека без такого багажа фильтр слабее. Работает ли описанный подход у того, кто только начинает, — честно не знаю. Как устроен процесс в команде из двадцати инженеров — тоже открытый вопрос.
Вопрос, который болит сильнее всего: как начинающий инженер вырастет в опытного, если не будет писать код руками? Ответа пока нет. Возможно, через разбор чужого кода и практику проверки. Возможно, ручной этап останется обязательным на старте — математику до сих пор учат без калькулятора. Вопрос серьёзный, и решать его в скобках я не готов.
Словарь терминов
Термины, которые появляются в этой статье и пригодятся в следующих частях серии.
Термины, которые появляются в этой статье и пригодятся в следующих частях серии.
MIR (от английского Manual Intervention Rate, «доля ручного вмешательства»). Доля задач, в которых потребовалась ручная правка кода после работы агента. Метрика качества процесса: чем ниже MIR, тем точнее спецификации и контекст.
Когнитивный долг (cognitive debt). Ситуация, когда код работает, но человек не понимает, как он устроен, потому что не принимал решений о его структуре. Отличается от технического долга: технический замедляет разработку, когнитивный делает её непредсказуемой.
Шлюзы качества (quality gates). Автоматические контрольные точки, которые блокируют задачу, если условия не выполнены. В базовой конфигурации SENAR их два: шлюз на входе (задача не берётся без спецификации) и шлюз на выходе (задача не закрывается без проверки).
API (от английского Application Programming Interface). Программный интерфейс, через который одна программа обращается к другой. В контексте статьи: набор адресов и правил, через которые приложение принимает внешние запросы.
Линтер. Инструмент автоматической проверки кода на стилистические и типовые ошибки. Работает до запуска программы, ловит опечатки, неиспользуемые переменные, нарушения принятого стиля.
Галлюцинация (в контексте языковых моделей). Уверенный, детальный, но фактически неверный ответ модели. Модель не «врёт» намеренно: она генерирует наиболее вероятное продолжение текста, и иногда это продолжение не соответствует действительности.
Код-ревью (code review). Проверка написанного кода другим человеком перед тем, как код попадёт в основную ветку проекта. Один из основных способов ловить ошибки и передавать знания внутри команды.
SENAR (от английского Supervised Engineering & Normative AI Regulation). Открытый стандарт инженерного процесса для разработки с ИИ-агентами. Лицензия CC BY-SA 4.0. Подробнее на senar.tech.
Спецификация задачи. Формальное описание задачи до начала работы агента: цель, критерии приёмки, границы, негативные сценарии. Чем точнее спецификация, тем меньше пространства для додумывания.
Контекстные документы. Набор файлов, которые передают агенту знания о проекте: архитектурные решения, принятые ограничения, история тупиков. Агент не может опираться на то, что ему не передали.
Технический долг. Код, который работает, но менять его дорого. Накапливается, когда выбирают быстрое решение вместо правильного. Отличается от когнитивного долга: технический замедляет, когнитивный делает непредсказуемым.
Декомпозиция. Разбиение большой задачи на части, с каждой из которых агент справится автономно. Ключевой навык при работе с ИИ-агентами.
Если унести из статьи одну мысль
Разница между программистом и агентом не в скорости. Она в том, как каждый из них поступает, когда чего-то не знает. Программист останавливается и спрашивает. Агент заполняет пробел сам — уверенно, детально, иногда неправильно.
Из этого одного различия выросли все пять наблюдений в этой статье. Из повторяющихся провалов — правила. Из правил — стандарт SENAR.
Агент — не программист. А кто тогда человек рядом с ним? Чем он занят, если не пишет код? Почему ручная правка маскирует проблемы вместо того, чтобы их решать? Об этом — в следующей статье серии.
Серия про инженерный процесс для разработки с ИИ-агентами:
Часть 1: Полтора года без ручного кода: почему инструкции ИИ-агенту не заменяют инженерную дисциплину
Часть 2: эта статья
Часть 3: Новая роль человека (скоро)
Термин cognitive debt применительно к коду, сгенерированному языковыми моделями, формализовала профессор информатики Маргарет-Энн Стори (University of Victoria); широкую известность он получил благодаря Саймону Уиллисону — одному из создателей Django и автору известного блога о практическом использовании LLM. Суть термина: технический долг замедляет разработку, когнитивный долг делает её непредсказуемой, потому что у разработчика нет ментальной модели того, как устроен код, с которым он работает. См. margaretstorey.com и simonwillison.net. ↩
