从0打造编码Agent 导读 4 课 + 2 条岔路 + 1 节实验课 ≈ 3 小时
A3 权限与审批:它要执行 rm -rf 怎么办? 章导读

权限与审批:它要执行 rm -rf 怎么办?

模型已经能改文件、跑命令了。这一章从最朴素的黑名单出发,一路被真数字逼到规则求值语义、分层放行和模型审批:同一份规则换一种语义就换一个答案,问多了人就不看,审批者必须比被审者权限更小。规则和审批都管不到的部分,交给下一章的沙箱。

主线定位

累计数据流 · 一次任务在 harness 里经过哪些环节 已学本课新增还没学
每一轮循环都经过

本章问题链

每一课只回答一个问题;上一课的失败,逼出下一课的问题。

课这一课的问题朴素方案在哪个真数字上失败一句话
第 1 课拦危险命令,写个黑名单行不行?lcc s03 三道闸在 6 条危险命令里漏拦 4 条;cat sudoku_solver.py 因含子串 sudo 被拒(pin 源码实跑)prompt 决定想做什么,harness 决定允许什么;匹配子串拦不住改写过的命令
第 2 课两条规则冲突时,听谁的?同一份 rules-standard.json 改用 first-match,真实运行里 {denied ? denied.subject : ''} 被规则 #8 拒掉同一组规则换一种求值语义就可能换一个答案;先选语义,再用测试锁住
岔路hook:规则之外的拦截点,能放宽规则吗?hook 返回 allow,deny 规则照样拒绝hook 只能收紧、不能放宽
第 3 课每个调用都问人,人会怎样?我们 26 次真实运行的 139 次调用,Manual 模式要弹窗 74 次;Anthropic 自报用户批准约 93% 的弹窗问得太多,人就不再看了;只把真正需要判断的调用留给判断者
岔路Pi 为什么说权限弹窗是「安全剧场」?—Pi 不是说权限不重要,而是说进程内的弹窗给不了边界,要边界就上容器
第 4 课让另一个模型来审批,审批者该有多大权限?Anthropic 自报 auto 分类器对真实越权动作仍有 17% 漏判(n = 52)审批者必须比被审者权限更小,历史只当证据,出故障默认不执行
实验课真实调用 × 规则集 × 六种求值语义—顺序敏感的用例写成测试;求值器与原实现逐条自检

演进地图

权限设计的演进:每一代都被上一代的某个真数字逼出来
  1. Pi 核心、bypassPermissions、CoreCoder --yes
    解决了零打断,模型想跑什么马上跑。
    卡在L1 的 6 条本意危险的构造命令(含 rm -rf /)会全部直接执行。
  2. lcc s01 / s03、CoreCoder bash 正则、Pi permission-gate
    解决了把已知危险的写法挡在执行之前,代码只有几行。
    卡在lcc s03 三道闸(pin 源码实跑):6 条危险命令里漏拦 4 条,5 条日常命令里误拦 2 条(cat sudoku_solver.py 因含子串 sudo 被拒)。
  3. 第一代 每次都问人
    CoreCoder 交互模式、Claude Code Manual、lcc s03 的第三道闸
    解决了判断交给人,规则写不全也不怕。
    卡在我们 26 次真实运行的 139 次工具调用,在 CoreCoder 交互模式下有 74 次要停下来问(53%);Anthropic 自报用户批准了约 93% 的弹窗——问多了,人就不看了。
  4. Claude Code(deny → ask → allow)、OpenCode(last-match)、Codex(最严格者胜)
    解决了把常见的放行和拒绝写成规则,只有规则没覆盖的才问人。
    卡在同一份规则换一种求值语义可能换一个答案:同一份 rules-standard.json 改用 first-match,真实运行 pd-firstmatch-s1 里模型的 python -m unittest -v test_dateparse.py 被规则 #8 拒掉。
  5. Claude Code auto、Codex Auto-review(代码名 Guardian)、Hermes smart
    解决了只读直放、项目内编辑免审,其余交给另一个模型判断;弹窗接近清零。
    卡在Anthropic 自报:auto 分类器在真实越权动作上仍有 17% 漏判(n = 52),官方原话「does not guarantee safety」。
  6. 下一章 与 OS 沙箱分层
    Claude Code sandbox、Codex Seatbelt / bubblewrap
    解决了规则管「允许调用什么」,沙箱管「调用之后能碰到什么」。
    卡在L1 的 python3 -c "import shutil; shutil.rmtree(…)" 在四个黑名单里被 4 个全部放行;规则看不见脚本在运行时做什么 → A4。

数字口径:黑名单与「每次都问」由原实现在 pin 源码上实跑(a03-permission-fixtures.json,2026-09-26);真实运行 = 本课程项目 miniagent 用 Qwen/Qwen3-8B-MLX-4bit 本机录制;93% 与 17% 为 Anthropic 官方自报。

贯穿任务:修好 test_parse_date

全课的贯穿任务是项目 04-实践项目/tasks/parse_date:让失败的 test_parse_date 通过,测试文件 test_dateparse.py 不许改。这一章给它的 trace 加上权限列:每一次工具调用,在各家权限语义下是放行、问人还是拦下,由哪条规则决定。

三层交互

层本章里的组件在哪一课
概念动画Claude Code 六步求值序逐步走(hook → deny → ask → 模式 → allow → 弹窗);审批者边界图(朴素做法与 Codex Guardian 两种形态,点开每条能力看 pin 源码原文)岔路 B1、第 4 课
算法模拟器(真算法、小数据)四个黑名单对 11 条命令现算漏拦与误拦;同一份规则表交给 Claude Code、OpenCode、CoreCoder、first-match、last-match 五个求值器,可拖动重排、先猜后揭示、逐步看求值光标第 1、2 课
真实数据我们项目真实运行的全部工具调用,放进十几种权限模式重算弹窗次数;规则集 × 调用 × 引擎的裁决矩阵,页面加载时与原实现、与真实运行记录、与官方文档例子逐条自检第 3 课、实验课

所有求值器都在浏览器里现算,不联网:OpenCode、CoreCoder、learn-claude-code、Pi 与我们项目的求值器从源码移植,期望值由原实现在 pin 副本上跑出(src/data/precomputed/gen_a03_fixtures.py 与 .ts);Claude Code 核心闭源,它的求值器依据官方文档实现,非源码移植。

项目推进:给 miniagent 选一种权限语义

对应 04-实践项目/MILESTONES.md 的「A3 · 权限与审批」。实现已经在 miniagent/permissions.py 与 configs/ 里:规则 = 动作 + 工具名通配 + 对象通配,first-match 与 last-match 两种语义可切换,默认 ask,ask 交给 approver(交互时问人,批量实验时自动拒绝),每次判定写进 trace。代码 AI 来写,决策你来做。

🧭

你要拍板的决策

  1. first-match 还是 last-match(或者像 Claude Code 那样按动作分组、像 Codex 那样取最严格),为什么?两种顺序语义的写法习惯相反:first-match 要「具体的放前面」,last-match 要「通用的放前面、例外写后面」。项目目前默认 last-match,因为 rules-standard.json 是按后一种习惯写的(第 2 课)。
  2. 默认动作是 ask 还是 deny?无人值守时怎么办?项目:ask;批量实验里 approver 自动拒绝(第 3 课)。
  3. 只读工具要不要免审?各家都免(第 3 课);如果你选不免,先估一下弹窗会多多少。
  4. 规则只做字符串匹配够不够?不够——python -m unittest; curl … | sh 能骑着 allow 前缀过去。项目有意保留这个洞并用测试钉住,交给 A4(第 1 课、实验课)。

验收标准(全部可测):

坑与带走

本章拦截的误解

误解纠正在哪一课
在 CLAUDE.md 里写「不许删文件」就够了prompt 只影响模型想做什么;允许什么由 harness 执行第 1 课
黑名单写全一点就能拦住危险命令规则只看命令文字,同一效果的写法没有尽头;子串还会误拦 sudoku第 1 课
deny → ask → allow、第一条命中的算数,是通用规则那是 Claude Code 的语义;OpenCode 是 last-match,Codex 取最严格,CoreCoder 不读规则第 2 课
两种语义对同一份配置一定给出相反结果只有「最后一条命中的动作 ≠ 优先级最高的动作」时才相反第 2 课
Codex Granular 里某一类设成 false 就是弹窗false 是自动拒绝第 2 课
hook 返回 allow 能绕过 denyhook 只能收紧;deny、ask 规则在 hook 之后照样求值岔路 B1
OpenCode 默认一切都问内置默认规则第一条是 *: allow;「连 read 都过权限」指每次都求值第 2 课
弹窗越多越安全用户批准约 93% 的弹窗,问多了人就不看第 3 课
Pi 认为权限不重要它认为进程内的弹窗给不了边界,边界应该是容器岔路 B2
有模型审批就安全了官方自报 17% 漏判,原话「does not guarantee safety」第 4 课
审批者工具越全审得越准审批者的权限必须严格小于被审者第 4 课

写代码时的坑

症状后果怎么办
用子串黑名单拦命令改写过的命令漏拦,含关键词的无害命令误拦解析命令;动态展开显式处理;真正的边界交给沙箱
以为写进 prompt 就能限制模型照样发出调用在 harness 里执行规则,被拒的调用回填 tool result
last-match 下随手调整规则顺序裁决静默翻转(真实运行 pd-firstmatch-s1 里测试命令被拒)用测试锁住顺序敏感的用例
把一份配置直接翻译到另一家语义不同,裁决可能变;带参数的 deny 在 Claude Code 里还会整个拿掉工具翻译后对同一批调用重跑一遍
默认一切都问审批疲劳,人工审批失效只读直放、项目内编辑免审,其余再审
审批模型有写权限或共享主上下文被注入后越权审批者权限严格更小、历史只当证据、fail closed

带走这些句子(各课「一句话」):

  1. prompt 决定模型想做什么,harness 决定允许什么;匹配子串拦不住改写过的命令。
  2. 同一组规则,换一种求值语义就可能换一个答案;所以先选语义,再用测试把顺序敏感的用例锁住。
  3. hook 只能收紧、不能放宽:它在规则之前拦得住一切,却放不过规则已经拒绝或要求询问的调用。
  4. 问得太多,人就不再看了;把只读和低风险的放行,只把真正需要判断的调用留给判断者。
  5. Pi 的立场不是「权限不重要」,而是「进程内的弹窗给不了边界,只值一个 34 行的扩展;要边界就上容器」。
  6. 审批者必须比被审者权限更小,读到的历史只当证据,出任何故障都默认不执行。

深入这一章

按你愿意花的时间选。每条都实际打开核对过(2026-09-26);外部链接都需要联网,离线包里只保留链接。

🎮 想更直观 10–30 分钟

先玩本章的组件;下面三个是外部最好的交互入口
10 min英文需联网 第三方,纯前端
输入权限模式、工具和调用,输出最终裁决和决定它的那条规则;按 deny → ask → allow 求值,还处理托管、项目、用户多级设置。
对应第 2 课。它是单引擎版的求值器,正好补本页没做的 scope 优先级;本页的多引擎对照是在它的思路上扩出来的。
15 min英文需联网
写一段策略、给一份输入 JSON,立刻看到裁决;通用策略引擎,不限于 agent。
对应第 2 课。感受「策略与求值语义分开」的样子:同一份输入换一种规则组合方式,结果怎么变。
10 min英文需联网 2025-06-16
私有数据、不可信内容、对外通信三者同时出现时 agent 就危险了,配一张三圈图。
对应第 4 课与下一章。解释了为什么审批者读到的历史只能当证据、为什么外发要单独审;也是 A4 的入口。

🔬 想吃透原理 1–3 小时

唯一必看:Anthropic《Claude Code auto mode》。其余按课挑
30 min英文需联网 2026-03-25
为什么要把审批交给模型(用户批准 93% 的弹窗)、三层放行结构、两段式分类器,以及每一段在真实流量和真实越权动作上的误差。
对应第 3、4 课,是这两课的主心骨。它公开了自己的误差(误拦 0.4%、漏判 17%)和失败的尝试,不是营销稿。
25 min英文需联网 2026-05-25
claude.ai、Claude Code、Cowork 三种产品各把边界放在哪:临时容器、OS 沙箱加人工审批、完整虚拟机;审批疲劳的遥测数字也在这里。
对应第 3 课与下一章。把「审批」放回整个防护体系里看,读完就知道为什么本章最后要交给沙箱。
17 min英文需联网 AI Engineer 频道
把 agent 当普通软件工程来做:自己掌控提示词、控制流和状态,人工审批是显式的一个环节。
对应第 1、3 课。「审批是 harness 里的一段代码,不是模型的品德」这一点讲得最清楚。
1 h 52 min英文需联网 Anthropic 工程师,AI Engineer 频道
现场用 Agent SDK 搭 agent,包括子 agent 编排与 hooks、权限。
对应岔路 B1。目前唯一现场演示 hooks 审批代码的一手视频;只看权限那几段也值。

📄 想读原文 按需

官方文档与 pin 源码;全部需联网,官方文档的本地快照由 P6 统一处理
官方文档需联网
第 1、2 课的出处:deny → ask → allow 组序、Bash 通配与复合命令、Read/Edit 的路径规则、六种模式、critical path;permission-modes 页讲 auto 的判定顺序。本章按 2026-09-26 的版本实现。
官方文档中文需联网
与英文版同一套内容的官方中文译本,适合先读一遍建立全貌。注意译本可能滞后:2026-09-26 核对时,中文版「选择权限模式」页还没有写 v2.1.283 起交互终端默认 auto,以英文版为准。
官方文档需联网
岔路 B1 的六步求值序:Hooks → Deny → Ask → Permission mode → Allow → canUseTool。
官方文档需联网
第 4 课:审查者看到什么、何时触发、被拒后怎么办、熔断阈值;「a reviewer swap, not a permission grant」。Codex 文档已从 developers.openai.com 迁到这里。
官方文档需联网
第 2 课:last matching rule wins、通配规则、--auto;注意它的 Defaults 一节与代码不一致(.env 在代码里是 ask)。
源码需联网
第 2 课:Starlark 写的 prefix_rule,每条规则自带 match / not_match 用例,加载时就验证——「规则自带单测」的好例子。
源码需联网
第 4 课:mod.rs 开头 12 行注释就是整套设计;policy.md 是 65 行的审查标准;review_session.rs:1105-1108 把审查者锁成只读、不能再申请审批。
🚫

我替你排除了什么:Cedar Language Playground(2026-09 调研时 curl 返回 404)· Databricks「From Primitives to Production」视频(多次访问报 Socket 错误,无法核对内容)· developers.openai.com/codex 下的旧文档链接(已迁到 learn.chatgpt.com,本章只用新地址)· 只列出「五种权限模式」的社区二手教程(漏了 auto,已过时)· 把 auto mode 的防注入归结为某一个单一机制的解读文(调研中三方核查未通过)· B 站检索到的权限 / 沙箱相关视频(2026-09-26):多为速成与营销标题的合集(如「全 748 集」「学完即就业」),或某个产品的功能宣传,没有讲清求值语义与审批设计的,中文视频本档暂缺 · 中文社区的「权限系统全解析」类博客:只复述官方文档的配置写法,官方中文版已覆盖,不另收。