构建 Claude Code 的经验教训:像 Agent 一样思考

@trq212
英语4个月前 · 2026年2月27日
4.0M
11.2K
1.4K
249
28.6K

TL;DR

深入了解 Claude Code 团队如何通过结构化工具调用、任务协调以及渐进式披露技术来优化 Agent 的性能。

构建 Agent 工具集最困难的部分之一,就是设计它的动作空间。

Claude 通过工具调用(Tool Calling)来行动,但在 Claude API 中,有多种构建工具的方式,比如 bash、技能(skills),以及最近的代码执行(想了解更多关于 Claude API 中程序化工具调用的内容,可以阅读 @RLanceMartin 的新文章)。

面对这么多选择,你该如何设计 Agent 的工具?你只需要一个工具,比如代码执行或 bash 吗?如果你有 50 个工具,每个对应 Agent 可能遇到的一个用例,那又该怎么办?

为了站在模型的角度思考,我喜欢想象自己面对一道复杂的数学题。你需要哪些工具来解决它?这取决于你自己的技能!

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

这是一个设计 Agent 的有用框架。你要给它提供适合其自身能力的工具。但你怎么知道这些能力是什么?你要关注它的输出、阅读它的结果、进行实验。你要学会像 Agent 一样观察。

以下是我们构建 Claude Code 时,通过关注 Claude 学到的一些经验。

改进引导(Elicitation)与 AskUserQuestion 工具

Thariq - inline image

在构建 AskUserQuestion 工具时,我们的目标是提高 Claude 提问的能力(通常称为引导)。

虽然 Claude 可以用纯文本提问,但我们发现回答这些问题似乎花费了不必要的时间。如何降低这种摩擦,提高用户与 Claude 之间的沟通带宽?

尝试 #1 - 修改 ExitPlanTool

我们首先尝试的是在 ExitPlanTool 中添加一个参数,让它能附带一组问题。这是最容易实现的,但它让 Claude 感到困惑,因为我们同时要求它制定计划并提出关于计划的问题。如果用户的回答与计划内容冲突怎么办?Claude 是否需要调用两次 ExitPlanTool?我们需要另一种方法。

(你可以在 我们关于提示缓存的文章 中了解更多关于我们为什么创建 ExitPlanTool 的内容)

尝试 #2 - 改变输出格式

接下来,我们尝试修改 Claude 的输出指令,让它输出一种稍作修改的 Markdown 格式,用来提问。例如,我们可以让它输出一个带括号选项的要点列表。然后我们可以解析并格式化该问题,作为用户界面展示给用户。

虽然这是我们能做的最通用的改动,而且 Claude 似乎也能较好地输出这种格式,但这并不保证。Claude 可能会添加额外的句子、省略选项,或者使用完全不同的格式。

尝试 #3 - AskUserQuestion 工具

Thariq - inline image

最后,我们决定创建一个工具,Claude 可以在任何时候调用它,但特别提示它在计划模式下调用。当工具触发时,我们会显示一个模态框来展示问题,并阻塞 Agent 的循环,直到用户回答。

这个工具让我们能够提示 Claude 输出结构化内容,并帮助我们确保 Claude 给用户提供多个选项。它还让用户能够组合使用这个功能,例如在 Agent SDK 中调用它,或者在技能中引用它。

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

这是 Claude Code 中引导功能的最终形态吗?我们不确定。正如你在下一个例子中会看到的,对一个模型有效的方法,对另一个模型可能不是最好的。

根据能力更新 - 任务与待办事项

Thariq - inline image

当我们首次推出 Claude Code 时,我们意识到模型需要一个待办事项列表来保持正轨。待办事项可以在开始时写入,并在模型工作时逐一勾选。为此,我们给了 Claude TodoWrite 工具,它可以写入或更新待办事项并显示给用户。

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

但随着模型的改进,它们不仅不再需要被提醒待办事项列表,反而可能觉得它有限制。收到待办事项列表的提醒会让 Claude 认为它必须坚持列表,而不是修改它。我们还看到 Opus 4.5 在使用子 Agent 方面变得更好,但子 Agent 如何在共享的待办事项列表上协调呢?

看到这一点,我们用 Task 工具替换了 TodoWrite(在这里了解更多关于 Task 的内容)。待办事项是为了让模型保持正轨,而 Task 则更侧重于帮助 Agent 之间相互通信。Task 可以包含依赖关系,在子 Agent 之间共享更新,模型还可以修改和删除它们。

随着模型能力的提升,你曾经需要的工具现在可能反而限制了它们。不断重新审视之前关于需要哪些工具的假设非常重要。这也是为什么坚持支持一小批能力特征相似的模型是有用的。

设计搜索界面

对 Claude 来说,特别重要的一组工具是搜索工具,它们可以用来构建自己的上下文。

当 Claude Code 刚推出时,我们使用 RAG 向量数据库来为 Claude 查找上下文。虽然 RAG 强大且快速,但它需要索引和设置,并且在各种不同的环境中可能很脆弱。更重要的是,Claude 是被给予这个上下文,而不是自己找到上下文。

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

随着 Claude 变得更聪明,我们看到了一个模式:如果给予正确的工具,它越来越擅长构建自己的上下文。

当我们引入 Agent 技能时,我们正式化了渐进式披露(progressive disclosure)的概念,它允许 Agent 通过探索逐步发现相关上下文。

Claude 可以读取技能文件,而这些文件可以引用其他文件,模型可以递归地读取这些文件。实际上,技能的一个常见用途是为 Claude 添加更多搜索能力,比如给它关于如何使用 API 或查询数据库的指令。

在一年时间里,Claude 从几乎不能构建自己的上下文,发展到能够跨多个文件层进行嵌套搜索,以找到它需要的精确上下文。

现在,渐进式披露是我们用来添加新功能而不增加工具的常用技术。

渐进式披露 - Claude Code 指南 Agent

Claude Code 目前大约有 20 个工具,我们经常问自己是否真的需要所有这些工具。添加新工具的门槛很高,因为这会给模型多一个选项去考虑。

例如,我们注意到 Claude 对如何使用 Claude Code 了解不够。如果你问它如何添加 MCP 或某个斜杠命令的作用,它无法回答。

我们本可以把所有这些信息放在系统提示中,但考虑到用户很少问这些问题,这会导致上下文腐烂,并干扰 Claude Code 的主要工作:编写代码。

相反,我们尝试了一种渐进式披露的形式。我们给 Claude 一个指向其文档的链接,它可以加载该链接来搜索更多信息。这很有效,但我们发现 Claude 会加载大量结果到上下文中以找到正确答案,而实际上你只需要答案。

因此,我们构建了 Claude Code Guide 子 Agent,当用户询问关于 Claude Code 自身的问题时,Claude 会被提示调用它。该子 Agent 有关于如何有效搜索文档以及返回什么内容的详细指令。

虽然这并不完美——Claude 在被问及如何设置自身时仍然可能感到困惑——但比以前好多了!我们能够在没有增加工具的情况下,向 Claude 的动作空间添加内容。

一门艺术,而非科学

如果你希望得到一套关于如何构建工具的严格规则,那么很遗憾,这不是本指南要提供的。为你的模型设计工具既是一门艺术,也是一门科学。它很大程度上取决于你使用的模型、Agent 的目标以及它运行的环境。

经常实验,阅读你的输出,尝试新事物。像 Agent 一样观察。

存到 YouMind

使用 YouMind 深度阅读爆款文章

保存原文、追问细节、总结观点,并在一个 AI 工作空间里把爆款文章沉淀成可复用笔记。

了解 YouMind

更多可拆解样本

近期爆款文章

探索更多爆款文章