像 Agent 一样看:从 Claude Code 的工具设计中学到什么

再次精读一篇Thariq关于 Claude Code 的文章,Lessons from Building Claude Code: Seeing like an Agent。它讨论的不是某一个具体功能怎么实现,而是一个更难也更本质的问题:我们该如何为 Agent 设计“动作空间”

这篇文章最打动我的地方在于,它没有把工具设计讲成一套静态规范,而是提出了一种很工程化、也很现实的方法:先观察模型如何思考、如何犯错、如何使用工具,然后再反过来塑造工具。 这就是所谓的“像 Agent 一样看”。

如果要用一句话概括全文,我会这样说:

Agent 的工具不是越多越好,也不是越强越好,而是要和模型当前的能力、任务目标以及运行环境相匹配。

这篇文章主要讲了什么

文章围绕 Claude Code 的几个真实设计案例,讲清了一个核心命题:构建 Agent harness 最难的部分之一,不是把工具接进来,而是设计一个适合模型的动作空间。

Claude 可以通过 Tool Calling 使用各种能力,比如 Bash、Skills、代码执行等。问题在于,选项一多,设计就会变复杂:

  • 是不是只给一个通用工具就够了?
  • 还是应该为每类场景都提供一个专门工具?
  • 一个工具到底应该做得多通用,还是多结构化?
  • 模型能力变了以后,原来好用的工具会不会反过来限制它?

文章的回答很有意思:不要先站在工程师视角想“系统应该有什么”,而是要站在模型视角想“如果我是这个 Agent,我现在最适合用什么来完成任务”。

这也是“Seeing like an Agent”的真正含义。

从“给工具”到“按能力塑形”

作者用了一个很直观的比喻:如果让一个人解一道复杂数学题,你会给他什么工具?

  • 一张纸,最低配,但很多计算要手工完成
  • 一个计算器,更强,但前提是你会用复杂功能
  • 一台电脑,能力最强,但你得知道怎么写代码、怎么执行程序

这个比喻背后的关键点是:同样一个任务,最适合的工具取决于使用者本身的能力。

Agent 也一样。一个模型并不是抽象意义上的“智能体”,它是一个具有当前能力边界、偏好和局限的具体系统。工具设计的目标,不是堆满可能性,而是让工具的形状和模型的能力结构尽可能对齐。

下面这张图可以概括文章的整体方法:

观察模型输出

识别摩擦与失败模式

调整工具或交互方式

再次测试实际效果

根据模型新能力继续迭代

这也是全文最重要的工程思路:工具设计不是一次性规划,而是持续观察后的迭代塑形。

案例一:AskUserQuestion 工具是怎么长出来的

文章第一个案例讲的是 Claude Code 里的 AskUserQuestion 工具,也就是让 Agent 更高效地向用户提问。

一开始,Claude 当然可以直接在聊天里提问,但作者发现这种方式有明显摩擦:

  • 用户回答成本高
  • 提问结构不稳定
  • 交互带宽低
  • 很难保证问题总是清晰且有选项

于是他们做了几轮尝试。

第一次尝试:把问题塞进 ExitPlanTool

他们先尝试在 ExitPlanTool 里增加一个参数,让模型在提交计划时顺便给出一组问题。

这在实现上最简单,但语义上很混乱。因为模型一边要产出计划,一边又要对计划提问。如果用户回答和计划冲突,系统到底应该信哪边?模型是不是还得再调用一次工具?

也就是说,这种设计在工程上省事,但在 Agent 的行动语义上不够清晰。

第二次尝试:改输出格式

接着他们尝试让 Claude 用一种稍微结构化的 markdown 来输出问题,比如要求它输出项目符号、括号中的候选项等等,再由前端解析成 UI。

这个方案很通用,但不稳定。模型虽然“多数时候”能照做,但并不保证:

  • 有时会多说几句
  • 有时会漏选项
  • 有时会自己换格式

这暴露了一个典型问题:让模型“模仿结构化输出”和让模型“真正调用结构化接口”,可靠性完全不是一个等级。

第三次尝试:单独做一个 AskUserQuestion 工具

最后他们做了一个专门工具,Claude 可以在任何需要的时候调用它,尤其是在 plan mode 里。工具触发后,UI 会弹出问题列表,并阻塞 Agent loop,直到用户回答。

这个设计的价值很明显:

  • 输出结构稳定
  • 可以强制包含选项
  • 交互更顺滑
  • 更容易在 SDK、skills 等场景里复用

更关键的是,Claude 喜欢调用它,而且调用得对。

这一点特别重要。因为一个工具是否成功,不只取决于它的功能完不完整,还取决于模型是否容易理解“什么时候该用、怎么用、用了之后会发生什么”。

案例二:Todo 不一定一直比 Task 更好

第二个案例更能说明“工具要跟着模型能力变化而变化”。

Claude Code 早期引入了 TodoWrite,让模型把任务写成 Todo list,并在执行过程中逐项勾选。这很好理解,因为早期模型确实容易跑偏,Todo 能起到“把模型拉回轨道”的作用。

但随着模型变强,这种工具开始出现副作用:

  • 模型不再需要频繁提醒
  • 固定 Todo 列表反而限制了灵活调整
  • 子代理之间也难以共享和协同 Todo 状态

于是他们用 Task Tool 替代了 TodoWrite。Tasks 不只是“提醒模型别忘了做什么”,而更像 Agent 之间共享的任务载体:

  • 可以有依赖关系
  • 可以跨子代理同步更新
  • 可以被修改、删除、重组

这里最值得记住的一点是:

一个工具曾经有用,并不意味着它会一直有用。随着模型能力提升,原本用于约束模型的结构,可能会逐渐变成束缚模型的结构。

这其实是做 AI 产品非常容易忽略的一件事。我们经常把某个设计当成固定最佳实践,但在 Agent 场景里,最佳实践往往是随模型能力迁移的。

案例三:搜索不是“喂上下文”,而是“让它自己找上下文”

文章第三个案例讲搜索接口设计,我觉得它非常重要,因为它直接关系到 Agent 如何构建自己的上下文。

Claude Code 早期也使用过 RAG 向量数据库来给模型提供上下文。这种方式快、强,但也有问题:

  • 需要预先索引和维护
  • 在不同环境里容易脆弱
  • 更关键的是,这些上下文是“系统替它找好再喂进去的”

后来他们意识到,如果 Claude 可以上网搜索,那为什么不能自己搜索代码库?

于是他们给了 Claude 一个 Grep 工具,让它自己找文件、自己探索、自己建立上下文。

这意味着系统设计思路发生了变化:

  • 过去是“替模型准备上下文”
  • 后来变成“给模型探索上下文的能力”

这不是一个小改动,而是一个范式变化。因为更强的模型往往不只是更会回答问题,也越来越会主动构建完成任务所需的信息环境。

渐进披露:少加工具,也能增加能力

文章后半部分引出了一个特别值得借鉴的设计思想:progressive disclosure,渐进披露。

它的核心不是把所有能力一次性塞给模型,而是让模型在需要时,逐层发现和展开更多上下文或能力。

少量核心工具

通过搜索发现文档或技能

按需读取更多上下文

必要时调用子代理

完成更复杂任务

这套思路的好处在于,它能在不持续膨胀工具集的前提下,让 Agent 处理更复杂的问题。

文章举了一个具体例子:Claude Code 本身并不总是知道“如何使用 Claude Code”。比如用户问它怎么配置 MCP、slash command 是什么,它未必能答好。

如果把所有文档知识都塞进 system prompt,会带来两个问题:

  • 上下文越来越臃肿
  • 干扰它的主任务,也就是写代码

所以他们没有把文档硬塞进 prompt,而是先给 Claude 文档入口。后来又进一步做了一个 Claude Code Guide 子代理,在用户问“关于 Claude Code 自身的问题”时,由这个子代理专门去检索文档并返回答案。

这里很妙的一点是:他们增加了动作空间,但没有新增一个显眼的大工具。

这说明,给 Agent 增强能力,不一定等于新增 API;也可以通过分层信息、子代理和渐进披露来完成。

这篇文章真正想表达什么

表面上看,这篇文章在讲 AskUserQuestion、Task Tool、Grep、Guide Agent 等具体设计。但如果往下看,它真正想表达的是一个更通用的方法论:

Agent 设计不是先定义一套完美工具箱,再让模型去适应;而是先观察模型如何工作,再让工具逐步贴合模型。

这意味着几个非常重要的设计原则:

1. 工具设计要以模型可用性为中心

一个工具再强,如果模型不会稳定调用,价值就很有限。好的工具不是“理论上功能最多”的工具,而是“模型最容易理解并正确使用”的工具。

2. 不要把当前最优解误当成永久最优解

模型能力在变化,工具的最佳形态也会变化。过去帮助模型保持轨道的结构,将来可能变成限制模型探索的约束。

3. 上下文构建能力本身就是一种核心能力

未来更强的 Agent,不只是更会推理,还会更会“找资料、找线索、找上下文”。因此搜索接口、技能系统、渐进披露机制,都会越来越重要。

4. 新功能不一定对应新工具

如果每次遇到新需求都加一个工具,动作空间会越来越复杂。更成熟的方式,往往是通过文档入口、技能引用、子代理或渐进披露来扩展能力。

对我们自己做 Agent 有什么启发

如果把这篇文章翻译成更可执行的工程建议,我觉得至少有下面几条。

先观察模型,再设计工具

不要只从“人类工程师觉得工具应该长什么样”出发。先看模型在真实任务里是怎么失败的、怎么绕路的、怎么误用工具的。

用结构化接口替代脆弱的格式约定

如果一类交互需要高稳定性,不要过度依赖“让模型输出特定格式再解析”。优先考虑真正的结构化工具调用。

定期审视旧工具是否已过时

模型升级之后,要重新问一遍:

  • 这个工具现在还在帮助模型吗?
  • 还是已经开始限制它?
  • 有没有更适合多代理协作的新抽象?

给模型探索环境的能力

很多时候,最有价值的不是多给它一段上下文,而是多给它一个自己找上下文的入口。

把动作空间控制在足够小但足够强的范围内

工具越多,决策复杂度越高。真正好的动作空间,往往不是最大化能力列表,而是最小化模型的选择负担,同时保证关键能力可达。

结语

这篇文章最后说了一句很值得记住的话:设计模型的工具,既是一门科学,也是一门艺术。

我很认同这一点。因为 Agent 系统不是纯静态软件,它面对的是一个会随着模型能力变化而不断迁移的交互对象。今天有效的设计,明天未必仍然最优。

所以,“像 Agent 一样看”并不是一句口号,而是一种工作方式:

  • 多观察输出
  • 多实验
  • 多调整工具边界
  • 多重新审视原来的假设

Agent 的工具设计,不该从“我们能接多少能力”开始,而应该从“模型真正能顺畅地用什么能力”开始。

这可能才是这篇文章最值得带走的结论。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐