April 10, 2026 Claude Code

像 Agent 一样观察:我们如何在 Claude Code 中设计工具

了解 Claude Code 团队如何站在模型的视角来设计、测试并演进工具。

构建 Agent 运行框架时,最难的部分之一是设计它的工具。

Claude 完全通过工具调用来行动,但在 Claude API 中,可以用 bash、skills 和 code execution 等基础能力构造工具。(你也可以阅读 @RLanceMartin 的新文章,了解 Claude API 中的程序化工具调用。)

那么,你该如何设计 Agent 的工具?是给它一个像 bash 或 code execution 这样的通用工具?还是给它五十个专用工具,每个场景一个?

为了进入模型的思维方式,可以想象自己拿到一道很难的数学题。你会希望手边有什么工具?这取决于你自己的能力。

纸至少是必需的,但你会受限于手算。计算器会更好,但你得知道如何使用更高级的功能。最快、最强大的选择是电脑,但你必须会用它编写并执行代码。

这是设计 Agent 时很有用的思考框架。你希望提供的工具要贴合它自身的能力。但你怎么知道它有哪些能力?你需要观察、阅读它的输出、做实验。你要学会像 Agent 一样观察。

如果你在构建 Agent,也会遇到和我们一样的问题:什么时候该加工具,什么时候该删工具,以及如何判断二者的区别。下面是我们在构建 Claude Code 时得到的答案,也包括我们一开始踩过的坑。

用 AskUserQuestion 工具改进提问能力

在构建 AskUserQuestion 工具时,我们的目标是提升 Claude 向用户提问的能力,这通常叫作 elicitation。

Claude 当然可以直接用纯文本提问,但我们发现用户回答这些问题时,会感觉耗费了不必要的时间。怎样才能降低这种摩擦,提高用户和 Claude 之间沟通的信息带宽?

尝试 1:修改 ExitPlanTool

我们最先尝试的方法,是给 ExitPlanTool 增加一个参数,让它在计划之外再携带一组问题。这是最容易实现的修复,但它让 Claude 感到困惑,因为我们同时要求它给出计划,又要求它提出关于计划的问题。如果用户的回答和计划内容冲突怎么办?Claude 需要调用两次 ExitPlanTool 吗?我们意识到这条路行不通,于是回到起点重新设计。(关于我们为什么设计 ExitPlanTool,可以阅读我们关于 prompt caching 的文章。)

尝试 2:改变输出格式

接着,我们尝试更新 Claude 的输出指令,让它使用一种略微修改过的 Markdown 格式来提问。比如,可以要求它输出项目符号问题列表,并把候选答案放在括号里。然后我们再解析这个问题,并格式化成给用户看的 UI。

Claude 通常能生成这种格式,但并不可靠。它会追加额外句子、漏掉选项,或者干脆放弃结构。于是我们继续尝试下一种方案。

尝试 3:AskUserQuestion 工具

最后,我们决定创建一个 Claude 可以在任意时刻调用的工具,并在 plan mode 中特别提示它使用。工具触发时,我们会显示一个模态框呈现问题,并阻塞 Agent 的循环,直到用户回答。

这个工具让我们可以要求 Claude 生成结构化输出,也帮助我们确保 Claude 会给用户多个选项。它还让用户可以组合使用这项能力,比如在 Agent SDK 中调用它,或在 skills 中引用它。

最重要的是,Claude 似乎喜欢调用这个工具,而且我们发现它的输出效果不错。毕竟,即使工具设计得再好,如果 Claude 不理解如何调用它,也没有意义。

这就是 Claude Code 中 elicitation 的最终形态吗?我们并不这么认为。随着 Claude 能力增强,服务它的工具也必须演进。下一节展示了一个例子:曾经有帮助的工具,后来开始碍事。

随能力更新:tasks 与 todos

Claude Code 刚发布时,我们意识到模型需要一个 todo list 来保持方向。Todos 可以在开始时写下,模型工作时逐项勾掉。为此,我们给 Claude 提供了 TodoWrite 工具,用来写入或更新 Todos,并把它们展示给用户。

但即便如此,我们仍经常看到 Claude 忘记自己要做什么。为了适应这个问题,我们每 5 轮插入一次系统提醒,提醒 Claude 它的目标。

随着模型进步,To-do lists 反而变成限制。收到 todo list 提醒会让 Claude 以为自己必须死守这份列表,而不是在发现需要改变方向时修改它。我们还看到 Opus 4.5 更擅长使用 subagents,但 subagents 要如何围绕一份共享的 todo list 协作?

看到这一点后,我们用 Task 工具替换了 TodoWrite 功能。todos 关注的是让模型不偏离轨道,而 tasks 帮助 agents 彼此沟通。Tasks 可以包含依赖关系,可以在 subagents 之间共享更新,模型也可以修改和删除它们。

随着模型能力提升,模型曾经需要的工具可能反过来束缚它们。因此,持续重新审视“需要哪些工具”这个旧假设非常重要。这也是为什么支持模型时,最好集中在一小组能力画像相近的模型上。

设计搜索接口

我们构建过的最重要工具,是那些让 Claude 能够自己寻找上下文的工具。

Claude Code 最早在内部发布时,我们使用 RAG:向量数据库会预先索引代码库,运行框架会在每次响应前检索相关片段并交给 Claude。RAG 强大且快速,但需要索引和设置,在各种环境里也可能很脆弱。最重要的是,Claude 是被动拿到这些上下文,而不是自己找到上下文。

但如果 Claude 可以在网络上搜索,为什么不能搜索你的代码库?通过给 Claude 一个 Grep 工具,我们可以让它自己搜索文件并构建上下文。

Claude 越聪明,在给对工具时就越擅长构建自己的上下文。

当我们引入 Agent Skills 时,我们正式化了 progressive disclosure 这个想法:Agent 可以通过探索,逐步发现相关上下文。

Claude 现在可以读取 skill 文件,而这些文件又可以引用模型能递归读取的其他文件。事实上,skills 的一个常见用途就是给 Claude 增加更多搜索能力,比如教它如何使用 API 或查询数据库。

一年里,Claude 从不太会构建自己的上下文,成长到能够跨多层文件进行嵌套搜索,找到它真正需要的精确上下文。

Progressive disclosure 现在是我们常用的一种技术:不用新增工具,也能增加新功能。下一节会解释原因。

Progressive disclosure:Claude Code Guide agent

Claude Code 目前大约有 20 个工具,我们团队经常重新评估 Claude 是否真的需要全部工具才能发挥最好效果。新增工具的门槛很高,因为这会给模型多一个需要思考的选项。

例如,我们注意到 Claude 对如何使用 Claude Code 了解不够。如果你问它怎么添加 MCP,或者某个 slash command 是做什么的,它答不上来。

我们本可以把所有这些信息放进 system prompt,但用户很少问这类问题,这么做会增加上下文腐化,并干扰 Claude Code 的主要工作:写代码。

于是我们尝试 progressive disclosure:给 Claude 一个文档链接,让它在需要时加载并搜索。这有效,但 Claude 为了找一个用户一句话就能得到的答案,会把大量文档拉进上下文。

所以我们构建了 Claude Code Guide,这是一个 subagent。当用户询问 Claude Code 本身时,Claude 会调用它。这个 subagent 在自己的上下文里搜索文档,遵循详细指令决定如何搜索、提取什么,然后只把答案交回来。主 Agent 的上下文保持干净。

虽然这不是完美方案(当你问 Claude 如何设置它自己时,它仍可能困惑),但我们能够把新能力加入 Claude 的行动空间,而不用新增工具。

像 Agent 一样观察是一门艺术,不只是科学

为模型设计工具既是艺术,也是科学。它强烈依赖你使用的模型、Agent 的目标,以及它运行的环境。

我们最重要的建议是:经常实验,阅读输出,尝试新东西。最重要的是,试着像 Agent 一样观察。

立即开始使用 Claude Code。

作者简介:Thariq Shihipar 是 Anthropic 技术团队成员,负责 Claude Code。

来源:https://claude.com/blog/seeing-like-an-agent