从 Claude Code 源码看一个 Agent 怎么串起来

借还原版 Claude Code 源码,把编程 Agent 从单循环到 Coordinator 多 Worker 编排串清楚,重点落在“自己做 Agent 时该抄什么、先做什么”。

📅 时间: 23:10

纸上得来终觉浅,绝知此事要躬行。

— 陆游 · 《冬夜读书示子聿》

上一篇我整理的是 Agent 知识地图:workflow、tool calling、state、guardrails、evaluation。那篇解决“该学什么”。

这篇更想回答:如果我要自己做一个能改代码的 Agent,主链路该怎么串?多 Agent 又该怎么加?

我的读法不是功能猎奇,而是倒着问:

源码里哪些设计,是我自己做 Agent 时几乎绕不过去的?

Agent 主循环示意

先定结论:做 Agent 通常分三层

读完还原树,我会把“开发一个编程 Agent”拆成三层,而不是一上来 multi-agent:

  1. 单 Agent 主循环:模型决定下一步,harness 执行工具、回灌结果、控制停止;
  2. 安全与上下文:权限、确认、压缩、可回看的状态;
  3. 多 Agent 编排(可选):需要并行研究或隔离脏上下文时,再上 Coordinator / Worker。

Claude Code 源码里,前两层主要在 QueryEngine.ts + query.ts + tools/*;第三层很清楚地落在 src/coordinator/,尤其是 coordinatorMode.ts

这和 effective agents 的气质一致:先把简单可组合的模式做稳,再加编排复杂度。

做 Agent 的三层

第一层:单 Agent 必须先跑通

如果你只做一件事,就做这件事:

TEXT
用户输入
  → 组装上下文
  → 调模型
  → 若 tool_use:权限检查 → 执行 → tool_result 回灌 → 再调模型
  → 若纯文本 / 达停止条件:结束

在还原源码里:

  • QueryEngine 更像会话管家:持有消息、usage、权限拒绝、文件缓存,对外 yield 一轮结果;
  • query.ts 才是 agent loop:里面有明确的 while (true),负责压缩、调模型、runTools、继续或退出。

QueryEngine.submitMessage() 会把控制交给:

TS
for await (const message of query({
  messages,
  systemPrompt,
  userContext,
  systemContext,
  canUseTool: wrappedCanUseTool,
  toolUseContext: processUserInputContext,
})) {
  // 记录消息、向外推流
}

自己做时,这一层建议写成很薄的循环 + 很厚的外围

你要有的能力源码里对应的直觉
统一工具表 + schemasrc/tools/* 注册,而不是主循环里 if-else
工具结果必须回灌tool_use → 执行 → tool_result 进消息栈
最大步数 / 中断 / 失败退出maxTurns、abort、stop hooks
可观察transcript、usage、permission denials

工具不必一上来很多。编程场景优先这几类就够起步:

  • 读文件 / 改文件
  • 搜索(glob/grep)
  • 受控 shell
  • (可选)MCP 扩展外部能力

工具调用闭环

MCP 的意义不是“多一个时髦词”,而是:核心循环不改,外部能力可插拔。 自己做时,先别把 GitHub、浏览器、数据库全焊死进 prompt 路由。

循环很短,复杂度在 harness

第二层:权限和上下文,决定 Agent 能不能活得久

编程 Agent 危险的是副作用,不是嘴炮。canUseTool 被包装、拒绝会进 permission_denials,说明产品把“拦下来”当成可观察事件。

自己做时,权限别写成 system prompt 里的“请谨慎”,而要写成路径:

  • 读:默认宽;
  • 写工作区:中等,最好有 diff / 可回看;
  • 任意 shell / 网络 / 外部系统:紧,默认确认或拒绝;
  • 会话恢复:不要无脑继承上次的“已批准随便跑”。

上下文同样是硬约束。长跑 Agent 会不断读文件、跑命令、堆日志;query.ts 在调模型前会做 snip / microcompact / autocompact 一类处理。你自己做,至少落地一件:

  • 截断过大的 tool 输出;
  • 或定期压缩历史;
  • 或把脏活丢进子 Agent,只回收摘要。

没有这一层,Agent 不是越用越强,而是越用越胖、越用越飘。

第三层:Coordinator —— 多 Agent 该怎么加

很多人一听 multi-agent,就想“产品经理 + 程序员 + 测试”。Claude Code 的 Coordinator 模式更冷静,也更值得抄。

源码位置:src/coordinator/coordinatorMode.ts
文档:仓库 docs/04-coordinator.md
开关:编译期 feature('COORDINATOR_MODE') + 运行时 CLAUDE_CODE_COORDINATOR_MODE

Coordinator 模式不是“多几个都会写代码的分身”,而是:

角色干什么工具面
Coordinator理解目标、拆任务、综合结果、对用户说话主要是派活 / 通信 / 停工
Worker具体研究、改代码、验证完整(或受控)工具集

Coordinator 与 Worker 分工

getCoordinatorSystemPrompt() 里写得很直白:你是 coordinator,要 direct workers 去 research / implement / verify,并 synthesize 结果;能直接回答的问题不要为了委派而委派。

工具上,提示词明确给了三类核心动作(名称以常量注入为准):

  • Agent:派一个新 Worker
  • SendMessage:给已有 Worker 追加指令(复用它已加载的上下文)
  • TaskStop:停掉跑偏的 Worker

也就是说,指挥官默认不自己操刀改仓库,而是通过这三件事管理执行面。Worker 侧则从允许工具集里拿 Bash/Read/Edit 等(还可带 MCP),但会滤掉一批“内部编排工具”,避免 Worker 再去建团队、互相乱指挥。

标准工作流是四阶段,不是无限自由群聊。 提示词里把大多数任务拆成:

  1. Research(Worker,可并行):查代码、定位问题
  2. Synthesis必须由 Coordinator 自己做):读发现、形成实施规格
  3. Implementation(Worker):按规格改
  4. Verification(Worker):证明改动真的生效,而不是“文件在就行”

四阶段工作流

并发策略也很工程:

  • 只读研究:大胆并行
  • 写同一批文件:串行,避免互相覆盖
  • 验证:有时可与其他区域的实施并行

这比“所有 Agent 同时开麦”靠谱得多。

源码提示词强调:

Workers can’t see your conversation.
Never write “based on your findings”…
You never hand off understanding to another worker.

这直接解决 multi-agent 最常见的翻车:上下文断裂 + 责任漂移。表面上有好几个 Agent,实际上没有人真正理解问题。

Continue 还是 Spawn,不是玄学。 Coordinator 要在“继续已有 Worker(SendMessage)”和“新开 Worker(Agent)”之间选:

场景更合适
刚研究过的文件就是要改的文件Continue,复用上下文
研究很广、实施很窄新开,避免噪声
修失败、小步推进Continue
验证别人刚写的代码新开,独立视角
第一方案完全错了新开,降低锚定

结果怎么回来。 Worker 完成不是靠“聊天里互相 @”,而是结构化通知。提示词约定结果以类似 <task-notification> 的 XML 注入 Coordinator 消息流,带上 task-id、status、summary、result、usage。Coordinator 用 task-id 继续 SendMessage 或 TaskStop。

多 Agent 之间优先用结构化事件,而不是散文式互聊。 事件好路由、好重试、好展示、好计费。

Scratchpad:共享知识用文件,不靠嘴传。 共享状态放在可检视的文件里,不把所有东西硬塞进某个 Agent 的隐藏记忆。

自己实现一个迷你 Coordinator(可抄骨架)

不必复刻 Claude Code。最小可运行版本可以是:

TEXT
Coordinator(只读工具 + 派工工具)
  ├─ spawn_worker(prompt, role)
  ├─ message_worker(id, prompt)
  └─ stop_worker(id)

Worker(读写/shell,独立消息栈)
  └─ 结束后返回 {id, status, summary, artifacts}

再加三条硬规则:

  1. Coordinator 禁止直接 FileEdit / 任意 Bash(或默认关掉);
  2. 派工 prompt 必须包含路径、目标、验收标准;
  3. Research → Synthesis → Implement → Verify 默认按阶段走,写操作默认不并行碰同一文件。

任务板可以用最土的方式:仓库里一个 tasks.json / markdown 清单。文件不够炫,但可审计、可恢复。

子 Agent 隔离与摘要回传

如果你现在就要动手:一条建议路径

结合上一篇知识地图和这篇源码阅读,我会按这个顺序做:

  • 单循环能改小仓库:薄 loop + 读改文件 + 搜索 + 受控 shell + tool_result 回灌 + max steps



  • 权限和可回看:危险动作确认、拒绝可观察、会话日志、基本 diff



  • 上下文治理:截断 tool 输出、简单压缩、失败重试



  • 才加编排:研究 Worker + 实施 Worker;禁止甩锅式委派写进提示与校验


不要反过来:先搭五人团队,再发现连 tool_result 都回灌不稳。

和上一篇怎么对上

上一篇的词这篇源码里的落点自己做时的优先级
Agent Loopquery.ts while 循环P0
Tool Callingtools/* + 回灌P0
GuardrailscanUseTool、权限模式P0
Statemessages / transcript / tasks 文件P0
Contextcompact / snipP1
MCP工具扩展总线P1
Multi-AgentCoordinator + WorkerP2(有并行/隔离需求再上)

一句话:

先做“会停、会调工具、会回灌、会拒绝”的单 Agent;再做“会拆任务、会自包含派工、会收结构化结果”的 Coordinator。

结语

Claude Code 源码给我最大的开发启发,不是目录有多全,而是层次很清楚:

  • 单 Agent 的核心是短循环 + 厚 harness;
  • 多 Agent 的核心不是人设热闹,而是角色工具面分离、阶段化工作流、自包含派工、结构化回传
  • Coordinator 最值钱的一句是:理解不能外包。

如果你也在做 Agent,不妨把验收标准改成这几个问题:

  1. 没有 multi-agent 时,单循环能不能稳定改代码?
  2. 危险动作能不能被拦住且留下记录?
  3. 上了 Worker 之后,派工是否自包含,是否还在甩锅?
  4. Worker 结果是结构化事件,还是靠大家在上下文里“意会”?

这四个问题比“要不要再加一个角色”更接近真实开发。

参考资料

说明:开关名、工具常量名、权限层数等会随版本变化;文中以本地还原树与社区文档为准,用于建立开发直觉,不保证与线上产品行为一一对应。


授权

从 Claude Code 源码看一个 Agent 怎么串起来

2026年07月14日
2511 字 · 10 分钟

© xiexienila · CC BY-NC-SA 4.0