首页 项目 博客 简历 联系 English
返回列表
2026年7月2日 约 3,362 字 预计 8 分钟读完

07:Permission & Safety:Claude Code 如何裁决一次动作能不能执行

上一篇讲 plan,重点是 Claude Code 怎样先把任务整理成一条行动路线。

#Claude Code#Coding Agent#工程化#Agent Runtime

07:Permission & Safety:Claude Code 如何裁决一次动作能不能执行

上一篇讲 plan,重点是 Claude Code 怎样先把任务整理成一条行动路线。

但 plan 只是“准备怎么做”。真正动手时,系统还要继续问:

这一步现在能不能执行?

这个问题很具体。比如 plan 里写“读取配置确认服务启动参数”,听起来很正常。可真正落到工具调用时,目标文件可能是 .env。再比如 plan 里写“运行测试验证修改”,如果命令是 pnpm test,通常是合理的验证动作;如果命令变成下载脚本并直接执行,风险就变了。

所以 07 先收住这一层:

Claude Code 面对一个准备执行的动作时,怎样把它裁决成可以做、先问人,或者换一条路。

这篇文章要拆的主线:plan 里的动作怎样被裁决

06 里说,plan 像一份行动草稿:先看什么、改什么、怎么验证。

07 接着看草稿里的动作怎样落地。Claude Code 会先把 plan 里的步骤落到具体工具调用上。每一步真正碰到文件、命令、网络或外部系统之前,都要经过一次运行时裁决。

可以把这个过程想成一条很短的链:

计划里的动作
-> 具体工具调用
-> 工具参数
-> 权限裁决
-> 执行、询问或改计划

这条链里最容易被忽略的是“具体工具参数”。

“读文件”只是动作类型;读哪个文件,才决定风险。“运行命令”只是动作类型;命令会不会删除、联网、改 git 历史,才决定是否要停下来。

几篇源码分析和安全文章给我的启发也是这一点:权限系统的核心是工具真正执行前,运行时怎样把工具、参数、上下文和规则放到一起判断[1],[2]。

为什么需要 permission:动作落到参数上以后,风险才清楚

Claude Code 的能力来自它能真正动手。

它能读项目、搜代码、改文件、跑命令,也能接 MCP、浏览器或其他外部工具。能力越接近真实环境,越需要边界。

很多风险会在“具体怎么做”的参数里变清楚。

比如:

  1. 读配置。
    config.example.ts 可能只是理解项目结构;读 .env 可能把密钥带进上下文。

  2. 清理构建产物。
    删除 dist/ 可能合理;路径拼错或通配符过大,就可能误伤源码。

  3. 运行测试。
    pnpm test 可能只是本地验证;某些测试脚本可能会访问网络、数据库或本地服务。

  4. 调用外部系统。
    读 issue 是观察;改 PR、触发部署、写数据库就是副作用。

所以 permission 解决的是动作意图和真实执行之间的落差。

prompt、CLAUDE.md、项目规则能提醒模型“应该怎么做”。运行时权限负责在工具执行前把动作收住:这次动作目标是什么,是否在任务范围内,风险能不能接受。

这也是为什么 03 讲 prompt 时要把“软规则”和“硬边界”分开。规则写给模型看,权限裁决发生在工具执行前。

一次动作进入权限判断前,Claude Code 会看什么

如果只按工具名判断,很快就会失真。

Read 看起来安全,但读密钥不安全。Bash 看起来危险,但跑测试、查版本、列目录又是 coding agent 的常规动作。Edit 有副作用,但修 bug 时它又是必要能力。

更合理的判断方式,是把一个动作拆成三层:

层次关注点例子
工具类型这是观察、写入、命令,还是外部调用Read、Edit、Bash、MCP
目标参数它具体指向哪里文件路径、命令文本、URL、服务名
当前理由它为什么服务于这次任务是否在 plan 范围内,是否用于验证

这样看,权限判断会从“Bash 危险,所以都问”,变成更具体的问题:

这次 Bash 要跑什么?为什么要跑?它会影响哪里?有没有更低风险的做法?

这也让 plan 变得有用。plan 里写清了范围、边界和验证方式,权限裁决就能拿它当理由。动作和 plan 对得上,风险会更容易解释;动作开始越界,系统就有理由停下来。

allow、ask、deny:一次动作的三个出口

一次动作经过权限裁决后,大致有三个出口。

出口含义下一步
allow风险低,或规则已经允许直接执行工具
ask动作可能合理,但需要用户看一眼暂停,说明风险,等待确认
deny命中明确边界不执行,换路径或改计划

这里真正要抓住的是:三个出口都应该带理由。

allow 最好知道为什么能放行。ask 要告诉用户风险在哪里。deny 也应该给出可继续的方向,让下一轮还有路可走。

从源码分析和安全文章的角度看,成熟的权限链会把裁决放到工具执行层附近,连同参数检查、hook、日志和错误回流一起处理[1],[2]。这样做的价值,是让每次裁决都能被解释,也能被后续环节接住。

比如:

action: read
target: .env
decision: deny
reason: 目标像敏感配置
fallback: 读取 .env.example,或请用户提供脱敏字段

这个结果比直接拒绝更有用。它告诉 agent 下一轮可以怎么继续。

permission mode:决定默认怎么问

permission mode 主要影响协作节奏。

有些任务需要用户一步步审:项目敏感、代码库陌生、动作影响范围不清楚。
有些任务可以少问一点:用户已经信任方向,仓库在隔离环境里,目标也很明确。
还有一种情况是先规划:只读、搜索、理解代码,暂时不修改文件。

我会把 permission mode 理解成“默认怎么问人”的策略。

它可以让 Claude Code 更谨慎,也可以让它更连续。但动作的真实风险仍然来自工具、参数和环境。一个会删除文件的命令,即使放在更宽松的模式里,仍然需要被当成高风险动作处理。

所以更稳的设计通常是组合式的:

  • mode 决定默认审批节奏。
  • permission rules 处理具体动作和参数。
  • sandbox 限制命令执行后的影响范围。
  • logs/session trace 记录发生过什么。

Fluid Attacks 把这些放在一起看:预过滤工具、规则评估、权限模式、hooks、sandbox 和 session logs 共同构成防线[2]。这个视角比单独讨论某个模式更接近真实工程系统。

被拒绝以后怎么办:权限结果也要回到 Agent Loop

权限拒绝更像任务路线上出现了一个路障。

它更像工具结果的一种:告诉 agent “这条路现在走不通”。接下来要做的,是把这个结果放回 Agent Loop。

假设 Claude Code 想读 .env,被拒绝了。下一步可以有几种走法:

  1. .env.example、README 或配置 schema。
  2. 让用户提供脱敏后的关键字段。
  3. 根据错误日志和示例配置继续推断。
  4. 修改 plan,把“读取真实环境变量”改成“使用公开配置和日志定位问题”。
  5. 如果确实必须访问敏感信息,停下来讲清理由,请用户决定。

命令被拒绝也一样。

rm -rf dist 被拒绝后,可以先列出目录、说明清理目标,再请求确认;curl | sh 被拒绝后,可以改成阅读安装说明、固定下载来源,或者让用户手动执行。

这部分把 permission 和 Agent Loop 连起来了:

权限裁决会给下一轮判断增加一个新的事实。

一个好的 permission result,最好带三样东西:

  • 被裁决的动作。
  • 裁决结果和原因。
  • 可继续的替代路径。

这样 agent 可以避开原来的危险路径,沿着更合适的证据继续推进。

sandbox 和 permission 的边界

sandbox 和 permission 很容易混在一起。

我会这样分:

permission 决定这次动作能不能执行;sandbox 限制动作执行以后最多影响哪里。

比如 pnpm test 被允许执行以后,sandbox 可以继续限制它能读写哪些目录、能不能访问网络、能不能碰到宿主环境。这样用户可以减少低风险命令的反复确认,系统也能把命令放在更清楚的运行边界里。

Fluid Attacks 的安全分析把 sandbox 放在一条纵深防线里看:它和预过滤工具、规则评估、session logs 一起降低真实执行的影响面[2]。Backslash 这类安全团队视角的文章则提醒,隔离环境也有边界:agent 能读到的敏感信息、能访问的网络、能写的目录,仍然会影响风险[5]。

所以 sandbox 适合回答“放行以后影响面多大”。

permission 适合回答“这次是否应该放行”。

两者放在一起,才比较稳。

hooks 在权限链里的位置

hooks 在 07 里只讲位置,配置细节留到后面。

它可以在工具执行前后补检查、补记录、补自动化。

比如:

  • Bash 执行前检查命令里有没有危险片段。
  • Edit 之后自动跑格式化。
  • 工具调用后记录审计事件。
  • 任务停止前做通知或收尾。

在权限链里,hooks 像一个可插拔的拦截点。默认权限规则负责基础裁决,hooks 可以接入团队自己的流程。

但 hooks 本身属于扩展机制。第 10 篇会单独讲它的生命周期、事件类型和配置方式。07 只需要留一句位置说明:

hooks 可以补强动作裁决,但主线仍然是“动作能不能执行”。

一个例子:读 .env 和运行测试

拿两个动作对比,permission 的作用会更清楚。

第一个动作是读 .env

Claude 可能是为了排查启动失败,想确认环境变量。这个意图合理,但目标文件敏感。一次比较稳的裁决会把它拦下来,然后给下一步:

  1. 先读 .env.example
  2. 再看配置读取逻辑。
  3. 如需真实值,请用户提供脱敏摘要。

第二个动作是运行 pnpm test

用户要求修 bug,plan 里也写了要验证。此时测试命令更像合理动作。系统仍然可以看命令参数、工作目录和网络行为,但它的理由更充足:这个动作服务于验证。

这两个例子真正要比较的是:

  • 目标是什么。
  • 为什么需要它。
  • 会不会碰敏感信息或产生副作用。
  • 被拒绝以后有没有替代路径。

这就是动作裁决的核心。

对我写 Agent 的启发:设计 PermissionDecision

如果自己写一个 coding agent,我会把权限判断设计成结构化对象。

它最好不只返回“允许 / 拒绝”,还要告诉用户和 agent:为什么这么判,接下来怎么走。

一个最小的 PermissionDecision 可以包含这些字段:

字段作用
action这次准备做什么
tool使用哪个工具
target路径、命令、URL 或外部资源
risk风险等级
reason为什么这个动作服务于当前 plan
decisionallow、ask、deny
evidence命中了哪些规则或上下文判断
fallback被拒绝以后可以怎么继续

这里最值得保留的是 fallback

deny 如果只返回“不能做”,agent 很容易卡住。带上 fallback,下一轮就能继续推进:

decision: deny
reason: 目标路径匹配敏感配置
fallback: 读取 .env.example,或请求用户提供脱敏字段

ask 也要讲清楚风险。用户最好看到这条命令准备做什么、影响哪里、为什么需要它,而不只是看到“是否允许运行命令”。

这样 permission 既是防线,也是一种协作界面。

我现在对 Permission & Safety 的理解可以收成一句话:

权限系统把模型的动作意图,翻译成可解释、可拦截、可继续推进的运行时决策。

参考资料

Claude Code 源码分析与安全解读

下一篇继续追的问题

这一篇先把 Permission & Safety 理解成动作裁决层。

核心结论是:

plan 说明准备怎么做,permission 判断这一步能不能做。

到这里,Claude Code 已经能看见上下文、整理 plan、裁决动作,再通过工具接触真实项目。

但这些动作发生以后,还要留下痕迹。否则用户很难知道它为什么放行、为什么拒绝、跑了什么命令、哪里失败、哪些步骤最耗时、上下文什么时候开始膨胀。

所以 08 继续追:

Claude Code 如何记录工具调用、权限结果、错误、成本和执行轨迹,让 Agent 的运行过程可调试、可复盘、可优化?