Claude Code 最厉害的工具,一行代码都不写
上周帮同事看一个 bug,他用某个 Agent 改了一个 Vue 组件的 if 条件。打开
git diff一看,整个文件被重写了——import 重新排序,单引号全变双引号,两个不相关的注释被删掉了。实际有用的改动就一行,但 diff 有 87 行。Review 的时候根本看不出他到底改了什么。后来我自己用 Claude Code 改了一个类似的条件。同样的任务,它的 diff 只有 3 行:旧条件删掉,新条件加上,完事。周围的代码一个字没碰。
我当时觉得这可能是模型性格差异——有的模型比较谨慎,有的比较奔放。但后来我把几个主流 Coding Agent(Pi、Codex、DSH、opencode)的工具定义全拆了一遍,才发现这不是性格问题,是工具设计问题。
Claude Code 的工具集里藏着一批不写代码的工具——它们不改文件、不跑命令,做的事情是约束 Agent 自己的行为。有的让 Agent 在执行前必须停下来出方案,有的强制它改代码前必须先引用旧内容,有的给无所不能的 Shell 套上权限锁链。
这批工具,才是 Claude Code 和其他 Agent 拉开差距的地方。
两类工具:做事的 vs 管事的
Claude Code 的工具列表(官方参考,数量随版本变化)按功能分,就是一堆"读、写、执行"。但换一个角度——按作用对象分——会看到一个完全不同的结构:
| 作用于代码和环境 | 作用于 Agent 自己 | |
|---|---|---|
| 例子 | Read、Grep、Edit、Bash | PlanMode、AskUserQuestion、Permission、Worktree |
| 做了什么 | 让模型能完成任务 | 让模型约束自己的行为 |
| 别的框架有吗 | 都有 | 几乎没有 |
右边那一列才是 Claude Code 可控性的真正来源。
Pi 只有 4 个工具(read、bash、edit、write),全是左边那一列——给模型工具,让它自己干。opencode 在 Prompt 里写"You MUST iterate and keep going until the problem is solved"——不解决不罢休,但也没有任何"停下来想一想"的机制。Claude Code 的思路不同:给模型工具的同时,给它装刹车——有些操作必须先读后写,有些操作必须等人批准,有些操作根本不让它做。
下面挑三个最值得说的设计,每个背后是一个独立的判断。
一、PlanMode 不是"建议",是断路器
EnterPlanMode 这个工具什么都不做。它不改代码、不读文件、不跑命令。它只做一件事:让 Agent 停下来,切到只读模式,出完方案等人批准了再动手。
问题来了:这事为什么不能在 Prompt 里说一句"复杂任务请先规划"?
因为 Prompt 是建议,Tool 是机制。
模型在上下文短的时候可能遵守 Prompt 里的"请先规划"。但上下文一长、任务一复杂,Prompt 约束最先被模型丢掉。而 Tool 调用是显式的、可被 Harness 拦截的——模型调用 EnterPlanMode,Harness 就切换状态;在 ExitPlanMode 拿到用户批准之前,写操作直接不执行。
本质上,PlanMode 是一个断路器——Agent 自己决定合上开关,Harness 负责在条件满足前不让电流通过。这个设计把"什么时候该谨慎"的决策留给了模型,把"怎么执行谨慎"的控制权交给了 Harness。
其他 Coding Agent Harness 很少这么做。我对比过几家主流的 System Prompt 和工具定义:Pi 只有 4 个工具 154 行 Prompt,完全没有"规划"的概念;Codex 10 个工具,也没有显式的规划工具;opencode 在 Prompt 里写"You MUST iterate"让模型死磕到底,但没有任何机制让它先停下来规划。PlanMode 做成 Tool 这件事,我目前只看到 Claude Code 一家在做。
二、Edit 的 old_string:不是查找替换,是安全机制
Edit 工具的接口很简单:给一段旧内容、一段新内容,匹配上了就替换。
但如果你仔细想,这个设计解决的不是"怎么改文件",而是"怎么防止模型乱改文件"。
Agent 改代码有两条路:
| 整文件重写 | 局部精确替换 | |
|---|---|---|
| 做法 | 模型输出整个文件的新版本 | 只提供要改的那一小段 |
| 风险 | 改一个 if 可能顺手重排 import、改缩进、丢注释 | 改动范围天然最小 |
| diff | 一大片,review 困难 | 干净,一眼能看出改了什么 |
| 前置要求 | 不需要精确读取旧内容 | 必须先读到真实的旧内容 |
Edit 强制走了第二条路。模型不能凭空生成新内容——它必须先把真实的旧内容填进 old_string,这意味着必须先 Read。如果文件已经被改了、old_string 匹配不上,Edit 直接报错中止,而不是静默覆盖。
这个设计把"先读后改"的认知流程焊死在了工具的接口契约里。不靠模型自觉,靠接口强制。
你可能会问:Bash 不是能执行 sed 吗?为什么不直接用 Bash 改文件?
能,但 Claude Code 的 System Prompt 明确引导模型优先用 Edit。因为 Bash 改文件没有 old_string 这种"你必须先知道旧内容才能改"的约束——sed -i 's/old/new/' 不需要知道上下文,匹配上就替换,不管那个 old 是不是你以为的那个 old。
Edit 的 old_string 本质上是一个乐观锁:我先读到了什么,我就只改什么。 世界变了(文件被别人改了),我就停下来。
对比另外两家就很清楚。Codex 用的是 apply_patch——模型生成自由格式的 patch 文本,Harness 去 apply。patch 可以包含任何改动,模型不需要先精确引用旧内容,它可以"描述"要改什么而不是"引用"旧内容。Pi 用 edits[] 数组——一次调用可以同时改多个不重叠的位置,效率更高,但也意味着一次出错影响面更大。
三种设计,三种对模型能力的信任度:
- Pi:信任模型能一次改对多个位置,给批量能力
- Codex:信任模型能生成正确的 patch,给自由格式
- Claude Code:不信任模型凭空生成改动,要求它必须先读、再引用、再改
三、Bash:能力无限,所以必须受限
Bash 是 Claude Code 工具集里最矛盾的。
没有它,Agent 连测试都跑不了——Git、npm、编译器、数据库客户端,所有环境能力都通过 Bash 暴露。但有了它,Agent 理论上可以做任何事:curl 发数据到外部、npm install 执行 postinstall 脚本、git commit 触发 pre-commit hook。
Bash 的能力边界是整个操作系统,但安全边界必须远小于此。
各家 Harness 对这个矛盾的处理方式差异极大。Pi 的 Bash 只有 2 个参数(command + timeout),没有权限控制、没有审批、没有沙箱——完全的 YOLO 模式,信任模型也信任用户。DSH(DeepSeek Harness)走到了另一个极端:Bash 工具有 7 个参数,每个调用必须声明 sandbox_permissions(workspace-write 还是 danger-full-access)和一句 justification 解释为什么要执行,被拒绝了可以升级权限重试。
Claude Code 站在中间——不像 Pi 那么放飞,也不像 DSH 那么重。它的防线是三层叠加:
- 命令分级:常见只读命令(
ls、git status、cat)自动放行;写操作和未知命令弹确认 - 执行链审查:不只看命令本身,还看管道、子命令、alias、脚本调用——
git commit看起来只读,但 pre-commit hook 可能执行任意脚本 - 沙箱兜底:部分部署环境下 Bash 运行在受限沙箱中
但这些防线都不完美。精心构造的命令可以绕过静态检查。Bash 的安全性最终依赖的是:模型判断力 + 用户警觉性 + Harness 兜底,三者缺一不可。
这也是 Claude Code 在 Bash 上最大的 trade-off:给模型一个无所不能的工具,但用所有其他机制来限制它什么时候用、怎么用。 给一个受限的 Edit + 一个受审的 Bash,而不是给一个无所不能的 FileEditor。
工具设计救不了的事
写到这里容易给人一种印象:工具设计够好,Agent 就可控了。
不是。
工具设计解决的是结构性约束——哪些操作必须先读、哪些必须等人批准、哪些互相隔离。它解决不了模型在约束内的决策质量。
实际踩过的坑:
- 模型该
Grep搜内容的时候用了Glob搜文件名,搜不到 - 模型跳过
Read直接Edit,old_string 不匹配失败,再 Read 再 Edit,绕了一圈 - 开了三个子 Agent 并行,三个结论互相矛盾,主 Agent 不知道怎么仲裁
这些问题不是工具设计的锅,是模型推理能力的边界。Claude Code 选择了"20+ 工具 + 分层约束"的路线,给了模型更大的灵活性,也给了更大的犯错空间。
一句话概括这个 trade-off:工具架构决定了可控性的上限,模型能力决定了下限。 底层模型够强时,这套架构能释放出远超简单工具集的效果;模型在决策点上犯糊涂时,再好的工具分层也兜不住。
附:完整工具速查
| 层次 | 工具 | 作用 |
|---|---|---|
| 感知 | Glob Grep Read LSP | 按路径 / 内容 / 语义查找和读取,零副作用 |
| 行动 | Edit NotebookEdit Bash Monitor | 修改文件、执行命令、监听进程 |
| 控制 | AskUserQuestion EnterPlanMode ExitPlanMode Skill | 约束 Agent 行为:询问、规划审批、复用流程 |
| 编排 | Agent Task* TodoWrite TaskStop/Output | 子 Agent 委托、任务状态管理 |
| 隔离 | EnterWorktree ExitWorktree | Git worktree 隔离并行修改 |
| 扩展 | MCP 资源读写、SendMessage ListAgents | 外部资源、Agent 间通信 |
| 交付 | Artifact SendUserFile PushNotification | 产物发布、文件交付、推送 |
| 定时 | Cron* ScheduleWakeup | 会话内定时与唤醒 |
| 其他 | ReportFindings SendFeedback RemoteTrigger ShareOnboardingGuide EndConversation | 审查报告、反馈、远程例程、会话控制 |