Learn-Claude-Code | 笔记 | Tools & Execution | s03_new Permission

目录

    • 写在前面
    • 前言
    • [1. s03: Permission](#1. s03: Permission)
    • [2. 问题](#2. 问题)
    • [3. 解决方案](#3. 解决方案)
    • [4. Permission Desk 流程图分析](#4. Permission Desk 流程图分析)
    • [5. 工作原理(代码分析)](#5. 工作原理(代码分析))
    • [6. 相对于 s02 的变更](#6. 相对于 s02 的变更)
    • [7. 小结](#7. 小结)
    • 结语
    • 参考

写在前面

learn-claude-code 项目目前有两条教程线,一条是之前的 s12 章节的,另一条是最近更新的 s20 章节的,大家如果刚学习这个项目的话推荐直接看最新的 s20 章节的教程即可,由于博主之前学习过 s12 章节的内容,因此打算把 s20 章节中新增章节的内容给补充学习,内容重复的章节博主这边就跳过了。

下面是旧版到新版的对应关系:

Legacy 12-lesson track Current 20-lesson track Topic
old s01 new s01 Agent Loop
old s02 new s02 Tool Use
old s03 new s05 TodoWrite
old s04 new s06 Subagent
old s05 new s07 Skill Loading
old s06 new s08 Context Compact
old s07 new s12 Task System
old s08 new s13 Background Tasks
old s09 new s15 Agent Teams
old s10 new s16 Team Protocols
old s11 new s17 Autonomous Agents
old s12 new s18 Worktree Isolation
new only s03, s04, s09, s10, s11, s14, s19, s20 Permission, Hooks, Memory, System Prompt, Error Recovery, Cron, MCP, Comprehensive Agent

从上表中我们可以看出我们需要补充的内容包括 s03、s04、s09、s10、s11、s14、s19 以及 s20 八个章节的内容。

新版学习路径如下:

主线:能动手 → 能做复杂任务 → 能记住和恢复 → 能长期运行 → 能协作 → 能扩展并合体

前言

在上篇文章 Learn-Claude-Code | 笔记 | Tools & Execution | s01 The Agent Loop | s02 Tools 中,我们介绍了开源项目 learn-claude-code 前两个章节 s01: The Agent Loop 和 s02: Tool Use 的内容,这篇文章我们继续跟着教程文档来学习工具与执行相关内容,记录下个人学习笔记,和大家一起分享交流😄

Note:本篇文章主要学习记录 新版教程 第一部分 Tools & Execution 中 s03: Permission 章节的内容。

githubhttps://github.com/shareAI-lab/learn-claude-code

referencehttps://chatgpt.com/

1. s03: Permission

如果说 s01 让我们看到了一个 Agent 最基础的循环结构,s02 让模型从单一 bash 扩展到了 read_filewrite_fileedit_fileglob 等多个工具,那么到了 s03,系统开始面对一个非常现实的问题:工具越多,Agent 能触达真实世界的能力越强,风险也越高。

在 s02 中,文件类工具已经有 safe_path() 做路径沙箱,但 bash 本身仍然是一个很大的安全面。模型只要调用了 shell,就有可能提出一些破坏性命令。教程文档里也说得很直接:安全不能靠 "相信模型不会乱来",而要靠代码在工具执行前先拦一层。

所以 s03 的核心变化可以概括成一句话:

模型仍然负责提出工具调用,但运行时必须先判断这个工具调用能不能执行。

也就是说,从这一节开始,Agent Loop 不再是 "模型说执行什么,程序就立刻执行什么",而是在模型和工具 handler 之间插入了一个新的中间层:Permission Pipeline

2. 问题

在 s02 里,工具系统已经比 s01 更强了。模型不再只能通过 bash 做所有事情,而是可以直接调用专门的文件工具,比如 read_filewrite_fileedit_file。这些工具有一个明显好处:它们可以在 handler 内部做约束,比如通过 safe_path() 防止路径逃出当前工作区。

但是,s02 仍然存在一个明显问题:bash 工具过于自由。

文件工具可以限制路径,但 shell 命令本身很难天然安全。模型如果想 "清理一下项目",它可能会调用删除类命令;如果模型理解错了用户意图,或者 prompt 里出现了危险指令,就可能触发一些不该执行的操作。

这里的关键不是 "模型会不会故意作恶",而是:只要运行时把执行权完全交给模型,风险就已经存在。

因此 s03 要解决的问题不是 "让模型更听话",而是:

在每一个工具真正执行之前,先由程序侧判断它属于 allow、ask 还是 deny。

这也是 Permission 这一节的核心定位:它不是新增一个业务工具,而是在工具执行前增加一层安全路由。

3. 解决方案

s03 的解决方案非常克制:s02 的 agent loop 基本不动,只在工具 handler 执行之前插入 check_permission()

上图把 s03 和 s02 的关系讲清楚了:s03 并没有重写 Agent Loop,而是在 s02 原有循环中,给工具执行前插入了一层 check_permission()

左侧的 messages[] -> LLM -> stop_reason? 仍然是 s02 已经建立起来的主线。如果模型没有产生工具调用,就直接返回结果;如果模型产生了 tool_use,原来 s02 会直接进入 TOOL_HANDLERS 执行,而 s03 在这里多加了一道权限检查。

图中红色虚线框里的 check_permission() 就是 s03 的新增部分。它内部包含三道闸门:拒绝列表、规则匹配、用户审批 。只有当这三道闸门最终给出允许结果时,请求才会继续流向右侧的 TOOL_HANDLERS。如果被拒绝,则不会执行 handler,而是返回一个拒绝结果给 Agent Loop。

所以这里想表达的核心不是 "权限系统很复杂",而是:权限系统的位置非常关键,它卡在 LLM 提出工具调用之后、工具 handler 真正执行之前。 也就是说,模型仍然可以提出工具调用,但真正是否执行,开始由 harness 层决定。

也就是说,s03 不是重写循环,不是重写工具系统,而是在原来的工具分发前面加一道门。教程文档把这个设计总结为三道闸门:先硬拒绝,再规则匹配,最后需要时让用户审批;三道都没命中,才直接执行。

整体流程可以理解为:

shell 复制代码
Tool call
   |
   v
Gate 1: hard deny
   |
   v
Gate 2: permission rules
   |
   v
Gate 3: user approval
   |
   v
Tool handler

这三道闸门分别对应三种结果:

第一类是 allow 。比如读取工作区内的 README.md,它是只读操作,不会写文件,也不会执行 shell,因此可以直接放行。

第二类是 ask。比如删除某个本地临时目录,这种操作不一定绝对禁止,但它可能造成不可逆影响,所以不能自动执行,而是应该暂停下来问用户。

第三类是 deny。比如包含 root 删除、sudo、关机、格式化磁盘等高危模式的命令,这类操作不应该进入 handler,更不应该让用户随手确认后执行,而是直接拒绝。

这套机制最重要的地方在于:权限判断发生在工具执行之前

也就是说,模型仍然可以 "提出" 一个工具调用,但能不能真正跑起来,由运行时决定。这就把 "模型建议" 和 "系统执行" 分开了:模型负责生成意图,Permission Pipeline 负责判断这条意图是否允许落地。

4. Permission Desk 流程图分析

这一节教程配了 6 张 Permission Desk 图,它们非常适合用来理解 s03 的整体心智模型。图中左边是模型提出的 tool requests,中间是 permission desk,右边是最终 outcome。

第一张图先展示了三类典型请求:

  • read_file README.md
  • bash rm -rf ./tmp/build-cache
  • bash sudo rm -rf /

它们分别对应三条路线:

shell 复制代码
safe read -> allow
risky local change -> ask
forbidden pattern -> deny

这张图最重要的地方在于,它把权限系统的定位讲清楚了:Permission 不是工具本身,而是工具请求的路由器

模型仍然可以生成各种工具调用,但每个调用都要先被送到 Permission Desk 里分类。分类结果不是 "模型自己决定",而是运行时根据规则判断。

第二张图展示的是安全读取场景。read_file README.md 首先经过 Gate 1,发现没有硬拒绝模式;再经过 Gate 2,命中安全读规则;由于这个请求已经被判定为安全,所以 Gate 3 用户审批会被跳过,handler 可以立刻运行。

这对应代码里的逻辑就是:如果没有命中 deny,也没有命中 ask 规则,check_permission() 返回 True,然后原来的 handler 正常执行。也就是说,Permission Pipeline 不是为了让所有操作都变慢,而是为了让安全操作自动通过。

第三张图展示的是风险操作。比如 rm -rf ./tmp/build-cache 这种本地删除操作,它不是 root 删除,也不是 sudo,因此 Gate 1 不会直接拒绝;但它包含删除行为,会在 Gate 2 被判定为潜在破坏性命令,于是进入 Gate 3,生成一个 approval ticket。

这一步非常关键,因为它说明 s03 并不是把所有风险操作都简单粗暴地 deny 掉,而是引入了一个中间状态:ask

也就是说,Permission Pipeline 不是二分类,而是三分类:

  • allow:直接执行
  • ask:暂停等用户确认
  • deny:直接拒绝

第四张图展示的是用户审批通过之后的结果。前面被标记为 ask 的命令,只有在用户明确批准之后,handler 才会真正运行。

这一点很重要:approval ticket 批准的是这一次具体工具调用,而不是永久放开所有类似命令

也就是说,用户批准的不是 "以后所有删除操作都允许",而是 "这个具体的 rm -rf ./tmp/build-cache 可以执行"。这让系统既能处理必要的风险操作,又不会把风险扩大成长期权限。

第五张图展示的是硬拒绝场景。sudo rm -rf / 这种命令会在 Gate 1 就被 hard deny 拦住。后面的 Gate 2、Gate 3 都会跳过,handler 根本不会被调用。

这张图最值得强调的是:deny 优先级最高

也就是说,某些操作不应该进入 "问用户" 阶段,因为它们本身就属于绝对禁止的模式。Permission Pipeline 的第一道闸门就是为了挡住这类请求。

最后一张图做了总收束:三种请求进入同一个 Permission Desk,最后产生三种结果:

shell 复制代码
allow -> handler runs now
ask   -> wait for approval
deny  -> blocked before handler

这里还有一句很关键的话:decision returned to loop

也就是说,Permission Pipeline 并不是脱离 Agent Loop 的独立系统。它最终仍然要把结果以 tool_result 或 blocked result 的形式返回给模型,让模型继续下一轮推理。

所以 s03 的机制不是把模型排除在外,而是让模型看到:这个工具请求被允许了、被拒绝了,或者需要用户审批。这样,Agent Loop 仍然保持闭环,只是工具执行前多了一层运行时安全判断。

完整动画演示如下图所示:

5. 工作原理(代码分析)

在正式分析代码之前,我们先来看看教程文档中提供的 permission pipeline 整体流程图:

这张图对应的就是代码里的 check_permission() 主流程。它把一次工具调用进入权限系统之后的路径拆成三步:先经过硬拒绝列表,再经过规则匹配,如果规则命中,则进入用户审批。

第一道闸门是硬拒绝。它对应代码中的 DENY_LISTcheck_deny_list(),专门处理 rm -rf /sudoshutdown 这类永远不应该继续往下走的命令。只要这一层命中,就直接得到 deny,不会再进入用户审批。

第二道闸门是规则匹配。它对应 PERMISSION_RULEScheck_rules(),处理的是上下文相关的风险操作,比如写工作区外、执行删除命令、修改系统路径等。它和第一道闸门不同:命中规则并不意味着立刻拒绝,而是说明这次调用需要用户确认。

第三道闸门是用户审批。它对应 ask_user()。当第二道闸门判断某次工具调用有风险时,程序会暂停,把工具名、参数和原因展示给用户,让用户决定是否允许。用户允许才继续执行 handler;用户拒绝则返回 Permission denied.

所以这张图和代码是一一对应的:

python 复制代码
check_deny_list() -> check_rules() -> ask_user()

最终会产生三种结果:

  • allow:直接执行工具
  • ask:暂停等待用户确认
  • deny:不执行工具,返回拒绝结果

这也解释了为什么 s03 的核心变化只需要插入一个 check_permission(block):三道闸门都被封装在这个函数里,Agent Loop 不需要理解每条规则的细节,只需要根据返回值决定要不要调用 handler。

看完 Permission Desk 的整体心智模型和 permission pipeline 之后,接下来我们正式进入 s03_permission_code.py。文件开头已经把这一节的核心机制写得非常清楚:

python 复制代码
"""
s03_permission.py - Permission System

Three gates inserted before tool execution:

    Gate 1: Hard deny list (rm -rf /, sudo, ...)
    Gate 2: Rule matching (write outside workspace? destructive cmd?)
    Gate 3: User approval (pause and wait for confirmation)

Only one line added to the agent loop:

    if not check_permission(block):
        continue
"""

这段注释里最重要的不是 "Three gates",而是最后那句:Only one line added to the agent loop。也就是说,s03 的设计目标不是让 loop 变复杂,而是在保持 s02 工具分发结构基本不变的前提下,把权限检查插到工具执行之前。代码文件也明确标出了这一点:s03 是在 s02 多工具 Agent 的基础上增加权限管线。

首先,s03 保留了 s02 的工具实现。比如 safe_path()run_bash()run_read()run_write()run_edit()run_glob() 这些函数依然存在:

python 复制代码
def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

这里说明文件工具自身仍然有路径沙箱。也就是说,s03 并没有废掉 s02 的工具级保护,而是在它外面又加了一层执行前权限判断。这样就形成了两层安全结构:

  • permission pipeline:决定能不能执行这个工具调用
  • tool handler:执行时继续做自己的局部校验

这点很重要,因为权限系统不是替代 handler 内部安全检查,而是提前做一层全局路由。

接着看 s02 保留下来的工具定义和 dispatch map:

python 复制代码
TOOLS = [
    {"name": "bash", "description": "Run a shell command.", ...},
    {"name": "read_file", "description": "Read file contents.", ...},
    {"name": "write_file", "description": "Write content to a file.", ...},
    {"name": "edit_file", "description": "Replace exact text in a file once.", ...},
    {"name": "glob", "description": "Find files matching a glob pattern.", ...},
]

TOOL_HANDLERS = {
    "bash": run_bash,
    "read_file": run_read,
    "write_file": run_write,
    "edit_file": run_edit,
    "glob": run_glob,
}

这一部分和 s02 的思想完全一致:TOOLS 给模型看,告诉模型有哪些工具可以调用;TOOL_HANDLERS 给运行时用,告诉程序每个工具名对应哪个 Python 函数。s03 并没有改变这个分层,而是在 TOOL_HANDLERS 真正执行之前多走一次权限检查。

真正新增的内容,从下面这段开始:

python 复制代码
# NEW in s03: Three-Gate Permission Pipeline

第一道闸门是硬拒绝列表:

python 复制代码
DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if=", "> /dev/sda"]

def check_deny_list(command: str) -> str | None:
    for pattern in DENY_LIST:
        if pattern in command:
            return f"Blocked: '{pattern}' is on the deny list"
    return None

这一层的特点是:只针对永远不应该执行的高危模式

如果命中,就直接返回阻止原因,后面不再进入规则匹配,也不会问用户。代码里也只在 block.name == "bash" 时检查 deny list,因为这些高危模式主要来自 shell 命令。

不过这里要注意,教程也明确提醒过:教学版使用简单字符串匹配只是为了讲清楚机制,不是生产级安全方案。真实系统里,shell 展开、命令别名、路径变体都可能绕过简单匹配,所以生产版需要更复杂的权限管线

第二道闸门是规则匹配:

python 复制代码
PERMISSION_RULES = [
    {
        "tools": ["write_file", "edit_file"],
        "check": lambda args: not (WORKDIR / args.get("path", "")).resolve().is_relative_to(WORKDIR),
        "message": "Writing outside workspace",
    },
    {
        "tools": ["bash"],
        "check": lambda args: any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
        "message": "Potentially destructive command",
    },
]

这层和 deny list 的区别在于:它不是绝对禁止,而是判断 "这个操作是否需要用户确认"。比如写工作区外、删除文件、修改系统目录、过宽权限变更,这些都不一定是永远不能做,但不能由模型自动决定。

所以 check_rules() 的作用就是遍历这些规则:

python 复制代码
def check_rules(tool_name: str, args: dict) -> str | None:
    for rule in PERMISSION_RULES:
        if tool_name in rule["tools"] and rule["check"](args):
            return rule["message"]
    return None

只要某条规则命中,就返回对应 reason。注意这里返回的不是 False,而是一个 message。因为规则命中不代表立刻拒绝,而是要进入第三道闸门:用户审批。

第三道闸门就是 ask_user()

python 复制代码
def ask_user(tool_name: str, args: dict, reason: str) -> str:
    print(f"\n\033[33m⚠  {reason}\033[0m")
    print(f"   Tool: {tool_name}({args})")
    choice = input("   Allow? [y/N] ").strip().lower()
    return "allow" if choice in ("y", "yes") else "deny"

这一段非常直观:如果规则认为这次工具调用有风险,程序就暂停,把原因、工具名、参数都展示给用户,然后等待用户输入 y/yes。如果用户不明确同意,默认就是 deny。

这也体现了一个很重要的安全设计:默认拒绝比默认允许安全

如果用户直接回车,或者输入其他内容,这次工具调用都会被拒绝。

最后,三道闸门被 check_permission() 串起来:

python 复制代码
def check_permission(block) -> bool:
    if block.name == "bash":
        reason = check_deny_list(block.input.get("command", ""))
        if reason:
            print(f"\n\033[31m⛔ {reason}\033[0m")
            return False

    reason = check_rules(block.name, block.input)
    if reason:
        decision = ask_user(block.name, block.input, reason)
        if decision == "deny":
            return False

    return True

这段代码的顺序非常重要:

第一步,先查硬拒绝。如果是 bash,先看命令里有没有命中 DENY_LIST。命中就直接 False,不执行,不询问。

第二步,再查规则。如果工具名和参数命中某条风险规则,就进入 ask_user()

第三步,如果用户拒绝,也返回 False。如果用户允许,或者没有命中任何规则,就返回 True

所以 check_permission() 的返回值非常简单:

  • True -> 可以执行 handler
  • False -> 不允许执行 handler

而真正和 agent loop 连接起来的地方,在下面这段:

python 复制代码
for block in response.content:
    if block.type != "tool_use":
        continue

    print(f"\033[36m> {block.name}\033[0m")

    # s03 change: run through permission pipeline before executing
    if not check_permission(block):
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": "Permission denied."
        })
        continue

    handler = TOOL_HANDLERS.get(block.name)
    output = handler(**block.input) if handler else f"Unknown: {block.name}"
    results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})

这就是 s03 最核心的代码变化。和 s02 相比,真正新增的其实就是:

python 复制代码
if not check_permission(block):
    results.append(...)
    continue

但这一行的位置非常关键:它正好卡在 模型生成 tool_use 之后,handler 执行之前。也就是说,模型已经提出了工具调用,但程序还没有真正碰文件系统、跑 shell、改代码。Permission Pipeline 就是在这个边界上做判断。

如果权限拒绝,程序不会静默跳过,而是追加一个普通的 tool_result

python 复制代码
"content": "Permission denied."

这就保证了 Agent Loop 仍然完整。模型下一轮仍然能看到:刚才那次工具调用被权限系统拒绝了,然后它可以根据这个结果重新规划下一步。教程文档也强调,s02 的循环完全保留,唯一变化就是工具执行前插入 check_permission()

所以从代码角度看,s03 的真正价值并不是 "多了几个 if 判断",而是第一次把工具调用拆成了两个阶段:

shell 复制代码
model proposes tool call
runtime authorizes tool call
handler executes tool call

这一步非常关键。因为从 s03 开始,模型不再直接拥有执行权,而只是拥有 "申请执行" 的能力。真正是否执行,由 harness 层的权限管线决定。

博主在给定下面的提示词情况下:

shell 复制代码
Create a file called test.txt in the current directory

想通过调试看看整个过程发生了什么,我们来具体分析下:

第一次 Loop

模型第一次响应结果


工具调用权限检查

工具第一次执行结果

在第一次 loop 中,模型收到用户请求后,并没有直接返回最终答案,而是先产生了一个工具调用。从调试图可以看到,response.content 中包含两个 block:前面是 ThinkingBlock,说明模型理解到用户想在当前目录创建一个 test.txt;后面是 ToolUseBlock,调用的是 bash 工具,命令为:

shell 复制代码
touch /home/zhouwenguang/project/learn-claude-code/test.txt

这一步其实非常典型:从模型视角看,它只是提出了一个 "我想执行 bash 命令" 的申请。真正是否能执行,还没有发生。

接下来进入 s03 新增的权限管线。程序在 agent loop 中遍历到这个 tool_use block 后,并没有马上执行 TOOL_HANDLERS["bash"],而是先调用:

python 复制代码
if not check_permission(block):
    ...

先看 Gate 1,也就是 check_deny_list()。调试图里可以看到,当前命令是 touch .../test.txt,而 DENY_LIST 中包含的是 rm -rf /sudoshutdownrebootmkfsdd if=> /dev/sda 这类高危模式。由于 touch 命令没有命中任何硬拒绝模式,所以 check_deny_list() 最终返回 None,说明第一道闸门通过。

然后进入 Gate 2,也就是 check_rules()。这里会遍历 PERMISSION_RULES,检查当前工具名和参数是否命中风险规则。调试图里可以看到,当前工具名是 bash,参数是:

shell 复制代码
{"command": "touch /home/zhouwenguang/project/learn-claude-code/test.txt"}

而 bash 相关规则主要检查命令中是否包含 rm> /etc/chmod 777 等潜在破坏性模式。当前只是创建一个空文件,不涉及删除、不涉及系统目录重定向,也不涉及危险权限修改,因此 check_rules() 同样返回 None

由于前两道闸门都没有返回风险原因,所以第三道 ask_user() 不会触发。程序直接走到 check_permission() 的最后:

python 复制代码
return True

也就是说,这次工具调用被权限系统判定为安全,可以继续执行 handler。

随后 agent loop 才真正进入工具执行阶段:

python 复制代码
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input)

因为当前工具名是 bash,所以最终执行的是 run_bash()touch 命令本身没有标准输出,因此工具结果中返回的是:

shell 复制代码
(no output)

最后,这个结果会被包装成标准的 tool_result,追加回 messages

python 复制代码
results.append({
    "type": "tool_result",
    "tool_use_id": block.id,
    "content": "(no output)"
})

所以第一次 loop 的完整过程可以概括为:

shell 复制代码
用户请求创建文件
    ↓
模型提出 bash 工具调用
    ↓
Gate 1:未命中 hard deny
    ↓
Gate 2:未命中风险规则
    ↓
Gate 3:无需用户审批
    ↓
handler 真正执行 touch
    ↓
返回 tool_result: (no output)

这里最值得强调的一点是:即便这只是一个简单的 touch 命令,s03 也不会让它绕过权限系统直接执行 。所有工具调用都必须先经过 check_permission(),只不过这次请求足够安全,所以最终被自动放行。

第二次 Loop

模型第二次响应结果

第二次 loop 中,模型拿到了上一轮工具返回的结果:

shell 复制代码
(no output)

虽然 touch 命令没有输出内容,但对模型来说,这已经表示工具调用正常完成,没有报错。于是这一次 response.content 中不再包含新的 ToolUseBlock,而是只包含思考 block 和文本 block。

从调试图可以看到,模型最终返回的文本大意是:

shell 复制代码
已创建了一个名为 test.txt 的空文件,位于 /home/zhouwenguang/project/learn-claude-code/test.txt。

由于这一轮没有新的工具调用,因此:

python 复制代码
if response.stop_reason != "tool_use":
    return

会直接结束 agent loop。

所以第二次 loop 的意义在于:权限系统不会改变 Agent Loop 的闭环方式 。第一轮工具调用被允许并执行后,结果仍然以标准 tool_result 的形式回到上下文;第二轮模型读取这个结果,再生成最终回答。换句话说,s03 只是把 "工具调用能不能执行" 这件事加上了运行时审查,并没有破坏原来的模型-工具-结果-再推理流程。

从这次调试可以很清楚地看到,s03 的权限系统并不是一个额外的业务功能,而是插在工具执行边界上的一层统一路由:模型可以申请执行工具,但工具是否真的执行必须先经过 Permission Pipeline。

这就是 s03 相对于 s02 最关键的变化。s02 中,模型生成 tool_use 后基本就会直接进入 handler;而 s03 中,模型生成 tool_use 只代表"申请执行",只有当 check_permission() 返回 True 后,handler 才会真正运行。

OK,以上就是 s03 Permission 工作原理的完整分析了。

那大家感兴趣的话可以试试下面这些 prompt 感受下这个工具执行权限检查加入后的一些变化:

1. Create a file called test.txt in the current directory(应该直接通过)

2. Delete all temporary files in /tmp(bash + rm 会触发闸门 2)

3. What files are in the current directory?(只读,全部通过)

4. Try to write a file to /etc/something(写工作区外,触发闸门 2)

6. 相对于 s02 的变更

组件 之前 (s02) 之后 (s03)
安全模型 无(信任模型) 三道闸门权限管线
新函数 --- check_deny_list, check_rules, ask_user, check_permission
循环 直接执行所有工具 执行前插入 check_permission()

7. 小结

s03 Permission 是新版 learn-claude-code 中非常关键的一节。它虽然代码量不大,但系统意义很大:从这一节开始,Agent 不再是 "模型提出工具调用,程序立刻执行",而是变成了 "模型提出工具调用,运行时先判断权限,再决定是否执行"。

这一节最值得记住的核心思想是:

不要信任模型,要信任运行时边界。

模型可以提出任何工具调用,但真正能不能落地,必须交给 harness 层判断。安全读取可以直接通过;风险操作需要用户确认;绝对危险的命令应该在 handler 执行前就被拦住。

这也是 s03 相对于 s02 的最大升级:s02 解决的是 "模型如何调用多个工具",s03 解决的是 "模型调用工具之前,系统要不要允许它执行"。从这里开始,Agent 的工具系统不再只是一个 dispatch map,而开始具备最基础的权限治理能力。

当然,当前教学版的权限系统仍然是简化版。它用字符串匹配模拟 deny list,用简单规则模拟 risk matching,用命令行输入模拟用户审批。真实 Claude Code 的权限系统会更复杂,包括多来源规则、hooks、工具自身校验、classifier 自动审批和子 Agent 权限冒泡等机制。教程文档也明确说明,教学版是为了降低理解门槛,把生产版复杂机制收缩成 deny / ask / allow 三道闸门。

但正因为它足够简单,我们反而更容易看清楚 Permission 的本质:

Permission 不是让模型更安全,而是在模型和真实世界之间加一个由代码控制的执行闸门。

OK,以上就是本期想要分享的全部内容了。

结语

本篇文章我们围绕新版教程 s03 Permission 这一节,从问题出发,结合 Permission Desk 流程图与代码实现,完整梳理了 Agent 在工具执行前是如何通过三道闸门建立最基础权限治理能力的。

相比 s01 的 Agent Loop 和 s02 的 Tool Use,s03 最本质的变化并不是新增了某个工具,而是在模型与真实执行之间,第一次正式插入了一层 "运行时边界"。从这一节开始,模型不再天然拥有执行权,而只是拥有"申请执行"的能力;真正是否允许落地,开始由 harness 层的 Permission Pipeline 决定。

从工程视角来看,这一步其实非常关键。前面的章节解决的是 "模型如何调用工具",而 s03 开始回答另一个更现实的问题:模型调用了工具之后,系统到底该不该让它执行。 这意味着 Agent 系统的关注点,第一次从 "能力扩展" 进入到了 "能力约束"。

而 Permission Pipeline 的设计也非常克制:既没有推翻原有 Agent Loop,也没有重写工具系统,而只是把 check_permission() 精准地插入在 tool_use → handler 的边界上。正因为它的位置足够清晰,所以整个权限模型也变得非常容易理解:

  • deny:绝对危险,直接阻止
  • ask:存在风险,需要用户确认
  • allow:安全请求,自动放行

这三种结果,本质上对应的是系统对 "模型意图" 的不同信任等级。

进一步来看,s03 的意义并不只是 "增加安全检查",而是让整个 Agent 系统第一次建立起一种非常重要的工程认知:模型负责生成意图,运行时负责决定这些意图能否真正影响世界。

也正因为如此,Permission 的价值并不在于它今天拦住了多少危险命令,而在于它正式把 "执行控制权" 从模型侧收回到了系统侧。从这一刻开始,Agent 不再只是一个会调用工具的循环,而开始逐渐演化为一个具备运行时治理能力的系统。

如果说 s01 建立的是 Agent 的 "行动能力",s02 建立的是 Agent 的 "工具能力",那么 s03 建立的,就是 Agent 最基础的 "边界意识"。而这一步,也正是后面 Hooks、Memory、Error Recovery 等机制能够继续向上扩展的重要前提。

下篇文章我们将来学习新版教程 s04 Hooks 章节的内容,敬请期待🤗

参考

相关推荐
Cosolar2 小时前
2026 年 AI 开源生态全景图
人工智能·面试·大模型·agent·rag
雪碧聊技术2 小时前
AI通识一文详解(大模型应用、大模型服务、大模型API)
人工智能·大模型·agent
带娃的IT创业者2 小时前
开源语音AI的边界:从 `luongnv89/claude-howto` 看前沿技术的落地实践
人工智能·开源·llm·大语言模型·claude·语音交互·语音ai
Highcharts.js3 小时前
|Highcharts图表专家智能体+Highcharts GPT +MCP 服务=智能图表开发服务体系
gpt·信息可视化·llm·图表·智能体·highcharts·图表开发
XLYcmy3 小时前
Agent身份与权限系统设计方案
windows·网络安全·ai·llm·飞书·api·agent
counterxing3 小时前
Reasonix 的设计哲学:不是在 Agent 上加缓存,而是把 Agent Loop 改造成可缓存的形状
agent·ai编程·claude
养肥胖虎11 小时前
完整学习LLM(六):上下文窗口是什么,为什么模型会忘东西
llm·上下文窗口·contextwindow
Terrence Shen12 小时前
Hermes agent的tools是怎么落地应用的系列
人工智能·llm·agent·hermes
Cosolar13 小时前
2026年AI Agent技术生态开源项目合集
人工智能·开源·agent·智能体