1. 前言
在上一篇文章中,我们深入剖析了 SubAgent System 的设计——Agent 如何将复杂任务拆解、委托给多个专职子 Agent 并行执行。如果说 Agent Loop 是 Agent 的"心跳",Tool System 是 Agent 的"双手",Context Management 是 Agent 的"记忆系统",SubAgent System 是 Agent 的"团队协作能力",那么 Permission, Approval & Sandbox(权限、审批与沙箱系统)就是 Agent 的"免疫系统"——它决定了 Agent 在拥有强大执行能力的同时,是否会对宿主环境造成不可逆的破坏。
这其实是一个在Agent规模化之后出现的工程难题,在 Demo 阶段,让 Agent “能跑起来"容易,但当 Agent 真正接入开发者的生产环境——能读写文件、执行 shell 命令、访问网络、修改 Git 历史——安全就不再是"锦上添花”,而是"生死攸关"。
Anthropic 曾经内部维护了一个事故日志,专门记录 Agent 行为失控的案例1:Agent 因误解指令而删除了远程 Git 分支;Agent 把工程师的 GitHub 认证 token 上传到了内部计算集群;Agent 试图对生产数据库执行迁移操作。这些事故的共同特征不是"模型意图作恶",而是模型过度热心(overeager)——它以用户没有预料到的方式主动采取了行动。
更隐蔽的威胁来自 prompt injection:攻击者在网页、文件或工具输出中植入指令,劫持 Agent 偏离用户任务、转向攻击者的目标。2025 年 10 月,Anthropic 发表的 Claude Code 沙箱技术博客开门见山地指出:即使是一次成功的 prompt injection,如果发生在 sandbox 之内,攻击者也什么都拿不到2。
这正是安全系统设计的核心能力:如何让 Agent 拥有足够的自主权以提高效率,同时确保每一点自主权都在可控的边界内? 如果每一条命令都要用户点"批准",开发体验会崩溃(Claude Code 用户在默认模式下批准了 93% 的权限弹窗1)。但如果把所有权限都放开,安全风险不可接受。好的安全系统,就是在这条光谱上找到最优平衡点。
本文是 Agent 实现原理系列的第五篇,继续参考 Codex CLI、Claude Code、nanobot、pi 四个主流开源项目的实现,同时融入 Anthropic 关于 Sandbox2 和 Auto Mode1 的两篇工程实践文章,从工程角度总结 Agent 安全系统的设计原则和关键权衡。
2. 安全系统的本质
2.1 从"相信模型"到"分层防御"
一个最朴素的 Agent 安全方案,可以写成这样:
# 在 system prompt 里加一句话
"不要删除重要文件,不要访问敏感目录,不要泄露用户信息。"
这确实会有效果,但距离"能在真实产品中安全运行的 Agent"还有巨大差距。原因很简单:prompt 是软约束,模型可能不遵守(或因 prompt injection 被覆盖);代码是硬约束,可以在执行前阻断。 安全约束应该尽量下沉到代码层,而不是依赖 prompt 说教。
更深一层看,Agent 的安全威胁模型包含四种不同的根因1:
- 过度热心(Overeager behavior):Agent 理解用户目标并真心想帮忙,但主动做了用户没打算让它做的事(比如顺手用了它碰巧发现的 credential)
- 诚实错误(Honest mistakes):Agent 误判了影响范围(比如以为某个资源是测试环境的,实际是共享的)
- Prompt injection:攻击者在文件、网页或工具输出中植入指令,劫持 Agent 偏离用户任务
- 模型对齐问题(Misaligned model):Agent 追求自己的目标而非用户的目标(目前在实践中的未见发生)
这四种根因的性质不同,不能用同一种机制防御。好的安全系统不是单点检查,而是多层叠加——每一层解决不同的问题,任何一层都可以阻断危险动作。
2.2 安全系统的通用分层模型
综合当前业界进展,Agent 安全系统可以抽象为四层防御架构:
flowchart TD
A["模型决定调用工具"] --> B["第1层:Tool级权限<br/>工具是否可见?参数是否合法?"]
B --> C["第2层:规则级权限<br/>是否命中 allow / deny / ask 规则?"]
C --> D["第3层:交互式审批<br/>ask → 谁来决定?用户?分类器?Guardian?"]
D --> E["第4层:OS级Sandbox<br/>进程实际能碰哪些文件、哪些网络?"]
E --> F["执行并回写审计"]
这四层关系其实层层收紧:
- 第1层(Tool 面) 决定"模型能不能看到并尝试调用这个能力"
- 第2层(规则面) 决定"即使看到了,运行前是否要被规则拦住"
- 第3层(审批面) 决定"拦住之后是直接 deny,还是 ask 某人/某系统"
- 第4层(Sandbox 面) 决定"就算前几层都放行,进程实际还能碰到哪些系统资源"
Anthropic 强调过一个关键原则:有效的 sandbox 必须同时具备文件系统隔离和网络隔离2。没有网络隔离,被攻破的 Agent 能把你的 SSH key 发送到攻击者的服务器;没有文件系统隔离,被攻破的 Agent 能轻松逃逸 sandbox 并重新获得网络访问。两者缺一不可。
这个四层模型的工程价值可以概括为一句话:
它把"模型会不会乱来"这个不可控问题,拆成了多个可观测、可配置、可中断、可隔离的工程控制点。
3. 主流项目的安全系统设计
3.1 Codex CLI3:双层状态机 + Guardian 自动审查 + Sandbox 优先执行
Codex CLI 的安全系统非常完善,它的核心设计可以概括为:规则裁决和交互审批是两层独立状态机,sandbox 优先扛第一道防线,Guardian 可替代人工做自动审查。
双层状态机:把"能不能申请"和"申请结果是什么"分开。 Codex 的审批流不是单层判断,而是两段独立状态机。第一段是规则裁决,产出三态结果——可直接执行、需要审批、彻底禁止。第二段是交互/审查结果——批准、批准并修改策略、会话级批准、拒绝、中止。两段分离意味着:规则层可以决定"这个动作连申请的资格都没有",而不只是"申请了但被拒"。
审批策略是"策略 × 环境"的乘积,不是静态配置。 Codex 支持五种策略风格:除非受信否则先问、只在 sandbox 挡住时才问、按需申请、精细控制每种审批、永不审批。关键设计是:ask/no-ask 不只取决于策略名,还取决于当前是否真的跑在受限 sandbox 内。同一个动作在 unrestricted 环境和 restricted sandbox 下的审批行为完全不同。
Guardian:锁死环境里的自动审查者。 Codex 最核心的"自动审批"实现,是把审批的审查者从"用户"切换为"Guardian 子 Agent"。Guardian 不是简单地替用户点"允许",而是重建压缩上下文、在锁死环境(只读 sandbox、自身不允许再申请审批、关闭非必要能力)中做风险评估。关键安全属性是 fail-closed:超时、解析失败、review session 执行失败,一律按高风险拒绝。
Sandbox 优先执行:先跑,被挡住了再问。 Codex 的 sandbox 策略有一个核心设计:每次工具调用先在最小权限环境尝试执行——大量动作不需要先问用户,sandbox 直接兜底。只有 sandbox 真正挡住了,才走审批升级。文件系统和网络策略被显式拆成两个独立维度管理,不再绑成一根开关。workspace 写入模式下,Agent 能改工作区代码,但不能碰 .codex、.git、.git/hooks 等控制面路径——“可工作,但不能影响后续执行权限”。
自动审批是不重叠的三类机制。 用户感知的"替我审批",实际是三个独立机制叠加:Guardian 自动审查、已批准同类请求的会话级缓存复用、某些 app tool 的默认免审批。不存在"全部静默全通过"的总开关——只有"哪些审批面可以被自动化接管"。
3.2 Claude Code4:四层叠加 + 多路竞争审批 + OS 级 Sandbox 硬隔离
Claude Code 的安全系统是四个项目中最复杂也最完整的,核心设计可概括为:把模型决策、用户规则、交互审批、系统隔离拆成独立层级,任何一层都可以阻断危险动作。
四层架构与判定哲学。 四层分别是:
- Tool 级: 每个工具自带路径级、命令级安全检查
- 规则级: 内容匹配的 allow/deny/ask,不只是管工具名,还管
Bash(git commit:*)这样的内容子集 - 交互审批级: ask 后不一定直接弹 UI,可能先走 hook、classifier、bridge、swarm leader
- OS sandbox 级: 前几层都放行,进程仍受文件系统和网络的硬限制
判定顺序的核心原则是:规则和安全检查先决定"能不能绕过",模式和 UI 只决定"怎么处理 ask"。 敏感路径即使在 bypass 模式下仍强制 ask,安全边界由规则定义,不得被模式开关悄悄绕开。
多路竞争审批。 这是 Claude Code 最独特的设计。当一个 ask 进入审批流程,系统同时等多路来源——本地 UI、Hook、后台 classifier、远端 bridge——谁先返回有效决定谁就赢,其余自动取消。主会话走多路竞争,后台 worker 先让 hook 和 classifier 消化、不行再惊动用户,swarm worker 没有本地裁决权只能向 leader 申请,无 UI 的 headless 模式则保守地 auto-deny。同一个 ask,不同场景不同策略。
“Yes, and don’t ask again” 的即时生效。 用户批准后,规则持久化 → 内存权限上下文刷新 → 当前队列里所有同类待决请求当场自动放行。权限更新不是只影响"下一次",而是立即辐射到"当前队列"。
Hook 作为权限策略的动态控制面。 Hook 是权限管线的一级决策者——可直接返回 allow/deny、修改工具输入、甚至产出新权限规则并持久化。Hook 本身也受安全约束,防止变成"高权限外发通道"。
Auto mode:三层自动化 + 两阶段 Classifier。 不是"全自动",而是递进三层:确定性快路径(安全编辑类操作直接放行)→ safe-tool allowlist → classifier。Classifier 刻意不看 assistant 思考和工具执行结果,既防 agent 说服 classifier,也防 prompt injection payload 通过 tool output 传播1。此外,Auto Mode 在输入侧还有一层 prompt-injection probe:tool output 入上下文前扫描是否含攻击载荷,有则附加警告标记。
Sandbox:OS 级硬隔离 + 与权限的协同。 基于 OS 级原语(Linux bubblewrap、macOS seatbelt)实现。文件隔离策略是"workspace 可工作,碰不到控制平面"——禁写 settings、skills 目录等。网络隔离支持域名白名单,还可由组织策略锁死。Sandbox 有独立审批管线,网络请求按 host 归并共享批准结果。最重要的设计洞察是 sandbox 和权限审批的协同:一旦 OS sandbox 圈住了副作用,大量低风险动作不需逐条手点。Anthropic 内部数据显示 sandbox 安全地减少了 84% 的权限提示2。
3.3 pi5:Extension 组合式安全 + 进程级 Sandbox
pi 的安全系统与前面三个项目有本质不同:没有一个内建的、中心化的 permission daemon。 内核保持最小化,暴露稳定的 tool/hook/UI/runtime 扩展点,由上层 extension 组合出权限模型、审批流和 sandbox。具体有四层安全,通过 extension 组合。
- 第一层,tool 暴露控制:白名单/黑名单做总开关过滤,extension 还可用同名 tool 覆盖 built-in tool(阻止读敏感路径、注入审计日志等)
- 第二层,前置裁决:handler 可修改输入、返回阻断,实现 rewrite/route/restrict/ask-before-run。这层是 fail-safe 的:任一 handler 返回 block 立即终止
- 第三层,交互式审批:不是内建审批器,而是 extension 通过统一 UI 抽象来弹确认框。这套抽象在 TUI 和 RPC 下都能工作,无 UI 时默认保守拒绝
- 第四层,OS sandbox:通过 extension 覆盖 bash 执行后端、配置文件和网络白名单来实现。
设计哲学:灵活性与责任的 trade-off。 pi 的 plan-mode extension 是"只读安全模式"的好例子——收窄工具集到只读、通过 destroy pattern + safe pattern 双重判断做命令过滤,只允许一个很窄的已知安全子集。Sandbox 配置支持 default → global → project 三层覆盖,不同仓库可有不同隔离策略。但这一切的安全保障依赖 extension 本身的质量——没有内建权威审批中心。
审批时机的设计取舍。 pi 的三阶段执行(prepare → execute → finalize)天然决定了 ask 只能在 prepare 阶段发生,而 prepare 是顺序执行的。这意味着多个危险 tool call 不会同时弹审批框——被天然串行化,但也意味着不能做并发审批。
3.4 nanobot6:五层静态防护 + 学术级克制
nanobot 的安全系统是四个项目中最简单的。目前仍处于 allow / deny 二态阶段,尚未形成交互式审批系统。
五层静态防护。 最外层是 channel 入口权限——在消息进入 agent loop 之前就做 allowlist 过滤。第二层是 group policy / DM policy,决定"什么场景下允许交互"。第三层是 tool 注册门控——不安全或敏感的工具根本不被注册。第四层是 tool 内部 guard,每个高风险工具自带最小安全内核(路径越界检查、危险命令拦截、SSRF 防护)。第五层是 provider-safe 请求清洗,防止内部元数据外泄。
Sandbox:应用层路径隔离。 nanobot 的隔离基于应用层路径边界检查而非 OS 级容器,目前还没用sandbox。
3.5 四项目安全系统横向对比
| 维度 | Codex CLI | Claude Code | nanobot | pi |
|---|---|---|---|---|
| 权限模型 | 双层状态机(规则裁决 + 交互结果) | 四层串联(tool → 规则 → 审批 → sandbox) | 五层静态防护(仅 allow/deny) | Extension 组合式(无内建中心) |
| 审批三态 | ✅ 规则三态 + 交互五态 | ✅ 多路竞争 resolve | ❌ 仅二态 | ✅ 通过 UI 抽象实现 |
| 自动审批 | Guardian 子 Agent 自动审查 + 缓存复用 | 两阶段 Classifier + 三层递进自动化 | ❌ 无 | ❌ 无内建 |
| 审批委派 | ✅ 父 Agent / Guardian 截获 | ✅ swarm leader 仲裁 | ❌ 无 | ❌ 无内建 |
| Sandbox 类型 | OS 级,文件/网络独立管理 | OS 级,独立审批管线 | 应用层路径检查 | Extension 层 OS 级 |
| 文件隔离 | workspace 可写 + 控制面路径保护 | workspace 可写 + 敏感路径强封锁 | 路径越界检查 | 读写白名单/黑名单 |
| 网络隔离 | 域名 allow/deny + unix socket 控制 | 域名白名单 + 可组织策略锁死 | SSRF 防护 | 域名白名单 |
| Hook 介入 | 可阻断 tool 调用 | 一级决策者(直接返回 allow/deny) | 仅观察 | 可修改输入 + 阻断 |
| Prompt injection | Guardian 锁死环境防御 | Input probe + Classifier 去 assistant 文本 | 无专门机制 | 无专门机制 |
| 整体风格 | 平台级,Sandbox 优先 | 生产系统,多路竞争 | 学术克制,缺交互审批 | 极简内核 + 自由组合 |
4. 关键设计维度
从上述项目中可以总结出,Agent 安全系统的八个关键设计维度。
4.1 权限三态机:allow / deny / ask
权限判定的最终结果不应该是布尔值,而应该是三态:allow(放行)、deny(拒绝)、ask(需要外部审批)。
三态机的关键是:ask 不是"系统不知道怎么办",而是"系统知道这个动作需要授权,现在正在收集授权"。deny 表示明确拒绝,agent 可以尝试别的方案;ask 表示暂停等待授权。此外还需要 Abort——表示当前 turn 应停住,等待新的用户输入。生产系统中必须区分 Denied(拒绝,可重试其他方案)和 Abort(中止,等新指令),否则一旦 UI 关闭或连接中断,就会被误当成"安全拒绝"。
4.2 多层权限架构
真实世界的安全问题需要分别回答:
- 这个 tool 能不能申请审批?(tool 级)
- 这条命令是否命中规则?(规则级)
- 当前 sandbox 是否已经足够安全?(环境级)
- 这次越权是用户批,还是 classifier 批,还是父 agent 批?(审批面)
单开关模型回答不了这些问题。四个项目的共识是:规则和安全检查先决定"能不能绕过",模式和 UI 只决定"怎么处理 ask"。 这个优先级很关键——它意味着安全边界由规则定义,不会被模式选择(如"全自动模式")悄悄绕开。
所有项目都有一个关键设计:bypass/skip 模式不是"彻底无权限",而是只绕过普通 ask,不绕过显式安全边界。 Claude Code 对敏感路径(.git/、.claude/、shell 配置等)即使在 bypass 模式下仍然强制回到 ask。Codex 则阻止把 python、bash、sudo 等过宽前缀加入永久白名单。
4.3 规则引擎:从工具名到内容级匹配
内容级匹配是指类似这样的匹配:Bash(git commit:*)、Read(/some/path/**)、Edit(/dangerous/path/**)、WebFetch(domain:example.com)、Agent(code-reviewer)。
Claude Code 的规则来源最丰富:本地设置、项目设置、用户设置、策略文件、CLI 参数、session 规则等汇聚成三类规则(始终允许、始终拒绝、始终询问)。Codex 的规则系统与之类似,但额外引入了解析级的命令判断——不是字符串匹配,而是先 parse shell 语法再投影到权限规则上。
此外,主流项目将规则应该在代码层表达,而不是在 prompt 里描述。prompt 里的"不要删除重要文件"是软建议,代码里的 denylist 是硬阻断。而且规则的执行语义应该是 fail-safe 的:权限模块挂了默认变成 deny,而不是 silently allow。
4.4 审批决策流:ask 之后由谁来拍板
四个项目在ask之后审批环节差异很大:
| 决策来源 | Codex | Claude Code | nanobot | pi |
|---|---|---|---|---|
| 本地用户 UI | ✅ | ✅ | ❌ | ✅ (通过 ctx.ui) |
| 远程 bridge/HTTP | ❌ | ✅ (REPL bridge) | ❌ | ✅ (RPC) |
| Classifier 自动 | ✅ (Guardian) | ✅ (两阶段) | ❌ | ❌ |
| Hook 返回 | ❌ | ✅ (PermissionRequest) | ❌ | ❌ |
| 父 Agent 代答 | ✅ (delegate) | ✅ (swarm leader) | ❌ | ❌ |
| 规则变更后自动转 allow | ❌ | ✅ (recheck) | ❌ | ❌ |
4.5 自动审批与 Classifier:自动化不是裸奔
Claude Code 和 Codex 通过 classifier prompt template、两阶段推理、熔断机制、fail-closed 兜底等机制实现自动审批,nanobot 和 pi 没有内建自动审批机制。
一个成熟的自动审批系统有几个关键设计:
先确定性后模型化。 便宜的确定性快路径(安全编辑直接放行、已知安全工具白名单)消化掉绝大多数动作,只把剩余高风险动作交给 classifier。Classifier 采用两阶段设计——Stage 1 单 token 快速过滤,Stage 2 只对被 flag 的动作做 chain-of-thought 推理,将误判率从 8.5% 降到 0.4%。
Classifier 有硬边界且不窥探 agent 的思考。 去掉了 assistant 思考文本和 tool 执行结果——既防 agent 说服 classifier,也防 prompt injection payload 通过 tool output 传播1。同时设有熔断保护:transcript 过长则 fallback,连续 deny 超阈值退回人工,headless 模式下超限直接 abort。
自动审批不是无限制的。 Guardian 只接管原本就可走审批流的请求,规则层已 Forbidden 的不会进入。被阻断时 agent 尝试替代方案(deny-and-continue),但 3 次连续 denial 或 20 次总 denial 后升级为人工1。
4.6 Sandbox:OS 级硬隔离
如果说权限和审批解决的是"逻辑上不该做什么",Sandbox 解决的是 “就算前几层都失效,进程还能实际碰到哪些系统资源”。
文件系统和网络必须分开管。 Demo 阶段往往只有一个 sandbox_mode 开关,但生产系统必须把读写范围和联网范围拆成两个独立维度,因为两者缺一不可2——没有网络隔离,被攻破的 Agent 能外泄 SSH key;没有文件隔离,被攻破的 Agent 能逃逸 sandbox 重获网络访问。
文件隔离的核心是"可工作,但碰不到控制平面"。 Agent 能改项目源码,但不能碰 .git/hooks、.claude/settings、~/.ssh、~/.aws 等会影响后续执行权限或泄露凭据的路径。最终可动范围收敛为:当前项目源码 + /tmp。
**网络隔离追求的是细粒度能力面:**域名白名单、unix socket、本地端口绑定、上游代理等各自独立控制,还可以由组织策略锁死。
Sandbox 和权限审批形成协同。 一旦 OS sandbox 圈住了副作用,大量低风险动作就不需要逐条审批——Anthropic 数据显示 sandbox 安全地减少了 84% 的权限提示2。安全边界从"逐个审批"前移为"预定义的安全域"。
4.7 多 Agent 下的权限委派
当系统有子 Agent 时,所有项目的共识是:子 Agent 不应该自己弹权限框。 审批主权属于父 Agent 或统一审批中心——worker 发起,leader 仲裁,worker 执行。Codex 的实现最完整:父 Agent delegate 拦截子 Agent 的审批请求,交给 parent 或 guardian 审批后回注结果。Claude Code 的 swarm worker 通过 mailbox 向 leader 申请裁决。pi 和 nanobot 因本身子 Agent 较轻量,未设专门委派机制。
4.8 Hook:从旁路到控制面
Hook 不是安全系统的必选项,但它让安全策略可插拔、可演进。四个项目中 Claude Code 的 hook 体系最完备——可直接返回 allow/deny、修改工具输入、持久化新权限规则,且 hook 本身也受 sandbox 约束以防变成高权限外发通道。pi 则区分了前置 veto gate(遇 block 即停)和后置 patch pipeline(审计注入、输出脱敏)两种语义。两者的共同方向是:让 hook 从旁观者升级为权限管线的一级决策者。
5. 设计共识
四个项目的实践共同指向了同一条设计主线:
把"模型会不会乱来"这个不可控问题,拆成多个可观测、可配置、可中断、可隔离的工程控制点。
具体而言:
- 安全采用多层叠加(Tool → 规则 → 审批 → Sandbox),每层独立阻断;
- 权限判定是三态而非布尔值(allow/deny/ask,且 deny 与 Abort 也需区分)
- 规则引擎做到内容级匹配而非仅工具名;
- Sandbox 文件与网络必须独立管理,协同审批一起把安全边界从"逐个审批"前移为"预定义的安全域"
- 自动审批有明确边界和熔断保护,不窥探 agent 的思考过程
- 子 Agent 是执行主体而非审批主体,审批主权上收到父级
- Hook 从旁观者升级为策略控制面的一级决策者
- 任何无法交互的场景都默认保守,无法弹窗就 auto-deny。
6. 总结
Permission, Approval & Sandbox 是 Agent 从"demo"迈向"可生产部署"的关键门槛。它的核心原则可以浓缩为一句话:调用前做逻辑审批,执行时做系统隔离,执行后把经验沉淀回规则层。
Anthropic 的工程实践12提出了两个关键结论:Sandbox 让安全从"逐条审批"变成"预定义安全域"(减少 84% 权限提示),而自动审批只有加上两阶段推理、熔断保护和严格的输入裁剪,才从"盲放"变成可信任的自动化。
安全系统不会让 Agent 更聪明,但它决定了 Agent 能不能被真正部署。好的安全系统不指望模型"小心一点",而是把每一层都设计成可独立阻断的工程控制点。
下一篇,我们将深入探讨 Agent 的 可观测性与运维(Observability & Operations)——Agent 在生产环境中如何被监控、追踪、调试和治理。
参考文献
John Hughes, “How we built Claude Code auto mode: a safer way to skip permissions,” Anthropic Engineering Blog, Mar 2026, https://www.anthropic.com/engineering/claude-code-auto-mode ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
David Dworken and Oliver Weller-Davies, “Beyond permission prompts: making Claude Code more secure and autonomous,” Anthropic Engineering Blog, Oct 2025, https://www.anthropic.com/engineering/claude-code-sandboxing ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
OpenAI, “Codex CLI,” https://github.com/openai/codex ↩︎
Anthropic, “Claude Code,” https://code.claude.com/docs ↩︎
nanobot, https://github.com/HKUDS/nanobot ↩︎