У Андрея Шапиро есть прекрасная статья «Феномены код-агентной разработки и слабости SDD». Она посвящена ограничениям разработки на основе спецификаций (SDD, Spec-Driven Development), в которой ИИ-агент создаёт и изменяет код, опираясь на эти самые спецификации.
Кратко перескажу. Человек замечает ошибки агента и уточняет требования, но может принять удачную догадку, везение, за исполнение замысла. Полезное поведение остаётся в коде, хотя его основания могут быть нигде не зафиксированы. При следующей правке оно способно исчезнуть. Автор также рассматривает отклонения от требований, потерю принятого содержания и перенос случайных свойств примеров. Но главный вопрос статьи, на мой взгляд: как сохранять стабильное поведение системы, когда возможности человека проверять изменения ограничены?
Андрей приводит наблюдение своего коллеги Романа Русакова:
мы не видим тех мест, где нам повезло
Это, по-моему, наиболее полезная мысль исходного текста. Можно остаться довольным результатом, не разобравшись, какие решения за ним стоят. Одни могли быть заданы явно, другие — следовать из общей модели продукта, третьи агент выбрал сам. Для дальнейших изменений важно понимать, к какой группе относится решение. При этом сохранять всё, что прошло приёмку, тоже не стоит: так можно закрепить случайные ограничения и незамеченные ошибки.
Мне же хочется рассмотреть эту проблему со стороны проектирования: что меняется, если агент получает согласованную функциональную и информационную архитектуру продукта? Именно этому посвящены мои циклы о ФА и ИА.
Обе архитектуры могут входить в спецификацию. В заметке о контрмерах Шапиро тоже рассматривает многоуровневые спецификации, карты понятий и основания решений. Поэтому здесь я разбираю, как использовать такие модели до реализации и при изменениях, а заодно — где уточнил бы выводы автора.
Что меняет проектирование
К примеру, вы делаете интернет-магазин. Правила магазина, которые я дальше опишу, — решения для нашего условного проекта, а не универсальная схема для любых магазинов.
Требование звучит так:
Администратор должен иметь возможность снять товар с продажи.
Кажется, ничего сложного. Можно уточнить права администратора, расположение кнопки и текст подтверждения. Но что означает «снять с продажи»? Удалить товар или изменить его состояние? Что произойдёт с уже оформленными заказами и товаром, который остался у покупателя в корзине?
Чтобы ответить, нужно рассмотреть устройство системы.
В моём подходе информационная архитектура определяет сущности продукта, их свойства, классификации и связи. Сущность при этом не равна экрану или таблице базы данных: экран представляет информацию в определённом контексте, таблица относится к реализации хранения.
Функциональная архитектура описывает действия и поведение: что инициирует функцию, какие данные она использует, от чего зависит и к каким результатам приводит. В неё входят системные действия, проверки и отрицательные ветки сценариев.
Допустим, мы решили, что снятие с продажи в нашем магазине будет реализовано переводом товара в состояние «Архив». Сам товар и связи с ним сохраняются. Позиция заказа содержит связь с товаром и зафиксированные при оформлении название, цену и количество. Последующее изменение карточки не должно переписывать сведения о покупке.
Здесь важно описать не только состав данных, но и правила их обновления. В практической части цикла об ИА я отдельно разбираю источники данных, жизненный цикл и ведение версий. Без этих правил даже самый полный перечень сущностей не определяет, как система должна сохранять и изменять данные.
Теперь агент, получивший задание на разработку, предлагает убрать название товара из позиции заказа. Зачем хранить его повторно, если всегда можно получить актуальную версию из записи товара в БД?
Без согласованной модели это может выглядеть разумной оптимизацией. В нашем же проекте предложение агента противоречит принятому решению: сведения об оформленной покупке должны сохраняться независимо от текущей карточки товара. При проверке можно сопоставить несколько связанных вещей: состав позиции заказа, правила обновления её данных и поведение истории покупок. Так у команды появляется конкретный предмет обсуждения: какое обязательство нарушено и где оно определено. Это не гарантирует обнаружения ошибки, но позволяет объяснить, почему предложенная оптимизация не подходит.
Но одной информационной модели недостаточно. Нужно определить, как выполняются действия.
В нашей функции ArchiveProduct сервер проверяет права сотрудника и допустимость перехода, меняет состояние товара, а интерфейс показывает результат или предусмотренную ошибку. Оформление заказа отдельно проверяет актуальную возможность покупки, даже если товар добавили в корзину до архивирования.
Можно просто убрать кнопку покупки и решить, что задача готова. Но остаются старая корзина, прямой запрос, другой клиент приложения. Поэтому тут логичнее выделить проверку возможности покупки как общую функцию и связать её со всеми нужными сценариями. Ответы системы при этом нужно связать с состояниями интерфейса, в том числе при отказах.
Что увидит администратор, если изменение не сохранилось? Что произойдёт при повторном архивировании? Эти вопросы стоит разобрать при согласовании функций и экранов. Если сервер способен вернуть отказ, а для интерфейса описан только успешный результат, расхождение можно обнаружить ещё до реализации.
Такое поведение не следует автоматически из структуры данных. ИА помогает определить, где находятся сведения и как они связаны. ФА определяет, где и при каких условиях они используются. В согласованном проекте эти правила уже определены и не должны становиться предметом нового, неявного выбора агента.
Разумеется, проектировать тоже можно с помощью ИИ. Но важные решения нужно предъявить и проверить до того, как они незаметно закрепятся в коде.
Как проверять изменения
Для длительной разработки особенно полезно будет иметь связь между решением и его основанием — и дальше, с реализацией и проверками. Трассировку.
В нашем примере она может выглядеть так:
| Уровень | Что фиксируем в условном проекте |
|---|---|
| Основание | Изменение каталога не должно менять сведения об оформленной покупке |
| ИА | В позиции заказа сохраняются название и цена на момент оформления |
| ФА | Оформление заказа записывает эти значения; последующее редактирование товара их не обновляет |
| Реализация | Определены участки кода, которые записывают и читают эти данные |
| Проверка | После изменения товара сохранённые сведения заказа остаются прежними |
На практике решению можно присвоить устойчивый идентификатор и связать его с описанием данных, функций и проверок. В поручении агенту указывать затрагиваемые решения, а при приёмке сопоставлять их с фактическими изменениями. Изменение правила или ожидаемого результата теста рассматривать отдельно от изменения реализации.
Теперь предложение убрать поле можно проверить по конкретной цепочке: какое правило затрагивается, какие сценарии от него зависят и какая проверка должна обнаружить нарушение.
Это помогает определить и границы поручения. Если агент меняет отображение архивированного товара в корзине, пересмотр исторических данных заказа не становится частью задачи только потому, что где-то рядом нашлась похожая логика. При необходимости изменить ИА/ФА нужно отдельно обсудить основания и последствия.
Внешнего сходства корзины и заказа здесь недостаточно для унификации: правила обновления их данных различаются.
Точно так же можно определять и область проверки. Изменение доступности товара затрагивает административную карточку, корзину и оформление заказа. В нашей модели эти связи определены до реализации. Поэтому при проверке правки можно опираться на уже описанные зависимости, а не только на то, какие сценарии «вспомнил» исполнитель.
При этом значительную часть свойств можно проверять автоматически, а человеку предъявлять изменения правил, новые допущения и затронутые зависимости. Для этого связи нужно поддерживать и сопоставлять с реализацией, устаревшая карта способна оставить важную область вне проверки. Документация, даже идеальная, сама по себе не создаёт ни тестов, ни контроля за действиями агента.
Есть ещё одно различие. Модель, восстановленная из кода, не становится автоматически согласованной архитектурой. Она описывает фактическую реализацию, которая может противоречить принятому решению.
Если обнаружено расхождение, нужно выбрать: исправить код, пересмотреть правило или уточнить модель. Просто синхронизировать документы с кодом и считать противоречие устранённым нельзя. По той же причине согласованность кода, архитектуры и тестов мало что доказывает, если агент может свободно переписать всё сразу. Изменение правила требует отдельного рассмотрения и соответствующего контроля.
Как это соотносится с выводами Шапиро
Во второй заметке Андрей сам предлагает фиксировать допущения агента и отдельно контролировать изменения ожидаемых результатов проверок. В этих мерах наши подходы близки. Ниже я уточню, как они связаны с проектированием ФА и ИА.
Асимметрия обратной связи
Андрей описывает асимметрию как общее свойство SDD: требования уточняются вслед за замеченными ошибками, а удачные самостоятельные решения могут остаться невыявленными.
Проектирование помогает заранее поднять часть вопросов. Например, правило сохранения истории заказа можно согласовать при сопоставлении жизненных циклов сущностей.
Но предварительное проектирование само по себе не устраняет асимметрию. После него команда всё ещё может реагировать исключительно на ошибки.
Поэтому при приёмке нужно разбирать и новые решения в работающем результате. Какие уже следуют из архитектуры? Какие требуют согласования? Какие остаются допустимыми деталями реализации? Такой разбор нужен и тогда, когда результат внешне устраивает команду.
Полноту гарантировать нельзя. Однако обратная связь здесь уже не ограничивается исправлением замеченных ошибок.
Поэтому описанную асимметрию можно уменьшать организацией работы: разбирать не только ошибки, но и новые решения в принятом результате. Это расширяет обратную связь, хотя не гарантирует, что все удачные неявные решения будут обнаружены.
Роль машинных тестов
О тестах, написанных машиной по той же спецификации, автор говорит, что они:
судьёй быть не могут
Во второй заметке Андрей сам рассматривает проверки на закреплённых входах и инварианты. Поэтому здесь я уточню лишь различие: тест не становится независимым источником правильного требования, но может обнаруживать нарушение уже согласованного правила.
Риск общей ошибки действительно есть: модель способна одинаково неверно понять правило и в коде, и в тесте. Но проверка заранее согласованного поведения может обнаружить нарушение.
В нашем выдуманном магазине можно оформить заказ, изменить название и цену товара, архивировать его, а затем проверить сохранённые сведения о покупке. То, кто написал код теста, само по себе не определяет его пригодность.
Я бы разделял эту тему на два вопроса: правильно ли определён ожидаемый результат и действительно ли тест его проверяет. Архитектура даёт согласованное основание для проверки, но и сама может содержать ошибку. А тест способен сравнивать не те данные или не затрагивать нужный участок.
Полезно также проверить, что намеренное внесение соответствующего дефекта приводит к падению теста. Если проверка проходит и при нарушении правила, нужно выяснить, что она в действительности проверяет. Сам по себе успешный прогон ещё не разрешает этот вопрос.
Передача проверки другой модели тоже не создаёт независимости автоматически. Важнее основание ожидаемого результата, способ проверки и контроль изменений самого теста.
Поэтому корректный тест полезен как свидетельство выполнения конкретного правила, хотя и не доказывает правильность всей системы. Утверждение об отсутствии независимого судьи я бы не распространял на способность таких тестов находить нарушения.
Чего архитектура не гарантирует
У Андрея Шапиро есть ещё одна формулировка, с которой я согласен:
Определённость требования не влечёт определённости исполнения…
Можно явно предусмотреть проверку доступности товара, а агент забудет её вызов. Причём сама функция может успешно проходить тесты: ошибка находится в её подключении к сценарию. Поэтому ФА и ИА должны помогать проверять реализацию, а не служить основанием доверять ей без проверки.
Сами модели тоже требуют проверки. Несколько согласованных документов способны воспроизводить одну неверную предпосылку. Чтобы проверить её, нужны сведения о предметной области, пользователях и реальных ограничениях.
Отдельная задача — доступ к актуальной части модели. Наличие архитектуры во внутренней вики не означает, что агент получил нужные правила и использовал их. В рабочем контексте должны быть относящиеся к задаче решения, ограничения и возможность обратиться к связанным материалам.
Наконец, под исчерпывающей проработкой я понимаю полноту в границах текущей задачи и её зависимостей, а не обещание однажды продумать весь продукт целиком и навсегда. В цикле о ФА я описываю итеративную работу: от общей картины и взаимосвязей к детализации.
При этом существенные открытые вопросы должны быть разрешены до соответствующего изменения либо блокировать его. Если непонятно, что происходит при одновременном архивировании товара и оформлении заказа, это проектный вопрос. Его нельзя молча передать агенту вместе с поручением реализовать обе функции.
ФА и ИА дают основания для дальнейших решений, но не заменяют техническую проработку хранения, конкурентного доступа, безопасности, производительности и эксплуатации.
Важная оговорка. Количественного ответа, насколько такой подход снижает частоту ошибок агента, этот разбор не даёт. Для него потребовалось бы сравнение на одинаковых задачах с учётом затрат на проектирование, поддержку моделей и проверки. Перечислить полезные механизмы ещё не означает показать, насколько они эффективны в конкретном процессе.
Практическую пользу я бы оценивал по тому, помогает ли подход обнаруживать значимые нарушения и сколько времени команда тратит на проверку и поддержку моделей. Если архитектура добавляет лишь ещё один комплект документов для ручной вычитки, проблема ограниченного внимания остаётся.
Итог
Статья Шапиро хорошо показывает, почему принятого результата недостаточно для уверенного развития системы. Я бы сделал больший акцент на том, как заранее согласовать решения и сохранить их основания при изменениях.
ФА и ИА полезны в агентной разработке, когда связаны с реализацией, проверками и порядком изменения правил. Тогда команда получает основания для проверки конкретной правки и её последствий, а не вынуждена заново восстанавливать весь замысел по коду.
Агент всё ещё может ошибиться. Но можно предметно установить, какое решение нарушено, где искать последствия и что необходимо проверить.
Если ИА и ФА исчерпывающе описаны в границах задачи и её зависимостей, они дают основу для непрерывного тестирования (Continuous QA) прямо по ходу разработки. Проверки можно поручить самому исполнителю или отдельному агенту: один меняет код, другой запускает тесты и возвращает замечания. При этом согласованные критерии проверки, разумеется, нельзя подгонять под реализацию.


