Перейти к основному содержимому

Кодирующим агентам нужен контракт песочницы, а не предупреждающая наклейка

Радиус поражения автономного кодирующего агента определяется его песочницей и правилами сетевого egress. Определите этот контракт до того, как определите агента.

Промышленная роботизированная рука внутри заводской ячейки, видны сочленения и кабели в тусклом свете

Предупреждающая наклейка — это не контроль

Большинство команд, развёртывающих кодирующего агента, начинают с одного и того же артефакта: системного промпта, который инструктирует модель не удалять файлы, не выносить секреты, не устанавливать недоверенные пакеты. Это предупреждающая наклейка. Это просьба, выраженная на естественном языке, к стохастической системе, на которую может повлиять любой прочитанный ею контент — README, postinstall-скрипт зависимости, стек-трейс, загруженный тред issue. Инструкция конкурирует с каждым другим токеном в контекстном окне.

Песочница исполнения не конкурирует. Это граница, обеспечиваемая ядром, средой выполнения контейнера и сетевым уровнем. Если агент не может писать вне /workspace, никакая инъекция в промпт не заставит его писать вне /workspace. Если egress запрещён по умолчанию, сгенерированный вызов curl завершится ошибкой на сокете, а не по усмотрению модели.

Именно поэтому контракт песочницы следует писать до того, как специфицирован агент. Возможности агента, его поверхность инструментов, уровень автономии — всё это производно от того, что допускает окружение. Спецификация агента в первую очередь порождает дизайн, под который песочнице затем приходится подстраиваться, а это неверное направление ограничения.

Где кодирующие агенты действительно причиняют вред

Вред от кодирующих агентов редко бывает драматичным. Обычно это одна из трёх заурядных категорий, каждая из которых является свойством окружения, а не свойством модели.

Доступ к файловой системе за пределами рабочего дерева

Агент, которому поручено отрефакторить модуль, может решить «прибраться» в соседней конфигурации, переписать lockfile или выйти в родительский каталог, чтобы разрешить импорт. Если контейнер монтирует домашний каталог разработчика или исходное дерево хоста, записываемое множество фактически неограничено. Отказ не в том, что модель выбрала плохо; а в том, что окружение предоставило выбор.

Доступ к учётным данным, унаследованный от окружения разработчика

Агенты, запущенные из оболочки разработчика, наследуют переменные окружения, файлы облачных учётных данных, сокеты SSH-агента и кэшированные токены. Долгоживущий облачный ключ в окружении — это постоянное предоставление всего, что этот ключ может делать. Агенту не нужно быть злонамеренным, чтобы злоупотребить им; ему достаточно выполнить команду, которая читает окружение и отправляет его куда-то, что является нормальным для инструментов сборки.

Эти три категории — не отказы модели. Это отказы конфигурации, и они устранимы до генерации хотя бы одного токена.

Спецификация контракта

Server rack closeup

Контракт песочницы состоит из трёх пунктов: что можно записывать, что можно читать и что может уходить.

Определите записываемое множество, читаемое множество и запретите всё остальное

Начинайте с запрета и перечисляйте разрешения. Рабочее значение по умолчанию для задачи кодирования выглядит так:

writable: /workspace/repo/**
readable: /workspace/repo/**, /usr/lib/**, /usr/share/**
denied:   everything else

Репозиторий — единица работы. Всё вне его — либо системная зависимость, которая должна быть доступна только для чтения, либо ресурс хоста, который не должен быть виден вовсе. Это политика монтирования, а не инструкция в промпте, и она обеспечивается средой выполнения контейнера.

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

Запретите egress по умолчанию и внесите реестры и эндпоинты в allowlist

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

egress: deny
allow:  registry.npmjs.org
allow:  api.internal.example

Это одновременно ограничивает путь установки и путь сгенерированного кода. Это также делает сетевое поведение агента наблюдаемым: любая попытка соединения вне allowlist — это сигнал, а не шум.

Честный компромисс — операционное трение. Легитимным сборкам иногда нужен эндпоинт, который не был предусмотрен, и режим отказа — заблокированный запрос, выглядящий как временная ошибка. Allowlist должен рассматриваться как конфигурация под контролем версий, ревьюиться как код и обновляться, когда задача законно этого требует, — а не ослабляться до allow: * из-за одной неудачной сборки.

Запускайте с эфемерными учётными данными, ограниченными задачей

Учётные данные должны выпускаться для задачи и истекать вместе с ней. Ограниченный, короткоживущий токен, выданный при старте песочницы и отозванный при её завершении, ограничивает ценность любого выноса. Собственные долгоживущие учётные данные разработчика никогда не должны попадать в песочницу; агент не должен иметь возможности действовать как разработчик за пределами границы задачи.

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

Circuit board macro

Обнаружение и реагирование

Контракт определяет границу. Обнаружение сообщает вам, когда что-то давит на неё, а реагирование определяет, что происходит дальше.

Рассматривайте аномальные паттерны системных вызовов как первичный сигнал

Инспекция промптов ненадёжна. Одна и та же инструкция может быть безобидной в одном контексте и частью инъекции в другом, а собственное описание моделью своих намерений не является доказательством её поведения. Надёжный сигнал — это то, что процесс фактически делает: неожиданные вызовы open вне читаемого множества, попытки connect к адресам вне allowlist, execve оболочек или интерпретаторов, отсутствующих в профиле задачи, необычная активность файловых дескрипторов.

Всё это наблюдаемо на уровне среды выполнения без инспекции рассуждений модели. Монитор на уровне системных вызовов может оповещать об отклонении от базового профиля задачи, и он не зависит от того, говорит ли агент правду о том, что делает.

Логируйте diff и историю команд вместе

Ревью постфактум требует обоих артефактов в одном месте. Diff без команд, которые его произвели, трудно атрибутировать; история команд без результирующего diff трудна для оценки. Хранение их вместе — команда, временная метка, код выхода, результирующий diff — делает возможным ревью постфактум и позволяет восстановить, что делал агент, когда тест падает спустя дни.

Это вопрос логирования, а не вопрос модели, и он должен быть специфицирован в контракте наряду с правилами монтирования и egress.

Требуйте одобрения человеком для необратимых операций вне песочницы

Некоторые операции нельзя отменить, откатив рабочее дерево: push в удалённый репозиторий, публикация пакета, удаление облачного ресурса, отправка письма, слияние pull request. Они должны быть закрыты шлюзом. Шлюз — это не инструкция в промпте «спроси перед push»; это политика, которая удаляет учётные данные или сетевой маршрут, необходимые для операции, пока человек не предоставит их для конкретного действия.

Порядок спецификации

Практическое следствие — изменение порядка работы. Прежде чем специфицировать инструменты агента, его память, его цикл планирования или уровень автономии, специфицируйте окружение, в котором он работает: записываемое множество, читаемое множество, allowlist egress, область действия учётных данных, монитор системных вызовов, объединённый лог diff и команд и шлюз одобрения для необратимых операций вне песочницы.

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

Компромисс в том, что жёсткий контракт будет иногда блокировать легитимную задачу, и команде придётся намеренно его расширять. В этом и суть. Граница, которую никогда не проверяют, — не граница; граница, которую расширяют через ревью, — это граница, которую понимают.

ПоделитьсяXLinkedInПостоянная ссылка6 мин чтения

Связанное чтение

На какие вопросы это отвечает

Why specify the sandbox before the agent?

The sandbox defines the agent's blast radius. Specifying the agent first produces a design the sandbox must accommodate, which inverts the constraint. Specifying the sandbox first gives the agent a known boundary to be designed against.

Is prompt-level instruction not sufficient for safety?

Prompt instructions are requests expressed in natural language and compete with all other context, including content the agent reads. Kernel, container, and network policy do not compete; they are enforced regardless of what the model decides.

What should the default network policy be?

Deny egress by default, then allowlist specific registries and endpoints under version control. This constrains both package installs and generated code, and makes any non-allowlisted connection attempt a clear signal.

Why are syscall patterns a better signal than prompt inspection?

Prompt inspection depends on interpreting intent, which is unreliable and context-dependent. Syscall patterns are observable behaviour at the runtime layer and do not depend on the agent accurately describing what it is doing.

What operations should require human approval?

Operations that are irreversible outside the sandbox: pushing to a remote, publishing a package, deleting cloud resources, sending messages, merging pull requests. These should be gated by removing the credential or network route until a human grants it for a specific action.