摘要
本文是 Anthropic 团队在构建 Claude Code 过程中,通过持续观察模型行为所总结的工具设计经验。核心观点:设计智能体的工具应当契合模型自身的能力,并需要不断随模型进步而演进。涵盖四个主题:信息引出(AskUserQuestion)、工具进化(Todo → Task)、搜索界面设计、渐进式暴露。
构建智能体框架最难的环节之一,是设计它的行动空间。
Claude 通过工具调用来执行操作,而 Claude API 提供了多种构建工具的方式——bash、技能,以及最近推出的代码执行(关于 Claude API 中以编程方式调用工具的更多内容,可以阅读 @RLanceMartin 的新文章)。
面对这么多选择,你该怎么设计智能体的工具?只需要一个工具(比如代码执行或 bash)就够了吗?还是说需要 50 个工具,每个对应一种可能遇到的场景?
为了让自己站在模型的角度思考,我喜欢做这样一个思想实验:假设有人给了你一道很难的数学题,你希望手边有什么工具来解答?答案取决于你自身的能力。
纸笔是最基本的工具,但你会受限于手工计算的速度。计算器更好,但你需要知道怎么操作那些高级功能。最快、最强大的选择是电脑,但前提是你得会用它来编写和运行代码。
核心原则
你要给智能体的工具应当契合它自身的能力。多做实验,仔细阅读它的输出、不断实验。你要学会以智能体的视角来看世界。
以下是我们在构建 Claude Code 的过程中,通过持续观察 Claude 所总结出的一些经验教训。
改善信息引出:AskUserQuestion 工具的诞生

在构建 AskUserQuestion(向用户提问)工具时,我们的目标是提升 Claude 向用户提问的能力(这种能力通常被称为”引导”或”信息引出”)。
虽然 Claude 可以用纯文本来提问,但我们发现回答这些问题总让人觉得不必要地耗时。如何降低这种摩擦,提高用户与 Claude 之间的沟通带宽?
尝试 #1 — 修改 ExitPlanTool
我们最先尝试的方案是给 ExitPlanTool(退出计划工具)增加一个参数,让它在输出计划的同时附带一组问题。这是最容易实现的方案,但它让 Claude 感到困惑——因为我们同时要求它输出计划和关于计划的问题。如果用户的回答与计划内容冲突怎么办?Claude 需要调用两次 ExitPlanTool 吗?我们需要另想办法。
(关于我们为什么要创建 ExitPlanTool,可以参阅我们关于提示缓存的文章)
尝试 #2 — 修改输出格式
接下来我们尝试修改 Claude 的输出指令,让它使用一种微调过的 Markdown 格式来提问。比如,要求它输出一个带方括号选项的项目符号列表,然后我们解析并渲染为用户界面。
虽然这是我们能做出的最通用的改动,Claude 的输出效果似乎也还行,但并不稳定。Claude 有时会额外添加多余的句子、遗漏选项,或者干脆使用完全不同的格式。
尝试 #3 — AskUserQuestion 工具

最终,我们决定创建一个专门的工具,让 Claude 可以在任何时候调用,同时特别引导它在计划模式下使用。当工具被触发时,我们会显示一个弹窗来展示问题,并阻塞智能体的运行循环,直到用户回答完毕。
这个工具让我们能够向 Claude 请求结构化输出,也确保了 Claude 会给用户提供多个选项。它还为用户提供了组合使用的可能——比如在 Agent SDK 中调用它,或在技能中引用它。
关键经验
即便设计得再精妙的工具,如果 Claude 不知道怎么调用,也毫无用处。Claude 似乎很乐意调用这个工具,而且输出效果很好。
这是信息引出在 Claude Code 中的最终形态吗?我们还不确定。正如下一个例子将展示的,适用于某个模型的方案,换一个模型未必好用。
随能力而进化——从 Todo 到 Task

最初发布 Claude Code 时,我们意识到模型需要一个待办事项列表来保持专注——在开始时列好待办事项,工作过程中逐项勾选。为此,我们给 Claude 提供了 TodoWrite(待办事项工具),用来写入或更新待办事项并展示给用户。
但即便如此,Claude 还是经常忘记自己要做什么。为了应对这个问题,我们每隔 5 轮对话就插入一条系统提醒,提醒 Claude 它的目标。
然而随着模型的进步,它们不仅不再需要这种提醒,反而可能因此受到束缚。被反复提醒待办事项列表会让 Claude 觉得必须严格遵循这份清单,而不是灵活调整。我们还发现 Opus 4.5 在使用子智能体方面有了长足进步,但子智能体之间要如何在一个共享的待办事项列表上协调呢?
看到这些变化,我们用 Task 工具(任务工具)替换了 TodoWrite(关于 Task 的更多信息请点这里)。待办事项的作用是让模型保持在正轨上,而任务则更侧重于帮助智能体之间相互协作。任务可以包含依赖关系、在子智能体之间同步更新,模型还能自由修改和删除它们。
工具会过时
随着模型能力的提升,曾经不可或缺的工具现在可能反而成为束缚。不断重新审视”需要什么工具”的既有假设,这一点至关重要。这也是为什么坚持支持一小组能力水平相近的模型很有帮助。
设计搜索界面
对 Claude 来说,一组特别重要的工具是它用来构建自身上下文的搜索工具。
Claude Code 刚推出时,我们使用 RAG(检索增强生成)向量数据库来为 Claude 查找上下文。虽然 RAG 功能强大且速度快,但它需要索引和配置,在各种不同环境下可能比较脆弱。更重要的是,这种方式是把上下文”喂”给 Claude,而不是让它自己去寻找。
但如果 Claude 能在互联网上搜索,为什么不能搜索你的代码库呢?通过提供 Grep 工具,我们让 Claude 能够自己搜索文件、自行构建上下文。
规律
随着 Claude 越来越聪明,只要给它合适的工具,它在自主构建上下文方面就会越来越强。
当我们引入 Agent 技能时,正式提出了渐进式暴露(Progressive Disclosure,一种让智能体通过探索来逐步发现相关上下文的设计模式)的概念。
Claude 可以读取技能文件,而那些文件又可以引用其他文件,模型可以递归地层层读取。事实上,技能的一个常见用途就是为 Claude 增加更多搜索能力——比如提供如何使用某个 API 或查询数据库的说明。
经过一年的发展,Claude 从几乎无法自行构建上下文,成长为能够在多层文件中进行嵌套搜索,精准找到所需的确切信息。
渐进式暴露如今已成为我们在不增加工具数量的前提下扩展功能的常用手法。
渐进式暴露实践——Claude Code Guide 子智能体
Claude Code 目前有大约 20 个工具,我们一直在自问是否真的全都需要。新增一个工具的门槛很高,因为这会给模型多一个需要权衡的选项。
举个例子,我们注意到 Claude 对 Claude Code 自身的了解还不够充分。如果你问它怎么添加一个 MCP 或某个斜杠命令是做什么的,它答不上来。
我们本可以把所有这些信息都塞进系统提示中,但考虑到用户很少问这类问题,这样做会造成上下文腐蚀,干扰 Claude Code 的本职工作——编写代码。
于是我们换了一种思路,尝试了渐进式暴露:给 Claude 提供一个指向文档的链接,让它在需要时加载并搜索。这个方案有效,但我们发现 Claude 会把大量搜索结果都加载到上下文中来寻找正确答案,而实际上用户需要的只是答案本身。
所以我们构建了 Claude Code Guide 子智能体——当你问 Claude 关于它自身的问题时,会被引导去调用这个子智能体。该子智能体拥有详尽的文档搜索指令,知道应该返回什么内容。
虽然这个方案还不完美——问 Claude 如何配置它自身时,它有时仍会感到困惑——但比起从前已经好太多了!我们在不新增工具的前提下,成功扩展了 Claude 的行动空间。
这是一门艺术,而非一门科学
如果你期望得到一套关于如何构建工具的刻板规则,很遗憾,这篇文章并不是那样的指南。为模型设计工具,与其说是科学,不如说是艺术。它在很大程度上取决于你使用的模型、智能体的目标以及它运行的环境。
结语
多做实验,仔细阅读输出,勇于尝试新事物。学会以智能体的视角来看世界。