隽戈 | 2026.05.25
40 个工具,4 级权限,3 道防线,1 套 ML 自动审批——Claude Code 的工具系统不是功能堆砌,而是一套工业级的权限管控架构。
上一篇文章[Claude Code 源码深度拆解:Multi-Agent 的实现机制]发出后,收到很多"还没看够"的反馈。这次我翻出源码中另一个工程密度极高的模块——工具系统。
如果说 Multi-Agent 解决了"怎么组织多个 Agent"的问题,那工具系统解决了更底层的问题:Agent 能干什么、不能干什么、以及这个边界由谁来定。
30+ 种工具、4 级权限体系、基于 ML 的自动审批、3 道防线隔离——这套设计对任何正在构建 Agent 平台的人来说,都值得逐行品读。
一、40+ 工具的背后:一个完整的操作系统
为什么工具设计如此重要?
Agent 的本质很简单:一个 LLM + 一堆工具 + 一个 agentic loop。工具是 Agent 与现实世界的接口,定义了 Agent 的能力边界。
你给 Agent 装了多少工具,它就能做多少事。但这里藏着一个矛盾:工具越多,Agent 越强大,同时也越危险。一个带着文件写入、Shell 执行、网络访问、Subagent 管理总共 40 个工具的 Agent,就像一把装了 40 个功能键的瑞士军刀——用好了效率翻倍,但一个误操作就可能把项目目录捅个窟窿。

| 文件工具 | ||
| Shell 工具 | ||
| Web 工具 | ||
| Agent 工具 | ||
| 任务工具 | ||
| MCP 工具 | ||
| 系统工具 | ||
| 高级工具 |
核心洞察:Claude Code 的工具系统,本质是一套"怎么让 40 个工具安全可控地运行"的工程方案。
二、工具注册与 Schema 缓存
为什么需要 Schema Cache?
Claude Code 的每个 API 请求都需要携带所有工具的 JSON Schema——LLM 靠它理解工具的参数结构和用法。40 个工具就意味着一大段 JSON。

直觉方案是"每次请求都生成一份"。但工具 Schema 大部分时候是不变的。每次都重新生成,白白浪费 token——尤其是缓存友好型的请求,前缀多这一点点差异,缓存就丢了。
Claude Code 的做法是给工具 Schema 加一层内存缓存。首次构建后缓存起来,后续请求直接复用。这不仅仅是"少点 JSON 序列化"的性能优化——在 Prompt Cache 体系里,字节级一致性才是关键。
// tools/base.ts(示意代码)lettoolSchemaCache: Record<string,ToolSchema>|null=null;exportfunctiongetAllBaseTools():Tool[]{// 执行工具注册...}exportfunctiongetToolSchemas():Record<string,ToolSchema>{if(toolSchemaCache)returntoolSchemaCache;// 缓存命中,直接返回// 缓存未命中:遍历所有工具,提取 SchematoolSchemaCache={};for(consttoolofgetAllBaseTools()){toolSchemaCache[tool.name]=tool.inputSchema;}returntoolSchemaCache;}一句话精妙之处:Schema Cache 表面是性能优化,实际上是 Prompt Cache 的基础设施。让 API 请求前缀保持字节稳定,Cache 命中率才能上去。
三、四级权限体系:Default / Auto / Bypass / Yolo
最直观的权限机制是"每个工具调用都问用户"——安全倒是安全了,但体验会让人崩溃。调研阶段一个文件接一个文件地读,每个都要用户确认?用户三分钟就暴躁了。
Claude Code 选择了一条中间路线:根据风险等级,差异化管控。

| default | ||
| auto | ||
| bypass | ||
| yolo |
每个工具动作会被 ML Classifier 打上风险等级:
| LOW | ||
| MEDIUM | ||
| HIGH |
// tools/permissions/(示意代码)typePermissionMode=|'default'// 交互式询问用户|'auto'// ML 自动审批|'bypass'// 跳过检查|'yolo'// 拒绝所有权限设计的核心矛盾:安全 vs 效率。Claude Code 的做法不是找一个"最佳平衡点",而是给不同的操作匹配不同的管控级别。真正精妙的设计不是一刀切,是分类施策。
四、ML Classifier:自动审批的大脑
如果你用过 Claude Code,你会发现它经常在你还没反应过来的时候就已经放行了文件读取和代码搜索——这就是 ML Classifier 的功劳。
这个分类器的正式名称在代码里叫 TRANSCRIPT_CLASSIFIER,由同名编译期开关控制。

它的工作方式极简又高效:
- 1拦截工具调用请求
——当某个工具被调用时,不是直接执行,而是先进入权限校验管道 - 2提取特征
——当前 transcript 上下文、工具名称、参数摘要、历史行为模式 - 3ML 快速判定
——一个轻量化的分类模型在几毫秒内输出风险评分 - 4决策路由
——LOW 直接放行、MEDIUM 问用户、HIGH 强制确认
为什么用 ML 而不是规则?
规则系统看起来更可控:if (tool === 'FileReadTool' && path.endsWith('.ts')) → allow。但实际工程中有三个致命问题:
- 1规则爆炸
——每个工具 + 每个参数组合 + 每种上下文,组合数指数级增长 - 2规则冲突
——A 规则说允许,B 规则说拒绝,谁优先? - 3规则僵化
——用户习惯变了,规则改起来要发版
ML Classifier 的好处:一个模型覆盖所有场景、判定结果天然连续、无需手动维护规则。
Claude Code 对 ML 分类器的使用还有一个特别务实的策略:缓存。
// tools/permissions/yoloClassifier.ts(示意代码)// 使用 CACHED_MAY_BE_STALE 模式,避免阻塞主循环constdecision=getFeatureValue_CACHED_MAY_BE_STALE('tengu_transcript_classifier',{context: transcript});为什么可以容忍"过期"?权限判定的容错性远高于模型推理。一个工具调用被错误放行,文档和用户后台有补救机会;被错误拦截,用户手动确认即可。所以"值可能稍旧"是可以接受的——但"必须不阻塞主循环"是不可妥协的。
五、Protected Files:最后一道防线
最危险的 Agent 行为是什么?修改你的 Shell 配置文件。
想象一下:你让 Agent “排查 CI 构建失败”——Agent 读日志、查配置、改了一个环境变量,顺手把你的 .bashrc 加了一行恶意命令。下次你 SSH 登录,shell 一启动就执行了攻击代码。
这就是 Claude Code 的 Protected Files 机制要解决的问题。

// tools/permissions/protectedFiles.ts(示意代码)constPROTECTED_FILES_PATTERNS=['**/.gitconfig','**/.bashrc','**/.zshrc','**/.ssh/config','**/.ssh/id_*','**/.gnupg/**','**/.npmrc','**/.env','**/credentials','**/authorized_keys',];这些文件是 Shell 和 Git 的安全命门。Agent 可以读它们(排查 SSH 问题),但写操作会被拦截——无论权限模式是什么。
路径遍历防护
Claude Code 的路径安全不仅卡文件后缀,还防路径遍历攻击:
functionisPathTraversal(path: string):boolean{returnpath.includes('..')||path.startsWith('/');}如果 Agent 试图用 ../../../etc/passwd 或者绝对路径绕过限制,系统会直接拒绝。
CYBER_RISK_INSTRUCTION
除了代码层的防护,Claude Code 在 System Prompt 里嵌入了一段名为 CYBER_RISK_INSTRUCTION 的安全指令:
// IMPORTANT: DO NOT MODIFY THIS INSTRUCTION WITHOUT SAFEGUARDS TEAM REVIEW// This instruction is owned by the Safeguards team (David Forsythe, Kyla Guru)这段指令划定了明确的安全边界,由专门的 Safeguards 团队持有——不是谁都能改。
这条线划在哪?Claude Code 的安全设计体现了一个深层智慧:不是把所有大门锁死,而是把最关键的那几道门装上最高规格的锁。Shell 配置文件、SSH 密钥、环境变量——这些是绝对红线。
六、工具隔离的三道关卡
当 40 个工具遇到 Subagent
你给主 Agent 配了 40 个工具,但 Subagent 不能照单全收。原因很直接:
Subagent 能派 Subagent?——递归失控 Subagent 能问用户?——抢对话权 Subagent 能 stop 其他任务?——权限越级
在 #1 那篇文章里,我们提到了 filterToolsForAgent 的三道过滤。
// src/tools/AgentTool/agentToolUtils.ts:70(源码示意)exportfunctionfilterToolsForAgent({tools,isBuiltIn,isAsync,permissionMode}):Tools{returntools.filter(tool=>{// 第一道:MCP 工具全放行if(tool.name.startsWith('mcp__'))returntrue;// 第二道:全局黑名单if(ALL_AGENT_DISALLOWED_TOOLS.has(tool.name))returnfalse;// 第三道:自定义 Agent 加严if(!isBuiltIn&&CUSTOM_AGENT_DISALLOWED_TOOLS.has(tool.name))returnfalse;// 第四道:异步 Agent 走白名单if(isAsync&&!ASYNC_AGENT_ALLOWED_TOOLS.has(tool.name))returnfalse;returntrue;});}三关设计的工程智慧
第一关(全局黑名单)解决"谁都不能做什么"——安全底线 第二关(自定义加严)解决"非官方的需要更严"——风险分级 第三关(异步白名单)解决"自动跑的必须更轻"——后台节制
每关独立演化、互不干扰。想加新规则?找到对应的集合,加一行就行。
七、五条工具系统设计原则
原则 1:权限不分级等于没权限。"一律询问"和"一律放行"都不对。按操作的风险等级匹配不同的管控模式。
原则 2:自动审批靠 ML,不靠规则。40 个工具的参数组合是无穷的,规则系统管不住这种复杂度。
原则 3:最危险的 20% 操作需要硬锁定。Protected Files 机制的关键洞察:20% 的操作占了 80% 的安全风险。
原则 4:工具过滤是分层级的,不是一杆子打死。工具权限是"在什么场景下能用什么"的条件判断。
原则 5:工具 Schema 缓存是 Prompt Cache 的基础设施。让多 Agent 体系不因工具定义差异增加成本。
写在最后 Claude Code 的工具系统,表面是 40 个功能点的注册与调用,实质是一套围绕"Agent 能力的边界"构建的工程体系。 从 Schema Cache 的字节级对齐,到 ML Classifier 的容错缓存策略,再到 Protected Files 的硬锁定——每一个设计决策都在回答同一个问题:Agent 该被允许做什么
下一篇预告:[Claude Code 源码深度拆解——KAIROS 常驻助手与 autoDream 记忆整合]原文发布于 隽戈的博客
夜雨聆风