构建 Claude Code 的经验教训:以 Agent 的视角看世界

构建 Claude Code 的经验教训:以 Agent 的视角看世界

本文分享了构建 Claude Code 过程中的核心经验教训,从 Agent 设计的视角探讨如何打造高效的AI 智能体。文章深入分析了操作集合(action space)设计的关键挑战,包括工具发现机制的三次迭代演进、AskUserQuestion工具的设计思路,以及如何随模型能力提升重新评估工具需求。同时介绍了从被动 RAG到主动上下文构建的搜索接口演进,以及渐进式信息披露模式在约20个工具间的平衡实践。核心洞察在于:理解模型需要什么,不能依赖固定规则,而需要持续观察模型行为、仔细阅读输出并不断实验调优。

构建 Agent 框架中最困难的部分之一,是设计它的操作集合(action space)·,比如可以调用哪些工具、能发出哪些指令、如何与环境交互等。Agent操作集合的设计得太大会导致 Agent 难以决策,太小则能力受限。

Claude 主要是通过工具调用(Tool Calling)来执行操作,但在 Claude API 中有多种方式来构建工具,包括 bash、skills,以及最近推出的代码执行(code execution)这些非常基础的能力。(更多关于 Claude API 中的编程式工具调用,请查看文章Give Claude a computerAPI文档)。

在面对如此之多选择时,我们该如何设计 agent 的工具?只需要一个工具(比如代码执行或 bash)就够了吗?还是说为Agent构建50个工具,为每个可能遇到的场景各配一个?

为了能从模型的角度去思考问题,我一般假设处在这一场景:自己遇到一个非常难的数学问题。我应该使用何种工具来解决这一难题呢?这完全取决于自己的知识和能力!这也是模型所处的场景,它的知识有限,但是工具很多,需要在所有工具中找出适合解决问题的工具。

最基础的时纸和笔,但是我们将只能慢慢演算。当然如果有计算器那就更好了,但是这是我们就需要知道如何操作计算器,如何使用计算器中的高级功能。当然最快,最好的选择时用电脑,但是这时候就要求我们有能力来编写代码,纠错已让程序正确执行。

这是一个在设计Agent非常有用的思维模式:我们需要要给模型与其自身能力相匹配的工具。但我们怎么知道模型有哪些能力?这时候就要靠观察、阅读模型的的输出、不断实验。我们要学会以Agent的视角来看问题。

以下是我们在构建Claude Code 过程中,通过仔细观察 Claude 所总结出的一些经验。

提升需求发现的能力与AskUserQuestion工具能力

HCLxeR3acAAf2R9.jpg

在构建 AskUserQuestion 工具时,我们的目标是提升 Claude 主动提问的能力(通常称为'需求发现',elicitation)。

虽然 Claude 可以直接用纯文本提问,但我们发现回答这些问题感觉不必要地耗时。我们该如何降低这种无效的沟通,提升用户与 Claude 之间的沟通效率呢?

第一次尝试:修改 ExitPlanTool

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

关于我们为什么要做 ExitPlanTool,可以阅读我们关于prompt caching的文章了解更多。

第二次尝试:改变输出格式

接下来我们尝试修改 Claude 的输出指令,让它使用一种稍有变化的 markdown 格式来提问。例如,我们可以让它输出一组可以单选/多选的问题列表。然后我们可以解析并将这些问题格式化为UI展示给用户。

虽然这是我们能做的最通用的改动,并且Claude在输出这种格式时看起来也还不错,但并不能保证格式的一致性。Claude 会额外附加多余的句子、遗漏选项,或者干脆使用完全不同的格式。

第三次尝试:AskUserQuestion 工具

HCL0gcObkAA4tKt.jpg

最终,我们选择了创建一个工具,这样Claude可以在任何时候调用它,但我们为计划模式(plan mode)添加了特定的提示词,让计划模式更倾向使用这个工具。当该工具被触发时,我们会弹出一个模态框来展示问题,在用户回答完毕前会一直阻塞Agent循环。

最重要的是,Claude似乎能非常好的使用这个工具,而且我们发现它的输出效果很不错。如果 Claude 不理解如何调用一个工具,即使是这个工具设计得再好,也没有任何作用。

这是 Claude Code中需求发现的最终形态吗?我们也不确定。正如下一个例子中我们就会看到,一个适用于特定模型的方案,对另一个模型来说未必是最好的,甚至是不可用的。

随能力升级而更新:任务与待办事项

HCLxrfXbEAUXwRV.jpg

首次发布Claude Code时,我们意识到模型需要一个待办清单来跟踪任务的进度。待办事项可以在任务开始时就写好,随着模型完成工作的情况逐项勾选完成。为此我们给Claude提供了TodoWrite工具,用于创建或更新待办事项并展示给用户。

但即便如此,我们仍然经常看到Claude在执行任务的过程中,忘记自己要做什么。为了应对这个问题,我们每隔5轮对话就插入一条系统提示,提醒Claude它的目标是什么。

但随着模型的改进,模型不仅不再需要待办清单的提醒,反而可能觉得这是一种限制。发送待办清单的提醒让 Claude 认为自己必须严格遵循清单,而不是去修改它。我们还发现 Opus 4.5在使用子代理(subagents)方面有了很大进步,但子代理之间该如何协调共享一个待办清单呢?

在发现这一点之后,我们用任务工具(Task Tool)替换了 TodoWrite(更多关于Tasks的内容请参阅此文章)。待办事项的目的是让模型可以跟踪进度,而任务工具则更多是帮助Agent之间相互协调。任务包含了依赖关系,在子代理之间共享更新,模型也可以修改和删除它们。

随着模型能力的提升,我们在过去的模型中曾经非常需要的工具,现在可能反而在限制它们。因此需要持续评估”需要哪些工具“的这个假设。这也是为什么最好只支持少数几个能力水平相近的模型。

设计搜索接口

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

Claude Code刚推出时,我们使用RAG向量数据库来为Claude查找上下文。虽然RAG强大且快速,但它需要索引和配置,而且在各种不同环境下可能比较脆弱。更重要的是,这些上下文是被动提供给Claude 的,而不是由它自己根据当前上下文找到的。

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

这是我们观察到的一个规律:随着Claude变得更聪明,只要给它合适的工具,它在自主构建上下文方面会越来越强。当我们引入 Agent Skills 时,我们正式确立了渐进式信息披露(progressive disclosure)的理念,让 Agent 通过逐步探索来增量地发现相关上下文。

Claude可以读取Skill Files,并且这些文件又可以引用其他文件,模型可以递归地读取它们。事实上,Skills的一个常见用途就是为Claude添加更多搜索能力,比如教它如何使用API或查询数据库。在一年的时间里,Claude从基本无法自主构建上下文,发展到能够跨多层文件进行嵌套搜索,精确找到所需的上下文。渐进式信息披露现已成为我们在不添加新工具的情况下扩展功能的通用技术。

渐进式信息披露:Claude Code Agent的使用指南

Claude Code目前有大约20个工具,我们一直在问自己是否真的都需要它们。添加新工具的门槛很高,因为每多一个工具,就多了一个让模型需要考虑的选项。

例如,我们发现Claude对如何使用Claude Code本身非常不了解。如果你问它怎么添加一个MCP或者某个斜杠命令的作用,它就无法回答了。

我们是可以把所有这些信息放在系统提示词中,但考虑到用户很少问这类问题,这样做会增加上下文腐化(context rot),干扰 Claude Code的本职工作:写代码。

于是我们尝试了一种渐进式信息披露的方式。我们给Claude提供了一个指向其文档的链接,它可以加载文档来搜索更多信息。这种方法虽然可以完成对应的任务,但我们发现Claude会把大量结果加载到上下文中来寻找正确答案,而实际上你需要的只是那个答案本身。

因此我们构建了Claude Code使用指南子代理,当你询问 Claude Code 自身相关问题时,Claude会被提示调用这个子代理。该子代理有详尽的指令,指导它如何高效地搜索文档以及应该什么内容。

虽然这个功能还不完美,当你问Claude如何配置它自己时,它仍然可能会出现混乱,但已经比以前好多了!我们在没有添加新工具的情况下,扩展了Claude 的操作集合。

这是个手艺活

如果你期望的是一套关于如何构建工具的刚性规则,很遗憾这篇文章不是。为模型设计工具既是科学,也是艺术。它在很大程度上取决于你使用的模型、Agent 的目标以及它所运行的环境。

多做实验,仔细阅读输出,尝试新方法。以 Agent 的视角看世界。

译者评注

Agent的开发不是一蹴而就的工作,很多时候我们的设计是很好的,但是实现后发现效果却非常差劲。这篇文章也是非常清晰的点出了自己在做Agent开发时候的一些问题。因此我们在开发Agent的时候应该多从模型和Agent的视角去看待问题,需要根据模型的能力,提供相匹配的工具,从而确保能充分的发挥出模型本身的实力。

就像自己前一段使用GLM5/MiniMax配合Claude Code进行开发的经历非常相似,对于一个模型进行了调优的工具,对另外一个工具未必合适。这里面不单单是提示词这单一的方面,也是模型对工具能力理解和使用的差异。

因此我们在开发Agent的时候,要遵循循序渐进的原则,先思考自己要解决的问题,都需要哪些必要的工具,并且使用日志等手段,尽可能地观察模型针对输入产生的输出和工具使用情况。

原文:Lessons from Building Claude Code: Seeing like an Agent