Clipping 微信公众号

从构建Claude Code中汲取经验教训:像Agent一样思考

by 扶苏 原文 ↗
Created: 2026-06-21

公众号名称:奇点先锋

作者名称:扶苏

发布时间:2026-03-06 09:00

Claude Code核心开发者Thariq发了篇技术文章《Lessons from Building Claude Code: Seeing like an Agent 》,下面是对该文章的概述。

在构建 Agent框架时,最棘手的工程挑战之一是动作空间 (Action Space) 的构建。

Claude 的核心驱动力是 Tool Calling(工具调用)。但在 Claude API 中,我们可以通过底层原语(如 bash 执行、skills 技能集以及近期的代码执行功能)来构建工具。面对如此多样的技术栈,我们该如何为 Agent 设计工具?是仅提供 bash 或代码执行等单一底层接口,还是穷举 50 种工具以覆盖所有边缘场景?

为了将自己代入大模型 (LLM) 的思维链路,我喜欢将其比作解一道复杂的数学题。你需要什么工具?这完全取决于你的“出厂能力”:

  • 纸和笔

    是最低配置,但计算带宽受限于人力。

  • 计算器

    是进阶选择,但你需要掌握复杂函数键面的交互语法。

  • 计算机

    是性能上限,但前提是你必须具备编写和执行代码的能力。

这是一个用于 Agent 架构设计的极佳思维框架:为你配置的工具,必须与模型自身的基座能力严格对齐。 而掌握这种匹配度的方法只有一种:监控日志、解析输出流、持续进行灰度实验。简而言之:学会用 Agent 的视角去审视系统。

以下是我们在研发 Claude Code 期间,通过深度监控 Claude 行为流所沉淀的架构经验。


1. 优化意图启发 (Elicitation) 与 AskUserQuestion 工具架构演进

在研发侧,我们希望优化 Claude 的“主动提问”能力(业界常称为 Elicitation / 意图启发)。虽然 Claude 可以直接输出纯文本问题,但这种非结构化的交互带来了过高的沟通摩擦,降低了 User 与 Agent 之间的信息吞吐量。

1.1 尝试一:编辑 ExitPlanTool

我们首先尝试的做法是:给 ExitPlanTool 增加一个参数,让它在返回方案的同时,附带一组问题。这是实现起来最简单的方式,但却让 Claude 产生了混淆 —— 因为我们同时要求它生成方案,又要它针对这个方案提出一系列问题。如果用户的回答与方案内容相矛盾怎么办?Claude 是否需要两次调用 ExitPlanTool?我们需要换一种思路。

1.2 尝试二:修改输出格式

接下来,我们尝试修改对 Claude 的输出指令,让它采用一种略有调整的 Markdown 格式来提出问题。例如,我们可以要求它以项目符号列表的形式输出问题,并将备选选项放在方括号中。之后我们就能对这些问题进行解析与格式化,呈现为给用户的界面。

尽管这是我们能做出的最通用的改动,而且 Claude 输出这种格式时看起来也还算正常,但结果无法保证稳定。Claude 有时会附加多余语句、遗漏选项,或者直接改用完全不同的格式。

1.3 尝试三:AskUserQuestion 工具

最终,我们决定创建一个工具,Claude 可以在任意时刻调用它,尤其在方案模式下会被明确提示去调用。当该工具触发时,我们会弹出一个模态窗口展示问题,并阻塞智能体的执行循环,直到用户做出回答。

这个工具让我们可以引导 Claude 输出结构化内容,也确保了 Claude 能向用户提供多个选项。它还让我们能够灵活组合这一功能,例如在智能体 SDK 中调用,或是在技能中引用。

最重要的是,Claude 似乎很 “愿意” 调用这个工具,实际输出效果也很好。就算设计得再好的工具,如果 Claude 不理解如何调用,也毫无用处。

这就是 Claude Code 中用户引导交互的最终形态吗?我们还不确定。正如你将在下一个例子中看到的,适用于一个模型的方案,对另一个模型未必是最优解。


2. 能力对齐与工具演进:从 Todos 到 Tasks

当模型能力 (Capabilities) 发生迭代时,旧的工具集往往会成为新的运行瓶颈。

2.1 早期阶段:TodoWrite 工具

在 Claude Code 启动初期,模型极度依赖待办事项列表(Todo List)来防止偏离主干 (Context Drift)。

  • 实现方式

    :我们下发了 TodoWrite 工具,允许模型读写 Todos。但为了防止遗忘,我们甚至在每 5 个轮次 (Turns) 强制注入系统级 Prompt 提醒其检查目标。

2.2 瓶颈显现与重构:引入 Task 编排

随着基座模型能力跃升(如 Opus 4.5),高频的系统提醒反而变成了性能约束。模型开始认为自己必须僵硬地遵循已生成的列表,不敢做动态路由修改。更致命的是,面对日益成熟的多子智能体 (Subagents) 架构,单例模式的 Todo List 根本无法处理高并发的跨 Agent 协同。

  • 场景目标

    :实现复杂工作流中的多子智能体 (Subagents) 状态同步与任务依赖树管理。

  • 核心指令 (Tool Command)

    # 废弃 TodoWrite 逻辑,全面迁移至 Task Tool 协议
    call tool: Task
    action: create / update / resolve / delete
    dependencies:[task_id_1, task_id_2]
  • 预期输出/效果

    :生成的任务节点不仅包含执行逻辑,还封装了依赖关系树 (Dependency Graph)。Subagents 可以通过 Task 总线共享 Context,实现异步状态流转,全面接管系统调度权限。


3. 设计上下文检索接口 (Search Interface)

对于代码编写场景,Agent 必须具备自主构建 Context(上下文) 的检索工具。

3.1 摒弃传统 RAG,回归文件系统搜索

早期我们依赖外置的 RAG 向量数据库来实现上下文加载。虽然 RAG 查询速度快,但需要极高的索引开销 (Indexing),且在不同开发者的本地环境中极其脆弱。更核心的问题是:被动投喂 Context 剥夺了模型的探索能力

  • 场景目标

    :提供轻量级、高通用性的代码检索能力,将构建上下文的主动权交还给 LLM。

  • 核心指令 (CLI)

    # 赋予 Claude 对宿主系统的直接扫描权限
    grep -rnw "search_pattern" /path/to/codebase
  • 预期输出/效果

    :模型通过调用 Grep 工具,实现自主遍历代码库,精准抓取文件分片并组合成高密度的 Context,不再依赖重量级的第三方向量服务。

3.2 渐进式暴露 (Progressive Disclosure) 的实践

在引入 Agent Skills(智能体技能) 后,我们将“渐进式暴露”设计理念工程化。Claude 现在可以通过读取某个入口 Skill 文件,递归加载 (Recursively read) 相关的子配置。历经一年的迭代,Claude 已经从“需要外部投喂”进化为“能在嵌套文件系统中执行深度溯源检索”。


4. 突破动作空间上限:Claude Code Guide 子智能体

增加工具的代价极其昂贵。 Claude Code 目前暴露给模型的工具维持在 ~20 个。每增加一个 Tool,都意味着消耗模型的推理配额,增加路由决策的复杂度。

我们在迭代时发现,Claude 对于自身的架构(如:如何挂载 MCP、什么是 Slash Command)认知不足。如果把官方文档硬编码进 System Prompt,将引发严重的“上下文腐败 (Context Rot)”,并干扰其编写代码的核心线程。

因此,我们采用子智能体架构实现了零工具入侵的知识扩充:

  • 场景目标

    :解答关于 Claude Code CLI 自身的用法,防止冗余文档污染全局主 Prompt。

  • 核心指令 (Subagent Routing)

    # 主进程捕获到涉及 "Claude Code 配置" 的意图时,触发子进程
    invoke_subagent: "Claude Code Guide"
  • 预期输出/效果

    :主模型将问题路由给内置了搜索权限的“指南子智能体 (Guide Subagent)”。该 Subagent 在沙盒内执行高复杂度的文档 RAG 检索,提取最精简的答案后,仅将结果变量返回主 Event Loop。我们在不增加全局工具暴露面的前提下,成功扩张了模型的业务能力边界。


5. 架构师的终极法则:Agent 设计是一门工程艺术

如果你期望在 Agent 的动作空间设计上找到一套刻板、可复用的银弹法则,那么本指南可能会让你失望。

为大模型设计工具,本质上是科学与艺术的结合体。它强依赖于你当前调用的模型基座、Agent 被赋予的生命周期目标,以及其宿主环境的物理约束。

保持高频次的实验,深度解析输出日志,灰度测试新的交互原语。最重要的是——学会像 Agent 一样思考并审视系统。


内容效果不满意?点此反馈

输入关键词开始搜索