权限机制:权限模式、规则匹配与多路赛跑
一句话定位:权限机制是 Harness 部件 ④执行环境 的审批层——工具执行的流水线上,分类器粗筛之后、工具落地之前,裁决「这一步要不要先问你」。前置:什么是 Harness、工具执行机制(裁决卡在站 3 与站 4 之间)、Hook(裁决的参与者之一,产品行为见彼篇)。分工:产品篇讲六档权限模式「怎么用、怎么切」,本篇讲裁决机器内部「怎么判」——同一个六档,机制视角。
先从一次「谁说了算」的困惑说起
你把 Agent 切到自动批准编辑的模式,它改文件确实不问了,但每次跑命令前还是会停下来等你点头——模式说好的「自动」呢?
更困惑的是装了一个安全 Hook 之后:它对某条命令明明说了「允许」,系统却还是拦下了;另一次它对某操作明明没意见,系统倒还停下来问你。同一个工具调用,Hook、规则、模式、分类器(auto 档背后替你审的那个模型,下文细说)都插得上手——到底谁说了算?
裁决顺序是什么?为什么 Hook 说「允许」还不算数?
答案是:权限是一道多路赛跑的裁决——「赛跑」指各层都有话语权、按固定优先级分胜负,不是同时开跑。Hook 先表态(可拦,但 allow 不终审),规则按 deny → ask → allow 定序(deny 是任何模式都翻不掉的一票否决,ask 是点名必问),模式只定「没人管时问不问」的基线。**allow 从来不是通行证,deny 才是真正的法律。**至于开头的两个困惑——「自动档为什么还问命令」,因为那份「自动」名单里只有编辑类;「Hook 没意见为什么还问」,因为没人放行它就落到基线,基线说问就问。
图:权限裁决四层漏斗——预检与三道关卡层层收窄,「第 1 关」到「第 3 关」是本篇编号
渐进拆解
把一次裁决想象成出入境检查,四层各司其职:
- 预检硬底线 = 没护照寸步难行:某些事(涉及交互的确认、关键路径的删除)无论什么口岸政策都必须本人到场
- 第 1 关 Hook = 海关抽查员:有权把你拦下;但他挥手放行不等于通关——后面该查的照查
- 第 2 关规则匹配 = 签证规则:黑名单(deny)上的人一票否决,谁来求情都没用;标了「必须面签」(ask)的当场排窗口谈;白名单(allow)上的人速通
- 第 3 关模式基线 = 口岸政策:三样都不沾的人,按政策办——政策宽松的直接过(auto 档请分类器代看一眼),政策严格的逐个面谈(default 档全问)
看一次「强推代码」的裁决走查——注意 Hook 的「允许」是怎么被规则层否掉的:
图:裁决走查——第 2 关的 deny 否掉了第 1 关的「允许」,这就是「allow 不是通行证」;未命中时的旁路见图内下两行
大白话:权限系统的设计哲学一句话——谁都可以拦,放行必须无人反对。「放行」永远只表示「这一关没意见」,不表示「这事定了」;Hook、规则、分类器、预检都能拦。想给自己留一道绝对兜底,就写 deny 规则——它比任何模式、任何 Hook 都硬。
模式族谱(基线):六档
六档模式定义「没人管时的默认动作」(行为速览见产品篇的六档表,这里只讲机制含义):
| 档位 | 没人管时的动作 | 注意点 |
|---|---|---|
| default(界面显示 Manual) | 只放行读取,其余先问 | — |
| acceptEdits | 额外自动批准文件编辑与常见文件系统命令 | 名单只此一类:跑命令、碰网络仍要问,且限工作目录之内 |
| plan | 只读加提议 | 编辑在计划批准前全锁 |
| auto | 全部放行,由独立分类器模型代审 | — |
| dontAsk | 只跑预批准的东西,其余自动拒绝而非弹窗 | 给 CI 与脚本用 |
| bypassPermissions | 全放行且关掉安全检查 | 官方要求仅限隔离容器或虚拟机 |
两句总纲:模式决定「问不问」,沙箱决定「跑起来后碰得到什么」,两者独立、可叠加;模式只是基线,规则叠加在其上。还有一条容易忽略:ask 规则、关键路径删除、需要人交互的工具,任何模式都不自动批准——包括 bypass。
规则匹配(三种规则、两级粒度)
三种规则一句话说清:
- deny:拉黑名单(一票否决)。
- ask:点名必问的名单,allow 也保不住。
- allow:白名单,速通放行。
粒度分两级:整工具级管「这个工具整体放行还是拉黑」(比如允许读、禁止写);内容级管到参数——Bash(git push *) 这种命令模式匹配,允许 git push 但限定范围。规则写在企业、用户、项目多层配置里,而 deny 的效力跨层最高:官方原话,任何一层 deny,其他层都无法放行。
多路赛跑(裁决顺序)
官方 SDK 口径的评估次序是六步:
- Hook 先跑。
- deny 规则一票否决。
- ask 规则命中就问——哪怕更精确的 allow 规则也命中,照样问。
- 模式基线表态:auto 交分类器、dontAsk 自动拒、default 弹窗。
- allow 规则速通放行。
- 最后落到 canUseTool 兜底——那是 Agent SDK 里程序化的裁决回调,无头会话中所有「需要确认」的出口都汇聚到它,ask 命中的调用与 plan 模式的编辑类调用都归它,连 bypass 模式也不例外。
规则之间的相对次序记住一句:deny > ask > allow。
为什么 Hook 说「允许」还要走规则匹配? 官方的理由是保住 deny 优先序(包括企业管理配置里的 deny)——Hook 可能是用户自写的,也可能是第三方装的,判断标准未必与你一致,它的 allow 不该拥有终审效力;它能做的是「拦下并说明理由」。
auto 分类器与兜底
auto 档的「全部放行」不是不审查,而是把审查外包给一个独立的分类器模型(默认跑在固定模型上,不随会话主模型切换)。它偏重拦「越界」而非「危险本身」——动作超出当前任务的请求范围、打向组织没声明过信任的基础设施(这是对官方配置项与社区观察的概括,官方未逐字定义拦截标准)。
误判的兜底是系统性回退(官方口径):连续拦截 3 次或累计拦截 20 次,auto 暂停、恢复逐条提问,你批准某个动作后 auto 再恢复——宁可退化成手动,也不让误判滚雪球。plan 档里分类器也兼职:规划期间的命令由它代审。
与粗筛分类器的分工
工具执行篇那位「裁决之前先粗筛」的命令分类器,与这里的 auto 代审分类器是两道工序——前者在站 3 与站 4 之间给 Bash 命令定风险级(只读/需确认/危险),是所有模式共用的前置过滤;后者只在 auto 档的第 3 关内工作,替人做「要不要放行」的完整裁决。一个管「这条命令像不像危险的」,一个管「这个动作越没越界」。
打断的艺术
裁决机器还有一半在「人」这边——何时打断、打断什么。设计取向(工程通则):不可逆的动作(删除、强推、对外发布)、越界的动作(碰工作目录之外)、陌生的目标(没见过的基础设施)值得打断;高频低危的动作打断只会制造「批准疲劳」——人被问烦了就会无脑点允许,权限系统形同虚设。所以好系统的打断是稀缺的:把问题留给真正的问题。
动手:写一个裁决器
伪代码按官方 SDK 口径的六步实现(注释里的关卡号呼应上图;硬底线是横切各层的约束,图中前置为教学分层):
def decide(call, mode, rules, hooks, can_use_tool):
if hard_guarantee_block(call): # 横切硬底线:关键路径 rm 等
return ASK # 任何模式(含 bypass)不豁免
for h in hooks.pre_tool_use: # 第 1 关:Hook 可拦可放
v = h(call)
if v is DENY:
return DENY(v.reason) # 拦下:理由稍后回填模型
if rules.deny.match(call): # 第 2 关①:deny 一票否决
return DENY(rules.deny.reason) # 任何层的 deny 都翻不了案
if rules.ask.match(call): # 第 2 关②:ask 命中必问
return ASK # allow 也保不住它
action = mode.baseline(call) # 第 3 关:模式基线表态
if action is DENY: # dontAsk:会触发提示的一律拒
return DENY("mode: dontAsk")
if action is ALLOW: # auto 批准 / bypass / acceptEdits
return ALLOW
if rules.allow.match(call): # 模式表态后 · allow 白名单速通(第 3 关之后)
return ALLOW
# default 基线不表态:弹窗出口由 canUseTool 兜底承接
return can_use_tool(call) # SDK 兜底:确认出口最后汇聚于此
手写 Harness 时的工程化建议:把高危拦截做成中间件链而不是散落在主循环里的 if——每条中间件只管一类风险(命令黑名单、路径越界、网络目标),裁决器按链依次过;新风险加一个链节,主循环一行不动。审批的出口统一走「打断问人」,问完的答案与拒绝理由都从回填口进上下文——权限机制与工具执行的回填铁律在这里会师。
同一概念的叫法对照:
| 本篇用词 | 文献中的其他叫法 | 深挖去处 |
|---|---|---|
| 裁决四层漏斗 | 审核流程 · 多路赛跑(固定优先级,非同时跑) | 本篇 |
| 模式基线 | 权限模式 · PERMISSION_MODES | Claude Code 总览 |
| 内容级规则 | 命令模式匹配 · Bash(git push *) 规则 | 本篇 |
| 分类器代审 | Auto 模式审查(与命令粗筛分类器是两道工序) | 工具执行 |
| 投机预执行 | Speculation(对提示建议的投机预执行)· 预执行 | 本篇 |
| 提示建议 | Prompt Suggestion | 本篇 |
四档模式在放权主轴上,plan 与 dontAsk 是挂在轴外的两个特殊档(与产品篇同一画法),「没人匹配时问不问」一眼看清:
图:模式光谱——主轴四档按「没人匹配时的放权程度」递增;plan 与 dontAsk 是轴外特殊档,画法与产品篇一致
边界与取舍:打断是稀缺资源
最严的权限系统往往最不安全
什么都问 = 批准疲劳 = 人无脑点允许——形式上的审批,实质上的全放行。auto 档存在的理由(官方口径)就是减少这种疲劳:把高频低危的裁决交给分类器,把人的注意力省给真正的高危时刻。配权限时的自查标准不是「我拦了多少」,而是「被打断的人每次是否真的在看」。
deny 是底线不是日常
规则系统的正确用法:高频动作用 allow 规则省掉确认(比如永远允许读项目文件),危险动作用 deny 规则焊死底线(比如永远禁止改持续集成配置),中间地带留给模式基线与人的判断。把大量「允许」写进 deny 的反面、试图用规则穷举一切,规则表本身会变成没人读得懂的负债。
Hook 是扩充点不是替代品
Hook 能实现自定义拦截(安全扫描、私有合规检查),但它的 allow 不终审——想让某类操作「无条件下」,正确位置是 allow 规则;想「有条件下」,写 Hook 判断后放行并接受规则层复检。把 Hook 当成绕过权限系统的后门,既违背设计(不生效),也违背安全常识(Hook 的退出码语义里有 fail-open 的坑,见 Hook 的产品行为篇的教训)。
坏味道预警
如果你的 Agent 「每次都在同一类操作上打断你」,缺的不是更严的规则,而是打断的分级——该沉淀成 allow 规则的高频动作没沉淀;反过来「从来不打断」,先查是不是有人开了 bypass 还以为自己在 auto 档。模式与规则谁在生效,用一次高危操作实测就知道。
自测
合上文章先自己答,再展开对照:
hook 返回 allow 之后为什么还要走规则匹配?
因为 Hook 与规则的话语权不同:Hook 可能是用户自写的,也可能是第三方装的,判断标准未必与你一致。官方的理由是「保住 deny 优先序」——包括企业管理配置里设的 deny:Hook 的合法权力是「拦下并说明理由」或「表示没意见」,allow 只意味着「这一关没意见」,裁决继续往下走:deny 规则命中照样一票否决(官方原话,任何一层 deny 其他层都无法放行),ask 规则命中照样问。让 Hook 的 allow 拥有终审效力,等于把安全底线外包给最不可靠的一环。
auto 模式分类器误判时的兜底设计是什么?
三层兜底。其一,分类器的审查对象偏重「越界」而非「危险本身」——拦动作超出当前任务请求范围、打向组织未声明信任的基础设施的情况,本分内的正常操作不误伤(此为对官方配置与社区观察的概括);其二,ask 规则与硬底线(关键路径删除、需人交互的工具)在 auto 档依然生效,分类器无权翻案;其三,系统性回退(官方口径):连续拦截 3 次或累计拦截 20 次,auto 暂停、恢复逐条提问,你批准某个动作后 auto 再恢复——分类器状态可疑时,宁可退化成手动确认,也不让误判滚雪球。三层合起来:分类器只在其统计优势可靠的区间工作,出了区间就交还给人。
参考与延伸
- Claude Code 官方文档:权限模式(六档行为与沙箱关系) — code.claude.com/docs/en/permission-modes
- Claude Code 官方文档:权限配置与规则(hook 决策不越过规则匹配) — code.claude.com/docs/en/permissions
- Claude Code 官方文档:Agent SDK 权限(评估顺序与 canUseTool) — code.claude.com/docs/en/agent-sdk/permissions
- Anthropic《Building Effective Agents》(2024-12):人机协同与审批节点的设计原则 — anthropic.com/engineering/building-effective-agents
- Pi 的 YOLO 路线一手材料(2026-10-24 汇总):wordman 播客「YOLO 是逼用户思考权限的 forcing function,CC/Codex 内置多是安全剧场」+ HN 定调文反方(chr15m「非只读工具调用应强制审批」)+ 容器化三方案(Gondolin/Docker/OpenShell)——三方对照完整,见「读 Pi 学 harness 26 零权限」与 W43 扫荡报告
本系列的后续学习路线(推荐顺序):记忆机制 · Skill 机制 → 状态机与 Checkpoint → 多 Agent 编排。