万字长文拆解 Agent 架构设计(二):工具系统设计¶
Ch04.434 万字长文拆解 Agent 架构设计(二):工具系统设计¶
📊 Level ⭐⭐ | 8.3KB |
entities/claude-code-tool-system-architecture-deep-dive.md
Claude Code 工具系统架构深度拆解¶
Claude Code 的工具系统不是简单的"函数注册表 + 调用分发器",而是包含权限分级、运行时风险评估、子 Agent 递归和两阶段安全分类器的完整架构。
→ 原文存档
摘要¶
如果把记忆系统比作 Agent 的"认知基础设施",工具系统就是 Agent 的"手脚"——没有工具的 Agent 只能说话,有了工具才能做事:读文件、执行命令、调用 API、甚至派出子 Agent。
Claude Code 的工具系统设计围绕一个核心洞察展开:工具的风险画像应当作为工具自身的属性固定下来,而不是散落在外部配置表里。在此基础上,它用统一的三档权限模型(auto/confirm/block)、运行时风险评估、两阶段安全分类器和"子 Agent 即工具"的递归抽象,同时解决好"赋予能力"与"约束越权"这两件事。这篇文章从源码出发,剖析其取舍逻辑与对自建 LLM 工具系统的启示。
核心要点¶
- AgentTool 统一契约:
name/description/inputSchema/defaultPermission/execute/assessRisk?是每个工具的基础接口,defaultPermission是工具自带属性而非外部配置表。 - 三档权限分级:
auto自动执行(如 ReadFile)、confirm需确认(如 WriteFile)、block默认拦截(如 Bash),用户只能调松(block→confirm)不能调紧。 - 运行时风险覆盖静态:
assessRisk()依实际输入动态改写默认权限,让ls放行为 auto、让rm -rf /拦截为 block。 - 子 Agent 即普通工具:与 ReadFile 同处一个工具列表、经 tool_use 调用,其
execute()内是另一个完整 Agent 循环,递归不增加架构复杂度。 - 两阶段安全分类器:先轻量规则快速拦截明显有害命令,再交 LLM 分析意图二分类输出 SAFE/UNSAFE,兼顾安全与效率。
- 默认安全原则:block 级工具不可被绕过,只能由用户显式授权,把最终风险决策交给人。
深度分析¶
为什么权限必须绑定工具而非用户或场景¶
defaultPermission 作为工具本身属性的设计,看似是工程细节,实则是权限建模的哲学选择。若把权限放进"用户 × 场景"的外部配置表,权限边界会随状态漂移、难以审计,每次调用都要做一次复杂的策略解析。把权限绑定到工具,等于把"这个操作的风险等级"固化为工具的自描述信息——工具知道自己是只读的还是破坏性的,这个事实与谁在调用、何时调用无关。
这一做法带来两个直接红利:一是局部可控,审查一个工具的安全性只需看定义,不必追踪所有调用点;二是粒度清晰,ReadFile 天然 auto、Bash 天然 block,权限级别与工具语义强关联,降低模型误判可能。它牺牲的是灵活性(同一工具对不同用户无法差异化授权),换来的是可理解、可审计、可组合——对这种难以一次枚举所有行为的执行体,以工具为最小风险单元是更稳妥的收敛。
运行时风险覆盖:静态快照与动态判断的取舍¶
纯粹静态的权限表虽简单,却无法应对"同一个工具不同输入风险天差地别"的现实——WriteFile 写日志与覆盖系统配置是两种安全级别。assessRisk() 正是为弥合这个差距:它在 execute() 之前基于本次真实输入做轻量评估,可返回覆盖默认权限的结果(如 Bash 对 ls 放行、对 rm -rf / 拦截)。
这本质上是静态分析与运行时分析的桥接:静态配置提供"最坏情况的兜底",运行时评估提供"针对具体输入的加成"。但 assessRisk() 必须足够快、足够可靠,否则要么拖慢每次调用(效率损失),要么因误判放行危险操作(安全损失)。Claude Code 把这条链路放进两阶段分类器,让"快而粗"的规则先挡枪,只有逃过第一层的请求才进入昂贵的 LLM 深判,从而把安全成本控制在可接受范围。
递归子 Agent 如何换取架构极简¶
把子 Agent 定义为普通 AgentTool,是本文最漂亮的抽象复用之一。父 Agent 调用子 Agent 与调用 ReadFile 在语义上毫无二致——都由工具列表驱动、靠 tool_use 触发、遵守同一套权限与执行流程;唯一不同的是子 Agent 的 execute() 内部跑着另一个完整的 Agent 循环。
这一设计的价值在于递归不再需要特化框架:没有独立的"编排层"、专门的子 Agent 协议,同一个运行时天然支持任意深度的嵌套分工。系统只需把 Agent 循环实现一遍,就既能当"顶层执行者"又能当"被调用的工具"。少一个抽象就少一份心智负担与出错缝隙——复杂度没有随"能派出子 Agent"而线性增长,正呼应更广的工程原则:当能力能经既有抽象递归获得时,优先复用抽象而非新建一层。
两阶段分类器:安全与效率的工程平衡¶
安全分类器最常见的失败模式,是"因为代价太高而干脆不用"或"因为用得太随意而拖垮延迟"。Claude Code 的两阶段设计正针对这对矛盾:第一阶段用轻量规则做近乎零成本的粗筛,把明显恶意/危险的命令挡在门口;只有逃过规则、语义模棱两可的请求才进入第二阶段,调用 LLM 分析意图,输出 SAFE(自动执行)或 UNSAFE(阻断并生成拒绝理由)。
实践启示¶
- 把工具的风险画像内建于工具定义:不要在外部配置表里零散维护权限,给每个工具一个明确的默认权限级别,让安全属性随工具一起被审查、被复用。
- 用运行时评估补齐静态盲区:为副作用随输入漂移的工具(尤其是 Bash 类)实现
assessRisk(),让ls放行、让rm -rf /拦截,动态判断并不等于放弃静态兜底。 - 优先用递归复用抽象而非新建框架:当子 Agent、子任务等能力可以经既有工具抽象递归实现时直接复用——少一个特化层就少一处复杂度与维护成本,参见 nanobot 的单文件调度 与 OpenClaw 的工具总线-子 Agent 架构 的同源印证。
- 安全分类要分层并核算成本:用零成本的规则先粗筛、再用 LLM 深判逃逸请求,把安全强度与延迟解耦,而不是让每次调用都为"最重的那个安全机制"买单。
- 默认安全、显式授权:高风险(block 级)工具必须由用户显式放行,且权限只能调松不能调紧——把不可逆破坏性操作的最终责任交给人类决策者,可对照 Agent 安全三步序列 的治理思路。
相关实体¶
- AI Agent 工具数量陷阱 — 5 个边界清楚的工具胜过 20 个模糊工具,与 Claude Code 工具设计互补
- Agent Harness 架构设计与实现 — 生产级 Agent 系统设计参考
- MCP 工具设计权衡 — 工具接口设计的 Anthropic 视角
- OpenClaw 工具总线与子 Agent 架构 — 同源思想的另一实现