跳转到主要内容

编码 Agent 需要的是沙箱契约,而不是警告标签

自主编码 Agent 的爆炸半径由其沙箱和网络出口规则决定。在定义 Agent 之前,先定义这份契约。

工厂单元内的工业机械臂,昏暗光线中可见关节和线缆

警告标签不是控制手段

大多数部署编码 Agent 的团队都从同一个产物开始:一段系统提示词,指示模型不要删除文件、不要窃取密钥、不要安装不受信任的包。这是一个警告标签。它是以自然语言向一个随机系统提出的请求,而这个系统可能受到它读到的任何内容的影响——一个 README、某个依赖的 postinstall 脚本、一段堆栈跟踪、一个抓取到的 issue 讨论串。这条指令要与上下文窗口中的每一个其他 token 竞争。

执行沙箱不参与竞争。它是由内核、容器运行时和网络层强制执行的边界。如果 Agent 无法写入 /workspace 之外,那么任何提示注入都无法让它写到 /workspace 之外。如果出口默认被拒绝,那么生成的 curl 调用会在套接字处失败,而不是由模型自行决定。

这就是为什么沙箱契约应当在定义 Agent 之前写好。Agent 的能力、它的工具面、它的自主级别——所有这些都取决于环境允许什么。先定义 Agent 会产生一个沙箱随后必须迁就的设计,这是约束的错误方向。

编码 Agent 实际造成危害的地方

编码 Agent 造成的危害很少是戏剧性的。它通常属于三类平淡无奇的类别之一,而每一类都是环境属性,而非模型属性。

文件系统触及范围超出工作树

一个被要求重构某个模块的 Agent 可能会决定“清理”相邻的配置、重写 lockfile,或遍历到父目录以解析某个 import。如果容器挂载了开发者的主目录或宿主机的源码树,可写集合实际上是无限的。失败之处不在于模型做出了糟糕的选择;而在于环境提供了这个选择。

从开发者环境继承的凭据访问

从开发者 shell 启动的 Agent 会继承环境变量、云凭据文件、SSH agent 套接字和缓存的 token。环境中一个长期有效的云密钥,就是对该密钥所能做的一切的长期授权。Agent 不需要有恶意才会误用它;它只需要运行一条读取环境并将其发送到某处的命令,而这对于构建工具来说是很正常的行为。

这三类都不是模型失败。它们是配置失败,而且可以在生成第一个 token 之前就修复。

定义契约

Server rack closeup

沙箱契约有三个条款:什么可以写、什么可以读、什么可以离开。

定义可写集合、可读集合,拒绝其他一切

从拒绝开始,逐一枚举授权。对于编码任务,一个可行的默认配置如下:

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

仓库是工作单元。它之外的任何东西要么是系统依赖(应为只读),要么是宿主机资源(根本不应可见)。这是挂载策略,不是提示词指令,并且由容器运行时强制执行。

权衡是真实的:狭窄的可写集合会破坏那些合法需要触及父目录、同级仓库或全局缓存的工作流。需要这些工作流的团队必须有意识地扩大契约并记录原因,而不是意外继承一个宽泛的契约。扩大是可审查的事件;意外宽泛的挂载则不是。

默认拒绝出口,并将注册表和端点加入允许列表

网络策略应当镜像文件系统策略。默认是不建立任何出站连接。允许列表是明确且精简的:项目实际使用的包注册表、Agent 调用模型时所需的模型提供商端点,以及任务所需的任何内部服务。

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

这同时约束了安装路径和生成代码路径。它还使 Agent 的网络行为变得可观测:任何超出允许列表的连接尝试都是一个信号,而不是噪声。

诚实的权衡是运维摩擦。合法的构建有时需要一个未被预料到的端点,而失败模式是一个看起来像临时错误的被阻止请求。允许列表必须被视为受版本控制的配置,像代码一样审查,并在任务合法需要时更新——而不是因为一次构建失败就放宽为 allow: *。

使用限定于任务的临时凭据运行

凭据应当为任务而签发,并随任务过期。在沙箱启动时签发、在拆除时撤销的限定范围、短时效 token 限制了任何窃取的价值。开发者自己的长期凭据绝不应进入沙箱;Agent 不应能够在任务边界之外以开发者身份行事。

这一条款往往最难实现,因为它需要一个能够签发狭窄、短时效授权的身份系统。在不存在该系统的场景下,退路是在完全没有凭据的情况下运行 Agent,并将任何特权操作通过一个单独的人工审批步骤来路由。

Circuit board macro

检测与响应

契约定义了边界。检测告诉你何时有东西在挤压它,响应则决定接下来发生什么。

将异常系统调用模式作为主要信号

提示词检查是不可靠的。同一条指令在一个上下文中可能是良性的,在另一个上下文中可能是注入的一部分,而模型对自己意图的叙述并不是其行为的证据。可靠的信号是进程实际做了什么:可读集合之外的意外 open 调用、对非允许列表地址的 connect 尝试、执行不在任务配置中的 shell 或解释器的 execve、异常的文件描述符变动。

这些都可以在运行时层观测到,而无需检查模型的推理过程。系统调用级监控器可以针对偏离任务基线配置的情况发出告警,并且它不依赖于 Agent 如实描述自己正在做什么。

将 diff 与命令历史一起记录

事后审查需要将两种产物放在一处。没有产生它的命令的 diff 难以归因;没有结果 diff 的命令历史难以评估。将它们一起存储——命令、时间戳、退出状态、结果 diff——使事后审查成为可能,并使在测试数天后失败时重建 Agent 所做之事成为可能。

这是日志记录层面的关注点,而非模型层面的关注点,并且应当与挂载和出口规则一起在契约中定义。

沙箱之外不可逆的操作需要人工审批

有些操作无法通过还原工作树来撤销:推送到远程、发布包、删除云资源、发送邮件、合并拉取请求。这些应当被门控。门控不是一条“推送前先询问”的提示词指令;而是一项策略,移除该操作所需的凭据或网络路由,直到人类为某个具体操作授予它。

定义的顺序

实际后果是工作顺序的改变。在定义 Agent 的工具、它的记忆、它的规划循环或它的自主级别之前,先定义它运行的环境:可写集合、可读集合、出口允许列表、凭据范围、系统调用监控器、合并的 diff 与命令日志,以及沙箱之外不可逆操作的审批门控。

The agent is then designed against a known boundary. Its capabilities are the intersection of what the model can do and what the sandbox permits, which is a smaller and more honest surface than a warning label in a system prompt.

权衡是,严格的契约偶尔会阻止一个合法任务,团队将不得不有意识地扩大它。这正是重点。一个从未被测试的边界不是边界;一个通过审查扩大的边界才是被理解的边界。

分享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.