Claude Code 的工具与权限:怎么在放手和失控之间画线(中) 上篇把主循环讲完了。循环本身很简单模型产出工具调用SDK 执行结果回灌。真正决定一个 agent 是「好用」还是「危险」的是循环两侧的两样东西——工具给了它什么手权限给它套了什么缰绳。中篇就拆这两样。工具池最多 54 个怎么拼出来的所有内置工具不是一次性全塞给模型的而是由tools.ts里的assembleToolPool()动态拼装。设计空间论文给了一个具体数字最多 54 个内置工具其中 19 个无条件开放35 个受功能开关和用户类型控制。拼好之后再和 MCP 提供的工具合并。为什么要这样分批因为工具的「可见成本」不低每个工具定义都要占上下文每个工具都是模型可能走的一条岔路。把不常用、或当前用户用不上的工具折叠起来既省 token也减少模型「挑花眼」的概率——这跟我们之前聊过的「工具给到六十个它开始挑花眼」是同一个机理只不过 Claude Code 是在 harness 层用功能开关主动控量。官方把内置工具归成五类每一类代表一种「能动性」文件操作读文件、改代码、建新文件、重命名和重组。搜索按模式找文件、用正则搜内容、探索代码库。执行跑 shell 命令、起服务、跑测试、用 git。网页搜网页、抓文档、查报错。代码智能编辑后看类型错误和警告、跳定义、找引用需代码智能插件。此外还有一类「编排工具」派生子代理、向你提问等等。几个关键工具值得单独看Read默认一次读约 2000 行超了要分页。这是刻意的设计——防止一次把整个大文件灌进上下文把窗口吃爆。Grep / GlobGrep 是完整正则对标 ripgrep 的能力。有意思的是官方明确选了正则而不是向量库或 embedding——模型本来就懂代码结构能自己写出精准正则省掉维护索引的复杂度。这也呼应了上篇说的「先做最简单的事」。Edit做的是外科手术式的 diff只改目标片段Write / Replace 负责整文件或新建。终端只显示最小 diff但每次改动都可追溯。Bash维护持久 shell 会话环境变量、工作目录跨调用保留并对命令做风险分级和确认。它还会主动过滤注入尝试比如拦反引号和$()结构在灵活和安全之间取平衡。Task / Agent派生子代理——这块下篇会展开它是把噪音挡在主会话之外的关键手段。权限系统为什么是 deny-first工具越多越需要一道闸门。Claude Code 的权限系统核心是permissions.ts里的deny-first先拒后放求值。规则分三档allow直接放行不弹窗。ask弹窗让你确认。deny直接阻断。求值顺序是先查 deny再查 allow都不命中就落到 ask。这个顺序很重要——deny 优先于 allow意味着你可以先写一条严防死守的 deny比如Bash(rm -rf *)再在上面开若干安全的 allow比如Bash(npm test:)既不会漏掉危险操作也不用每次都点确认。settings.json里就是这么配的{ permissions: { allow: [Bash(npm test:), Read(**)], ask: [Bash(git push:)], deny: [Bash(rm -rf /), Read(.env)] } }除了手写规则Claude Code 还有一个auto-mode 机器学习分类器对模糊操作它自动判断该放行还是拦截减少「审批疲劳」。生产里常见的一个反模式是让 Claude 一天问你四十次npm test要不要跑问到最后你闭眼全点 yes——那一刻整套安全模型就成了摆设。把高频安全命令写进 allow本质上是在保住安全模型的严肃性。Hook比 CLAUDE.md 狠得多的那层理解 Claude Code 的人和不理解的人分水岭往往就在一句话CLAUDE.md 是建议Hook 是强制。CLAUDE.md 的内容是作为上下文喂给模型的模型「尽量照做」但文档自己也说不保证严格服从。如果某件事必须每次都发生、零例外别写进 CLAUDE.md写个 hook——一个在生命周期事件上确定性执行的 shell 命令。Hook 挂在哪些事件上PreToolUse工具执行前、PostToolUse执行后、UserPromptSubmit你提交消息时、SessionStart、Stop、PreCompact等。PreToolUse有个关键机制退出码 2 直接阻断该工具调用并把 stderr 回灌给 Claude 当错误信息。比如下面这段拦危险 Bash 的逻辑COMMAND$(jq -r .tool_input.command $CLAUDE_TOOL_INPUT) if echo $COMMAND | grep -q rm -rf; then echo rm -rf blocked 2 exit 2 # ← 阻断 fi exit 0 # 放行退出 0 放行退出 2 阻断其它退出码是非阻断的警告。这才是真正的护栏拦写受保护目录、在内容推进前先打分——这类事写在 Markdown 里的建议永远保证不了。沙箱执行环境的最后一道墙权限管的是「该不该做」沙箱管的是「做了会不会炸到宿主」。执行环境由shouldUseSandbox.ts决定是否走可选沙箱隔离——把 shell 执行、文件系统操作关进隔离边界里危险命令即使被放行影响也被框住。把这几层连起来看模型想调工具 → 权限系统先判 deny/allow/ask → 若放行PreToolUse hook 还能最后拦截或修改 → 真正执行时沙箱决定隔离边界。这是一条层层递减的防线不是一道开关。一句话带走中篇工具决定了 agent 能碰什么权限决定了它敢碰什么。deny-first 保证危险操作优先被挡hook 保证关键流程零例外执行沙箱保证即便漏了也不炸宿主。这三层加起来才是「放手让 agent 干活」却不会「失控」的真正原因。下篇讲上下文工程200K 窗口怎么被一点点吃掉、接近上限时五段压缩管线怎么触发、子代理怎么把大文件读取的噪音挡在主会话之外、Skills 和 MCP 怎么在不动主循环的前提下给 agent 续能力。参考资料Anthropic.How Claude Code works. https://docs.anthropic.com/en/docs/claude-code/how-claude-code-worksAnthropic.Sub-agents. https://code.claude.com/docs/en/sub-agentsAnonymous.Dive into Claude Code: The Design Space of Today’s and Future AI Agent Systems. arXiv:2604.14228Community.Claude Code: Behind-the-scenes of the master agent loop. https://blog.promptlayer.com/claude-code-behind-the-scenes-of-the-master-agent-loop/作者唐悦玮 | 从后端出发用 AI 拓展到全栈的工程师。