构建 Claude Code 代理系统的经验:以 Agent 的视角看问题
快速阅读
本文总结了 Anthropic 团队在开发 Claude Code 时的宝贵经验。要打造一个强大的 AI 代理(Agent),最困难的部分在于构建其“行动空间(Action Space)”。文章提出了“以 Agent 的视角看问题”的理念,即赋予代理与其能力相匹配的工具。
核心经验包括:
- 优化信息诱导(Elicitation):尝试了多种方法让 Claude 更好地向用户提问。最终开发了专门的
AskUserQuestion工具,能有效地在规划模式下触发弹窗,提升了用户与 Claude 的沟通带宽。 - 能力升级与任务管理:随着模型能力的提升,过去用于辅助模型的 Todo 工具反而成了限制。团队将简单的 Todo 升级为
Task Tool,让任务不仅用于模型自我追踪,更成为多 Agent 之间协作通信的桥梁。 - 搜索引擎设计:从最初依赖脆弱的 RAG 系统“被动接受”上下文,转变为使用 Grep 工具和“渐进式披露(Progressive Disclosure)”机制,让 Claude 自己去搜索、阅读并递归构建代码库上下文。
- 子 Agent 扩展功能:为了让 Claude 了解如何使用自身(如如何配置 MCP),团队并没有直接把文档塞进系统提示词,也没有增加新工具,而是开发了一个专门查询文档的“Claude Code Guide”子代理,成功在不增加主代理工具负担的情况下扩展了功能。
构建 Agent 架构最难的部分之一,就是构建它的行动空间。Claude 通过工具调用(Tool Calling)来执行操作,但是在 Claude API 中构建工具的方式有很多种,比如 Bash 命令、技能(Skills)以及最近推出的代码执行功能。
面对这么多选项,你该如何为你的 Agent 设计工具?你只需要像 Bash 或代码执行这样的单一工具吗?如果你有 50 个工具,分别应对 Agent 可能遇到的各种用例,又该如何处理?
为了站在模型的角度思考,我喜欢想象被抛出一个数学难题。为了解决它,你会希望拥有哪些工具?这取决于你自身的能力!纸笔是最起码的,但你会被局限在手工计算上。计算器更好,但你需要知道如何操作进阶功能。最快、最强大的选项是一台电脑,但前提是你必须懂得如何使用它来编写和执行代码。
这是一个非常有用的工具设计框架:你希望赋予 Agent 的工具,其形态必须与其自身的能力相契合。 但你如何知道它的能力边界在哪里?答案是保持关注,阅读它的输出,不断实验。你要学会“以 Agent 的视角看问题”。
以下是我们密切关注 Claude 在构建 Claude Code 时的几点经验。
改进信息诱导与 AskUserQuestion 工具

在构建 AskUserQuestion 工具时,我们的目标是提升 Claude 提问(即 elicitation,信息诱导)的能力。虽然 Claude 可以直接用纯文本提问,但我们发现回答这些问题会消耗不必要的时间。我们该如何降低这种摩擦,并增加用户与 Claude 之间的沟通带宽呢?
尝试 1:修改 ExitPlanTool
我们最先尝试的是在 ExitPlanTool 中添加一个参数,使其在生成计划的同时,包含一个问题数组。这是最容易实现的方法,但它让 Claude 感到困惑,因为我们同时在要求一个计划和一组关于该计划的问题。如果用户的回答与计划内容冲突怎么办?Claude 需要调用两次 ExitPlanTool 吗?我们需要另一种方法。

尝试 2:改变输出格式
接下来,我们尝试修改 Claude 的输出指令,让它输出一种轻微修改过的 Markdown 格式,以便用来提问。比如,我们可以要求它输出一个带选项的无序列表问题。然后我们可以解析这种格式并将其渲染为用户界面(UI)。虽然这是我们能做的最通用的改动,且 Claude 似乎也能够输出这种格式,但这并不能得到保证。Claude 经常会附加额外的句子、遗漏选项,或者干脆使用一种完全不同的格式。
尝试 3:AskUserQuestion 工具
最终,我们决定创建一个 Claude 随时都可以调用的专用工具,并且特别提示它在“规划模式(Plan Mode)”下调用。当该工具被触发时,我们会显示一个弹窗展示问题,并阻塞代理的循环,直到用户作答。
这个工具不仅让我们能提示 Claude 给出结构化的输出,还确保了 Claude 为用户提供了多个选项。它也为用户提供了组合此功能的方式,比如在 Agent SDK 中调用或在 Skills 中引用。最重要的是,Claude 似乎很喜欢调用这个工具,而且我们发现它的输出效果很好。

哪怕是设计得最完美的工具,如果 Claude 不理解如何调用它,也是徒劳。这是 Claude Code 诱导机制的最终形态吗?我们并不确定。正如下一个例子所展示的那样,对一个模型有效的方案,对另一个模型可能并非最佳。
随能力升级:Tasks 与 Todos
在我们刚推出 Claude Code 时,我们意识到模型需要一个 Todo(待办事项)列表来使其保持正轨。Todo 可以在任务开始时编写,并在模型完成工作时逐项勾选。为了实现这一点,我们赋予了 Claude TodoWrite 工具,用于写入或更新 Todo 并展示给用户。
但即便如此,我们仍经常看到 Claude 忘记了自己要做什么。作为应对,我们每 5 轮对话就会插入一条系统提醒,告诉 Claude 它的目标是什么。
然而,随着模型的进步,它们不仅不再需要被反复提醒 Todo 列表,反而会觉得这是一种束缚。不断向 Claude 发送 Todo 列表的提醒,会让它认为自己必须死板地遵循这个列表,而不是根据实际情况去修改它。同时,我们也看到 Opus 4.5 在使用子代理(Subagents)方面变得强大得多。可是,子代理们如何在一个共享的 Todo 列表上进行协调呢?

看到这一痛点后,我们将 TodoWrite 替换为了 Task Tool。如果说 Todo 主要是为了让模型保持在正轨上,那么 Task 则更多是用来帮助 Agent 之间相互沟通的。Task 可以包含依赖关系,能在子代理之间共享更新,而且模型可以更改甚至删除它们。
随着模型能力的提升,过去那些你的模型曾经需要的工具,现在可能反而成了约束。 不断重新审视之前对于“需要哪些工具”的假设至关重要。这也是为什么最好只支持少量具有相似能力配置的模型的原因。
设计搜索接口
对 Claude 来说,极其重要的一组工具是“搜索工具”,它可以让模型构建自己的上下文。
在 Claude Code 刚问世时,我们使用了一个 RAG(检索增强生成)向量数据库来为 Claude 寻找上下文。虽然 RAG 强大且快速,但它需要建立索引和额外配置,并且在各种不同的环境中可能显得脆弱。更重要的是,在 RAG 模式下,Claude 是被动被赋予上下文的,而不是自己主动去寻找。
但是,既然 Claude 能够在网络上进行搜索,为什么不让它去搜索你的代码库呢?
通过给 Claude 配备 Grep 工具,我们可以让它自己搜索文件并构建上下文。这也是我们观察到的一种模式:随着 Claude 变得越来越聪明,只要赋予合适的工具,它在自我构建上下文方面的能力就会越来越强。
当我们引入 Agent Skills(代理技能)时,我们将这种“渐进式披露(Progressive Disclosure)”的理念规范化。这种理念允许 Agent 通过探索逐步发现相关的上下文。Claude 可以阅读技能文件,而这些文件又可以引用模型能递归阅读的其他文件。事实上,Skills 的一个常见用法就是为 Claude 添加更多搜索能力,比如给它提供如何使用某个 API 或查询数据库的说明。
在过去一年里,Claude 从几乎无法构建自己的上下文,演变成了能够跨越多个文件层级进行嵌套搜索,准确找出所需上下文的高手。渐进式披露现在是我们用来在不增加新工具的情况下,添加新功能的常用技术。
渐进式披露:Claude Code 指南代理
Claude Code 目前有大约 20 个工具,并且我们不断在问自己:我们真的需要这么多工具吗?增加新工具的门槛很高,因为这给了模型更多需要思考的选项。
举个例子,我们注意到 Claude 对于如何使用 Claude Code 本身知之甚少。如果你问它如何添加一个 MCP,或者斜杠命令(slash command)是做什么用的,它根本答不上来。
我们本可以把所有这些信息全塞进系统提示词(System Prompt)里。但考虑到用户很少问及这类问题,这只会带来“上下文腐烂(Context Rot)”,并干扰 Claude Code 的主要工作——写代码。
相反,我们尝试了一种渐进式披露的方式。我们给了 Claude 一个指向其文档的链接,让它可以加载该链接来搜索更多信息。这确实起效了,但我们发现 Claude 为了找到正确答案,会把大量搜索结果加载到上下文中——而实际上你需要的只是那一个答案而已。
于是,我们构建了 Claude Code Guide 子代理(Subagent)。当用户问起 Claude 自身相关的问题时,主模型会被提示去调用这个子代理。该子代理内置了关于如何高效搜索文档以及如何返回精准结果的详细指令。
虽然这并不完美(当你让 Claude 设置自身时它仍可能犯迷糊),但这比以前要好太多了!通过这种方式,我们在不增加新工具的情况下,成功扩展了 Claude 的“行动空间”。
一门艺术,而非科学
如果你期望这篇指南能提供一套严格的工具设计法则,那恐怕要让你失望了。
为你的模型设计工具,是一门科学,更是一门艺术。 它极大地依赖于你所使用的模型、Agent 的终极目标,以及它所处的运行环境。
多做实验,阅读输出,勇于尝试新事物。学会“以 Agent 的视角看问题”。
🔗 原文链接:Lessons from Building Claude Code: Seeing like an Agent