权限、Sandbox、Session 与恢复不能压成一个开关¶
上一篇:Agent Loop、工具与执行 · 返回课程总目录 · 下一篇:编排、协议与产品表面
当你问「这个 Agent 有权限吗」,至少要分开问五件事:模型看不看得到这个工具,策略放不放行,用户批不批准,执行环境做不做得到,以及中断后系统能不能安全接着做。这篇把六套 Harness 摆在同一条动作链上,看清界面显示 Allow 时,为什么不能证明操作系统已把动作隔离起来,更不能承诺随时撤销副作用。
五层边界¶
模型可见性:本轮是否提供这个 Tool Schema
↓
参数与策略:名称、参数、路径或命令是否符合规则
↓
用户决定:是否需要一次性或持续批准
↓
执行能力:宿主、容器、Sandbox 或远程环境实际允许什么
↓
恢复语义:中断后能否确认副作用并安全继续
前四层一起决定动作能不能发生,第五层要处理的是另一件事:动作做到一半,副作用还不知道有没有发生,系统该怎样诚实地收拾现场。少任何一层,出错的方式都不一样。
六条课程各自暴露了什么¶
| 课程 | 应用层判断 | 执行或隔离边界 | 状态与恢复入口 | 深入阅读 |
|---|---|---|---|---|
| DeepSeek Harness | 工具、安全策略与 Code Mode 配置 | 普通工具与代码执行路径需要分别说明真实宿主 | Session 与 Compaction | 工具、权限与 Code Mode |
| Codex | Exec Policy、审批和具体工具策略 | OS Sandbox/执行环境与审批分开 | Rollout、Thread Store、压缩与恢复 | 执行策略、审批与 Sandbox |
| Gemini CLI | Policy、Confirmation 与 Safety | Sandbox 是独立执行边界 | Session、历史与压缩 | Confirmation、Policy、Safety 与 Sandbox |
| Claude | SDK Options、can_use_tool 与 Hooks 的公开契约 |
实际 CLI/产品执行边界不能由公开 SDK 全部证明 | Session/Resume/Store 的公开接口 | 动态权限决策 |
| pi | 工具 Hook、项目 Trust 与交互表面 | 默认继承宿主权限;Sandbox Extension 可能回退本地 | Session Tree、JSONL 与 Resume | CLI、TUI、权限与外部隔离 |
| OpenCode | Tool Registry、Permission Rules 与 Question | Permission 是应用规则,不自动构成 OS Sandbox | Storage、Compaction、Patch/Revert | Tools、Permission、Question 与 Patch |
看到「项目提供 Sandbox 选项」时,先别认定所有工具、Extension、Plugin 和外部进程都会经过它。你得顺着每个具体工具的执行器往下追,再查它失败时会不会回退,才能下这个结论。先顺着路径查到底。
运费任务里的三个动作风险不同¶
| 动作 | 常见策略 | 仍需注意的环境事实 |
|---|---|---|
读取 shipping.ts |
工作区内可自动允许 | 符号链接、外部目录和敏感文件 |
| 编辑比较符 | 展示 Diff 或请求批准 | 实际写入范围、并发修改、编码与回退 |
| 运行测试 | 按命令和工作目录判断 | 子进程、网络、环境变量、后台任务和超时 |
系统不必对每个动作都弹出同样的确认框,因为它可以按动作会碰哪些资源、影响多大范围、能不能撤销来分级。但动作就算已经获批,也应放进一个只拥有必要能力的环境里运行。批准不等于放开限制。
Allow、Ask、Deny 只是产品策略¶
应用层的 Permission 通常先读取工具名和已经规范化的 Pattern,然后回答 Allow、Ask 或 Deny。OpenCode 会按顺序检查规则,以最后命中的一条为准,Gemini CLI 让 Policy 和 Confirmation 跟着工具从提出调用走到结算,Codex 则让 Exec Policy、审批和 Sandbox 各管一层。Claude SDK 虽然公开了 can_use_tool 回调,我们仍然不能用它证明闭源产品内部的所有决策。
用户选择 Always 时,通常只是让当前应用规则多放行一些动作,它不会跟着改写操作系统 ACL。反过来也一样:OS 能执行某个动作,产品不一定就该放行。宿主用户可能有权访问整个磁盘,Harness 却仍然要把 Agent 限制在当前工作区。
Sandbox 约束能力,不解释意图¶
Sandbox 可以限制路径、进程、网络和系统调用,却不懂「修改测试来让测试通过」违反了任务。Policy 要读懂任务,并在动作发生前拦住这类行为。独立 Eval 只能在运行后判断任务有没有违规,Sandbox 则在动作获准后继续守住已经配好的资源边界。三层不能混用。
检查真实隔离至少要问:
- 哪些工具被重定向到隔离环境;
- 工作区以只读还是读写方式挂载;
- 网络默认允许、拒绝还是经网关;
- 凭据是否进入环境;
- 初始化失败时失败关闭还是回退宿主;
- Plugin/Extension 是否能直接使用宿主 API 绕过工具执行器。
pi 的示例 Sandbox Extension 在隔离失败后可以回退到本地,所以只看到「代码库里有 Sandbox」,还不能证明所有动作默认都在隔离环境里运行。
Session 保存历史,不自动保存环境¶
Session 通常能把消息、工具结果、模型选择、分支和摘要重新读回来,但它无法让已经退出的进程回到原状,也无法复原远端事务或抹掉工作区后来发生的变化。所以 Resume 之前,你得重新查看环境:
重放消息和重做副作用是两件事。如果编辑工具已经把内容写进文件,却在记下「完成」之前崩溃,Session 只能说它的状态未知。恢复时应该先读文件,确认 Patch 到底有没有应用,不能直接再做一遍。
Compaction 会让恢复变得更难¶
摘要可能写着「代码已经修复」,却漏了「测试尚未运行」,所以必须让人能从模型当前看到的 Context,一路追回完整 Session 里的记录和环境当时的状态。Codex Rollout、Gemini CLI Session、DeepSeek Harness Session、pi Entry Tree 和 OpenCode Message/Part 虽然投影历史的方式不同,却都得防止人们把摘要当成精确的执行账本。
恢复时还会用到 Tool Call ID、工具状态、目标路径、命令、退出码、工作区版本和未完成事项,这些内容应该尽量存成结构化数据。自然语言摘要能帮模型继续往下做,却不能证明副作用真的发生过。判定时不能靠它。
Revert 不是通用事务¶
文件 Snapshot 或 Git Patch 可以逆转工作区修改,却通常不能撤销:
- 已发送的网络请求;
- 数据库写入;
- 发布到外部系统的包或消息;
- 工作区外删除;
- 启动后仍在运行的后台进程。
OpenCode 会把 Patch/Revert 和 Session 后缀放在一起处理,但它仍然没有越过上面这些边界。其他 Harness 就算依赖 Git,也得说清哪些状态根本没进版本控制。对高风险工具,应当优先使用幂等键、事务或预演,别等出事后才把希望都寄托给 Revert。
练习:设计一次崩溃恢复¶
场景:Harness 已经请求把 > 改成 >=,但进程随后崩溃,而 Session 里只有 Tool Call 和「开始执行」,没有最终结果。请为两条课程分别写出恢复步骤,并指出应该在哪一层确认用户批准仍然有效。