Допущение об одном процессе
Большая часть публичных споров о завершении агентов несёт невысказанную предпосылку: агент — это один процесс, и у этого процесса есть выключатель. Версия со стороны безопасности спрашивает, позволит ли достаточно способный агент себя выключить. Версия со стороны вендора обещает границу изоляции, политический шлюз, кнопку. Оба — рассуждения об одной управляемой единице.
В продакшене такой единицы не существует. Развёрнутый агент — это узел в графе. Родительский агент порождает потомков — субагентов для retrieval, исполнения кода, ревью, эскалации. Каждый потомок может держать сессии инструментов: открытый shell, аутентифицированный API-клиент, контекст браузера, транзакцию базы данных. Эти сессии держат lease-ы: короткоживущие учётные данные, блокировки файлов, резервации очередей, токены rate-limit. Граф динамичен; потомки создаются во время выполнения и могут пережить запрос, который их создал.
Завершение корня этого графа не завершает граф. Оно завершает один процесс и отсоединяет всё, что под ним. Потомки продолжают работать по своим расписаниям и, что критично, с учётными данными, выданными им, а не корню. Потомок, держащий облачный токен, унаследованный через делегирование от родителя, может продолжать действовать после исчезновения родителя. Это находка о dormant-агентах, пересказанная в операционных терминах: опасность не в процессе, который отказывается умирать, а в учётных данных, которые никто не догадался отозвать, потому что никто не смоделировал цепочку делегирования.
Что на самом деле нужно протоколу завершения
Флоту нужен протокол как минимум с тремя свойствами. Они независимы, и пропуск любого из них оставляет режим отказа, до которого kill switch не дотянется.
Состояние drain. Завершение не бинарно. Между running и terminated должно быть состояние, которое отвергает новые вызовы инструментов, позволяя выполняющимся вызовам разрешиться или прерваться детерминированно. Без него резкая остановка оставляет вызовы инструментов в неизвестном состоянии: платёж отправлен, но не подтверждён, файл наполовину записан, сообщение отправлено, но не подтверждено. Состояние drain заставляет принять решение по каждому выполняющемуся вызову — дождаться результата или отменить и зафиксировать отмену, — вместо того чтобы оставлять решение тому, что сделает транспорт при закрытии сокета.

Минимальный конечный автомат выглядит так:
requested -> draining -> reconciling -> terminated
\-> aborting -> reconciling -> terminated
draining отвергает новые вызовы инструментов и позволяет незавершённым закончиться в ограниченном окне. aborting отменяет их и требует, чтобы каждая отмена была подтверждена слоем инструментов. reconciling записывает запись, описанную ниже. Только после этого достижимо terminated. Переход из requested инициируется полномочным органом вне рабочей нагрузки, а не самим агентом.
Правила распространения. Lease потомка должен быть отзываем любым предком в цепочке делегирования, а не только компонентом, который его выдал. Если учётные данные внука может отозвать только его прямой родитель, а прямой родитель — это то, что отказало, учётные данные фактически становятся постоянными. Полномочие на отзыв должно течь вниз по цепочке и быть осуществимым из корня или из control plane, действующего от имени корня. Это свойство capability-системы, а не агента: каждый грант несёт набор принципалов, имеющих право его отозвать, и этот набор включает делегирующих предков.
Запись сверки, созданная до выхода. Post-mortem не должен зависеть от собственных логов агента. Агент, которого завершают — особенно тот, что ведёт себя неправильно, — наименее надёжный рассказчик о своих последних действиях. Запись сверки пишет супервизор или control plane, снаружи, и в ней указано: какие вызовы инструментов выполнялись, как каждый разрешился, какие lease-ы отозваны, а какие истекли, какие потомки подтверждённо завершены, а какие не учтены. Неучтённые потомки — важнейшее поле. Протокол, который не может их перечислить, не может утверждать, что флот остановлен.
Это обычные обязательства распределённых систем. Они неэффектны, и они — разница между контролем и жестом.
Где заканчивается рамка правил завершения

Недавняя работа над агентами, которые сами пишут свои правила завершения, — настоящий вклад, и стоит точно понять, в чём он. Самостоятельно написанная политика завершения — это слой политики: она позволяет агенту выразить, когда ему следует остановиться, и делает это выражение инспектируемым. Это полезно, потому что превращает неявное поведение в явный конечный автомат, который можно ревьюить и тестировать.
Ограничение в том, что слой политики не обеспечивает транспорт для отзыва. Правило «этот агент должен завершиться, когда выполняется условие X» — это запрос. Оно становится контролем только тогда, когда есть путь, по которому внешний полномочный орган может принудительно осуществить переход — отозвать lease-ы, осушить сессии, подтвердить, что потомки исчезли. Политика без пути отзыва — это запрос, а не контроль. Агент может его соблюсти; флот не может предполагать, что соблюдёт, и не может проверить, что соблюл.
Полезный ход — поднять явный конечный автомат из агента в инфраструктуру. Самоописанные условия завершения агента становятся одним из входов супервизора, который владеет фактическими переходами. Агент предлагает; control plane располагает. Именно это разделение делает конечный автомат тестируемым независимо от поведения конкретной модели.
Правило проектирования: завершение до графа задач
Определите конечный автомат завершения до графа задач агента. Это обратно тому, как строятся большинство флотов: сначала проектируется граф, возможности выдаются по мере необходимости, а завершение приделывается задним числом как таймаут. Приделывание задним числом проваливается, потому что форма графа определяет, что должно делать завершение, а к моменту, когда граф существует, цепочки делегирования уже несущие.
Флот, который нельзя осушить, нельзя и безопасно обновить. Тот же протокол, который чисто останавливает флот, позволяет заменить версию модели, ротировать учётные данные или выкатить новую схему инструментов, не оставляя незавершённой работы в старой генерации. Drain, reconcile, restart — это путь обновления; если завершение — это kill switch, обновление — это азартная игра.
Поэтому каждому гранту возможности сопоставьте handle отзыва, хранящийся вне рабочей нагрузки. Handle живёт в control plane, индексируется по цепочке делегирования и осуществим любым предком. Агент никогда не держит собственный выключатель, потому что выключатель, который держит то, что выключают, — это предложение.
Честный компромисс: это стоит латентности и сложности. Окно drain означает, что завершение занимает время, и кто-то должен выбрать границу. Записи сверки — ещё одно хранилище в эксплуатации и ещё одна схема для миграции. Lease-ы, отзываемые предком, требуют capability-модели, с которой большинство фреймворков агентов не поставляются, так что команды построят её сами или примут более слабую гарантию. Ничего из этого не избежать, если флот должен быть управляемым. Альтернатива — kill switch, который убивает один процесс и оставляет остальной граф с учётными данными, — а это не изоляция, только её видимость.
