AI 编程工具的演进,正在经历一次根本性的范式转变:从"补全光标处的代码",到"自主完成端到端工程任务"。这种转变有一个专有名词——Agentic Coding

围绕 Coding Agent 的讨论,常见两种极端:将其神化为自主智能体,或将其贬为"不过是提示词工程"。两种判断都失之简单。理解这个转变,需要从三个层面展开:工具层(OpenCode 的能力边界)、框架层(多 Agent 协作编排)、方法论层(如何让 Agent 真正服务于工程流程)。本文从真实的架构出发,拆解 Claude Code、OpenCode 等工具的实现模式,厘清各自的设计取舍,深入探讨子 Agent 的本质与多 Agent 协作的核心问题。

什么是 Agentic Coding

传统 AI 编程助手的工作模式是响应式的:开发者提问,AI 回答;开发者选中代码,AI 补全。人始终是执行者,AI 是辅助工具。

Agentic Coding 的工作模式是自主式的:开发者描述目标,Agent 自主规划步骤、调用工具、执行操作、验证结果,直到任务完成。人退出执行循环,成为目标定义者和结果审查者。

这不是量变,是质变。一个能够自主编码的 Agent,需要具备:

  • 代码理解能力:不只是文本匹配,而是理解代码的语义结构、类型关系、调用链路
  • 工具调用能力:读写文件、执行命令、调用外部 API
  • 规划与反馈能力:将大任务分解为步骤,根据执行结果调整计划
  • 上下文管理能力:在有限的上下文窗口内,按需加载相关信息

其中,代码理解能力是基础,也是工具层最关键的差异点。关于代码理解能力的核心——LSP(语言服务协议),已在独立文章 LSP:语言服务协议与AI编程助手的代码理解能力 中详细阐述。

从单 Agent 到 Agent 团队

OpenCode 解决了单个 Agent 的代码理解问题。但工程任务的复杂度,往往超出单个 Agent 的能力边界。

理解这个边界在哪里,是理解为什么需要多 Agent 的前提。一个单独的 Agent,即便配备了 LSP 和完整的工具权限,仍然面临三个结构性限制:上下文窗口有限(无法同时持有整个大型项目的全貌)、角色混淆(规划者和执行者是同一个 Agent,容易在"想清楚"和"动手做"之间反复横跳)、无法真正并行(单线程的问答流水线,无法同时搜索文档和修改代码)。

这些限制不是模型能力的问题,而是单 Agent 架构的天花板。突破它,需要引入外部编排层——把规划、执行、验证拆给不同的角色,让它们并行工作、相互制衡。

编程 Agent 的本质

在讨论任何具体的 Agent 工具或框架之前,有必要先把"编程 Agent 到底是什么"这个问题说清楚。理解了本质,才能理解为什么各种框架要做它们做的事。

一个编程 Agent,剥去所有包装,本质上是四个要素的组合:

while loop:Agent 不是一次性的问答,而是一个持续运行的循环。每一轮循环,Agent 观察当前状态、决定下一步行动、执行行动、再观察新状态。循环在何时退出,取决于完成信号的检测——可以是文件中的特定标记(如 DONE),可以是测试通过,也可以是外部脚本的判断。

system prompt:Agent 的"人格"和"职责"由 system prompt 定义。它告诉 Agent:你是谁、你的目标是什么、你能用哪些工具、你应该遵守哪些约束。system prompt 是 Agent 行为的根本来源——同一个底层模型,配上不同的 system prompt,就变成了完全不同的 Agent。这也是为什么多 Agent 框架的核心工作,往往是精心设计每个角色的 system prompt,而非替换底层模型。

tool calling:Agent 通过工具与外部世界交互。读文件、写文件、执行 Shell 命令、调用 API——这些都是工具。工具调用是 Agent 从"思考"到"行动"的桥梁。没有工具,Agent 只能输出文本;有了工具,Agent 才能真正改变世界的状态。

persistent state(文件系统):Agent 的记忆不在上下文里,而在文件系统中。代码文件、任务列表、中间结果——这些都是持久化的状态。文件系统是 Agent 跨轮次、跨会话保持连贯性的基础。这也是为什么 Agent 框架普遍依赖文件来传递状态:task.mdboulder.json.sisyphus/plans/——本质上都是在用文件系统弥补上下文窗口的有限性。

1
编程 Agent = while loop + system prompt + tool calling + persistent state

一个循环统治一切

先说结论:所有 Coding Agent 的核心,都是一个 while(tool_use) 循环

通过对 Claude Code 实际 API 流量的追踪分析,其核心逻辑可以用伪代码描述如下:

1
2
3
4
5
6
7
8
while True:
response = llm.call(context)
if response.has_tool_call():
result = execute_tool(response.tool_call)
context.append(result)
else:
# 没有工具调用 = 任务完成,等待用户输入
break

没有复杂的状态机,没有多 Agent 协调框架,没有专门的"停止决策模块"。模型自己决定什么时候停——当它不再调用工具,循环就自然结束。

这个设计有一个被低估的优雅之处:模型可以通过"输出纯文本而不调用工具"来向用户提问或汇报进度,不需要任何额外的机制。

messages 数组:唯一的状态载体

上述伪代码中的 context,在所有主流 LLM API 里的实际形态是一个 messages 数组。没有其他格式——Agent 的全部状态,包括历史、推理过程、工具调用结果,都存储在这个数组里。

每条消息有四种角色,工具调用的插入位置是固定的:

1
2
3
4
5
6
7
8
[
{"role": "system", "content": "..."},
{"role": "user", "content": "用户输入"},
{"role": "assistant", "content": null,
"tool_calls": [{"id": "call_abc", "function": {"name": "read_file", "arguments": "{...}"}}]},
{"role": "tool", "tool_call_id": "call_abc", "content": "文件内容..."},
{"role": "assistant", "content": "基于文件内容,我的分析是..."}
]

每一轮循环的操作模式是:模型输出 assistant 消息(含 tool_calls)→ 框架执行工具 → 把结果以 tool 角色追加 → 再次调用模型。Agent 框架在做的核心事情,就是决定往这个数组里追加什么、以什么顺序追加、以及何时停止追加。

这个数组的增长方式,取决于框架策略:

策略 描述 适用场景
无限叠加 保留所有消息,数组持续增长 短任务、大 context 窗口
工具结果清除 保留工具调用记录但丢弃返回内容,减少冗余 工具结果可重新获取的场景
压缩摘要 用 LLM 总结历史,用摘要替换原始消息数组 超长任务(Claude Code 的 compaction 机制)
滑动窗口 丢弃旧消息,保留最近 N 条 长对话、有限 context 窗口

这四种策略的激进程度递增,但本质上都在做同一件事:改写 messages 数组,用一个更短的数组替代原来的数组,然后基于这个更短的数组继续推理。这就是上下文压缩的全部秘密——没有魔法,只有对数组的精确操纵。

终止条件的最简实现:不需要专门的 stop 工具或终止正则,让模型的"不调用工具"行为本身成为停止信号,是最低耦合的设计。

Claude Code 的真实架构

工具集:精简而非全能

Claude Code(截至 2025 年中,基于 system prompt 追踪分析)共有 14 个工具,分为四类:

类别 工具 设计意图
命令行 bashglobgrepls bash 需要用户审批,其余不需要
文件操作 readwriteeditmulti_editnotebook_readnotebook_edit Jupyter 单独处理,因为原始格式极长
网络 web_searchweb_fetch 基础信息获取
控制流 todo_writetask 计划管理与子 Agent 调度

没有 critic 模式,没有角色扮演,没有复杂的记忆数据库。简单是刻意的选择,不是能力不足。

TODO 列表:外化的计划

Claude Code 的第一个工具调用几乎总是 TodoWrite,创建一个结构化的任务列表:

1
2
3
4
5
6
7
{
"todos": [
{"id": "1", "content": "分析现有代码结构", "status": "completed"},
{"id": "2", "content": "实现新功能", "status": "in_progress"},
{"id": "3", "content": "编写测试", "status": "pending"}
]
}

这个设计解决了一个关键问题:模型在执行数百步操作后会"忘记"自己在做什么。TODO 列表是外化的工作记忆,不依赖模型的上下文记忆能力。

更聪明的是,工具调用的返回结果里会附带提醒文字,要求模型继续使用 TODO 列表跟踪进度。指令在工具结果里重复出现,比只放在 system prompt 里遵从率高得多。

计划外化原则:把计划写成可被工具读写的结构化文件(TODO 列表、plan.md),比依赖模型"内化"计划更确定、更可控。内化的计划会随着上下文增长而衰减,外化的计划不会。

System Reminder:动态注入的上下文

Claude Code 会在每次用户消息后附加 <system-reminder> 块,内容随状态动态变化:

  • 会话开始:注入基本行为约束(不要主动创建文件、不要做多余的事)
  • TODO 列表为空时:提醒模型考虑是否需要创建计划
  • TODO 列表更新后:把最新的任务状态注入上下文

这不是"提示词工程",而是程序化的上下文管理——根据 Agent 的运行状态,精确控制每一步模型能看到什么信息。

子 Agent:上下文隔离与并行

当任务复杂到单个上下文窗口装不下,Claude Code 会通过 task 工具派发子 Agent。子 Agent 是一个完整的 Claude Code 实例,接收相同的 system prompt,但有自己独立的上下文窗口。

关键设计决策:子 Agent 不接收主 Agent 的上下文历史,且在设计上被约束为不能再派发子 Agent(防止无限递归)。子 Agent 的 system prompt 中包含对其行为边界的约束,但其上下文窗口是全新的、干净的——这正是上下文隔离的核心价值。这一设计保证了行为的可预测性。值得注意的是,这个约束只针对 Agent 实例的创建——Skill(通过 SKILL.md 注入的行为扩展)则可以无限递归调用,因为 Skill 是上下文内的提示词注入,不涉及新 Agent 实例的创建。

关于子 Agent 的本质,还有一个常被误解的问题:"子 Agent"是关系描述,不是能力描述。子 Agent 可以拥有与主 Agent 完全相同甚至更强的能力,它的核心价值在于上下文隔离,而非能力弱化。详细分析见后文"子 Agent 的本质"章节。

子 Agent 的两个用途:

  1. 上下文管理:把大任务拆成小任务,每个子任务有干净的上下文
  2. 并行加速:多个独立子任务可以同时执行

安全检查:用小模型做守门员

一个鲜为人知的细节:Claude Code 在执行 bash 命令前,会把命令发给 Claude Haiku(最小最快的模型)做安全检查,判断命令是否读取或修改了敏感文件。

这个设计体现了一个重要的工程权衡:用便宜的小模型做确定性判断,不让安全检查拖慢主循环。Haiku 的输出是结构化的 XML,不是自然语言,降低了解析的不确定性。

异构模型协作:主循环用强模型做复杂推理,安全/分类等确定性任务用小模型,是成本与能力的最优分配。

Claude Code 的核心竞争力:上下文工程

上文所描述的每一个机制——system reminder 的动态注入、TODO 列表的外化记忆、子 Agent 的上下文隔离、messages 数组的增长策略——本质上都在做同一件事:精确控制每一步模型能看到什么信息。这不是"改改 prompt 就能做到的事",而是对模型行为的系统性工程,需要对模型的注意力机制、上下文衰减、工具调用模式有深刻理解。

Anthropic 在其工程博客中给出了精确定义:Context Engineering(上下文工程)是在 LLM 推理期间策划和维护最优 token 集合的策略集合。它与 Prompt Engineering 的区别不在于程度,而在于维度——Prompt Engineering 关注"如何写好一段提示词",Context Engineering 关注"在每一次推理调用中,什么样的 token 配置最可能产生期望行为"。后者是一个迭代过程,涵盖 system prompt、工具定义、messages 历史、外部数据的全部组合。

一个有力的佐证:Claude Code 的 system prompt 和工具集在每次版本更新时都会变化,这些变化直接影响模型行为。这说明 Anthropic 在持续做的是 Context Engineering,而不是一次性的提示词设计。

操纵 messages 数组的艺术

前文提到 messages 数组是 Agent 的唯一状态载体。但"操纵 messages 数组"远不止"快满了就压缩"这一件事。Agent 框架对 messages 数组的操纵,至少包含五个维度:注入、定位、保护、清理、重复。 压缩只是清理维度的一个子集。

维度一:注入(Injection)——往数组里放什么

messages 数组中有四种角色的消息,每种角色的语义和注意力权重截然不同:

角色 语义 注意力权重 是否被压缩
system 全局行为指令,定义模型身份和约束 最高——位于数组开头,享有首因效应(primacy bias) ❌ 永远不会被压缩
user 用户输入,包含实际请求和上下文 高——最新的 user message 享有近因效应(recency bias) ✅ 历史 user message 会被压缩
assistant 模型响应,包含思考过程和工具调用 中——随对话增长被推向中间位置 ✅ 会被压缩
tool 工具执行结果,返回给模型 中——与 assistant 的工具调用配对出现 ✅ 会被压缩或清除

Claude Code 的注入策略是分层的,不同类型的信息被注入到不同角色的消息中:

第一层:system prompt(不可变层)。 核心身份定义、基础行为规则、工具使用指南、安全声明。这些内容约 16,000-23,000 tokens,对所有用户共享,永远不会被压缩。Claude Code 的 system prompt 有一个精妙的设计:安全声明在 prompt 的开头和结尾都出现——利用 U 形注意力曲线的两个峰值,确保安全约束在任何上下文长度下都不会被忽略。

第二层:system prompt 动态扩展(半不可变层)。 Output Style(如 software-architect 风格指令)被追加到 system prompt 数组中。这些内容随用户配置变化,但在单次会话内保持稳定,同样不会被压缩。

第三层:<system-reminder> 注入(可变层)。 这是 Claude Code 最独特的机制。CLAUDE.md 的内容、skill 元数据、当前日期、Git 仓库状态等信息,被包裹在 <system-reminder> 标签中,作为 user message 注入到对话历史中:

1
2
3
4
5
6
7
{
"role": "user",
"content": [{
"type": "text",
"text": "<system-reminder>\n# claudeMd\nContents of /path/to/CLAUDE.md:\n\n[your CLAUDE.md content]\n</system-reminder>"
}]
}

这个设计有一个关键的工程权衡:CLAUDE.md 的内容虽然用 <system-reminder> 标签标记为"系统级",但它的实际角色是 user message。这意味着它享有 user message 的注意力权重,但也承受 user message 的命运——会被压缩。这个设计选择是为了兼容 prompt caching(后文详述),但也埋下了 skill 遗忘问题的种子。

第四层:工具调用与结果(动态层)。 每一轮循环产生的 assistant tool_call 和 tool result 消息。这些是数组增长最快的部分,也是压缩的主要目标。

维度二:定位(Positioning)——放在数组的哪个位置

Stanford 的 Lost in the Middle 论文(arXiv:2307.03172,引用 3700+)揭示了一个关键发现:LLM 对 messages 数组中不同位置信息的注意力呈 U 形分布——开头和结尾的信息获得最高注意力,中间位置的信息最容易被忽略。

1
2
3
4
5
6
7
8
9
10
注意力权重

|██ ██
|████ ████
|██████ ██████
|████████ ████████
|██████████████████████████████████
└──────────────────────────────────→ messages 数组位置
开头 结尾
(system prompt) (最新消息)

Claude Code 的定位策略充分利用了这个特性:

  • 开头位置(primacy bias):system prompt 固定在数组最前面,核心规则前置,身份定义优先
  • 结尾位置(recency bias):最新的 user message 和最近的工具调用结果自然位于数组末尾
  • 中间位置(注意力低谷):历史对话、早期的工具调用结果——这些是压缩的首选目标,因为即使不压缩,模型对它们的注意力也已经很低了

这解释了一个反直觉的现象:为什么 Claude Code 的 system prompt 长达 16,000+ tokens 却不会显著影响性能。因为 system prompt 位于数组开头,享有最高的注意力权重;而且它通过 prompt caching 被缓存为不可变前缀,不会随对话增长而被"推"到中间位置。

维度三:保护(Protection)——哪些内容不能丢

并非 messages 数组中的所有内容都是平等的。Claude Code 建立了一个隐式的信息保护层级

保护级别 内容 保护机制
永久保护 system prompt、工具定义 位于 API 调用的顶层参数,不在 messages 数组中,永远不会被压缩
缓存保护 system prompt + Output Style 通过 prompt caching 创建不可变前缀,缓存后的内容不会被修改
摘要保留 架构决策、未解决的 bug、最近 5 个文件 compaction 时被明确要求保留
无保护 历史工具调用结果、早期对话、CLAUDE.md 内容、skill 内容 压缩时可能被摘要化或丢弃

Prompt Caching 的保护作用值得单独说明。Claude API 的 prompt caching 通过前缀匹配工作:toolssystemmessages 按此顺序构成缓存前缀。前缀中的任何变化都会使缓存失效。Claude Code 围绕这个机制做了一个关键的架构决策:所有用户共享相同的 system prompt。这使得 system prompt 成为一个稳定的、可缓存的前缀,减少 90% 的输入 token 成本和 85% 的延迟。

但这也意味着,任何需要个性化的内容(CLAUDE.md、skill、项目规则)都不能放在 system prompt 中——否则会破坏缓存共享。这就是为什么这些内容被降级为 user message 注入,也是它们在压缩时容易丢失的根本原因。这不是一个 bug,而是一个成本与持久性之间的工程权衡

维度四:清理(Pruning)——从数组中移除什么

这是大多数人理解的"上下文压缩",但它实际上包含三个递进的层次:

第一层:工具结果清除(Tool Result Clearing)。 最轻量的清理形式。Agent 保留"我调用了 read_file 读取了 config.yaml“这个记录,但丢弃实际返回的文件内容。Anthropic 已将此作为 Claude Developer Platform 的正式功能发布(clear_tool_uses)。这是"最安全的轻触式压缩”——不丢失任何决策上下文,只丢弃可重新获取的原始数据。

第二层:上下文摘要压缩(Compaction)。 最核心也最复杂的清理形式。其操作可以用一句话概括:把整个 messages 数组传给 LLM,让它生成一个高保真摘要,然后用这个摘要替换原始数组,开始一个新的上下文窗口

1
2
3
4
5
6
7
if token_count(messages) > threshold:
summary = llm.summarize(messages)
messages = [
{"role": "system", "content": original_system_prompt},
{"role": "user", "content": summary},
# 保留最近的几条消息或最近访问的文件
]

压缩的艺术在于选择保留什么、丢弃什么——过于激进的压缩会丢失微妙但关键的上下文。Anthropic 在其工程博客中给出了调优建议:先最大化召回率(确保压缩 prompt 捕获了 trace 中的每一条相关信息),再迭代提升精确率(消除多余内容)。

第三层:结构化笔记(Structured Note-taking)。 不是对 messages 数组的直接操作,而是一种补偿机制:Agent 在运行过程中主动将关键信息写入外部存储(TODO 列表、memory 文件),使得即使上下文被压缩甚至完全清空,关键状态也不会丢失。

维度五:重复(Repetition)——关键指令的多点锚定

这是最容易被忽视、却可能是最重要的维度。Claude Code 的一个核心设计哲学是:关键指令不能只出现一次,必须在 messages 数组的多个位置重复出现

具体实现包括:

  • system prompt 首尾重复:安全声明在 system prompt 的开头和结尾都出现,利用 U 形注意力的两个峰值
  • tool result 中嵌入提醒:每次 todo_write 工具调用的返回结果中,都会附带固定文字提醒模型"keep using the TODO list to keep track"。这意味着只要模型在使用 TODO 列表,每次工具调用都会重新强化这条指令
  • <system-reminder> 的每轮注入CLAUDE.md 的内容不是只在会话开始时注入一次,而是在每个 user turn 中都重新注入。这确保了即使中间的 CLAUDE.md 内容被注意力衰减影响,最新一轮的注入仍然位于数组末尾的高注意力区域

为什么重复比单次注入更有效?原因有三:

  1. 对抗注意力衰减:单次注入的信息会随着对话增长被推到中间位置(注意力低谷),重复注入确保关键信息始终出现在高注意力区域
  2. 提供容错冗余:即使某一次注入被模型忽略,其他位置的重复仍然有效
  3. 压缩后恢复:即使历史中的注入被压缩掉,最新一轮的注入仍然完整保留

指令在工具结果里重复出现,比只放在 system prompt 里遵从率高得多——这不是经验之谈,而是 Claude Code 团队通过大量 A/B 测试验证的工程结论。

五个维度的协同:一个完整的例子

以 Claude Code 处理一个复杂编码任务为例,观察五个维度如何协同工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
messages 数组的完整生命周期:

[注入] system prompt(身份 + 规则 + 安全声明×2) ← 永久保护,首因效应
[注入] system prompt(Output Style: software-architect)← 缓存保护
[注入] user: <system-reminder>CLAUDE.md 内容</system-reminder> ← 每轮重复注入
[注入] user: "帮我重构 OrderService" ← 用户输入
[注入] assistant: tool_call(todo_write, ...) ← 模型决策
[注入] tool: "TODO 已创建。提醒:继续使用 TODO 跟踪进度" ← 重复提醒
[注入] assistant: tool_call(read_file, ...)
[注入] tool: "文件内容..." ← 后续可被清除
... 数十轮工具调用 ...
[清理] 早期的 tool result 被 clear_tool_uses 清除 ← 工具结果清除
[清理] 当 token 接近 95% 时触发 compaction ← 摘要压缩
[保护] system prompt 不变,摘要保留架构决策和最近文件
[重复] 压缩后的第一个 user turn 重新注入 <system-reminder>
[定位] 摘要位于新数组的前部,最新交互位于末尾

这个例子展示了一个关键洞察:messages 数组的操纵不是某个单一时刻的操作,而是贯穿 Agent 整个生命周期的持续性工程。每一条消息的注入位置、每一次清理的时机、每一条指令的重复频率,都是精心设计的。

裸 OpenCode 的能力边界

OpenCode 是一个开源的终端 AI 编程 Agent,支持 GPT-4/5 系列、Claude 等多种模型(通过界面切换模型),内置四个角色:

Agent 类型 能力
Build 主 Agent 全工具权限,默认开发模式
Plan 主 Agent 只读分析,禁止文件修改
General 子 Agent 通用研究与多步骤任务
Explore 子 Agent 只读代码库探索,快速搜索

值得注意的是,表格中 General 和 Explore 被标注为"子 Agent",但这只是描述它们在 OpenCode 默认工作流中的调用关系,而非能力限制。它们本身是完整的 Agent,拥有独立的上下文和工具权限,完全可以作为主 Agent 直接使用。"子 Agent"是关系描述,不是能力描述——关于这一点的深入分析,见后文"子 Agent 的本质"章节。

Plan 与 Build:模式切换,而非 Agent 切换

在 OpenCode 的终端界面中,按 Tab 键可以在 Plan 模式Build 模式之间切换。切换的是同一个 Agent 的工具权限集,而不是切换到另一个独立的 Agent。底层调用的是同一个 LLM,区别仅在于工具白名单:

  • Plan 模式:只读权限。Agent 可以读文件、搜索代码库、分析调用链,但禁止写文件和执行命令。这一限制强迫 Agent 只思考、不动手,输出分析报告和行动计划。
  • Build 模式:全工具权限。Agent 可以读写文件、执行 Shell 命令、调用外部 API。

分离两种模式的核心原因是防止 Agent 在理解不充分时就开始修改代码。Plan 模式提供了一个只读沙箱,让 Agent 先把问题想清楚。一个实用的工作流是:先用 Plan 模式分析代码库、理解依赖关系、输出修改方案;人类确认方案合理后,再切换到 Build 模式执行。

OpenCode 的 Skills 机制(SKILL.md 文件)实现了"渐进式披露"的上下文管理——Agent 只在需要时才加载技能的完整内容,而不是一次性把所有知识塞进上下文。这与 AI Agent 领域的上下文最小化原则高度吻合:

“The model should only know what it needs to know to make the next immediate decision.”

裸 OpenCode 的天花板

裸 OpenCode 有比较明确的能力边界:

  • 并不以内置并发编排为核心体验:子 Agent 可以被调用,但原生形态下并没有像外部编排框架那样,把并行调度作为主工作流来强调
  • 无角色分工体系:Build 和 Plan 是模式切换,而非职责分明的团队角色
  • 无跨会话状态持久化:每次会话独立,无法在多天的复杂项目中保持进度
  • 无业务域知识:工具是通用的,不理解特定团队的技术栈、部署流程、业务规则

裸 OpenCode 是一把锋利的瑞士军刀,但尚未构成一支完整的工程团队。突破这些限制,需要在 OpenCode 之上引入外部编排层。

命令与配置:从单工具到可编排工作流

如果只把 OpenCode 当作一个终端里的编码助手,那么 /init/undo/redo/share 这些内置命令,更多是在提升单个会话的可用性。但当工作流开始延伸到 OMO 这种多 Agent 编排层时,这些命令的意义就变了:它们不再只是“方便操作”,而是在为后续的自动化协作准备上下文、状态和回退能力。

其中最值得关注的是两类能力:

  1. 项目知识初始化:例如 /init/init-deep,负责把代码库的结构、规范和目录知识沉淀成可供 Agent 读取的配置文件;
  2. 会话控制与工作流命令:例如 /undo/redo/share,以及 OMO 扩展出的 /ralph-loop/refactor/start-work,负责把“单次回答”变成“可持续推进、可回退、可验证的工程过程”。

先看 OpenCode 自带的基础命令:

命令 功能 使用场景
/init 生成项目 AGENTS.md 新项目初始化
/undo 撤销上一条消息和文件变更 AI 改错了,想回退
/redo 重做已撤销的操作 撤销后想恢复
/share 生成会话分享链接 需要分享对话给他人
/help 显示帮助信息 忘记命令用法

/undo 与 /redo:安全的迭代探索

这两个命令构成了 OpenCode 的"时间旅行"机制。

工作原理(以下 ASCII 图为摘要,展示三步操作的状态变化):

1
2
3
4
5
6
7
8
9
10
11
12
13
用户操作历史:
Message 1: "创建 utils.ts" → 文件变更: +utils.ts
Message 2: "添加测试" → 文件变更: +utils.test.ts
Message 3: "重构函数" → 文件变更: ~utils.ts

执行 /undo
→ 回到 Message 2 结束时的状态
→ utils.ts 恢复为重构前的内容
→ 可以多次 /undo 回到更早的状态

执行 /redo
→ 重新应用 Message 3 的变更
→ utils.ts 再次变为重构后的内容

注意事项

  • /undo/redo 依赖 OpenCode 内部的会话状态追踪,在 Git 仓库中效果最佳
  • 文件变更会被完整恢复(包括删除的文件)
  • 可以连续执行多次 /undo 回退多步

/share:会话分享

1
2
3
4
/share

# 输出示例:
# Session shared: https://opncd.ai/s/abc123

分享链接包含完整的对话历史和代码变更,适合:

  • 向同事展示解决方案
  • 提交 Bug 报告时附上复现步骤
  • 记录学习过程

OMO 内置命令详解

OMO(Oh My OpenAgent,原名 Oh My OpenCode)在 OpenCode 基础上扩展了一组更偏"工作流模板"的斜杠命令。它们的价值不只在于省输入,而在于把原本需要人手动维持的节奏——规划、循环、重构、收尾——固定成可重复执行的流程。

命令 功能 复杂度
/init 生成根目录 AGENTS.md(OpenCode 原生,OMO 继承)
/init-deep 生成层级化 AGENTS.md 知识库
/ralph-loop 自引用开发循环,持续工作直到完成
/ulw-loop Ultrawork 版本的 ralph-loop
/cancel-ralph 取消活跃的 Ralph Loop
/refactor 智能重构,完整工具链验证
/start-work 从 Prometheus 计划开始工作

/init:OpenCode 原生的项目引导

/init 是 OpenCode 的内置命令,用于为项目创建 AGENTS.md 配置文件。

解决的问题:Agent 需要理解项目结构、技术栈、编码规范才能高效工作。每次新项目都要从零开始学习,效率低下。

工作原理(以下 ASCII 图为摘要,展示扫描→生成的主干流程;各分支的详细说明见下方文字):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/init 命令执行流程:

用户输入 /init

Agent 扫描项目结构
├── 识别包管理器(package.json / go.mod / pom.xml)
├── 识别框架和语言
├── 分析目录结构
└── 提取编码规范(ESLint / Prettier / .editorconfig)

生成 AGENTS.md
├── 项目概述
├── 技术栈说明
├── 目录结构
├── 编码规范
└── 常用命令

输出完成提示

使用方式

1
2
3
4
5
# 在 OpenCode TUI 中输入
/init

# 如果 AGENTS.md 已存在,会增量更新
# 如果不存在,会创建新文件

关键特性

  • 只生成根目录文件/init 只在项目根目录生成一个 AGENTS.md
  • 增量更新:如果文件已存在,会保留原有内容并补充新信息
  • Git 友好:生成的文件应该提交到版本控制,供团队共享

/init-deep:层级化知识库

/init-deep 是 OMO 扩展的命令,在 /init 的基础上实现了层级化的知识库生成

解决的问题:大型项目结构复杂,单一根目录的 AGENTS.md 无法承载所有模块的细节。Agent 在处理特定模块时,需要该模块级别的上下文。

工作原理(以下 ASCII 图为摘要,展示并行分析→层级生成的主干流程):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/init-deep 命令执行流程:

用户输入 /init-deep

并行分析项目结构
├── 扫描所有目录
├── 识别模块边界
└── 提取各模块的核心功能

层级化生成 AGENTS.md
├── 项目根目录 AGENTS.md(全局概览)
├── src/hooks/AGENTS.md(模块级知识)
├── src/agents/AGENTS.md(模块级知识)
└── ... 每个关键目录一个

输出完成报告

使用方式

1
2
3
4
5
6
7
8
# 基础用法
/init-deep

# 强制重新生成所有文件(删除旧的)
/init-deep --create-new

# 限制生成深度
/init-deep --max-depth=2

层级化知识库的价值

层级 作用 示例内容
根目录 AGENTS.md 项目全局视图 技术栈、整体架构、部署流程
模块级 AGENTS.md 模块专属知识 该模块的 API、依赖关系、设计模式
子模块级 AGENTS.md 细节实现 具体函数的用法、边界情况处理

/init 的对比

维度 /init(OpenCode 原生) /init-deep(Oh My OpenAgent)
生成范围 仅根目录 根目录 + 所有关键子目录
知识粒度 项目级 项目级 + 模块级 + 子模块级
上下文精度 粗粒度 细粒度,按需加载
适用场景 小型项目、单模块项目 大型项目、多模块项目
Token 消耗 中等(但按需加载,不一次性全部读入)

最佳实践

  1. 新项目首次使用:先用 /init-deep 建立完整知识库
  2. 定期更新:当项目结构有重大变化时重新运行
  3. Git 提交:将所有 AGENTS.md 文件提交到版本控制
  4. 增量更新:默认模式会保留已有内容,避免覆盖人工补充的知识

/ralph-loop:持续工作直到完成

这是 OMO 中最有代表性的命令之一,实现了"自引用开发循环"。

核心机制(以下 ASCII 图为摘要,展示循环的主干结构;循环与 OMO 多 Agent 模式的关系见后续"能力层 vs 循环层"小节):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/ralph-loop "任务描述" 执行流程:

启动循环

┌── Iteration 1 ──────────────────────────────┐
│ Agent 分析任务 │
│ 执行第一步 │
│ 检查是否输出 <promise>DONE</promise>
│ 未完成 → 继续下一轮 │
└──────────────────────────────────────────────┘

┌── Iteration N ──────────────────────────────┐
│ Agent 完成任务 │
│ 输出 <promise>DONE</promise>
│ 循环结束 │
└──────────────────────────────────────────────┘

输出:Ralph Loop Complete!

使用方式

1
2
3
4
5
6
7
8
# 基础用法
/ralph-loop "修复登录页面的认证问题,确保所有错误情况都被处理"

# 自定义完成标记
/ralph-loop "编写单元测试" --completion-promise="TESTS_DONE"

# 限制最大迭代次数
/ralph-loop "优化性能" --max-iterations=50

关键参数

参数 默认值 说明
--completion-promise DONE Agent 输出该标记表示任务完成
--max-iterations 100 最大迭代次数,防止无限循环

适用场景

  • 复杂的多步骤任务(重构大型模块)
  • 需要多次迭代才能完成的功能开发
  • Bug 修复(可能需要多次尝试不同方案)

设计哲学:能力层 vs 循环层

一个常见的困惑是:既然界面已经选择了 Sisyphus (Ultraworker) 模式,Agent 已经可以自行多步操作,为什么还需要 /ralph-loop/ulw-loop

答案在于两者解决的是不同层面的问题

概念 解决的问题 行为特点
Sisyphus (Ultraworker) “代理能力” 多代理编排、能多步执行、能并行探索
/ralph-loop /ulw-loop “代理纪律” 强制循环验证、防止假完成、必须满足停止条件

能力层(Sisyphus/Ultraworker) 解决的是"Agent 能不能做这件事"——它提供多代理编排、并行调度、工具调用等能力。但有能力不代表有纪律。一个有能力执行多步的 Agent,仍可能在第一次响应时就声称"我完成了",或者跳过验证环节直接结束。

循环层(ralph-loop/ulw-loop) 解决的是"Agent 会不会假完成"——它通过自引用循环机制强制 Agent:

  1. 执行 → 自检 → 发现问题 → 继续执行 → 再自检
  2. 直到满足明确的 --completion-promise 条件
  3. 防止 Agent 在第一轮就声称完成

用一个比喻:

  • Sisyphus (Ultraworker) 像一个全能的工程师:能写代码、能调试、能查文档、能并行处理多个任务
  • /ralph-loop 像一个严格的质检流程:不信任工程师的"我做完了",必须通过验证才能结束

两者是正交互补的关系,而非替代关系。

使用建议

任务类型 推荐组合
日常小改动 普通提示,无需特殊命令
中等功能开发 ulw: 前缀(激活 Ultraworker)
复杂多步骤任务 ulw + 分阶段提示
追求极致/大重构 ulw + /ralph-loop

/ulw-loop:Ultrawork 版本的循环

/ulw-loop/ralph-loop 的增强版,专为 Ultraworker 模式优化。它在标准 Ralph Loop 的基础上增加了更严格的验证机制和更智能的任务分解能力。

/ralph-loop 的区别

特性 /ralph-loop /ulw-loop
验证强度 基础自检 多重验证(编译、测试、Lint)
任务分解 简单分步 智能拆解为子任务
错误恢复 重试当前步 自动回退并调整策略
适用场景 一般复杂任务 高可靠性要求的重构

使用方式

1
/ulw-loop "重构用户认证模块,确保所有测试通过"

/cancel-ralph:取消活跃的循环

当 Ralph Loop 陷入死循环或需要中断时,使用此命令。

1
/cancel-ralph

注意事项

  • 会立即终止当前循环
  • 保留已完成的中间状态
  • 适合在发现方向错误时及时止损

/refactor:智能重构

/refactor 是一个专门用于代码重构的命令,它集成了完整的工具链验证。

工作流程

  1. 分析阶段:识别需要重构的代码块及其依赖
  2. 规划阶段:生成重构计划,包括目标结构和迁移步骤
  3. 执行阶段:逐步执行重构,每步后进行验证
  4. 验证阶段:运行测试、Lint、编译检查

使用方式

1
/refactor "将 UserService 拆分为 UserQueryService 和 UserCommandService"

关键特性

  • 原子性保证:每一步重构都是原子的,可随时回退
  • 依赖追踪:自动更新所有引用点
  • 回归测试:重构后自动运行相关测试用例

/start-work:从 Prometheus 计划开始工作

/start-work 用于从已有的 Prometheus 计划文件中恢复工作状态。

使用场景

  • 中断的工作需要继续
  • 多人协作时接手他人的计划
  • 从长期规划中提取具体任务

使用方式

1
/start-work "plans/user-auth-refactor.md"

命令速查表

命令 用途 适用场景
/init 生成项目知识库 新项目初始化
/init-deep 生成层级化知识库 大型项目
/ralph-loop 自引用开发循环 复杂多步骤任务
/ulw-loop Ultrawork 循环 高可靠性重构
/cancel-ralph 取消循环 中断死循环
/refactor 智能重构 代码结构优化
/start-work 从计划恢复 继续中断工作
/undo 撤销操作 回退错误变更
/redo 重做操作 恢复撤销的变更
/share 分享会话 协作与复盘

模式速查表

模式 特点 适用场景
Plan 模式 只读分析,禁止修改 代码审查、方案设计
Build 模式 全工具权限 日常开发、功能实现
General Agent 通用研究能力 多步骤调研、文档查阅
Explore Agent 快速代码探索 定位代码、理解结构
Sisyphus (Ultraworker) 多代理并行编排 复杂任务并行处理
Ralph Loop 自引用循环验证 防止假完成、强制迭代

大型重构工作流:持续数周的项目实战

在实际工程中,最考验 Agent 编排能力的不是写一个新功能,而是对一个运行多年的大型项目进行持续数周的重构。这类任务的特点是:

  • 规模大:涉及数百个文件、数千处引用
  • 周期长:需要多天甚至数周的持续工作
  • 风险高:任何一步出错都可能导致系统不可用
  • 上下文深:需要理解历史债务、业务约束、技术演进路径

下面以一个真实的"单体服务拆分"项目为例,展示如何设计一个可持续数周的大型重构工作流。

四阶段工作流设计

我们将整个重构分为四个阶段,每个阶段有明确的目标、交付物和退出条件:

阶段一:分析与规划(第 1-3 天)

目标:全面理解现有系统,制定详细的拆分方案。

关键动作

  1. 代码库扫描:使用 /init-deep 生成层级化知识库
  2. 依赖分析:识别模块间的调用关系、数据流向
  3. 边界划定:确定拆分后的服务边界、接口契约
  4. 风险评估:识别高风险区域,制定回滚预案

交付物

  • plans/refactoring-plan.md:详细的重构计划
  • diagrams/dependency-graph.mmd:依赖关系图
  • risks/risk-register.md:风险登记册

退出条件

  • 计划经过人工评审并通过
  • 所有高风险项都有对应的缓解措施
  • 测试环境准备就绪

阶段二:基础设施准备(第 4-7 天)

目标:搭建新服务的基础设施,确保可以独立运行。

关键动作

  1. 脚手架生成:创建新服务的项目结构
  2. CI/CD 配置:配置自动化构建、测试、部署流水线
  3. 监控接入:集成日志、指标、链路追踪
  4. 数据迁移脚本:编写数据库迁移脚本

交付物

  • 新服务的基础代码仓库
  • 可运行的 Hello World 服务
  • 完整的 CI/CD 流水线

退出条件

  • 新服务可以独立部署和运行
  • 监控指标正常上报
  • 数据迁移脚本在测试环境验证通过

阶段三:渐进式迁移(第 8-20 天)

目标:将功能从单体服务逐步迁移到新服务,保持系统始终可用。

关键动作

  1. 流量灰度:通过网关将少量流量路由到新服务
  2. 双写验证:关键数据同时写入新旧存储,比对一致性
  3. 功能开关:使用 Feature Flag 控制新旧逻辑切换
  4. 每日回归:每天运行核心测试用例,确保无回归

交付物

  • 每日迁移进度报告
  • 双写一致性验证报告
  • 性能对比数据

退出条件

  • 100% 流量已切换到新服务
  • 双写验证连续 7 天无差异
  • 性能指标不低于原系统

阶段四:清理与收尾(第 21-25 天)

目标:移除旧代码,完成最终收尾。

关键动作

  1. 旧代码删除:移除单体服务中的已迁移代码
  2. 依赖清理:移除不再需要的依赖包
  3. 文档更新:更新架构文档、API 文档
  4. 经验总结:记录本次重构的经验教训

交付物

  • 清理后的代码仓库
  • 更新后的架构文档
  • 重构总结报告

退出条件

  • 旧代码完全移除,系统正常运行
  • 所有文档已更新
  • 团队完成知识传递

每日工作节奏

在长达数周的重构过程中,保持稳定的每日工作节奏至关重要。我们采用以下节奏:

上午(9:00-12:00):执行阶段

  • 运行 /ralph-loop 执行当天的迁移任务
  • 监控实时指标,发现异常立即暂停
  • 记录遇到的问题和解决方案

下午(14:00-17:00):验证与调整阶段

  • 运行回归测试,验证上午的变更
  • 分析测试结果,修复发现的问题
  • 更新第二天的工作计划

傍晚(17:00-18:00):同步与复盘

  • 向团队同步当日进度
  • 记录当日学到的新知识到 AGENTS.md
  • 规划第二天的具体任务

关键实践

在整个重构过程中,以下实践被证明是关键成功因素:

  1. 每日提交:每天的变更都提交到 Git,保持进度可追溯
  2. 自动化验证:所有验证步骤都自动化,减少人为疏漏
  3. 渐进式暴露:每次只迁移一个小模块,控制风险范围
  4. 双向同步:新旧系统之间的数据保持双向同步,确保可随时回退
  5. 知识沉淀:每天将学到的领域知识写入 AGENTS.md,避免重复探索

完整工作流图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
阶段一:分析与规划(3天)

[/init-deep] → 生成知识库

[依赖分析] → 绘制依赖图

[方案设计] → 输出重构计划

[人工评审] → 通过则进入下一阶段

阶段二:基础设施准备(4天)

[脚手架生成] → 创建项目结构

[CI/CD配置] → 自动化流水线

[监控接入] → 日志/指标/链路

[Hello World部署] → 验证基础设施

阶段三:渐进式迁移(13天)

┌── 每日循环 ──────────────────────┐
│ 上午:[/ralph-loop] 执行迁移 │
│ 下午:[回归测试] 验证变更 │
│ 傍晚:[同步复盘] 更新计划 │
└─────────────────────────────────┘

[100%流量切换][双写验证7天]

阶段四:清理与收尾(5天)

[删除旧代码][清理依赖]

[更新文档][经验总结]

[重构完成]

Ralph Loop:上下文重置驱动的迭代工程模式

在讨论完具体的命令和工作流之后,我们需要深入理解支撑这些实践的核心理念:Ralph Loop。它不仅是一个命令,更是一种应对"上下文腐化"(Context Rot)的工程模式。

源头视角:Geoffrey Huntley

Ralph Loop 的概念最早由 Geoffrey Huntley 提出。他在维护大规模开源项目时发现,AI Agent 在长对话中会逐渐"迷失"——上下文窗口被大量中间状态填满,Agent 开始重复犯错、忽略之前的约束、甚至产生幻觉。

他的解决方案非常朴素:不让 Agent 在同一个上下文中运行太久。每一轮迭代完成后,强制开启一个新的会话,只保留必要的状态文件,丢弃所有中间对话历史。

这就是 Ralph Loop 的核心思想:用进程级的上下文重置,换取长期的稳定性

核心理念:Context Rot

Context Rot(上下文腐化) 是指随着对话轮次增加,Agent 的上下文窗口逐渐被无关信息污染,导致性能下降的现象。具体表现包括:

  • 注意力分散:Agent 开始关注无关细节,忽略核心任务
  • 约束遗忘:之前设定的规则被逐渐忽略
  • 重复劳动:Agent 重复执行已经做过的操作
  • 幻觉增加:由于上下文混乱,Agent 开始编造不存在的信息

传统的解决方法是增加上下文窗口大小,但这只是治标不治本。更大的窗口意味着更多的噪声,Agent 的注意力机制仍然会在海量信息中迷失。

Ralph Loop 的思路相反:不追求更大的窗口,而追求更干净的窗口。通过定期重置上下文,确保 Agent 每一轮都从一个清晰的状态开始。

例一:open-ralph-wiggum

open-ralph-wiggum 是 Ralph Loop 的一个参考实现。它的核心逻辑非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# open-ralph-wiggum 简化版

TASK="$1"
MAX_ITERATIONS="${2:-100}"
COMPLETION_PROMISE="${3:-DONE}"

for i in $(seq 1 $MAX_ITERATIONS); do
echo "=== Iteration $i ==="

# 启动新的 Agent 会话
opencode run "$TASK"

# 检查是否完成
if grep -q "<promise>$COMPLETION_PROMISE</promise>" output.md; then
echo "Task completed!"
exit 0
fi

# 更新任务状态
TASK=$(cat state.md)
done

echo "Max iterations reached"
exit 1

关键点

  • 每一轮都启动新的 opencode run 进程
  • 只通过文件系统(state.mdoutput.md)传递状态
  • 不保留任何对话历史

例二:OMO 实现

OMO 在 open-ralph-wiggum 的基础上做了增强,主要改进包括:

  1. Hook 机制:在每轮迭代前后注入自定义逻辑(如运行测试、更新知识库)
  2. 状态管理:使用结构化的 boulder.json 替代简单的文本文件
  3. 错误恢复:检测到失败时自动回退到上一个稳定状态
  4. 并行验证:在 Agent 执行下一轮的同时,后台运行验证脚本

OMO 的 Ralph Loop 流程

1
2
3
4
5
6
7
8
9
10
启动 /ralph-loop

┌── 迭代开始 ─────────────────────────┐
│ 1. Pre-Hook: 运行测试、更新知识库 │
│ 2. Agent 执行: 启动新会话 │
│ 3. Post-Hook: 验证结果、更新状态 │
│ 4. 检查完成条件 │
│ - 完成 → 退出循环 │
│ - 未完成 → 继续下一轮 │
└─────────────────────────────────────┘

两种实现的本质差异

维度 open-ralph-wiggum OMO
状态传递 简单文本文件 结构化 JSON
扩展性 硬编码逻辑 Hook 机制可扩展
错误处理 简单重试 智能回退
验证机制 并行验证
适用场景 个人小项目 团队大型项目

本质上,open-ralph-wiggum 证明了 Ralph Loop 理念的可行性,而 OMO 将其工程化为一个可用的生产工具。

两种循环的信号检测视角

从信号检测的角度看,Ralph Loop 和传统长对话的本质差异在于噪声累积的控制

传统长对话

1
2
3
4
5
轮次 1: 信号 90% + 噪声 10%
轮次 2: 信号 80% + 噪声 20% (噪声累积)
轮次 3: 信号 60% + 噪声 40% (噪声继续累积)
...
轮次 N: 信号 20% + 噪声 80% (信号被淹没)

Ralph Loop

1
2
3
4
5
轮次 1: 信号 90% + 噪声 10% → 重置
轮次 2: 信号 90% + 噪声 10% → 重置
轮次 3: 信号 90% + 噪声 10% → 重置
...
轮次 N: 信号 90% + 噪声 10% (噪声不累积)

通过进程级重置,Ralph Loop 确保每一轮的噪声不会带入下一轮,从而保持长期的稳定性。

深挖三种模式

在实际使用中,Ralph Loop 有三种常见模式:

模式一:严格 Ralph Loop

特点:每轮完全重置,只保留最少量的状态文件。

适用场景

  • 任务可以清晰分解为独立步骤
  • 每步的输出可以作为下一步的输入
  • 对上下文依赖较低

示例

1
/ralph-loop "逐个修复 lint 错误" --completion-promise="NO_ERRORS"

模式二:混合 Ralph Loop

特点:部分状态持久化,保留关键的中间结果。

适用场景

  • 任务有一定的上下文依赖
  • 需要保留部分历史信息
  • 平衡稳定性和连续性

示例

1
/ralph-loop "重构模块 A" --persist-state="dependencies.md,api-contracts.md"

模式三:Ultrawork Ralph Loop

特点:结合 Ultraworker 的多代理能力,在每轮中进行并行验证。

适用场景

  • 高可靠性要求的重构
  • 需要多维度验证的任务
  • 团队级的大型项目

示例

1
/ulw-loop "拆分单体服务" --parallel-validation="tests,lint,build"

本质:短跑选手的接力赛

Ralph Loop 的本质可以用一个比喻来概括:

传统长对话像一个马拉松选手,试图一口气跑完全程。但随着距离增加,体力下降、注意力分散、错误增多。

Ralph Loop像一群短跑选手的接力赛。每个选手只跑一小段,保持最佳状态,然后通过接力棒(文件系统)将状态传递给下一个选手。

这个比喻揭示了 Ralph Loop 的核心优势:

  1. 每轮都是最佳状态:每个"短跑选手"都从清晰的上下文开始
  2. 状态传递标准化:接力棒(文件系统)是标准化的状态载体
  3. 容错性强:一个选手失误,不影响下一个选手从头开始
  4. 可观测性好:每轮的结果都可以独立检查和审计

理解了这一点,就能理解为什么 Ralph Loop 成为大型重构的标准模式——它不是简单地"多跑几轮",而是通过架构级的上下文管理,解决了单 Agent 长对话的根本缺陷。

子 Agent 的本质:上下文隔离与专门化

"子 Agent"这个词在多 Agent 系统的讨论中频繁出现,却鲜有人把它说清楚。它是一个能力弱化的 Agent,类似一个 Agent 化的工具?还是一个拥有更小上下文的原始 Agent,像从主 Agent fork 出来的进程?还是一个在指挥体系里听从领导 Agent、但拥有更强资源和能力的 Agent?

这三种直觉都不完全准确。本文从 Anthropic、LangChain、Claude Code 等权威来源出发,厘清子 Agent 的真实本质,并探讨一个更深层的问题:"子 Agent"究竟是能力描述,还是关系描述?

三种直觉,三种误解

在深入定义之前,先把三种常见直觉逐一检验。

误解一:子 Agent 是能力弱化的 Agent

这种直觉来自于"子"字的字面含义——子集、子系统、子进程,往往意味着更小、更弱。但 LangChain 官方文档明确指出:

“An interesting aspect of this approach is that sub-agents may have the exact same capabilities as the main agent.”

子 Agent 可以拥有与主 Agent 完全相同的能力。它不是弱化版,不是受限版,更不是"工具的高级包装"。

误解二:子 Agent 是拥有更小上下文的 fork

这种直觉来自操作系统的进程模型——子进程从父进程 fork,继承部分状态,但资源受限。这个类比有一定道理,但关键词不是"更小",而是"独立且干净"。

Anthropic 官方工程博客描述其多 Agent 研究系统时写道:

“Subagents facilitate compression by operating in parallel with their own context windows, exploring different aspects of the question simultaneously before condensing the most important tokens for the lead research agent.”

子 Agent 拥有独立的上下文窗口(own context windows),而不是主 Agent 上下文的子集。这个上下文窗口是全新的、干净的,不是"更小的"。

误解三:子 Agent 是听从指挥但能力更强的下属

这种直觉来自军事或企业的层级模型——将领指挥士兵,但士兵在具体战术上可能比将领更专业。这个类比在某些场景下成立,但它把"子 Agent"理解成了一种永久的身份,而非一种临时的关系

真正的问题在于:一个 Agent 是否是"子 Agent",取决于它在当前任务中的角色,而非它的固有属性。

子 Agent 的真实本质

综合 Anthropic、Claude Code、LangChain 的权威定义,子 Agent 的本质可以用一句话概括:

子 Agent 是一个完整的、专门化的 Agent 实例,运行在独立的上下文窗口中,由主 Agent 通过工具调用方式协调,其核心价值在于上下文隔离,而非能力弱化。

五个核心特征

1. 上下文隔离

这是子 Agent 最核心的价值。LangChain 的表述最为精辟:

“This provides context isolation: each subagent invocation works in a clean context window, preventing context bloat in the main conversation.”

每次调用子 Agent,都是在一个干净的上下文窗口里开始工作。主 Agent 的历史对话、工具调用记录、中间推理过程,都不会污染子 Agent 的上下文。这防止了主 Agent 的上下文膨胀(context bloat)。

2. 完整的 Agent 能力

子 Agent 不是工具(Tool)。工具是单一功能的函数,被动执行,没有决策能力,没有 Agent Loop。子 Agent 是完整的 Agent 实例,拥有:

  • 独立的推理-行动循环(Agent Loop)
  • 自主规划和决策能力
  • 多步骤任务执行能力
  • 执行期间的短期记忆

3. 无状态执行

子 Agent 是无状态的(stateless)——它不保留跨调用的记忆。每次被主 Agent 调用,都是一次全新的执行。所有跨步骤的状态,由主 Agent 维护。

4. 专门化任务处理

子 Agent 通过自定义 system prompt 定义专门职责,可以针对特定领域优化。这种专门化不是能力的限制,而是能力的聚焦。

5. 不能递归派发子 Agent

Claude Code 官方文档明确指出:

“Subagents cannot spawn other subagents, so Agent(agent_type) has no effect in subagent definitions.”

子 Agent 不能再派发子 Agent。这是一个刻意的设计约束,防止无限递归和控制流混乱,保证行为的可预测性。

一个重要的附加结论:Skill 可以无限递归

子 Agent 不能递归派发,但 Skill 可以

这里需要区分两个概念:

概念 本质 递归能力
子 Agent 完整的 Agent 实例,独立上下文窗口 ❌ 不能递归派发
Skill System prompt 的动态注入,上下文内的行为扩展 ✅ 可以无限递归

Skill(如 Claude Code 中的 SKILL.md 机制)本质上是提示词注入,而非 Agent 实例的创建。当一个 Skill 被触发时,它的内容被注入到当前 Agent 的上下文中,Agent 按照 Skill 的指令行动。这个过程发生在同一个上下文窗口内,不涉及新 Agent 实例的创建。

因此,Skill A 可以触发 Skill B,Skill B 可以触发 Skill C,形成任意深度的调用链——这是纯粹的上下文内行为扩展,不存在"无限递归创建 Agent 实例"的风险。

这个区别揭示了一个深层设计哲学:Agent 实例的创建是重量级操作(需要独立上下文窗口),应该被约束;而上下文内的行为扩展是轻量级操作,可以自由组合。

"子 Agent"是关系描述,不是能力描述

这是本文最重要的洞见。

当我们说某个 Agent 是"子 Agent"时,我们描述的是它在当前任务中与其他 Agent 的关系,而不是它的固有属性或能力等级。

以 OMO(Oh My OpenAgent)的 Agent 团队为例:

1
2
3
4
5
6
7
8
用户请求

Sisyphus(主编排器)
├─→ Hephaestus(深度工作)
├─→ Prometheus(战略规划)
├─→ Oracle(架构咨询)
├─→ Librarian(文档研究)
└─→ Explore(代码搜索)

在这个架构图里,Prometheus、Oracle、Librarian 看起来是 Sisyphus 的"子 Agent"。但这个判断是错误的,或者说是不完整的。

Prometheus、Oracle、Librarian 都可以独立运行。

  • 用户可以直接对话 Prometheus,让它制定战略规划,完全不经过 Sisyphus
  • 用户可以直接调用 Oracle 进行架构咨询
  • Librarian 可以作为独立的文档研究 Agent 被任何工作流调用

它们之所以在某些场景下表现为 Sisyphus 的"子 Agent",仅仅是因为 Sisyphus 在那个特定的任务中扮演了编排者的角色,通过工具调用的方式协调了它们。一旦任务结束,这种"主-子"关系就消失了。

这与传统软件工程中的"子系统"概念截然不同。子系统是静态的架构关系,一旦被设计为子系统,就永远是子系统。而 Agent 的"主-子"关系是动态的、任务级别的

类比:项目中的角色 vs 职位

一个更直观的类比:

在一个软件项目中,张三是"项目经理",李四是"开发工程师"。但在另一个项目中,李四可能是项目经理,张三是开发工程师。他们的职位(固有属性)没有变,但他们在不同项目中的角色(关系描述)是不同的。

Agent 的"主-子"关系正是如此。Prometheus 的固有属性是"战略规划专家",但它在不同的任务中可以是:

  • 独立运行的规划 Agent(直接与用户对话)
  • Sisyphus 的子 Agent(被 Sisyphus 通过工具调用协调)
  • 某个更高层编排器的子 Agent(在更复杂的多层架构中)

子 Agent vs Tool:本质区别

既然子 Agent 不是弱化的 Agent,那它和 Tool 的区别是什么?

维度 Tool 子 Agent
本质 单一功能的函数/方法 完整的 Agent 实例
上下文 无独立上下文 拥有独立上下文窗口
决策能力 无,被动执行 有,可自主规划
循环能力 拥有 Agent Loop(推理-行动循环)
记忆 执行期间有短期记忆
复杂度 单步操作 多步骤复杂任务
状态 无状态 执行期间有状态,调用间无状态

何时用 Tool,何时用子 Agent?

  • Tool:任务是单步的、确定性的、不需要推理的(读文件、执行命令、调用 API)
  • 子 Agent:任务需要多步骤推理、需要上下文隔离、需要专门化处理

实践含义

理解子 Agent 的本质,对实际的多 Agent 系统设计有直接影响:

1. 不要把"子 Agent"设计成永久的层级关系

如果你的系统里有一个 Agent 永远只能被另一个 Agent 调用,永远不能独立运行,这往往是过度设计的信号。好的 Agent 应该是可以独立运行的,"主-子"关系是任务级别的协调,而非架构级别的约束。

2. 上下文隔离是选择子 Agent 的核心理由

当你考虑是否要用子 Agent 时,最重要的问题不是"这个任务需要多强的能力",而是"这个任务需要独立的上下文吗?"如果任务足够简单,一个 Tool 就够了;如果任务需要多步骤推理但上下文不需要隔离,在主 Agent 内处理就够了;只有当任务需要多步骤推理需要上下文隔离时,才需要子 Agent。

3. Skill 是上下文内的行为扩展,子 Agent 是上下文外的任务委托

这两种机制解决的是不同层面的问题。Skill 扩展的是当前 Agent 的行为能力,子 Agent 委托的是独立的任务执行。前者是"让我学会新技能",后者是"把这个任务交给专家去做"。

4. 协调者 Agent 应该"不写代码"

在多 Agent 团队的设计中,一个关键原则是:协调者 Agent 应当专注于调度与决策,而不直接参与执行细节

这个设计有两层深意:

第一,保护协调者的全局视野。 执行细节(如代码实现、文件操作、调试过程)会迅速填满上下文窗口。如果协调者亲自下场执行,它会在技术细节中逐渐丢失对整体任务目标、约束条件和进度安排的掌控。就像一个项目经理如果整天埋头写代码,就会忘记关注项目风险、资源协调和交付时间线。

第二,确保执行者的上下文纯净。 当协调者将任务派发给子 Agent 时,子 Agent 只接收到最小化的任务描述——它需要做什么、遵循什么约束、返回什么格式。子 Agent 的上下文窗口不会被协调者的历史对话、中间推理、或其他无关任务污染。这种"干净"的上下文让子 Agent 能够高度专注地完成单一任务。

这正是 Divide and Conquer(分治) 思想在 Agent 系统中的体现:

层级 职责 上下文特征
协调者(Orchestrator) 任务分解、进度追踪、质量把控、异常处理 保留全局任务图、约束条件、各子任务状态
执行者(Executor) 具体实现、细节处理、单任务完成 仅包含当前子任务的输入、工具、输出规范

执行层往往是"重"的——涉及大量代码、多轮工具调用、错误重试。调度层必须是"轻"的——保持敏捷,随时准备调整策略。如果让协调者同时承担执行,这两层会互相干扰:执行细节膨胀会挤压全局规划的上下文空间,而频繁的全局状态查询又会打断执行的连贯性。

因此,"协调者不写代码"不是能力的限制,而是架构的自觉——通过严格的角色分离,让系统获得更好的可预测性和可维护性。

5. 上下文消耗的恶性循环与"就改一行"陷阱

如果这篇文章你只记住一件事,记这个:中等复杂度以上的任务,协调者绝不写代码。

为什么?因为 LLM 的上下文窗口是有限的。Agent 执行长任务时,窗口里慢慢堆起来的是代码 diff、编译错误、测试输出、lint 报告、重试记录……到第 40 次 tool call,早期的关键信息已经被压缩或丢弃了。Agent 开始"忘记"最初的架构决策,做出跟前面矛盾的改动。

这是一个恶性循环

1
2
3
任务越复杂 → 消耗的上下文越多 → 关键信息被压缩 → 决策质量下降 
↑ ↓
更多修复 ← 产生更多错误 ← 做出矛盾改动 ← 丢失早期决策

一旦进入这个循环,就很难出来。到第 60 次 tool call,Agent 可能已经忘了最初的任务目标是什么了。你让它加个用户认证功能,它却在那儿纠结一个完全不相关的 import 路径问题。

解法是把 Agent 拆成两层。 协调者(Coordinator)只管规划、委派、汇总,一行代码都不碰;执行者(子 Agent)每次从干净的上下文开始,拿到精确的 prompt,干完就释放。子 Agent 不知道之前发生了什么,但它拿到的 prompt 里包含了它需要知道的一切。任务完成后,详细上下文被丢掉,协调者只保留一段摘要。信息经过了压缩和筛选,而不是无差别地堆在上下文里。

破坏这个原则最常见的方式是"只是快速改一下"。 协调者发现一个小问题,心想"这么简单的东西不用启子代理,我直接改了"。一次编辑变成了五次(因为牵扯出其他地方),五次变成二十次,上下文就被消耗殆尽了。

这个陷阱看起来很合理——“就改一行而已嘛”——但你低估了代码的牵连性。如果你发现协调者正在用 Edit 或 Write 工具修改源代码,立刻停下来,启动子代理。没有例外。

复杂度分层决策框架

不同复杂度的任务,执行方式也不同:

复杂度 判断标准 执行方式 示例
简单 能用一句话描述,且不包含"和"字 协调者直接执行 改 typo、加行日志
中等 需要清单来跟踪改了哪些地方 委派给子 Agent 多文件一致性修改
复杂 需要做设计决策和权衡 委派 + Git Worktree 隔离 重构、新模块开发

Git Worktree 隔离相当于仓库的临时副本,子 Agent 在独立工作区执行结构性变更,成功了合并,失败了丢掉,不污染主分支。

模型选型的经济账

委派子 Agent 时还有一个容易被忽略的杠杆:不是所有任务都需要用最强的模型。

一个"重命名变量"的任务和一个"重构认证模块"的任务,对模型能力的要求完全不同。前者要的是快和便宜,后者要的是深度推理。如果所有子代理都用同一个顶级模型,既浪费钱又浪费时间。

协调者在委派时可以根据任务性质指定模型:

任务类型 推荐模型 理由
快速执行类(改 typo、简单重命名) Claude Haiku 响应快、成本低
深度推理类(复杂重构、架构级变更) GPT-5.3 Codex / Claude Opus 质量远比速度重要
代码检索类(大型代码库中定位文件) Gemini 3 Flash 速度第一

这种分层模型策略让系统在保证质量的同时,显著降低运行成本。

OMO:Prompt Engineering 构建的虚拟团队

OMO(Oh My OpenAgent,原名 Oh My OpenCode)通过精心设计的 system prompt 和 Hook 机制,在 OpenCode 内部构建了一支由 11 个专业 Agent 组成的虚拟团队。这不是简单的工具集合,而是一个基于 Prompt Engineering 的组织架构

三层架构:规划层、执行层、工作者层

OMO 的 Agent 团队分为三个层次,各司其职:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
用户请求


┌───────────────────────────────────────────────────────────────┐
│ 规划层(Planning Layer) │
│ Prometheus:战略规划师,只做计划不写代码 │
│ Metis:前置分析师,在规划前做 gap 分析 │
│ Momus:计划审查员,验证计划的可执行性 │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 执行层(Execution Layer) │
│ Sisyphus:主编排器,接收请求后委派任务 │
│ Hephaestus:自主深度执行器,端到端完成任务 │
│ Atlas:Todo 列表编排器,按波次并行调度 │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 工作者层(Worker Layer) │
│ Explore:内部代码库搜索 │
│ Librarian:外部文档/代码搜索 │
│ Oracle:只读高智商顾问 │
│ Multimodal Looker:多模态文件分析 │
│ Sisyphus-Junior:分类任务执行器 │
└───────────────────────────────────────────────────────────────┘

规划层负责将用户需求转化为结构化的执行计划。执行层负责任务的编排和调度。工作者层负责具体的执行工作。

多轨主 Agent:四个入口的选择逻辑

OMO 提供了四个用户可直接选择的主 Agent 入口,对应不同的工作模式:

入口 适用场景 核心特点
Prometheus 需求不明确,需要先规划 只做计划,不写代码
Atlas 已有计划,需要执行 按 Todo 列表波次执行
Hephaestus 需要深度自主执行 单 Agent、单上下文,禁止委托
Sisyphus 日常多 Agent 协作 委派优先,多上下文隔离

这四条路径彼此独立、互不委托。路由决策权保留给人类用户——Agent 不具备自主判断"当前任务更适合另一条路径"并切换的能力。

Sisyphus vs Hephaestus:委派优先 vs 深度自主

四个主 Agent 中,Sisyphus 和 Hephaestus 的对比最能体现"多轨"设计的核心张力:

维度 Sisyphus Hephaestus
核心哲学 委派优先、并行默认、证据驱动 禁止询问、不中途停下、100% 完成
底层模型 Claude Opus(编排能力优先) GPT-5.3 Codex(推理能力优先)
Todo 列表 ✅ 使用 boulder.json 管理 ❌ 没有 Todo 列表
子 Agent 委托 ✅ 可委托多种子 Agent ❌ 禁止委托子 Agent
上下文模式 多上下文:主 Agent 持续存在,子 Agent 每次重建 单上下文:全程一个 Agent
探索方式 按 Todo 顺序,Atlas 调度时探索 写代码前强制并行探索(2-5 个 Explore Agent)
中途交互 可通过 Todo 状态观察进度 不中途停下,直到 100% 完成

Hephaestus 是 OMO 中唯一一个"单 Agent、单上下文"的主 Agent。 它的核心设计哲学是:给目标,不给菜谱——用户只需描述最终目标,Hephaestus 自主决定如何达成,中途不停下来询问。

Sisyphus 是 OMO 的核心主编排器,代表"委派优先"的执行哲学。 它的核心设计哲学是:能委派就不自己做——Sisyphus 把任务拆解成结构化的 Todo 列表,然后委派给最合适的子 Agent 去执行。

两者的互斥性,恰恰反映了广度优先和深度优先在执行策略上的根本不兼容——你不能同时既并行委派又深度自主。

OMO Agent 角色规格表

OMO 完整的 11 个 Agent 各有明确的模型选型和职责边界:

Agent 默认模型 温度 模式 核心职责
Sisyphus claude-opus-4-6 0.1 primary 主编排器,接收用户请求后进行意图分类、委派任务、验证结果
Hephaestus gpt-5.3-codex 0.1 primary 自主深度执行器,端到端完成复杂任务,不中途停下
Prometheus claude-opus-4-6 0.1 all 战略规划师,只做计划不写代码,输出 .sisyphus/plans/*.md
Atlas claude-sonnet-4-6 0.1 primary Todo 列表编排器,按波次并行调度任务执行
Oracle gpt-5.2 0.1 subagent 只读高智商顾问,用于架构决策和疑难调试
Metis claude-opus-4-6 0.3 subagent 规划前顾问,在 Prometheus 生成计划前做 gap 分析
Momus gpt-5.2 0.1 subagent 计划审查员,验证计划的可执行性和引用正确性
Librarian glm-4.7 0.1 subagent 外部文档/代码搜索,克隆仓库、查官方文档、搜 GitHub
Explore grok-code-fast-1 0.1 subagent 内部代码库搜索,回答"X 在哪里"类问题
Multimodal Looker gemini-3-flash 0.1 subagent 多模态文件分析,处理 PDF/图片/图表
Sisyphus-Junior claude-sonnet-4-6 0.1 all 分类任务执行器,由 category 系统派生,不能再委派 task()

温度差异值得注意:大多数 Agent 使用 temperature: 0.1(确定性输出),但 Metis 使用 0.3——作为"前置分析师",它需要更多创造性来发现潜在问题和盲点。

协作拓扑形成了清晰的分层结构:

1
2
3
4
5
6
7
用户请求
├─→ Sisyphus (日常编排) ──→ Explore/Librarian (后台搜索)
│ │ ──→ Oracle (高难度咨询)
│ │ ──→ task(category=X) → Sisyphus-Junior (执行)
├─→ Hephaestus (深度自主) ──→ 同上,但更自主,不中途停下
├─→ Prometheus (规划模式) ──→ Metis (前置分析) → Momus (计划审查)
└─→ Atlas (计划执行) ──→ task(category=X) → Sisyphus-Junior (按波次执行)

OMO 的一次循环:从请求到代码落地

以一个具体例子拆解 OMO 内部的执行过程。

场景:在 OpenCode 终端输入:

1
ulw: 把 utils/date.ts 里的 formatDate 函数重构,用 Intl.DateTimeFormat 替换手写的格式化逻辑

第一步:Hook 拦截,识别 ulw 魔法词

OMO 通过 OpenCode 的 UserPromptSubmit Hook 拦截输入。检测到 ulw(ultrawork 的缩写)后,Hook 在提示词前注入系统级指令,激活完整的多 Agent 协作流程。这一注入对用户透明——Sisyphus 收到的实际提示词已包含"启动并行子 Agent、强制完成 Todo、使用 LSP 重构"等完整指令。

架构洞察:中间件/拦截器模式

从实现角度看,OMO 是一个典型的中间件/拦截器模式。它不直接实现 AI 对话逻辑,而是通过 OpenCode 提供的各个生命周期钩子点(UserPromptSubmitPostToolUse 等)注入自己的逻辑。整个插件本质上是一个巨大的"拦截器集合"——每个钩子点对应一个拦截层,OMO 在这些层上叠加规划、调度、验证等能力,而 OpenCode 的核心对话引擎完全不感知这些注入的存在。

第二步:Prometheus 规划(规划层)

Sisyphus 框架首先调用 Prometheus(规划师),将需求拆解为结构化的 Todo 列表:

1
2
3
4
5
6
Todo 列表(由 Prometheus 生成):
[ ] 1. 用 Explore Agent 扫描 utils/date.ts,理解现有 formatDate 的所有调用方
[ ] 2. 用 Librarian Agent 查询 Intl.DateTimeFormat 的 MDN 文档,确认 API 用法
[ ] 3. 用 Sisyphus-Junior 重写 formatDate,替换为 Intl.DateTimeFormat
[ ] 4. 用 LSP 验证重构后所有引用点无类型错误
[ ] 5. 运行测试,确认行为一致

Todo 列表是 OMO 的核心状态——写入 boulder.json(或 .sisyphus/ 目录下的状态文件),而非对话历史。这使得 OMO 能跨会话恢复:即使中途关闭终端,Todo 列表仍保存在文件中。

第三步:并行子 Agent 执行(工作者层)

Atlas(指挥官) 读取 Todo 列表,识别无依赖关系的任务并行执行。Todo 1(扫描调用方)和 Todo 2(查文档)互相独立,Atlas 同时启动两个子 Agent:

1
2
3
4
5
6
7
8
9
10
11
12
Atlas 并行调度:
┌─────────────────────────────────────────────────────────┐
│ 同时启动 │
│ │
│ 子 Agent A: Explore
│ 任务:扫描 utils/date.ts,找出 formatDate 的所有调用方 │
│ 模型:Gemini Flash100万+ tokens,快速代码库搜索) │
│ │
│ 子 Agent B: Librarian
│ 任务:查询 Intl.DateTimeFormat MDN 文档 │
│ 模型:Claude Sonnet(平衡速度和质量) │
└─────────────────────────────────────────────────────────┘

两个 Agent 并发工作,各自完成后将结果汇报给 Atlas。这是"并行"的真实含义——多个 AI 进程同时运行,而非顺序问答。

第四步:Sisyphus-Junior 执行核心编码

Atlas 将 Explore 和 Librarian 的结果(调用方列表 + API 文档)传给 Sisyphus-Junior(主力执行者),开始重构:

1
2
3
4
5
6
7
8
9
10
11
Sisyphus-Junior 的工作流程:
读取 utils/date.ts 当前内容

根据 Intl.DateTimeFormat API 重写 formatDate

调用 LSP(语言服务器)验证:
- 类型检查:新函数签名是否兼容所有调用方?
- 引用检查:有没有遗漏的调用点?

如果 LSP 报错 → 自动修正,再次验证
如果 LSP 通过 → 标记 Todo 3、4 为完成

LSP 的介入是关键:Sisyphus-Junior 通过 LSP 的 publishDiagnostics 接口获得编译级别的反馈,确保重构不破坏任何引用,而非依赖人工跑测试验证。

第五步:Todo 执行器强制收尾

OMO 内置 Todo 执行器(Todo Enforcer):通过 PostToolUse Hook,每次工具调用结束后检查 Todo 列表,若有未完成项,则将"继续完成剩余 Todo"的指令注入到下一轮提示词。这一机制确保 Agent 持续工作直到任务完成,而非依赖模型的自律性。

流程 ASCII 图:多轨主 Agent 入口

OMO 有四个用户可直接选择的主 Agent 入口,对应不同的工作模式。以下展示核心路径的完整流程。

路径一:Sisyphus ulw 路径(委派优先,Todo 驱动)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
输入:ulw: 重构 formatDate


┌───────────────────────────────────────────────────────────────┐
│ UserPromptSubmit Hook(OMO 注入) │
│ 检测到 ulw → 在提示词前注入多 Agent 协作指令 │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 规划层:Prometheus │
│ 输入:需求描述 │
│ 输出:结构化 Todo 列表 → 写入 boulder.json
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 执行层:Atlas(指挥官) │
│ 读取 Todo 列表,识别无依赖任务,并行调度子 Agent │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Explore Agent │ │ Librarian Agent │ ← 并行 │
│ │ 扫描代码库调用方 │ │ 查询 MDN 文档 │ │
│ │ Gemini Flash │ │ Claude Sonnet │ │
│ └──────────┬──────────┘ └──────────┬───────────┘ │
│ └──────────┬──────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Sisyphus-Junior(主力执行者) │ │
│ │ 拿到调用方列表 + API 文档 │ │
│ │ 重写 formatDate → LSP 验证 → 自动修正 → 标记完成 │ │
│ └─────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ PostToolUse Hook:Todo 执行器 │
│ 检查 boulder.json:还有未完成 Todo? │
│ 是 → 注入"继续"指令,强制下一步 │
│ 否 → 任务完成,退出 │
└───────────────────────────────────────────────────────────────┘


代码已落地,LSP 验证通过,等待人工审查

路径二:Hephaestus 路径(自主深度执行,推理优先)

Hephaestus 是与 Sisyphus 并列的第二个主 Agent,使用 GPT-5.3 Codex 模型,专为深度架构工作和复杂调试设计。它的核心哲学是"给目标,不给菜谱"——用户只需描述最终目标,Hephaestus 自主决定如何达成,中途不停下来询问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
输入:[用户在 OpenCode 中切换到 Hephaestus 模式]
目标:把 utils/date.ts 的 formatDate 重构为 Intl.DateTimeFormat


┌───────────────────────────────────────────────────────────────┐
│ Hephaestus(单 Agent,单上下文) │
│ 核心约束: │
- 禁止委托子 Agent(delegate_task: false) │
- 禁止调用其他 OMO Agent(call_omo_agent: false) │
- 不使用 Todo 列表 │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 前置并行探索(唯一允许的子 Agent 使用场景) │
│ 强制启动 2-5 个 Explore Agent 并发扫描代码库 │
│ 目的:形成完整的代码库理解,避免碎片化信息干扰 │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 深度自主执行 │
│ Hephaestus 基于探索结果,自主规划执行路径 │
│ 重写 formatDate → LSP 验证 → 自动修正 │
│ 不中途停下,直到 100% 完成 │
└───────────────────────────────────────────────────────────────┘


代码已落地,等待人工审查

boulder 机制:对抗 Context Rot 的注意力刷新

**Context Rot(上下文腐化)**是 Transformer 架构的数学属性:随着上下文增长,模型对中间位置信息的注意力最弱(Lost-in-the-Middle 效应),每个 Token 分到的注意力权重趋近于零(注意力稀释),语义相似但无关的内容会主动误导模型(干扰项叠加)。

研究表明,每个 AI Agent 的成功率在 35 分钟后都会下降,任务时长翻倍,失败率翻四倍。这是因为 35 分钟后,Agent 通常已经读取了 15-30 个文件,上下文中有效信号的占比已经低到临界点。

boulder 机制的应对策略非常直接:通过 PostToolUse Hook,在每次工具调用结束后,将"继续完成剩余 Todo"的指令重新注入到提示词的尾部。这意味着:

  • 无论上下文已经积累了多少历史,当前任务目标始终出现在上下文的最后一条消息
  • 模型在每次推理时,都能以最高注意力感知到"我还有什么没做完"
  • Todo 列表本身存储在 boulder.json 文件中(持久化状态),而非依赖上下文记忆

这是一个精妙的设计:用文件系统承载状态,用尾部注入保持注意力。boulder 机制没有试图解决 Context Rot(那需要改变 Transformer 架构),而是绕过了它——通过持续将任务目标"刷新"到注意力最强的位置,让 Context Rot 的影响无法积累到足以干扰任务执行的程度。

多维度委派:像造房子一样同时发展

问题:往不同维度委派不同的 tuned 过的 Agent 工程师团队,是不是像造房子一样,长宽高同时发展的最优模式?

答案:这个类比非常准确,但需要补充一个关键约束条件。

传统软件开发的瓶颈之一是串行依赖:前端等后端 API,后端等数据库 Schema,测试等功能完成。这种串行性不是因为工程师不够聪明,而是因为人类工程师的注意力是单线程的——同一时间只能深度专注于一件事。

多 Agent 架构打破了这个约束。OMO 的 11 个 Agent 按职责维度分工:

  • 规划维度(Prometheus):负责任务分解,不参与执行
  • 执行维度(Sisyphus-Junior):负责代码实现,不主导规划
  • 搜索维度(Librarian):负责文档查询,不干扰编码
  • 审查维度(Atlas):负责代码审查,不参与生成

这确实像造房子:地基、框架、水电、装修可以在不同维度同时推进,只要接口约定清晰(墙的位置、管道走向),各工种互不干扰。

但"造房子"类比也揭示了一个关键约束:并行的前提是接口的提前约定。如果前端和后端同时开发,必须先约定好 API 契约;如果多个 Agent 并行执行,必须先约定好文件边界和状态格式。OMO 的 .sisyphus/plans/ 目录和 boulder.json 正是扮演这个"建筑图纸"的角色——它们定义了各 Agent 的工作边界,使并行成为可能。

所以更精确的表述是:多维度委派是最优模式,但前提是有一个强规划层(Prometheus)先把"图纸"画好。没有图纸的并行,不是造房子,是各自为战。

计划-委派-执行-测试-审查:瀑布流的复活?

问题:当前的计划→委派→执行→测试→审查模式,是不是重新复现了当年瀑布流的工程项目管理模式?

答案:形似瀑布,但本质不同——关键差异在于迭代速度和反馈机制。

表面上看,Agentic Coding 的工作流确实与瀑布模型高度相似:

瀑布模型 Agentic Coding
需求分析 Prometheus 规划,生成 Todo 列表
系统设计 技术方案 Agent 输出架构文档
编码实现 Sisyphus-Junior 并行执行
测试验证 测试 Agent 部署到测试环境验证
部署上线 部署 Agent 执行发布流程

但瀑布模型的核心问题不是"有没有阶段",而是阶段之间的反馈延迟:需求分析完成后,要等几个月才能看到测试结果,发现问题时已经积累了大量错误的实现。

Agentic Coding 的关键差异在于:

  1. 迭代速度:一个完整的"计划→执行→测试"循环可以在分钟级完成,而不是月级。这使得"发现问题→修正方向"的成本极低。
  2. 并行反馈:测试 Agent 不需要等所有功能完成才开始工作,可以在执行 Agent 完成部分功能后立即介入验证。
  3. 动态规划:Prometheus 的规划不是一次性的,而是可以根据执行结果动态调整——这是瀑布模型最根本的缺陷所在(需求冻结)。

更准确的类比是:Agentic Coding 更像极速迭代的敏捷开发,只是把"Sprint"的时间单位从两周压缩到了几分钟,把"团队成员"从人类工程师替换为了专职 Agent。它保留了敏捷的核心——快速反馈、持续验证——而不是瀑布的核心——阶段冻结、线性推进。

当然,有一个场景确实更接近瀑布:当任务足够大、规划足够复杂时,Prometheus 的初始规划会变得非常重要,一旦规划方向错误,后续所有 Agent 的执行都会偏离。这是 Agentic Coding 目前最需要人类介入的环节——不是执行,而是规划的正确性验证。这也是为什么 Human-in-the-Loop 在企业级框架中被显式设计为"需求澄清"阶段:让人类在规划阶段介入,而不是在执行阶段救火。

两种循环的检验对象:它们到底在检验谁?

问题:外部 ralph CLI 和 OMO 内部 /ralph-loop 这两种循环,检验的是西西弗斯的完成状态,还是西西弗斯的子 Agent 的完成状态?

这个问题触及了 Agentic Coding 中一个常被忽视的结构性盲点。要回答它,需要先厘清一个前提:在 OMO 的多 Agent 架构中,实际执行任务的是子 Agent,而不是西西弗斯本身。西西弗斯是编排者,它把具体工作委派给 Sisyphus-Junior、Explore、Librarian 等子 Agent 去完成。

那么,当任务未完成时,未完成的是谁?是子 Agent——是 Sisyphus-Junior 没有真正重构完 formatDate,是 Explore 没有找到所有调用方。西西弗斯自己只是一个调度层,它感知不到子 Agent 是否真正完成了任务,只能看到子 Agent 的文字报告文件变更

这就引出了两种循环的检验对象问题。

外部 ralph CLI(open-ralph-wiggum)检验的是西西弗斯 session 的输出。

外部 ralph 是 Shell 层面的进程级循环脚本。每次迭代,它启动一个全新的西西弗斯 session,等待该 session 结束,然后检查 session 的 stdout 输出中是否包含完成标记(默认是 <promise>COMPLETE</promise>):

1
2
3
4
5
6
7
8
9
10
11
外部 ralph 的检验逻辑:
启动西西弗斯 session

西西弗斯内部调度子 Agent(对外部 ralph 不可见)

西西弗斯 session 结束,输出 stdout

外部 ralph 扫描 stdout:
包含 <promise>COMPLETE</promise>?
是 → 退出循环
否 → 重新启动西西弗斯 session,继续

关键在于:外部 ralph 看不到子 Agent 的 session。子 Agent 在西西弗斯内部运行,它们的输出被西西弗斯消化后,只有西西弗斯的最终输出才暴露给外部 ralph。完成标记 <promise>COMPLETE</promise>由西西弗斯输出的,不是子 Agent 输出的。

OMO 内部 /ralph-loop 检验的也是西西弗斯 session 的输出。

/ralph-loop 是 OMO 通过 Stop Hook 实现的内部循环机制。当西西弗斯 session 尝试退出时,Stop Hook 拦截这个退出信号,检查 session 输出中是否包含完成标记(如 <promise>DONE</promise>):

1
2
3
4
5
6
7
8
9
/ralph-loop 的检验逻辑(Stop Hook):
西西弗斯 session 尝试退出

Stop Hook 拦截退出信号

检查西西弗斯 session 的输出:
包含 <promise>DONE</promise>?
是 → 允许退出(exit code 0
否 → 阻止退出,注入"继续"指令(exit code 2),强制循环

与外部 ralph 一样,Stop Hook 监听的是整个西西弗斯 session 的输出,而不是任何子 Agent session 的输出。子 Agent 的 session 在西西弗斯内部是隔离的,Stop Hook 无法直接感知它们的状态。

两种循环的本质共同点:都只能检验西西弗斯层面。

维度 外部 ralph CLI OMO /ralph-loop
实现位置 Shell 进程层(外部) OpenCode Stop Hook(内部)
检验对象 西西弗斯 session 的 stdout 西西弗斯 session 的输出
完成标记输出者 西西弗斯(主 Agent) 西西弗斯(主 Agent)
子 Agent 可见性 不可见 不可见
会话管理 每次迭代启动全新 session 同一 session 内循环

这揭示了一个深层的结构性问题:两种循环都依赖西西弗斯的"自我报告"。西西弗斯说"完成了"(输出完成标记),循环就退出;西西弗斯没说完成,循环就继续。但西西弗斯自己对子 Agent 的感知,也只是子 Agent 的文字报告——这是一个信任传递链

1
Agent 声称完成 → 西西弗斯相信并输出完成标记 → 循环检验器退出

链条上的每一环都是"荣誉系统"(honor system):没有机制能自动验证子 Agent 是否真正完成了任务,也没有机制能验证西西弗斯是否正确评估了子 Agent 的工作。如果子 Agent 伪完成(声称完成但实际未完成),西西弗斯可能会被误导,进而输出完成标记,导致循环提前退出。

这就是为什么 Human-in-the-Loop 在 Agentic Coding 中不可或缺——不是因为 Agent 不够聪明,而是因为完成状态的验证链条本质上是软约束,最终需要人类的眼睛来做最后一道确认。

Agentic Coding 的人机分工

Agentic Coding 改变的不只是工具,而是人在研发流程中的角色定位。

在传统开发模式下,开发者是执行者:写代码、调试、部署、验证。在 Agentic Coding 模式下,开发者是架构师和审查者

  • 定义目标:描述要解决的问题,而不是描述解决步骤
  • 设定约束:告诉 Agent 不能做什么(如不能修改某个核心模块)
  • 审查结果:在 Agent 完成任务后,判断结果是否符合预期
  • 介入纠偏:当结果偏离预期时,提供更精确的指导

这种分工有一个关键前提:人类的"品味"。品味不是审美,而是决策力——在众多可行方案中判断"哪个是对的选择",尤其体现为"选择不做"。AI 擅长执行与优化,但缺乏责任意识与经验直觉;人类凭借实践经验积累、业务理解与后果承担能力,把控质量边界与必要性判断。

多工具生态融合:配置文件如何协同

从单工具到多 Agent 团队,这条演进路径的终点并不是"选一个最好的工具",而是让多个工具协同工作——这正是"从工具到团队"这个命题的自然延伸:当 Agent 本身已经开始组队,工具之间的配置知识也需要统一管理,否则团队协作的收益会被配置漂移抵消掉。

一个现实的问题是:团队可能同时使用 Claude Code、Cursor 和 OpenCode(配合 OMO)。每个工具都有自己的配置体系——Claude Code 用 CLAUDE.md,Cursor 用 .cursorrules,OMO 用 AGENTS.mdSKILL.md。真正的挑战不是"配置写在哪",而是如何避免同一份项目知识在多个文件里重复、漂移和失真

更稳妥的做法是把它理解为一个分层配置体系

  • 项目全局知识放在 AGENTS.md,作为跨工具共享的主入口;
  • 工具专用规则放在 CLAUDE.md.cursorrules 之类的轻量文件里,只保留该工具独有的行为约束;
  • 领域技能放在 SKILL.md 中,按需加载;
  • 任务计划与进度放在 .sisyphus/plans/ 等工作目录中,负责跨会话延续执行。

换句话说,多工具协作的关键不是"每个工具都配一遍",而是建立单一真相源 + 工具侧薄包装的结构:共识写一次,工具按需引用。这样,Agentic Coding 才不会从"工程协作系统"退化成"配置文件迷宫"。

如果只用 OpenCode,如何继承 Cursor 和 Claude Code 的 Rule 遗产?

如果你只用 OpenCode,但项目里已经有了 .cursorrulesCLAUDE.md,不必推倒重来。可以按以下步骤渐进迁移:

第一步:分类审计现有规则

.cursorrulesCLAUDE.md 里的内容按"通用性"分类:

类型 特征 示例
项目共识 与工具无关,描述业务、架构、约定 "所有 API 返回格式为 { code, data, message }"
工具行为约束 特定工具的交互模式 “Cursor 的 Tab 补全触发条件”
编码规范 语言/框架层面的风格 “TypeScript 项目使用 4 空格缩进”

第二步:迁移项目共识到 AGENTS.md

把"项目共识"类内容提取出来,写入 AGENTS.md。这是跨工具共享的主入口,格式建议:

1
2
3
4
5
6
7
8
9
10
11
12
13
# PROJECT KNOWLEDGE BASE

## OVERVIEW
项目的核心定位和技术栈简介。

## ARCHITECTURE
关键模块、依赖关系、数据流向。

## CONVENTIONS
编码风格、命名规范、目录结构约定。

## ANTI-PATTERNS
明确禁止的做法,避免踩坑。

第三步:保留工具专用规则

.cursorrulesCLAUDE.md 不要删除,但只保留该工具独有的行为约束。例如:

1
2
3
4
5
6
7
# .cursorrules(精简后)
- Tab 补全时优先展示项目内部模块
- 代码审查时自动运行 ESLint

# CLAUDE.md(精简后)
- 优先使用 MCP 工具进行文件操作
- 遇到不确定的 API 时先查官方文档

第四步:建立引用关系

在工具专用规则文件里,显式引用 AGENTS.md

1
2
3
4
5
# .cursorrules
> 本文件仅包含 Cursor 特有约束。项目通用规则见 AGENTS.md。

# CLAUDE.md
> 本文件仅包含 Claude Code 特有约束。项目通用规则见 AGENTS.md。

第五步:用 SKILL.md 承载领域技能

如果项目有特定领域的复杂规则(如 React 组件开发规范、API 设计指南),可以拆分为独立的 SKILL.md,放在 skills/ 目录下。OpenCode 的 SKILL 机制支持按需加载,避免 AGENTS.md 臃肿。

1
2
3
4
5
6
7
skills/
├── react-component/
│ └── SKILL.md # React 组件开发规范
├── api-design/
│ └── SKILL.md # RESTful API 设计指南
└── testing/
└── SKILL.md # 测试编写规范

迁移后的结构对比

迁移前 迁移后
.cursorrules 包含所有规则(重复) .cursorrules 只保留 Cursor 特有约束(薄包装)
CLAUDE.md 包含所有规则(重复) CLAUDE.md 只保留 Claude Code 特有约束(薄包装)
AGENTS.md AGENTS.md 作为单一真相源(项目共识)
无 SKILL 机制 skills/*/SKILL.md 按需加载(渐进披露)

关键原则

  • 不要删除原文件:保留 .cursorrulesCLAUDE.md,但只保留工具特有的内容
  • 先审计再迁移:理解每条规则的目的,避免无脑搬运
  • 渐进式迁移:可以先迁移高频使用的规则,低频规则后续再处理
  • 测试验证:迁移后让 Agent 执行几个典型任务,验证规则是否生效

这样,你既继承了 Cursor 和 Claude Code 的规则遗产,又避免了多工具协作时的配置漂移问题。

小结:Agentic Coding 真正改变的是什么?

回过头看,Agentic Coding 真正带来的变化,不只是"AI 能写更多代码",而是软件工程的组织方式开始被重新包装为可调用、可验证、可并行的流程

从裸 OpenCode 到 Ralph Loop,再到 OMO 和更偏企业化的 Agent 框架,演进路径其实很清晰:

  • 第一阶段解决"单个 Agent 能不能理解并修改代码";
  • 第二阶段解决"任务能不能在验证约束下持续推进";
  • 第三阶段解决"多个角色、多个上下文、多个知识源能不能像团队一样协作"。

这也是为什么我更愿意把 Agentic Coding 看成一次从工具到组织的迁移。代码生成当然重要,但更重要的是:规划、委派、验证、知识沉淀这些原本分散在人类团队中的动作,正在被重新抽象为一套可执行的协作系统。

结合前文对子 Agent 本质的分析,我们可以得出几个关键结论:

  • 子 Agent 是完整的 Agent,可以拥有与主 Agent 相同甚至更强的能力,其核心价值在于上下文隔离而非能力弱化
  • "子 Agent"是关系描述,不是能力描述——Prometheus、Oracle、Librarian 这样的 Agent 可以独立运行,也可以在特定任务中作为子 Agent 被协调
  • 主-子关系是动态的、任务级别的,不是静态的架构约束,好的 Agent 设计应该让每个 Agent 都能独立运行
  • 协调者不写代码是架构的自觉,通过严格的角色分离让系统获得更好的可预测性和可维护性
  • 上下文隔离是选择子 Agent 的核心理由,中等复杂度以上的任务,协调者绝不写代码,以避免上下文消耗的恶性循环

理解了这些,你就能更清晰地设计多 Agent 系统:让每个 Agent 都能独立运行,让"主-子"关系服务于任务,而不是被架构固化。

参考资料

核心概念

  • Context Rot 研究Long Context is Not All You Need: Diagnosing and Mitigating the Performance Degradation of Long-Context LLMs(Chroma,2025)——测试了 18 个前沿模型,发现所有模型都会随上下文增长而性能下降,提出了"35 分钟墙"和 Lost-in-the-Middle 效应的量化数据。
  • Everything is a Ralph Loopghuntley.com/loop/(Geoffrey Huntley,2026)——Ralph Loop 设计哲学的源头文章,提出"软件开发是陶轮上的黏土"和"编程循环而非砌砖"的核心论断。
  • 子 Agent 的本质:本文第三部分深入分析了子 Agent 的上下文隔离特性、关系描述本质以及与 Skill 的区别。

工具与框架

延伸阅读