这篇文章分享了构建 Claude Code 过程中关于 AI Agent 工具设计的经验。它强调了根据模型能力调整工具的重要性,通过迭代改进用户提问工具(AskUserQuestion)、任务管理工具(Tasks)和搜索接口,并介绍了“渐进式披露”等策略来优化Agent的交互和上下文构建能力,指出工具设计既是科学也是艺术。

构建 Claude Code 的经验教训:像 Agent 一样思考
Claude 通过 Tool Calling 执行操作,但在 Claude API 中,可以使用诸如 bash、skills 以及最近的代码执行(在 @RLanceMartin 的新文章中阅读更多关于 Claude API 上程序化工具调用的信息)等基本功能来构建工具。
鉴于所有这些选项,你该如何设计 agent 的工具呢?你是否只需要一个像代码执行或 bash 这样的工具?如果你的 agent 可能遇到 50 种不同用例,而每个用例都有一个工具,那该怎么办?
为了设身处地地模拟模型的心态,我喜欢想象自己面对一个困难的数学问题。你会希望拥有哪些工具来解决它?这取决于你自己的技能!
纸张是最低限度,但你会受限于手动计算。计算器会更好,但你需要知道如何操作更高级的选项。最快、最强大的选项是计算机,但你必须知道如何使用它来编写和执行代码。
这是一个设计 agent 的有用框架。你希望为它提供与其自身能力相符的工具。但你如何知道这些能力是什么?你需要关注、阅读它的输出、进行实验。你需要学会像 agent 一样思考。
以下是我们通过在构建 Claude Code 过程中关注 Claude 而学到的一些经验教训。

在构建 AskUserQuestion 工具时,我们的目标是提高 Claude 提问的能力(通常称为 Elicitation)。
虽然 Claude 可以直接用纯文本提问,但我们发现回答这些问题感觉花费了不必要的时间。我们如何降低这种摩擦并增加用户与 Claude 之间的沟通带宽呢?
我们尝试的第一件事是向 ExitPlanTool 添加一个参数,使其在计划旁边包含一个问题数组。这是最容易实现的方法,但这让 Claude 感到困惑,因为我们同时要求一个计划和一组关于该计划的问题。如果用户的回答与计划内容冲突怎么办?Claude 需要调用 ExitPlanTool 两次吗?我们需要另一种方法。
(你可以在我们关于提示缓存的帖子中阅读更多关于我们为什么要创建 ExitPlanTool 的信息)
接下来,我们尝试修改 Claude 的输出指令,以提供一种稍微修改过的 Markdown 格式,它可以用来提问。例如,我们可以要求它输出一个带有备选方案(在括号中)的要点问题列表。然后我们可以将该问题解析并格式化为用户界面。
虽然这是我们可以做出的最普遍的改变,而且 Claude 似乎也擅长输出这种格式,但它并不保证每次都成功。Claude 会附加多余的句子、省略选项或使用完全不同的格式。

最后,我们决定创建一个 Claude 可以在任何时候调用的工具,但在计划模式下,它会被特别提示这样做。当该工具被触发时,我们会显示一个模态窗口来显示问题,并阻塞 agent 的循环,直到用户回答。
这个工具允许我们提示 Claude 输出结构化的内容,它帮助我们确保 Claude 为用户提供了多个选项。它还为用户提供了组合此功能的途径,例如在 Agent SDK 中调用它或在 skills 中引用它。
最重要的是,Claude 似乎喜欢调用这个工具,我们发现它的输出效果很好。即使设计最好的工具,如果 Claude 不理解如何调用它,它也无法发挥作用。
这会是 Claude Code 中 Elicitation 的最终形式吗?我们不确定。正如你在下一个示例中会看到的,适用于一个模型的方法可能不适用于另一个模型。

当我们首次推出 Claude Code 时,我们意识到模型需要一个 Todo list 来使其保持专注。Todos 可以在开始时编写,并在模型完成工作时被勾选。为此,我们给 Claude 提供了 TodoWrite 工具,该工具可以编写或更新 Todos 并将其显示给用户。
但即便如此,我们仍然经常看到 Claude 忘记它必须做的事情。为了适应这种情况,我们每隔 5 轮就会插入系统提醒,提醒 Claude 它的目标。
但随着模型的改进,它们不仅不需要被提醒 Todo List,反而会觉得它具有限制性。收到 Todo List 的提醒让 Claude 认为它必须坚持列表而不是修改它。我们还看到 Opus 4.5 在使用 subagents 方面也做得更好了,但是 subagents 如何在一个共享的 Todo List 上进行协调呢?
看到这一点,我们用 Task Tool 替换了 TodoWrite(在此阅读更多关于 Tasks 的信息)。Todos 是为了让模型保持专注,而 Tasks 更多是为了帮助 agents 之间进行沟通。Tasks 可以包含依赖项,在 subagents 之间共享更新,并且模型可以修改和删除它们。
随着模型能力的提高,你模型曾经需要的工具现在可能会限制它们。不断重新审视关于需要哪些工具的旧假设是很重要的。这也是为什么坚持支持一小组具有相当相似能力配置的模型很有用的原因。
对于 Claude 而言,一组特别重要的工具是可用于构建其自身上下文的搜索工具。
当 Claude Code 首次推出时,我们使用 RAG 向量数据库为 Claude 查找上下文。虽然 RAG 强大且快速,但它需要索引和设置,并且在各种不同的环境中可能很脆弱。更重要的是,Claude 是被动地获得这个上下文,而不是自己去查找上下文。
但如果 Claude 可以在网上搜索,为什么不能搜索你的代码库呢?通过为 Claude 提供一个 Grep 工具,我们可以让它搜索文件并自行构建上下文。
这是我们随着 Claude 变得更智能而看到的一种模式,如果给予适当的工具,它在构建上下文方面会越来越出色。
当我们引入 Agent Skills 时,我们正式确定了渐进式披露(progressive disclosure)的概念,它允许 agents 通过探索逐步发现相关的上下文。
Claude 可以读取技能文件,然后这些文件可以引用模型可以递归读取的其他文件。事实上,skills 的常见用途是为 Claude 添加更多搜索功能,例如向它提供如何使用 API 或查询数据库的说明。
在一年多的时间里,Claude 从一开始无法真正构建自己的上下文,到能够跨多层文件进行嵌套搜索,以找到它所需的精确上下文。
渐进式披露现在是我们用来在不添加工具的情况下添加新功能的常用技术。
Claude Code 目前有大约 20 个工具,我们一直在问自己是否需要所有这些工具。添加新工具的门槛很高,因为它给了模型多一个考虑的选项。
例如,我们注意到 Claude 对如何使用 Claude Code 了解不够。如果你问它如何添加 MCP 或斜杠命令的作用,它将无法回答。
我们可以把所有这些信息都放在系统提示中,但鉴于用户很少问到这些,这会增加上下文腐蚀(context rot),并干扰 Claude Code 的主要工作:编写代码。
相反,我们尝试了一种渐进式披露的形式。我们给了 Claude 一个指向其文档的链接,它可以加载该链接以搜索更多信息。这确实有效,但我们发现 Claude 会加载大量结果到上下文中以找到正确的答案,而实际上你只需要答案。
所以我们构建了 Claude Code Guide subagent,当你询问关于 Claude 自身的问题时,Claude 会被提示调用它,该 subagent 包含了关于如何很好地搜索文档以及返回什么的详细说明。
虽然这并不完美,当你询问如何设置 Claude 自身时,它仍然会感到困惑,但它比以前好多了!我们能够在不添加工具的情况下向 Claude 的行动空间(action space)添加内容。
如果你希望找到一套关于如何构建工具的严格规则,那么很遗憾,这不是本指南的目的。为你的模型设计工具既是一门艺术,也是一门科学。它在很大程度上取决于你使用的模型、agent 的目标以及它所运行的环境。
经常实验,阅读你的输出,尝试新事物。像 agent 一样思考。
- 原文链接: x.com/trq212/status/2027...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!