工具间的决策树
为什么 description 里要讲'何时用别的工具'
一、原理:工具集是网络不是孤立列表
到目前为止我们看了 opencode 几个工具——read、edit、bash、task、TodoWrite。每个工具自己看都合理。但它们一起作为一个集合,怎么协调?
这是 AI Agent 设计里一个被低估的问题——工具集的整体设计,不是单个工具的设计。
很多 AI 产品在加工具时按"功能完备"思路——缺什么加什么。结果工具集变成一堆孤立的"能做什么"列表。LLM 拿到这个列表后困惑——"我应该用哪个?"
LLM 看工具时面对几个决策:
决策 1:要不要用工具?
用户问"今天星期几"——LLM 应该直接回答(用自己知识)还是调 webfetch 查?
用户问"代码里有没有 TODO"——LLM 应该自己猜还是 grep 查?
判断"工具调用 vs 知识回答"的门槛——是工具集设计的第一道决策。
决策 2:用哪个工具?
用户问"src 目录下有几个 TypeScript 文件"——可以用 glob、可以用 grep、可以用 bash 的 find、可以用 task 派生 explore agent。哪个最合适?
LLM 必须有"工具选择"的能力——根据任务匹配最佳工具。
决策 3:怎么组合工具?
复杂任务需要工具组合:"找到所有 TODO 注释,按文件分组,输出 markdown 报告"。这需要 grep + 文件操作 + 字符串处理。
LLM 要会"工具编排"——把多个工具组合成一个工作流。
这三个决策对应工具集设计的三件事:
- 决策门槛——什么时候应该调工具(而不是直接回答)
- 工具选择——多个候选时选哪个
- 工具编排——如何组合工具
opencode 通过**让每个工具的 description 包含"何时用别的工具"**来解决这三个问题。让我们看怎么做的。
二、案例:opencode 的"何时用别的"模式
我们看几个工具 description 里的"工具间引用":
glob.txt 的引用:
"When you are doing an open-ended search that may require multiple rounds of globbing and grepping, use the Task tool instead."
glob 主动告诉 LLM——"如果是复杂搜索,用 Task 代替我"。glob 的边界是简单文件名匹配;复杂搜索不属于它的能力范围。
grep.txt 的引用:
"If you need to identify/count the number of matches within files, use the Bash tool with rg (ripgrep) directly. Do NOT use grep."
grep 告诉 LLM——"如果你要数匹配数,用 Bash + ripgrep 而不是我"。grep 工具的 description 显示文件 + 行号;数数应该用更专门的 bash 调用。
webfetch.txt 的引用:
"IMPORTANT: if another tool is present that offers better web fetching capabilities, is more targeted to the task, or has fewer restrictions, prefer using that tool instead."
webfetch 主动谦虚——"如果有别的工具更合适,用别的"。这种谦虚让 webfetch 不会被 LLM 滥用为"万能上网工具"。
task.txt 的引用:
"When NOT to use the Task tool:
- If you want to read a specific file path, use the Read or Glob tool instead
- If you are searching for a specific class definition, use the Grep tool instead
- If you are searching for code within a specific file or set of 2-3 files, use the Read tool instead"
task 给出 4 条详细反例——把"轻量任务"重新引导到 read / grep / glob。
bash.txt 的引用(隐性):
bash 工具描述里强调"shell command 执行"——意味着只在需要 shell 能力时用。如果只是读文件,应该用 read 不要 bash cat。
这些引用合起来形成一张工具间的决策网:
有任务需要做
├── 读特定文件?→ Read
├── 找文件名 pattern?→ Glob (如果复杂 → Task)
├── 找文件内容?→ Grep (如果数数 → Bash rg)
├── 跑 shell 命令?→ Bash
├── 上网抓数据?→ Webfetch (如果有更专门工具优先用)
├── 多步骤复杂任务?→ Task (如果简单子任务先用其他工具)
├── 改文件?→ Edit (Edit 之前必须 Read)
├── 全量替换文件?→ Write (尽量优先 Edit)
├── 派生子 AI?→ Task
├── 写待办清单?→ TodoWrite
├── 加载专家知识?→ Skill
└── 问用户?→ Question
这是个显式的决策树。LLM 看到任务后,按决策树走——选择最便宜、最直接、最匹配的工具。
这种决策树不是 opencode 团队凭空设计的——它是从"LLM 实际选错工具的模式"反向推导的。每个 description 里的引用对应一类常见误用:
- LLM 经常用 task 做简单文件读 → task.txt 加反例 4 条
- LLM 经常用 grep 数匹配数 → grep.txt 加引用 bash rg
- LLM 经常用 glob 做复杂搜索 → glob.txt 加引用 task
- LLM 经常用 webfetch 当万能工具 → webfetch.txt 加"prefer better tools"
把这些"误用 → 修正"对应关系全部显式写到工具 description——这是 opencode 团队的隐藏工作量。它不是工具开发的一部分——是工具集协调的一部分。
三、设计启示:怎么设计一个有协调能力的工具集
这一章的核心论点:工具集不是"功能列表"——它是"决策网"。设计工具集需要协调,不只是堆功能。
如果你做 AI 产品有多个工具,下面几条原则有用:
1. 工具集 ≠ 单个工具的集合
设计第 11 个工具时不只考虑这个工具本身——要考虑它跟已有 10 个工具的关系。
- 这个新工具的能力跟哪些已有工具重叠?
- 重叠场景下应该选哪个?
- 我应该在哪些 description 里加引用?
不做这种"协调思考",工具集会变成混乱的列表。
2. 让每个工具"知道"其他工具的存在
每个工具的 description 里要提到几个最相关的工具——主要是"重叠场景"和"上下游关系"。
例:
- read description 提到 glob ("如果需要找文件,先 glob")
- glob description 提到 read ("找到文件后用 read 看内容")
- task description 提到几乎所有工具("很多场景应该用别的,不要用我")
这样 LLM 在看任何工具时,都能感受到"工具集的拓扑"。
3. 从用户误用反推决策树
让 LLM 实际跑你的产品,记录"它用错工具"的模式。每个常见误用对应一个 description 里的明确引用。
不要凭空想象决策树——要从真实数据推。
4. 给"昂贵"工具加最多反例
task 这种贵的工具应该有最详细的"什么时候不用我"清单。否则 LLM 会"默认用最强工具"——浪费 token。
便宜工具(grep、glob)反例少一些就行——用错代价低。
5. 鼓励工具组合
某些任务需要工具链。让 LLM 在工具组合时有引导:
- "读完文件用 Edit 改"
- "Grep 找到后用 Read 看上下文"
- "Webfetch 拿到内容后用 Edit 保存"
这种"工具链"模式在 description 里提一两次——让 LLM 知道工具不是孤立的。
6. 不要重复造工具
如果你发现要加新工具——先问"已有工具能不能稍微改一下满足这个需求"。
opencode 有 read 不需要 read_pdf——read 内部识别 PDF 就够了。如果硬要加 read_pdf,LLM 选择困难。
宁愿单个工具复杂一点,也不要工具集冗余。
7. 工具集要有"清晰职责"
每个工具应该有明确"我是干这个的"——不模糊、不重叠。
如果你发现两个工具职责模糊——合并或者重新划分边界。
8. 定期审视工具集
工具集会随产品迭代变化。每隔一段时间审视:
- 哪些工具用得多?哪些没人用?
- 哪些工具经常被混淆?
- LLM 经常选错哪些场景?
根据数据调整——加/删/改 description / 重新划分边界。
第 3 篇小结
第 3 篇我们看了 10 章工具篇。核心论点贯穿全篇:
工具描述不是 API 文档,是 LLM 教材。每个工具的 description 教 LLM "何时用我、何时不用、跟别的工具什么关系"。
围绕这个论点,10 章覆盖了:
- Tool Calling 协议(3.1)—— LLM 跟世界交互的接口
- 5 阶段流程(3.2)—— tool call 的完整生命周期
- description 设计原则(3.3)—— 给 LLM 的微型教材
- Read 的 12 条规则(3.4)—— 高频工具的"教新手"使命
- Edit 的 9 层 fallback(3.5)—— 错误即教学
- 必须先 Read(3.6)—— 工作流约束的显式表达
- Bash 双语法树(3.7)—— 跨平台一致性的复杂度
- Task 工具(3.8)—— 最贵的工具反例最多
- TodoWrite(3.9)—— 给 LLM 一个外部脚手架
- 工具决策树(3.10)—— 工具集协调
这些机制不是相互独立——它们形成一个有协调能力的工具系统:
- description 教 LLM 何时用(3.3)
- 工作流约束让某些工具有顺序(3.6)
- 错误信息提供修复路径(3.5)
- 工具间引用形成决策树(3.10)
- 状态机让 LLM 有显式认知脚手架(3.9)
读完第 3 篇你应该有了 AI Agent 的"动手能力模型"——理解 tool 怎么设计、怎么协调、怎么处理错误。
接下来第 4 篇我们看 AI 的"记忆"——上下文与记忆系统。这是 AI 能"持续工作"的关键基础设施。