构建智能体工具带时,最棘手的部分之一就是为其设计动作空间。
Claude 通过工具调用来执行操作,但在其 API 中,你可以使用多种方式,如 bash、skills 以及最近新增的代码执行等原语来构造工具(关于 Claude API 中的编程工具调用,可阅读 @RLanceMartin 的新文章以了解更多)。
面对这么多选项,你该如何为智能体设计工具呢?难道只提供一个像代码执行或 bash 这样的单一工具就够了吗?又或者,如果你的智能体可能遇到 50 个不同的用例,是否就该为每个用例配备一个专属工具呢?
为了理解模型的思维方式,我喜欢想象自己被给予一个困难的数学问题。你会选择用什么工具来解决它?这完全取决于你自己的技术水平!
纸和笔是最基础的,但你将受限于手动计算。计算器会更好些,但你需要懂得如何操作其高级功能。最快、最强大的选择无疑是计算机,但前提是你必须知道如何用它来编写和执行代码。
这个类比为我们设计智能体提供了一个有用的框架:你想为它提供与其自身能力相匹配的工具。但如何得知这些能力是什么呢?你需要集中注意力,仔细阅读它的输出,不断进行实验。换句话说,你要学着像智能体一样看问题。
以下便是我们在构建 Claude Code 的过程中,通过观察 Claude 学到的一些宝贵经验。
改进启发(Elicitation)与 AskUserQuestion 工具
在构建 AskUserQuestion 工具时,我们的目标是提升 Claude 主动提问的能力(这一过程通常被称为“启发”)。

尽管 Claude 可以直接提出问题,但我们发现用户回答这些问题的过程似乎带来了不必要的耗时。我们该如何降低这种摩擦,从而增加用户与 Claude 之间的交流带宽呢?
我们最初的尝试是向 ExitPlanTool 添加一个参数,以便在生成计划的同时,附带一系列问题。这是最容易实现的方式,但这却让 Claude 感到困惑,因为我们同时要求它制定一个计划,又要提出关于该计划的一系列问题。试想,如果用户的回答与计划内容相冲突怎么办?Claude 需要调用两次 ExitPlanTool 吗?显然,我们需要另一种方法。
尝试 2:改变输出格式
接下来,我们尝试修改给 Claude 的输出指令,要求它使用一种略微修改过的 markdown 格式来提问。例如,我们可以让它输出一个带括号选项的要点问题列表,然后由我们来解析这些问题并将其格式化为用户界面。
虽然这是我们能做的最通用的更改,Claude 有时也能很好地输出这种格式,但这并不能得到保证。Claude 可能会附加额外的句子、省略选项,或者干脆使用完全不同的格式。
尝试 3:AskUserQuestion 工具

最终,我们决定创建一个 Claude 可以在任何时候调用的专用工具,并特别提示它在“计划模式”期间使用。当该工具被触发时,我们会显示一个模态框来呈现问题,并暂停智能体的运行循环,直到用户给出回答。
这个工具允许我们引导 Claude 进行结构化输出,并确保它能为用户提供多个选项。同时,它也赋予了用户组合此功能的能力,例如在代理式 SDK 中调用它或在技能中引用它。
最重要的是,Claude 似乎很喜欢调用这个工具,并且我们发现其输出效果良好。即使是最好的设计工具,如果 Claude 不知道如何调用它,也是行不通的。
这就是 Claude Code 中启发的最终形态吗?我们并不确定。正如你将在下一个例子中看到的,适用于一个模型的方法,可能并不适用于另一个模型。
更新能力:从待办事项到任务
当我们首次发布 Claude Code 时,我们意识到模型需要一个待办事项列表来保持工作节奏。它可以在开始时写下待办事项,并在工作时进行检查。为此,我们为 Claude 提供了 TodoWrite 工具,它可以编写或更新待办事项并将其显示给用户。
但即便如此,我们也常常观察到 Claude 会“忘记”自己的职责。为了适应这种情况,我们每进行 5 轮对话就会插入一条系统提醒,以唤起 Claude 对目标的记忆。
然而,随着模型的进化,它们不仅不再需要被提醒待办事项列表,甚至开始发现这种列表的局限性。收到待办事项清单的提醒,会让 Claude 认为它必须严格遵守,而非灵活修改。我们还注意到 Opus 4.5 版本在使用子智能体方面表现更佳,但子智能体之间又该如何协调一个共享的待办事项列表呢?
意识到这一点后,我们用“任务”工具取代了 TodoWrite 工具。待办事项的作用更多是约束模型,使其按部就班;而“任务”的作用则更多是帮助智能体之间进行沟通。任务可以包含依赖关系,在子智能体之间共享更新,并且模型可以更改或删除它们。
随着模型能力的增强,那些它曾经依赖的工具,现在可能会反过来限制它。因此,持续审视之前关于“需要什么工具”的假设至关重要。这也说明了为什么坚持支持一组功能配置相似的小规模模型是有益的。
设计搜索界面
对于 Claude 而言,一组特别重要的工具是搜索工具,它能用来构建自己的上下文。
当 Claude Code 首次推出时,我们使用了一个 RAG 向量数据库来为 Claude 查找相关上下文。虽然 RAG 功能强大且速度快,但它需要索引和设置,并且在许多不同的环境中可能很脆弱。更重要的是,上下文是“给”Claude 的,而不是它自己“找到”的。
但如果 Claude 能在网络上搜索,为什么不能搜索你自己的代码库呢?通过为 Claude 提供一个 Grep 工具,我们可以让它自主搜索文件并构建上下文。
这是我们观察到一个模式:随着 Claude 变得越来越智能,如果给予合适的工具,它会越来越擅长为自己构建所需的环境。
当我们引入智能体技能时,我们正式定义了“渐进式披露”这一概念,它允许智能体通过探索逐步发现相关的上下文。
Claude 可以读取技能文件,而这些文件又可以引用模型能够递归读取的其他文件。实际上,技能的一个常见用途就是为 Claude 添加更多的搜索能力,例如告诉它如何使用某个 API 或查询某个数据库。
在短短一年时间里,Claude 的能力发生了显著变化:从几乎不能构建自己的上下文,发展到能够在多层嵌套的文件中进行搜索,精准定位所需的确切信息。
如今,渐进式披露已经成为我们在不增加新工具的前提下,为系统增添新功能的常用技术。
渐进式披露实战:Claude Code 指南智能体
Claude Code 目前拥有大约 20 个工具,我们不断在问自己:是否真的需要所有这些工具?添加新工具的门槛很高,因为这会给模型带来更多的选择负担。
例如,我们注意到 Claude 对“如何使用 Claude Code”本身了解不足。如果你问它如何添加 MCP 或者斜杠命令的作用,它可能无法回答。
我们本可以将所有这些信息都塞进系统提示中,但考虑到用户很少主动询问这些信息,这样做只会增加上下文的冗余,并干扰 Claude Code 的主要任务——编写代码。
相反,我们尝试了一种渐进式披露的形式:我们给了 Claude 一个文档链接,它可以下载该链接并搜索更多信息。这个方法虽然有效,但我们发现 Claude 会将大量搜索结果放入上下文,只为找到一个正确答案,而这并非高效的做法。
因此,我们构建了 Claude Code 指南子智能体。当用户向 Claude 询问关于它自身的问题时,Claude 会被提示调用这个子智能体。该子智能体配备了详尽的指令,指导它如何高效地搜索文档以及应该返回什么内容。
虽然这并非完美的解决方案(Claude 有时仍会感到困惑),但当你问它如何设置自己时,效果比以前好太多了!我们成功地在不增加新工具的情况下,扩展了 Claude 的动作空间。
是艺术,而非纯粹的科学
如果你期望这里有一套关于如何构建工具的严格规则,很遗憾,这并不是本指南的目的。为模型设计工具既是一门科学,也是一门艺术。这在很大程度上取决于你正在使用的具体模型、智能体的核心目标以及它所处的运行环境。
关键在于:要经常实验,仔细阅读产出结果,并勇于尝试新事物。试着像一个智能体那样去看待问题,你或许能发现更优的设计路径。对于更多的 智能体 设计思路和 RAG 应用实践,你也可以在技术社区如 云栈社区 中找到同行们的交流与分享。
原文链接:
https://x.com/trq212/status/2027463795355095314