Как защитить multi-agent систему от регрессий своих же агентов: три слоя

3 июня 2026 года я открыл 4bos.ru/blog/ чтобы сделать скриншот для презентации Terra Business Club. На экране была голая страница — 121 статья списком на белом фоне, без шапки, без футера, без стилей. Как будто кто-то взял и вырвал всю обвязку сайта.

Два дня назад SEO-агент получил задачу оптимизировать скрипт генерации индекса блога. Агент оптимизировал честно: убрал всё что посчитал лишним. Шапка, футер, подключение стилей — они не упоминались в задаче явно. Агент их убрал. Каждая отдельная статья открывалась нормально. Индекс блога я смотрел редко. Поэтому 48 часов этот регресс работал в проде незамеченным.

У меня 26 агентов. Любой из них в любой момент может сделать то же самое с другим куском инфраструктуры. И это не ошибка агента — он выполнил задачу. Это архитектурная проблема: система не была защищена от собственных исполнителей.

Откуда берётся проблема: агент не знает что он не знает

Большинство людей, сталкиваясь с ошибкой AI-агента, ищут проблему в промпте: «написал недостаточно чётко», «не уточнил требования», «надо было добавить ограничение». Это ловушка. Промпт-инженерия решает класс задач где агент не понял что делать. Она не решает задачи где агент понял правильно, но не имел контекста о последствиях.

SEO-агент получил задачу с полным набором требований к оптимизации скрипта. Он её выполнил корректно в рамках того, что видел. Проблема в том, что он не видел: что этот скрипт генерирует публичную страницу сайта, что шапка и футер — это не «лишний» HTML, а часть дизайн-системы, что 121 статья живёт за этим индексом.

Агент работает в пузыре своей задачи. Он не знает что не знает. Это фундаментальное ограничение любого автономного исполнителя — не только AI-агента. Разработчик в команде, не знакомый с кодовой базой, может сделать то же самое. Разница в скорости: агент совершает сотни операций в час.

Почему запреты в инструкциях не работают

Когда я починил шаблон, первая мысль была написать в инструкции агента: «не трогай шаблоны блога». Это не работает по трём причинам, которые проявляются по-разному.

Первая причина — контекстное смещение. Агент читает инструкцию в начале сессии. При длинных задачах или многошаговых операциях ранний контекст вытесняется. К моменту когда агент добирается до записи файла, он уже не держит в активном контексте запрет из начала инструкции. Это не баг — это природа работы с большим контекстом. Тест простой: возьмите любой запрет из начала длинных инструкций и проверьте соблюдается ли он ближе к концу многошаговой задачи. Соблюдается хуже.

Вторая причина — изоляция правила. Запрет в инструкциях SEO-агента не покрывает остальные 25 агентов. Любой из них может получить аналогичную задачу: «исправь шаблон», «обнови скрипт», «оптимизируй генератор». И каждый работает со своей копией инструкций.

Третья причина — интерпретация. Агент видит запрет «не трогай шаблоны» и может решить что его задача под это определение не подпадает. «Я не трогаю шаблон, я оптимизирую скрипт который генерирует файл из шаблона» — технически разные вещи с точки зрения буквальной интерпретации.

Запреты — это намерение. Намерение зависит от интерпретации. Для критичных компонентов нужны механизмы, которые делают ошибку физически невозможной независимо от намерений.

Слой первый: sentinel в скрипте

Sentinel — это проверка перед деструктивной операцией. Скрипт не может перезаписать файл если результат нарушает инварианты. Это 15 строк кода, которые работают вечно без обслуживания.

В rebuild_blog_index.py, который перезаписывает /opt/4bos-static/blog/index.html, добавил проверку перед записью. Скрипт читает сгенерированный HTML и ищет четыре обязательных маркера: class="header" — шапка сайта, class="footer" — подвал, /assets/css/style.css — основная таблица стилей, class="tg-float" — плавающая кнопка Telegram. Если хоть один маркер отсутствует — скрипт падает с явной ошибкой и не перезаписывает файл.

Вот реализация:

REQUIRED_MARKERS = [
    'class="header"',
    'class="footer"',
    '/assets/css/style.css',
    'class="tg-float"'
]

def validate_template(html: str, source: str = 'unknown') -> None:
    missing = [m for m in REQUIRED_MARKERS if m not in html]
    if missing:
        raise ValueError(
            f"[SENTINEL] Template validation failed. "
            f"Missing markers: {missing}. "
            f"Source: {source}. File NOT written."
        )

new_html = generate_index(articles)
validate_template(new_html, source='rebuild_blog_index')
with open(INDEX_PATH, 'w') as f:
    f.write(new_html)

Ключевое свойство sentinel: он fail-loud. Скрипт кричит об ошибке с явным сообщением, а не молча пропускает. Агент, который пытается записать сломанный шаблон, получит ошибку в логах, задача не завершится успешно, я увижу failed run. Вместо 48 часов до обнаружения — несколько секунд.

Когда писать sentinel: любая операция которая перезаписывает публичный файл, удаляет записи в базе данных, изменяет конфигурацию работающего сервиса. Не надо sentinel на каждое действие — только на те где регресс незаметен сразу и критичен для пользователя или для другой части системы.

Как найти точки для sentinel в своей системе

Практический метод: перебери все операции которые агенты делают с финальными артефактами. Финальный артефакт — это то что видит пользователь или от чего зависит работа другой части системы.

Для каждой операции задай вопрос: «если здесь пройдёт ошибка, через сколько времени я об этом узнаю?» Если ответ — «через день», «через неделю», «случайно» — это кандидат на sentinel.

В моей системе такие точки: запись HTML-файлов сайта (обнаружение через часы), изменение nginx конфигов (обнаружение когда перезапустится сервер), обновление скриптов которые читают продовую базу данных (обнаружение на следующем cron-прогоне), запись в таблицы с финансовыми данными инвесторов (обнаружение при следующем отчёте — через несколько дней). На каждую добавил проверку инвариантов перед записью.

«Юрий Солар, основатель Solar OS: главный вопрос при проектировании защиты — не "как запретить агенту ошибиться", а "через сколько времени я узнаю об ошибке". Если ответ больше часа — там нужен sentinel.»

Слой второй: правило в конституции с прецедентом

Sentinel защищает конкретный скрипт. Но инфраструктура шире: есть другие скрипты, другие шаблоны, другие операции которым нужен контекст для принятия решений. Для всего этого нужен второй слой.

Конституция — это документ /opt/constitution.md, который является источником правды для всей системы агентов. Он инжектируется в начало AGENTS.md каждого агента и обновляется каждые 15 минут. В него добавил запрет:

Публикация статей в блог 4bos.ru — только через agent_publish.py. Прямая правка blog/index.html, rebuild_blog_index.py, blog_template.py — запрещена. Прецедент 01.06: SEO-агент переписал скрипт с упрощённым шаблоном без header/footer — регресс ушёл в прод, 121 статья 48 часов отображалась без обвязки сайта.

Формулировка важна. «Прецедент 01.06» — это не просто запрет, это история. Агент, видящий что это правило появилось из реального инцидента, понимает ставки. Он не просто следует правилу — он понимает почему оно существует и может применять его к граничным случаям которые явно не описаны.

Это принципиально отличается от запрета «не трогай шаблоны». Агент с контекстом про инцидент, получив задачу «улучши скрипт блога», задаст уточняющий вопрос. Агент без контекста — просто сделает.

Что ещё стоит добавить в конституцию: любая операция которая уже приводила к инциденту. Конституция становится коллективной памятью всех агентов об ошибках системы. Каждый новый инцидент расширяет её на одну строку. Через полгода это живая документация реальных отказов.

Слой третий: автораспределение по всем агентам

Правило в конституции работает только если оно актуально у всех агентов. Это третий слой — и самый важный для масштабируемости.

constitution_distribute.py запускается по cron каждые 15 минут. Скрипт: читает /opt/constitution.md как источник правды, находит все AGENTS.md во всей файловой системе Paperclip, в каждом файле находит маркеры CONSTITUTION-START и CONSTITUTION-END, заменяет содержимое между маркерами актуальной версией, пишет лог с timestamp и списком обновлённых файлов.

После добавления нового правила оно попадает во все AGENTS.md за максимум 15 минут. Обновляю один документ — получаю синхронизацию по всей системе. Это единственный способ держать 26 агентов консистентными без ручного труда.

Структура маркеров в каждом AGENTS.md:

<!-- CONSTITUTION-START — auto-distributed -->
[§0 Идентичность и голос]
[§1 Основной продукт — клуб]
[§13 Запреты — свод всех правил с прецедентами]
<!-- CONSTITUTION-END -->

# Инструкции конкретного агента
<!-- AGENT_CONTENT_START -->
[зона ответственности, KPI, инструменты]
<!-- AGENT_CONTENT_END -->

Агент видит конституцию в начале каждой сессии. Если задача противоречит конституции — у него есть контекст для отказа, уточнения или эскалации к CEO-агенту.

Почему 15 минут оптимальный интервал: достаточно быстро чтобы правило применялось к следующей задаче любого агента, достаточно редко чтобы не создавать лишний I/O. Для критичных обновлений можно запустить скрипт вручную немедленно.

Параллельный инцидент: nginx и осколки удалённых сервисов

В тот же день обнаружил что 4bos.online не отвечает. Инвестор-дашборд, нужный для презентации, лежал. Это другой класс проблемы — не агент сломал что-то активно, а осколок прошлого лежал и ждал своего часа.

Причина: конфигурационный файл nginx от Chatwoot. Сервис удалили полгода назад — перешли на другой инструмент для поддержки. Удалили правильно: остановили процесс, убрали из systemd. Но nginx-конфиг в /etc/nginx/sites-enabled остался. Полгода он висел и ничему не мешал. Потом что-то изменилось в конфигурации сервера, старый конфиг начал создавать конфликт, и nginx при старте завершался с ошибкой.

Диагностика заняла 20 минут. Решение — 2 минуты. Это несоразмерно, и именно несоразмерность — маркер системной проблемы. Если диагностика занимает в 10 раз больше лечения, значит причина не была встроена в процесс.

Добавил в конституцию агентов явный чеклист при отключении любого сервиса: остановить процесс, удалить nginx конфиг из sites-enabled, nginx -t для проверки, reload. Добавил quarterly-задачу: аудит sites-enabled, сопоставление с работающими сервисами. Теперь любой агент, который отключает сервис, получает этот чеклист в контексте своих инструкций. И я сам тоже.

Система на 26 агентов: что работает, что не работает

За год работы с мультиагентной системой я прошёл через несколько конфигураций управления. От ручных инструкций в каждом агенте (не масштабируется при изменениях), к shared документу который агенты читают по запросу (не гарантирует что прочитают), к автодистрибуции конституции (текущий вариант).

Что работает на 26 агентов: единая точка правды (конституция), автораспределение по cron, sentinel на критичных операциях, иерархия с CEO-агентом как точкой маршрутизации, явные прецеденты в правилах.

Что не работает: ручное обновление инструкций каждого агента, запреты без объяснения почему, доверие что агент «запомнит» что говорили месяц назад, попытка покрыть все edge cases в промпте.

При переходе от 26 к 50 и более агентов нужны иерархические компании: несколько CEO-агентов с собственными командами, каждая с отдельной конституцией. Единая конституция на 50 агентов становится слишком объёмной для эффективного восприятия в начале сессии.

Как тестировать sentinel до попадания в прод

Sentinel который никогда не срабатывал — это неизвестная переменная. Он может быть написан правильно и проверять не то. Поэтому после добавления sentinel нужно убедиться что он работает намеренно.

Три теста на каждый sentinel. Первый: передай намеренно сломанный input — убедись что скрипт падает с ошибкой и не записывает файл. Для rebuild_blog_index.py: создай шаблон без class=header, запусти скрипт, убедись что он упал и файл не изменился. Второй: передай правильный input — убедись что скрипт завершается успешно и файл записан корректно. Третий: проверь что сообщение об ошибке достаточно информативно чтобы понять что сломано без чтения кода.

Третий тест важнее первых двух. Я видел sentinel'ы которые падали с сообщением «validation error» без деталей. Пока разбираешься какой именно маркер потерялся — теряешь 15 минут. Хороший sentinel пишет: «Missing markers: class=footer. Source: rebuild_blog_index. File index.html NOT written.» Это самодиагностирующийся сбой.

Ещё важный момент: sentinel должен быть идемпотентным. Если запустить скрипт дважды с правильным input, второй прогон не должен ломать результат первого. Это кажется очевидным, но при написании validate-функций легко добавить side effect который нарушает идемпотентность.

Мониторинг: как узнать что sentinel сработал

Sentinel кричит об ошибке в stdout/stderr. Но если никто не читает stdout — кричит в пустоту. Для автономных агентов нужна доставка сигнала до человека.

В моей системе: все failed runs агентов попадают в дайджест который собирается в 07:30 WITA ежедневно. Если задача агента завершилась с ошибкой, я вижу это утром. Для критичных sentinel'ов добавил прямой алерт в Telegram: скрипт ловит исключение от validate-функции и пишет сообщение в чат. Это сокращает время реакции с нескольких часов до нескольких минут.

Структура алерта простая: что сломалось, где, кто запустил, что делать дальше. Если sentinel сработал при запуске агентом — в алерте должен быть идентификатор задачи. Если при cron — метка cron-задачи. Это избавляет от детективной работы «а кто вообще запускал rebuild сегодня ночью».

Дополнительно: раз в квартал проверяю что sentinel'ы не начали тихо отключаться. Бывает что в процессе рефакторинга validate-функция переезжает в другое место и вызов убирают «временно». Аудит: просматриваю все публичные скрипты на наличие validate-вызова перед деструктивными операциями. Если вызов пропал — добавляю обратно и добавляю тест.

Практический чеклист: как внедрить три слоя

Для команды из 5-10 агентов можно начать за неделю. Для крупной системы из 20-30 агентов тот же план работает, но добавляется шаг с инвентаризацией.

Прежде чем начать: сделай список всех мест где агенты пишут данные. Не только файловая система — база данных, nginx конфиги, systemd unit-файлы, S3 если используешь. Это занимает час, но без этого список sentinel-кандидатов будет неполным. Пропущенный критичный артефакт — это регресс который произойдёт именно там где проверки нет.

День 1-2: инвентаризация. Список всех финальных артефактов (файлы, таблицы БД, конфиги). Для каждого — оценка «через сколько узнаю об ошибке». Приоритизация по этому критерию. Сначала делаешь топ-5 по «самый долгий обнаружение + самый критичный», именно на них идут первые sentinel'ы.

День 3-4: sentinel на топ-3. Начать с трёх самых критичных операций. Написать validate-функцию для каждой. Добавить в скрипты перед записью. Протестировать намеренной ошибкой — убедиться что sentinel кричит с понятным сообщением.

День 5: конституция. Создать /opt/constitution.md или аналог. Добавить существующие правила и известные прецеденты. Формат: запрет + «прецедент DD.MM: что произошло».

День 6-7: дистрибуция. Написать distribute.py с маркерами CONSTITUTION-START/END. Добавить cron на 15 минут. Проверить что все AGENTS.md обновились. Убедиться что новый агент добавляет маркеры по умолчанию при создании.

После этого: каждый новый инцидент → добавление прецедента в конституцию → автораспределение через 15 минут. Система самообучается от своих ошибок, а не только от твоих инструкций.

День 3-4: sentinel на топ-3. Начать с трёх самых критичных операций. Написать validate-функцию для каждой. Добавить в скрипты перед записью. Протестировать намеренной ошибкой — убедиться что sentinel кричит.

День 5: конституция. Создать /opt/constitution.md или аналог. Добавить существующие правила и известные прецеденты. Формат: запрет + «прецедент DD.MM: что произошло».

День 6-7: дистрибуция. Написать distribute.py с маркерами CONSTITUTION-START/END. Добавить cron на 15 минут. Проверить что все AGENTS.md обновились.

После этого: каждый новый инцидент → добавление прецедента в конституцию → автораспределение через 15 минут. Система самообучается от своих ошибок.

Итог

Регресс с блогом я заметил через 48 часов. После внедрения трёх слоёв sentinel поймал бы ошибку за секунды: агент получил бы failed run, я бы увидел алерт сразу.

За две недели после внедрения sentinel'ов зафиксировал три случая когда они сработали: при тестовом запуске скрипта с неправильными параметрами, при попытке агента записать HTML с незакрытым тегом, при моём собственном эксперименте с шаблоном. Во всех трёх случаях файл не был перезаписан, ошибка была немедленно видна в логах.

Три слоя защиты multi-agent инфраструктуры: sentinel в скриптах (fail-loud проверка перед деструктивной операцией, независимая от намерений исполнителя), конституция с прецедентами (агент знает что нельзя, почему это важно и какой реальный инцидент это вызвал), автораспределение по cron (правила актуальны у всех 26 агентов через 15 минут после обновления без ручного труда).

Ключевой сдвиг: от «объяснять каждому агенту каждый раз» к «делать ошибку невозможной технически». Запреты — это намерение, которое зависит от интерпретации. Sentinel — это механизм, который не зависит от неё.

Ещё одно наблюдение из практики: sentinel'ы меняют поведение агентов в лучшую сторону. Когда агент знает что его задача может упасть из-за конкретной проверки, он начинает явно сообщать о том что собирается делать — перед тем как сделать. Это не запрограммировано, это следствие того что агент работает в системе с явными инвариантами. Вместо молчаливого выполнения — «собираюсь перезаписать index.html, проверяю маркеры». Стало удобнее контролировать что происходит.

Если хотите посмотреть как устроена конституция изнутри, как выглядит distribution скрипт, и какие ещё классы sentinel'ов я добавил в свою инфраструктуру — всё это в клубе «Solar — внутрянка». Реальные артефакты: скрипты, AGENTS.md, дашборды. Бери и адаптируй: https://4bos.ru/inside/

Другие статьи по теме: AGENTS.md — как писать инструкции для AI-агента, AI-агенты для бизнеса без найма.

— Solar OS.

Частые вопросы

Почему AI-агент ломает архитектуру даже при правильно написанной задаче?
Агент выполняет инструкцию буквально. SEO-агент получил задачу «оптимизировать скрипт генерации» — и оптимизировал, убрав всё что посчитал лишним. Шапка, футер и стили не были явно в задаче, агент их выбросил. У него не было контекста что это критично для сайта. Решение — не объяснять каждый раз, а сделать ошибку технически невозможной через sentinel и явные запреты в инструкциях.
Что такое sentinel в скриптах автоматизации и как его написать?
Sentinel — проверка перед деструктивной операцией. В rebuild_blog_index.py перед перезаписью index.html скрипт ищет маркеры class=header, class=footer, путь к style.css. Если хоть один отсутствует — скрипт падает с ошибкой и не перезаписывает файл. Добавляется 10-15 строками кода. Работает независимо от того, кто вызвал скрипт — агент, разработчик или cron.
Как распределить правила governance на десятки агентов автоматически?
Через скрипт constitution_distribute.py по cron каждые 15 минут: читает /opt/constitution.md, находит все AGENTS.md всех агентов в системе, инжектирует конституцию между маркерами CONSTITUTION-START и CONSTITUTION-END. Новые правила попадают во все 26 агентов за 15 минут без ручных правок. Обновляешь один файл — вся система синхронизирована.
Что делать когда nginx упал из-за конфига удалённого сервиса?
Чеклист при отключении любого сервиса: остановить процесс, удалить nginx конфиг из sites-enabled, nginx -t для проверки, reload. В кейсе 4bos.online потребовалось 20 минут диагностики и 2 минуты лечения — это маркер что причина не была встроена в процесс. Добавить чеклист в конституцию агентов и в свои процедуры offboarding сервисов.
Сколько агентов можно держать в одной multi-agent системе без хаоса?
В текущей конфигурации Solar работают 14 активных агентов в default-компании. 26 агентов — граница, при которой ручное управление инструкциями уже невозможно и нужна автоматическая дистрибуция. При 50 и более агентах нужны иерархические компании с отдельными конституциями и CEO-агентами. Управляемость системы зависит не от числа агентов, а от зрелости governance.

Читайте также

Подписаться на блог в Telegram

Читайте свежие кейсы об AI-автоматизации, системной архитектуре и масштабировании бизнеса.

Подписаться