TL;DR: Agent 安全不能只靠模型遵守提示词。先按任务给工具,设置工具白名单,比如只读 agent;再是工具设计的时候,要把工具接口设计的小一些,限制工具能操作的资源;真正调用时通过权限门禁检查规则,这些规则可以是某个工具特有的命令或者参数,比如递归删除等,也可以是某些工具通用的写到一处;除了硬性规则,还有审批机制,交给人 HITL 或独立审查模型批准;真正执行代码时,再用沙箱限制文件、网络和进程访问。YOLO、人工审批和自动审批是不同的授权方式,QuickJS、OS 沙箱和 VM 则隔离不同的执行对象,不能混为一谈。

一、先看整体:工具、规则、批准和沙箱怎么配合#

模型负责提出操作,Agent 宿主程序负责执行。权限控制主要就在宿主程序里:它掌握工具、规则、审批入口和执行环境,而不是把“能不能做”完全交给模型。

可以把主线记成:给哪些工具 → 调用前检查规则和授权 → 必要时批准 → 在受限环境执行。 这不是所有产品统一的四层架构,而是理解主流实现的几个关键问题。

例如,用户让编码 Agent 修复项目并推送代码:

  1. 启动时给它读文件、编辑、执行命令的工具,不给部署生产环境的工具。
  2. 读取和修改普通项目文件,按已配置的规则直接执行;读取私钥则拒绝。
  3. 测试命令在限制了文件访问和网络的环境中运行。
  4. 推送代码如果需要批准,就暂停这次调用,交给用户或审查模型;批准后才执行。

这些检查不一定各有一个独立模块。比如 Codex 会把沙箱范围内的许多操作直接放行,越界或命中需要批准的规则时再申请授权。沙箱不只是“最后兜底”,也能减少日常操作的确认次数。[1]

二、先收窄工具:不只是“给不给”,还包括“给多大能力”#

第一步是按 Agent 的任务分配工具。 阅读 Agent 可以只给文件读取和搜索;编码 Agent 再加编辑和测试;发布 Agent 才拿到发布工具。

实现上,宿主为每个 Agent 保存可调用工具集合,既控制发给模型的工具说明,也在执行入口检查。只从提示词中删掉工具说明不够:还要避免通过嵌套调用、其他 Agent 或通用 Bash 获得同样的能力。

第二步是限制工具本身。 不是每个工具都要做成一个权限系统,通常有三种直接做法:

工具白名单和工具能力是两回事。一个只提供 read 的 Agent 可能仍能读取私钥;一个保留任意 Bash 的“只读 Agent”仍然可能写文件。

通用 Bash 很难在工具内部彻底限权。 它既能运行测试,也能启动 Python、删除文件、发送网络请求。即使接口叫 run_tests,如果运行的是可修改的项目脚本,也仍然属于代码执行。

所以 Codex 等产品不是把 Bash 改造成只能执行几个固定动作的工具,而是结合命令检查、审批和执行沙箱。Pi 默认工具则主要使用启动 Pi 的操作系统账号权限;它提供扩展拦截点,但不自带完整的工具调用权限策略和 OS 沙箱。[1][4]

三、规则在哪里执行:通用门禁、工具内部与沙箱#

Policy 是“允许什么、禁止什么”的规则;Permission Gate 是工具调用前的权限检查入口。 它通常在 Agent 宿主程序里,不一定是独立服务,也不能代替所有执行限制。要看一条规则是否有效,关键是看它在哪里检查、能覆盖哪些操作。

  1. 通用工具门禁:在调用工具之前检查。 宿主根据 Agent 身份、工具名和参数,决定允许、拒绝或询问。例如“阅读 Agent 不得调用编辑工具”“发送邮件需要批准”。共同规则可以在这里集中维护,嵌套调用和子 Agent 也应接入检查。局限:只能管住经过这个入口的调用,不能仅凭参数看清任意脚本内部的全部行为。

  2. 工具内部校验:在具体操作发生前检查。 文件工具检查实际目标路径是否在允许目录内,发布工具检查目标是不是测试环境。多个文件工具可以复用同一个路径检查函数;工具特有的业务限制则留在工具内部。局限:只约束这个工具。 禁止 read 读取私钥,并不能自动禁止 Bash 或 Python 读取同一文件。

  3. 沙箱规则:在进程真正访问资源时限制。 启动命令时配置可读写目录、网络范围等,由 OS 沙箱约束命令及其子进程。这样不必提前猜出脚本会打开哪些文件。局限:只覆盖放进沙箱的进程和已配置的资源限制。 宿主上的其他工具不一定受约束;只限制写入也不会阻止读取。

  4. 服务端权限:由被调用的系统检查。 例如数据库只读账号、仅能访问某个仓库的 token。即使 Agent 门禁漏判,外部服务仍可拒绝越权请求。局限:授权范围内的误操作仍可能发生。 有权发邮件,不代表这封邮件符合用户意图。

例如,要实现“禁止读取私钥”:文件工具可以复用敏感路径规则;Bash 的文件访问需要由沙箱限制。仅在 Bash 命令字符串里匹配私钥路径不够,因为脚本可以拼接路径,或通过其他程序读取。规则可以统一定义,但必须在相应执行位置落实,不能只写一份配置就认为所有工具都受保护。

命令规则仍然有用,例如拒绝常见的 force push 命令、要求发布前确认;但简单匹配 rm、curl 不能覆盖脚本和解释器中的同类操作。只设置工作目录,也不等于把文件访问限制在这个目录。

产品的组合不同:Claude Code 提供 allow / ask / deny 规则;Codex 结合命令策略、权限配置和沙箱;Pi 的 tool_call 扩展事件适合实现调用前检查,但不会自动产生 OS 隔离。[1][2][5]

四、需要批准时:问人、帮我批准,还是直接放开#

人工批准:HITL#

HITL(Human in the Loop)就是在操作执行前暂停,把决定权交给人。不是执行后再展示一条通知。

“所有有副作用的工具都询问”是简单实现,但实际产品通常更细:工作区编辑可能已经获得授权,不必次次问;读取敏感数据虽然不修改文件,也可能需要确认。是否弹窗取决于具体操作、目标、规则和已有授权,不只是工具是否会写。

一次确认通常提供:

“始终允许”必须看清允许什么:一个命令前缀、一个目录、一个域名,还是一个工具?有效期是当前会话还是未来会话?各产品并不相同,也不是每种操作都支持持久授权。

审批界面应显示真实参数和目标。批准“给 A 发这封邮件”不等于批准任意收件人;执行参数超出批准范围时,要重新判断。需要人批准却无人响应时,应保持等待或拒绝,不能把超时当批准。

帮我批准:独立模型审查#

自动审批不是让主 Agent 自问“我这样做安全吗”。常见设计是:把待执行操作交给另一个职责更窄的模型,由宿主根据审查结果决定是否继续。

以 Codex 的 Auto-review 为例:主 Agent 提出需要批准的操作;宿主把精确请求和相关对话、工具结果交给独立 reviewer;reviewer 判断操作风险及用户是否授权,并返回决定和理由。[3]

例如用户只要求修复代码,安装项目依赖可能与任务有关,但探测浏览器 cookie、上传私钥、永久关闭安全设置就不应因此获得批准。审查要看操作本身及上下文,而不能只相信主 Agent 写的“为了完成任务”。

Codex 这套机制只接管符合条件的审批请求,不逐条审查已经允许的普通操作,也不直接取消原有沙箱。Claude Code 的 auto 模式同样使用分类器审查动作,但具体覆盖范围和失败处理不同。[2][3]

自动审查仍可能误判,也可能受不可信内容影响。 它适合减少人工确认,不应取代服务端授权和执行隔离。审查失败时,系统应明确等待、拒绝或转人工,而不是无条件放行。

YOLO / Full Access:必须看产品定义#

YOLO 不是统一的权限标准,不能理解成“只是少弹几个窗”。

因此要分别问:还检查哪些规则?还需要哪些批准?执行时还有什么隔离? 不询问也可以保留沙箱;关闭产品内置沙箱后,也可能还有外层容器或 VM。不能只凭模式名称判断安全性。

五、沙箱:先确认隔离的是哪段代码#

“代码在沙箱里运行”还不够准确。QuickJS 隔离的是一段脚本,OS 沙箱限制的是进程,VM 则提供独立的操作系统环境。它们可以组合使用。

QuickJS:脚本只能通过宿主提供的接口做事#

Pi 的 codemode 把模型生成的 JavaScript 放进 QuickJS 运行。这里不提供 Node.js 的文件、网络和子进程 API;脚本主要负责循环、判断、并行调用和结果整理,通过宿主暴露的 tools 等接口访问外部能力。[7]

关键不是“用了 QuickJS 所以安全”,而是脚本没有直接访问宿主系统的能力,只能使用宿主明确开放的接口。运行时还可以配合内存限制和执行期限,约束脚本资源消耗。

但脚本调用 tools.bash 后,真正的 Bash 由宿主工具执行,不会自动进入 QuickJS 的隔离范围。如果这个 Bash 没有 OS 沙箱,它仍然使用宿主权限。工具权限检查因此必须覆盖 codemode 中的嵌套调用。

另外,脚本失败不等于回滚:它前面已经调用工具写入的文件,不会因为后续 JavaScript 报错而自动恢复。

OS 沙箱:限制命令及其子进程的实际访问#

OS 沙箱在启动命令时施加限制,由操作系统约束文件访问、网络和部分系统功能。命令内部再启动解释器或其他进程,也需要受相应边界约束,而不是只检查最外面的一行命令。访问被拒绝时,通常表现为程序报错、请求被拒或超时,再由工具把执行结果反馈给 Agent。

典型限制包括:只允许写项目目录、不允许读密钥目录、禁止直连网络,或强制经受控代理访问允许的目的地。

只读不等于保密。 工作区外禁止写入,不代表工作区外也禁止读取;如果要保护私钥,必须明确限制读取。网络允许访问某域名,也不代表允许向该域名下的任意账号上传数据。

Codex 和 Claude Code 的本地沙箱都使用平台相关机制,例如 macOS Seatbelt、Linux 上的 namespace / bubblewrap 等。这里不必记底层配置,重点是:访问限制由操作系统执行,不依赖模型是否听话。[1][8]

容器、VM 与 Agent 云电脑#

容器提供隔离的文件和进程环境,通常仍共享宿主内核;VM / MicroVM 有独立的客体内核,可以承载完整的系统、浏览器和开发工具。云电脑常用后一类环境,让 Agent 在远端操作,而不是直接控制你的本机。

接入主要有两种方式:整个 Agent 放进去运行,或者 Agent 留在外面,只把命令和文件操作交给隔离环境。后一种就是 sandbox as a tool,也适合临时准备不同语言环境,不只是为了安全。

只隔离部分工具时,宿主上的扩展、浏览器或其他 MCP 工具未必受同一个沙箱约束。Pi 官方也明确区分“整个进程隔离”和“仅工具隔离”。[9]

云 VM 没有本机目录挂载或连接通道时,通常不能直接访问你的本机文件。但如果给它生产 token、登录态或可写挂载,它仍能损害这些资源。隔离本机,不等于隔离你的所有账号和数据。

远端生成的文件首先保存在远端环境;要长期保留,需要持久卷、快照或导出。销毁环境也不能撤销已经发出的邮件、支付或代码推送。

六、防御 Prompt injection:具体可以做什么#

Prompt injection 可以来自用户输入,也可以藏在网页、issue、文件和工具结果里。后者会诱导 Agent 把本来应该读取的资料,当成新的操作指令。防御可以按下面这些做法落实。[10]

防止注入本身:输入端

  1. 少给凭据,并缩小权限。 按任务提供受限、短期凭据,尽量不让原始密钥进入模型上下文或执行脚本。Agent 即使运行在 VM 中,拿到生产 token 后仍可能影响生产系统。
  2. 在进入模型前增加检测。 对用户输入、检索内容和工具结果,用规则或专门的检测模型识别可疑注入,发现风险后拒绝、隔离或转交审查。不能只检查最初的用户 prompt,也不能认为检测通过就一定安全:规则会漏掉变体,模型也会误判。
  3. 把外部内容标为数据,不混成可信指令。

保证注入后的安全:和第一点中的内容一致

  1. 从工具设计上减少能力。 按任务提供工具,只读任务不开放任意 Bash;优先提供查询、生成草稿等窄接口,而不是任意数据库操作、直接发送等宽接口。模型即使被骗,也不应凭空获得任务不需要的能力。
  2. 对高风险工具操作增加审批。 发邮件、上传文件、发布等操作,可交给人工或独立审查模型批准。检查实际参数、目标和用户授权,而不是只看主 Agent 的解释;批准一次,不等于放开整个工具。审批也可能误判,因此不能替代资源限制。
  3. 让执行边界在模型被骗后仍然有效。 用第三节的通用规则、工具内部校验、沙箱和服务端权限,落实“哪些文件不能读、哪些地址不能访问、哪些操作不能做”。例如 issue 诱导读取并上传私钥,应该由读取限制和外发限制阻止,而不只靠模型拒绝。敏感信息检查也要覆盖外发工具参数,不能只检查最终聊天回复。
  4. 保留日志记录,准备恢复手段。 记录脱敏后的调用、批准范围和结果;重要修改使用版本控制或快照。日志便于追查,但不能阻止已经发生的外泄,快照也不能撤回已发出的邮件。

Guardrails 是这些防护措施的统称,不是另外一个万能模块。 输入/输出检测、工具调用规则都可以称作 guardrails;设计时要说清具体由谁检查、在哪里阻断,以及漏判后还有哪些限制。

Sources#

产品行为会随版本变化;以下用于核对机制,不把某一家产品的模式名称当成通用标准。Pi 部分按本机 1.3.0 文档核对。

  1. Codex:Sandboxing
  2. Claude Code:Permission modes
  3. Codex:Auto-review
  4. Pi:Security
  5. Pi:Extensions
  6. Codex:CLI reference
  7. Pi:Codemode
  8. Claude Code:Sandboxing
  9. Pi:Containerization
  10. OWASP:Prompt Injection Prevention