Представьте обычный рабочий вечер. Вы оставили агентам задачу, закрыли ноутбук и ушли домой. Утром открываете проект. Там готовая реализация. Агенты разобрались с требованиями, написали код, добавили тесты. Все проверки, включая нагрузочные, интеграционные и e2e-тесты, проходят.

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

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

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

Системный подход. Представьте аквариум как механизм: рассчитываем мощность фильтра, объём воды, режим кормления. Заранее определяем, как всё должно работать.

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

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

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

Если посмотреть на большой муравейник, сначала кажется, что там полный хаос. Тысячи, десятки тысяч муравьёв куда-то бегут. Но приглядишься — видны чёткие дорожки, по которым они идут в одну сторону и возвращаются обратно. Одни нашли кусок сахара и тащат его в муравейник, другие воюют с другими насекомыми. При этом их работа хорошо скоординирована.
Наши команды обычно гораздо меньше муравейника, и у нас постоянно проходят стендапы и созвоны. А вы хоть раз видели, чтобы муравьи устраивали zoom-call на весь муравейник? Собирались и договаривались, кто сегодня куда пойдёт? У них нет и менеджера или главного оркестратора, который каждому муравью выдаёт задачу. Как же они без этого строят муравейник и добывают пищу?
Муравей оставляет за собой феромоны — химический след в среде. Следующий муравей реагирует на этот след и по нему находит дорогу. Для такой координации не нужно каждому договориться с каждым или получить указание из одного центра. Достаточно оставить след, на который смогут отреагировать другие.
Такой паттерн координации называется стигмергией. Участник оставляет в общей среде след, на который могут опереться другие участники, в том числе работающие параллельно.
Например, один агент оставил отчёт о проваленной проверке. Другой использовал этот отчёт, чтобы исправить реализацию. Если в среде задано, как реагировать на такой результат, никакому оркестратору не нужно отдельно передавать его следующему агенту и каждый раз поручать исправление. При большом числе агентов постоянная раздача таких указаний сама может стать бутылочным горлышком.
В нашей мастерской такими следами становятся версия контракта, принятое архитектурное решение, изменение в репозитории или результат проверки. Агент может увидеть, что задача уже занята, почему вариант отклонили и какое ограничение нужно учесть в следующей попытке.
Представьте теперь, что агент всего один, и у него сбросился контекст. Он возвращается к незаконченной задаче и по оставленным артефактам разбирается, на чём остановилась работа. Особенно полезной может оказаться запись о неудачной попытке: это решение уже пробовали и отказались от него по такой-то причине. У агента появляется возможность продолжить поиск верного пути реализации с учётом этой ошибки. Для этого записи ведутся в общем формате, который понимают участники работы.
Если же просто складывать всё в общий каталог, несколько агентов могут одновременно взяться за одну задачу и перезаписать работу друг друга. Поэтому в среде фиксируется, кто занят задачей и с какой версией результата работает. Разрешение конфликтов тоже входит в устройство такой координации.
Это механизм координации самой разработки. Он ничего не говорит о том, должна ли готовая программа обмениваться событиями, вызывать функции последовательно или иметь оркестратор. Эти решения мы принимаем на уровне продукта.
Теперь вернёмся к адаптеру, который получили агенты. Как решить, что с ним делать дальше?
Агент предложил реализацию адаптера. Среда проверяет её на сценариях, контролирует архитектурные ограничения и решает, можно ли принять результат, нужно ли вернуть его на доработку или пора остановиться.
Например, архитектурный тест обнаружил обращение к чужой БД. Проверка показала нарушение закона, и при отборе этот вариант возвращается на доработку. Здесь мы применяем уже заданное требование к конкретному кандидату.
Допустим, адаптер не прошёл проверку. Агент исправляет код по её результатам и снова запускает проверку. На каждую такую попытку уходят время и токены, а пригодного результата может так и не получиться. Поэтому в среде заранее задаём предел числа повторных попыток или бюджет, который можно на них потратить. Если лимит исчерпан, а адаптер всё ещё не проходит обязательные проверки, этот вариант отбрасываем. Явно непригодные варианты стоит отсеивать сразу, чтобы не тратить ресурсы на их доработку.
Остановившись, агент зовёт инженера: вот на этой проверке я застрял, вот что уже пробовал. Возможно, два требования противоречат друг другу, и выполнить оба не получится, сколько бы попыток мы ни дали. А возможно, условия согласованы, но агент пока не справился. По самому факту остановки этого не понять. Результаты попыток помогают инженеру разобраться в причине и решить, есть ли основания менять условия.
Здесь и проявляется важность независимой проверки. Если агент сам ослабил приёмочный тест, зелёный результат мало о чём говорит. Дополнительные тесты он вполне может писать. За условия приёмки при этом отвечает другой участник, и от изменений исполнителем они защищены.
Отбор тоже несовершенен. Проверки покрывают только то, что мы смогли выразить и проверить. А несколько агентов могут повторить одну и ту же ошибку, особенно если опираются на одинаковый контекст.
Несколько альтернативных реализаций одного адаптера можно сравнивать между собой и отбрасывать непригодные. Даже если большинство получилось удачным, конкретная версия, которую мы выпускаем, должна пройти свои проверки.
Один и тот же результат проверки может участвовать и в стимулировании поиска, и в отборе. Для следующей попытки он подсказывает агенту, что исправить. При отборе по нему решается, можно ли принять этот вариант или нужно продолжить работу.
Готовый результат проверяем при любом способе создания: человеком, агентом по готовому плану или агентами в среде.
А если кандидаты раз за разом проваливают проверку, пересматривать нужно и саму среду.
Соберём итерацию работы над средой в пять шагов:
Определяем нужный результат
Задаём условия
Получаем варианты реализации
Проверяем результат
Корректируем среду
Допустим, в предложенном адаптере мы обнаружили обращение к чужой БД, хотя архитектурный тест прошёл. Можно исправить эту реализацию, но прежняя проверка пропустит такое же нарушение в следующей. Поэтому инженер, отвечающий за правила среды, разбирается, почему тест не обнаружил запрещённое обращение.
В нашем примере проверка оказалась неполной. Инженер дополнил её, и теперь на найденном дефекте тест падает. Эта же проверка применяется к следующим реализациям: при обнаруженном нарушении вариант вернётся на доработку. Мы изменили условие отбора, а сетевой запрет при этом продолжает действовать отдельно.
После этого снова даём агентам исходную задачу. Тест уже обнаруживает прежний дефект, но получится ли теперь пригодная реализация? Повторный запуск покажет, как агенты справляются с задачей в изменённой среде. К тому, сколько таких результатов достаточно для её оценки, мы ещё вернёмся.
Так мы работаем с причиной, по которой среда раз за разом пропускала плохие реализации. А если дефект аварийный, конечно, можно сначала исправить его руками как срочный хотфикс. К разбору условий, в которых он появился, вернёмся после этого. Образ садовника не должен запрещать инженерное вмешательство, когда оно необходимо.
У такого цикла да и у всего средового проектирования есть несколько опорных принципов.
Возделывай условия вместо самостоятельной лепки результата.
Мы создаём условия, в которых агенты могут создать нужный результат. Не нужно заранее описывать каждый шаг каждой попытки. При настройке среды мы сами разбираем результаты и проверяем свои предположения. Ручное исправление отдельного адаптера тоже возможно, но само по себе оно не меняет условий, в которых будет создаваться следующий.
Управляй законом, а не инструкцией.
Закон работает для всех элементов, включая ещё не существующие. Проверка совместимости применится и к адаптеру, которого сегодня ещё нет. Новая реализация встретится с тем же техническим ограничением, что и предыдущие.
Оценивай качество среды по серии результатов.
Допустим, агент создал один адаптер, все проверки прошли, метрики отличные. Мы решаем, что среда готова, и запускаем создание следующих интеграций. Но один раз нам могло просто повезти, а следующие результаты окажутся гораздо хуже.
Поэтому для оценки среды нам нужны несколько типовых интеграций для разных компаний. По этому набору результатов, по всей популяции, мы смотрим метрики и оцениваем качество среды. Нас интересует, как она справляется с задачами этого класса, когда мы подключаем новых доставщиков. При этом каждый выпускаемый адаптер по-прежнему должен проходить независимые обязательные проверки.
Наблюдай и рули на макроуровне.
За повторяющимися отклонениями мы ищем общую причину в условиях работы. Разбор конкретного дефекта помогает её увидеть.
У нас агенты регулярно выполняют задачу, которую мы называем саморефлексией. В отчёте агент отмечает удачные решения и разбирает повторявшиеся ошибки. Конечный результат может пройти проверки, хотя по дороге агент несколько раз заходил в тупик и зря расходовал токены. По одному готовому адаптеру мы этого не увидим. Отчёт даёт нам возможность разобраться, что происходило по ходу работы, и уже на основе этого решать, стоит ли и каким образом менять среду.
На самом деле многие детали из средового подхода нам уже знакомы. Точечно такие механизмы мы используем уже давно. Например, CI — автоматическая сборка и проверка изменений — вместе с архитектурными тестами даёт обратную связь и отсеивает нарушения. Платформа разработки делает одни действия доступными и удобными, другие ограничивает. Политики доступа задают технические границы полномочий.
Вопрос в том, как собрать эти механизмы в среду для выбранного класса задач и оценить качество её работы. В своей практике я уже столкнулся с такой сборкой, хотя сначала называл её иначе.
Просто называем их harness
Прошлой осенью я говорил, что к средовому подходу мы, возможно, придём лет через пять. Потом сам участвовал в разработке harness — обвязки для работы агентов — и SDD-фреймворка для разработки по спецификациям, и довольно долго не замечал сходства с тем, о чём рассуждал.
Мы собирали рабочие материалы, архитектурные решения, инструменты, проверки и обратную связь вокруг агента. То есть создавали условия, в которых он должен производить пригодный результат.
До меня постепенно дошло, что передо мной уже один из способов реализации этой идеи. Со сроком своего прогноза я, похоже, не угадал.
Пока отдельные элементы этого подхода работают у нас в разных проектах. Мы развиваем свой общий harness, но о полноценном работающем конвейере (среде) говорить ещё рано.
Это наблюдение ещё не доказывает экономическую эффективность подхода и не превращает любой harness в автономную среду. Но оно даёт вполне конкретное место, где эту гипотезу можно проверять.
Вернёмся к различию между мастерской и её продуктом. Если агенты каждый раз идут разным путём, чего мы можем требовать от готовой программы?
(как будто закидывая требования в команду людей, там процесс детерминированный 😅)
В одном из обсуждений этой идеи меня спросили: разве наблюдаемость и воспроизводимость не противоречат сути LLM? Здесь смешиваются разные требования. Наблюдаемость нужна, чтобы разобраться, что происходило при создании программы. Воспроизводимость означает возможность повторить результат (например, прохождение функциональных тестов) при тех же входных условиях, несмотря на недетерминированный (каждый раз несколько разный) способ его реализации.
Генерация может пройти разными путями и дать разный код. Наблюдать этот процесс всё равно можно: сохранять входную задачу, версии инструментов и контекста, вызовы, изменения и результаты проверок. Такой журнал помогает разбирать произошедшее, хотя не гарантирует, что повторный запуск модели даст тот же ответ.
Для принятого артефакта мы фиксируем исходники, зависимости и условия сборки. Собирать его заново будем из сохранённого кода; ещё раз генерировать тот же исходник для этого незачем.
Детерминированность поведения — ещё одно требование: при одинаковых входных данных, состоянии и внешних условиях программа должна давать одинаковый результат. Если это нужно нашему адаптеру, требование закладываем в его контракт и проверяем. Разные запуски генерации могут дать разный код, который выполняет этот контракт. Прохождение тестов при этом подтверждает поведение только на проверенных случаях; само по себе оно не доказывает детерминированность во всех возможных условиях.
Возможность проверить результат ещё не делает среду безошибочной.
Если хорошее правило помогает множеству будущих изменений, плохое правило способно испортить их так же массово. Чем дольше ошибка в правиле остаётся незамеченной, тем больше реализаций может её унаследовать.
Мы можем поощрять прохождение неполного набора тестов и получить решения, которые хорошо выглядят на этих тестах. Можем сделать единый удобный шаблон, а потом размножить вместе с ним архитектурную ошибку. Можем запустить петлю исправлений, которая съедает токены и ничего полезного не производит.
Поэтому нам нужны ограничения числа попыток, возможность остановить процесс, независимая проверка и разбор общих причин отказа. Разнообразие полезно там, где действительно снижает риск одной общей ошибки.
Что может произойти, если свести цель к одной метрике и не ограничить способы её достижения? У этого риска есть известная предельная иллюстрация.

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

Работа готова, тесты зелёные. Теперь за этим результатом стоят условия, которые мы задали заранее. Агент не мог ослабить обязательные проверки под свой код, а мы знаем, какие требования они проверяют. Если для принятия решения этих оснований недостаточно, автоматический успех не обязывает нас выпускать изменение.
Возможно, мы примем изменение. Возможно, остановим его и попросим дополнительную проверку. Такая возможность осмысленно выбирать и была нашей целью.
Между этими двумя утрами — вся работа по проектированию среды.
И вот ради какой возможности, на мой взгляд, стоит этим заниматься.
Мы привыкли заканчивать архитектурную работу конкретной системой. Её можно показать, запустить, объяснить, как она устроена.
Мне хочется, чтобы результатом нашей работы могла стать ещё и способность создавать следующие системы в выбранном классе задач. Те, для которых сегодня нет конкретного запроса и нет готового чертежа. Чтобы наш опыт помогал принимать решения и тогда, когда мы лично не контролируем каждое изменение.
Границы этой возможности ещё предстоит выяснить. Но задача уже есть: проектировать условия, в которых системы рождаются, сохраняя возможность проверить результат и пересмотреть правила.
Для меня это большое и интересное продолжение инженерии.

Технический директор и партнёр Бындюсофт. Внедряю и проверяю средовой подход как на коммерческих проектах нашей компании, так и в академической среде — в ИТМО, где я преподаю, мы с магистрантами исследуем и развиваем его в рамках проектных работ.
Следить за результатами экспериментов и обсуждать развитие подхода можно в моём канале «Архитектура распределённых систем».
В первой статье о средовом подходе я рассматривал более широкий набор примитивов и паттернов, включая полупроницаемые мембраны и гомеостаз, но сама статья получилась тяжеловатой для чтения и понимания.
Эта статья — расширенная версия моего выступления на HardFest: