subtask2 深度调研:为 opencode 命令注入确定性的智能体编排循环
一个把 opencode 单发式 /command 扩展成链式、循环、并行、可传上下文的编排系统的插件——核心主张是"降低会话熵",把控制权交还给用户。
1. 定位与背景:opencode 的 /command 与 subagents
subtask2 是一个 opencode 插件(opencode-plugin),通过 opencode 的插件机制加载。理解它必须先理解 opencode 的三层扩展模型。
opencode 是开源的终端 AI 编码代理(anomalyco/opencode,约 17 万 star)。它的可扩展性主要由三块构成:
- 命令(Commands):在
~/.config/opencode/commands/(全局)或.opencode/commands/(项目)下放 Markdown 文件,文件名即命令名。Markdown 的 YAML frontmatter 定义属性(agent、model、subtask、description),正文成为发给 LLM 的模板。可占位符如$ARGUMENTS、!`command`注入 shell 输出。 - 代理(Agents):分 primary 与 subagent 两类。内置三个 subagent:General(可改文件)、Explore(只读快速搜索)、Scout(依赖/外部文档研究)。primary 代理可经 Task 工具或
@提及自动/手动调用 subagent。 - 插件(Plugins):JS/TS 模块,导出返回 hooks 对象的函数,可挂钩
tool.execute.before/after、event、command.executed、message.part.updated、session.*等事件。可本地放置,也可从 npm 安装({"plugin": ["pkg"]})。
subtask2 正是这样一个插件,它挂钩命令与消息生命周期,在命令完成、子代理返回等关键节点改写默认行为。安装方式极简:
json{
"plugins": ["@spoons-and-mirrors/subtask2@latest"]
}
下表概括它相对 opencode 原生的增益:
| 维度 | opencode 原生 | subtask2 增益 |
|---|---|---|
| 命令完成后 | 注入隐藏的 "summarize the task tool output" 合成消息 | 完全移除,改用 return 显式驱动 |
| 多步流程 | 需手动多次触发命令 | return 数组自动链式触发 |
| 循环 | 无原生支持 | loop + until 条件循环 |
| 并行 | 上游 PR 进行中 | parallel 声明式并发(依赖 PR #6478) |
| 上下文传递 | 仅 $ARGUMENTS | $TURN[n] 会话历史 + {as:name}/$RESULT[name] 结果捕获 |
| 模型/代理选择 | frontmatter 固定 | 行内 {model:...}/{agent:...} 覆盖 |
2. 它要解决的问题:会话熵与"通用摘要"陷阱
subtask2 的核心动机可以用它自己的标语概括:"A more deterministic agentic loop"(更确定性的智能体循环)。
opencode 在一个 subtask: true 命令完成时,会向对话注入一条对用户隐藏、但对模型可见的合成用户消息,要求模型 "summarize the task tool output..."。这条消息带来了两个问题:
- 行为不透明:用户看不到这条驱动模型下一步动作的指令,却要承担它的后果。
- 会话熵增高:默认的"摘要"行为把控制权交给模型的自由发挥,容易跑偏、重复总结、偏离用户意图,长会话中尤其明显。
subtask2 的处理方式很激进——无论是否定义 return,都把这条合成消息从历史中移除:
- 定义了
return:prompt 类返回作为真实可见的用户消息发出(就像用户亲手输入);/command类返回立即执行该命令。 - 未定义
return:若配置replace_generic: true(默认),则发出内置兜底提示:
Review, challenge and validate the task output against the codebase then continue with the next logical step.
审查、质疑并对照代码库验证任务输出,然后继续下一步逻辑步骤。
优先级为:return 参数 > 配置 generic_return > 内置默认 > opencode 原始消息。
这一改动看似微小,实则是整个插件的哲学基石:把"模型下一步该做什么"从隐式、不可控,变成显式、可链式、可观测。
3. 核心能力拆解
subtask2 围绕六个主特性构建,外加一个行内语法体系与一个 /subtask 命令。
3.1 return — 链式编排
return 告诉主代理命令完成后做什么,支持 prompt、/command 与数组链式。
yamlsubtask: true
return: Look again, challenge the findings, then implement the valid fixes.
---
Review the PR# $ARGUMENTS for bugs.
数组形式按序触发,每条在上一轮 LLM turn 完成后发出:
yamlsubtask: true
return:
- Implement the fix
- Run the tests
---
Find the bug in auth.ts
return 里也能触发命令,且被触发的命令拥有自己的 parallel 与 return(可嵌套):
yamlreturn:
- /revise-plan make the UX as horribly impractical as imaginable
- /implement-plan
- Send this to my mother in law
注意:第一条 return 不能是 /command,因为它要替换 opencode 注入的字符串消息。
3.2 loop — 条件循环
固定次数或条件满足为止,条件用自然语言表达,由主会话 LLM 评估。
yamlloop:
max: 10
until: "all features implemented correctly"
---
Implement the auth system.
行内等价:/fix-tests {loop:10 && until:all tests pass with good coverage}。
orchestrator-decides 评估流程:子任务完成 → 主会话收到含条件的评估 prompt → 主 LLM 读文件/查 git/跑测试做真实验证 → 回复 <subtask2 loop="break"/>(满足)或 <subtask2 loop="continue"/>(继续)→ 达 max 自动停。作者强调:不用假的 "DONE" 标记,而是对真实条件的真实评估。
3.3 parallel — 并发子任务(依赖上游 PR)
声明式并发,主命令与若干并行命令同时跑,全部完成后主会话收到 return。
yamlsubtask: true
parallel:
- /plan-gemini
- /plan-opus
return:
- Compare and challenge the plans, keep the best bits and make a unified proposal
- Critically review the plan directly against what reddit has to say about it
---
Plan a trip to $ARGUMENTS.
参数传递有三层优先级:管道参数 || > frontmatter arguments > 继承主 $ARGUMENTS。
bash/mycommand main args || pipe1 || pipe2 || pipe3
并行命令的约束:
- 强制转为 subtask,无视自身
subtask设置; - 自身
return被忽略,只有父命令的return生效; - 嵌套并行自动拍平,最大深度 5,防止无限递归。
3.4 $TURN[n] — 会话历史注入
把最近 N 轮(或指定索引)对话注入命令模板,让命令"看见"上下文。
| 语法 | 含义 |
|---|---|
$TURN[6] | 最近 6 条消息 |
$TURN[:3] | 倒数第 3 条 |
$TURN[:2:5:8] | 索引 2/5/8 的消息 |
$TURN[*] | 全部会话历史 |
输出格式为 --- USER --- / --- ASSISTANT --- 分段。可用于命令体、参数、并行 prompt、管道参数。
3.5 {as:name} + $RESULT[name] — 结果捕获与引用
捕获任意命令(subtask/并行/行内/普通)的最终输出,在后续 return 链中按名引用。
yamlsubtask: true
parallel:
- /plan {model:anthropic/claude-sonnet-4 && as:claude-plan}
- /plan {model:openai/gpt-4o && as:gpt-plan}
return:
- /deep-analysis {as:analysis}
- "Compare $RESULT[claude-plan] vs $RESULT[gpt-plan] using insights from $RESULT[analysis]"
未找到的名字替换为 [Result 'name' not found],跨会话作用域(父会话可拿到子会话捕获的结果)。
3.6 行内语法 — 覆盖与临时子任务
无需改命令文件即可覆盖参数,&& 分隔多参数:
text/plan {model:anthropic/claude-sonnet-4 && agent:build} implement the feature
/subtask {...} prompt(注意 /subtask 与 { 间必须有空格)可在 return 链或聊天中直接生成临时子任务:
text/subtask {loop:10 && until:tests pass} Fix failing tests and run the suite
行内 return 用 || 分隔:/subtask {return:validate the output || run tests || deploy} implement the feature。
3.7 /subtask 命令
subtask2 经插件 config hook 注册 /subtask 命令,无需手写命令文件即可发起临时子任务。无覆盖时 /subtask tell me a joke 即起一个简单子任务。
3.8 通用消息处理(配置)
~/.config/opencode/subtask2.jsonc 控制:
jsonc{
"replace_generic": true, // 无 return 时是否替换 opencode 默认消息
"generic_return": "custom prompt" // 可选自定义兜底
}
4. 控制流哲学:orchestrator-decides 模式
subtask2 的设计可归入智能体编排模式谱系中的 orchestrator-decides / hierarchical(coordinator-worker) 变体,但有一个关键取舍:控制流是确定性代码(harness),判断权交给主会话 LLM。
这与业界主流观点高度一致:控制流应在代码里保证,模型只负责箱内的判断。Azure 架构中心与 Addy Osmani 都指出——生产系统多为"确定性骨架 + 步骤内 LLM 灵活性"的混合体;Google ADK 的 SequentialAgent/ParallelAgent/LoopAgent 正是把顺序/并行/循环做成确定性工作流代理。
subtask2 把这三种原语对应到 opencode 命令层:
| 编排原语 | ADK 等价 | subtask2 表达 |
|---|---|---|
| 顺序(sequential) | SequentialAgent | return 数组按序触发 |
| 并行(fan-out/gather) | ParallelAgent | parallel + 等全部完成 |
| 循环(iterative refinement) | LoopAgent | loop + until 条件 |
| 路由/委派 | LlmAgent 动态委派 | 主会话 LLM 决定 break/continue |
但 subtask2 与"纯代码确定性"方案(如 athena-loops 的 orchestrator→worker→reviewer 闭环 harness)不同:它的循环评估不是代码 gate,而是把条件原样交给主会话 LLM 读真实文件/git/测试来判断。这是"用确定性骨架编排、用 LLM 兜底验证"的折中——既不像纯 prompt 方案那样无法保证循环,也不像纯代码 gate 那样僵化。
值得对照的是 Azure 文档对并发模式的提醒:并行需冲突解决策略。subtask2 用 $RESULT 收集 + return 里的比较 prompt 来做 fan-in 合成,属"LLM 综合摘要"式聚合。
5. 关键依赖:上游 PR #6478
parallel 特性标注 "pending PR",依赖 sst/opencode#6478。从 fork Latitudes-Dev/shuvcode#225 的合并说明看,该 PR 合并了三项相关改进:
- 并行子任务执行 — slash 命令可经
Promise.all()同时 spawn 多个 subagent; - 模型继承修复 — 子任务完成后正确恢复父会话的 agent/model 上下文(模型解析链改为 Command Frontmatter → Agent → Session → Default);
- 新插件钩子 —
command.execute.before,供插件在命令执行前介入。
涉及的文件与改动要点:
| 文件 | 改动 |
|---|---|
session/message-v2.ts | SubtaskPart schema 新增 model/parentAgent/parentModel |
session/prompt.ts | 重构 subtask 处理以支持并行执行、修复模型继承 |
tool/task.ts | 模型解析改用 ctx.extra.model |
plugin/src/index.ts | 新增 command.execute.before hook 类型 |
作者正是 @spoons-and-mirrors 本人——subtask2 与上游 PR 同源,plugin 是 PR 能力的"面向用户封装"。这意味着 subtask2 的 parallel 实质是上游能力的前端 DSL。
6. 与上游并行 subtask 现状的对照
并行子任务在 opencode 上游是个长期痛点,社区多次反馈"多个 Task 调用串行执行":
- Issue #14195:多条 Task 工具调用在单次响应里被
tasks.pop()逐个await,而非并发;维护者澄清该路径只处理 compaction/subtask 调用,且默认希望按序。一个 review-team 编排者报告其多模型审查 subagent 无法并行。 - Issue #29638:同样症状,定位到
prompt.ts约 1734 行tasks.pop()+handleSubtask阻塞;提议用Effect.forEach(..., {concurrency: "unbounded"});另有background参数被experimentalBackgroundSubagentsflag 隐藏。衍生 PR #29819、#29848。 - Issue #19999:提出临时子代理团队(ephemeral team)做多代理并行编排 + fan-out/fan-in,甚至规划 mailbox 通信原语(
team_send/team_recv)。
结论:subtask2 的 parallel 与上游这些 issue 是同一问题的不同解法。上游在修基础设施(session loop 的并发调度),subtask2 在命令层提供声明式 DSL。两者互补但耦合——PR #6478 未进主线前,subtask2 的 parallel 仍是 pending 状态。
7. 同类编排方案对比
| 方案 | 形态 | 控制流 | 与 subtask2 关系 |
|---|---|---|---|
| subtask2 | opencode 插件 | 命令层 DSL + orchestrator-decides | — |
| xxcoder | Claude Code 技能/代理 | Sisyphus 路由脑 + thin proxy subagent + codeagent-wrapper 多后端 | 多模型路由思路相近,但走 wrapper 调外部 CLI,非命令链 |
| athena-loops (agentloop) | 独立 Python + MCP + CLI | 确定性 harness:orchestrator→worker→reviewer 闭环 | 哲学最接近,但 control-flow 在纯代码 gate,subtask2 用 LLM 评估 |
| Google ADK | SDK | SequentialAgent/ParallelAgent/LoopAgent 确定性工作流代理 | subtask2 三原语的理论原型 |
| orchagent | 编排框架 | managed loop(LLM 工具调用)或 code runtime | managed loop 与 subtask2 的 orchestrator-decides 同源 |
subtask2 的差异化在于深度绑定 opencode 命令语义,而非提供独立编排运行时——它是 opencode 原生扩展性的"语法糖+行为改写",不引入新的代理运行时。
8. 工程质量与待办
package.json 显示:@spoons-and-mirrors/subtask2 v0.3.5,TypeScript 100%,peer 依赖 @opencode-ai/plugin ^1.0.216,运行依赖 @opencode-ai/sdk 与 yaml。入口 index.ts 仅一行转发到 src/core/plugin。
PLAN.md 给出了一份相当完整的测试矩阵,覆盖 10 大类共约 60+ 用例(return 链/行内覆盖/临时 subtask/loop/parallel/named result/$TURN/管道/auto mode/配置),外加 5 个集成场景(多模型 A/B、fix-until-pass、复杂行内 subtask、上下文感知命令、全编排)。这份计划质量是加分项。
TODO.md 暴露了三个值得关注的工程债:
- 会话状态内存泄漏(中高优先级):
src/core/state.ts用多个 Map 存会话状态(returnState、pendingReturns、pipedArgsQueue、subtaskResults等),无 session 结束清理机制。长期运行的 opencode 实例会累积内存。建议加session.end/session.close事件清理或 TTL/LRU。1.0 前必修。 - auto mode 重构:
/subtask --auto prompt让 LLM 动态生成并执行工作流(解析<subtask2 ...>标签提取工作流),是实验特性。 - model aliases:
{model:opus}代替冗长的{model:github-copilot/claude-opus-4.5},以及考虑改用--行内语法。
CONTRIBUTING.md 要求版权转让:提交 PR 即把版权不可撤销地转让给维护者,以便其在 PolyForm Noncommercial 之外另签商业许可。这对贡献者是实质条款。
9. 许可与分发的混乱
这里存在一个需要警惕的不一致:
| 来源 | 包名 | 版本 | 许可 |
|---|---|---|---|
GitHub package.json | @spoons-and-mirrors/subtask2 | 0.3.5 | PolyForm-Noncommercial-1.0.0 |
npm @openspoon/subtask2 | @openspoon/subtask2 | 0.3.9(2026-01-31) | MIT |
| README 安装示例 | @spoons-and-mirrors/subtask2@latest | — | — |
| 每周下载(npm) | @openspoon/subtask2 | 196 | — |
两个不同的 npm 包名、两种许可、两个发布账号(spoons-and-mirrors vs openspoon)。PolyForm Noncommercial 1.0.0 的含义:仅授权"非商业目的"使用——个人研究/实验/测试、慈善/教育/公共研究/政府机构等可任意使用;但商业用途需另购商业许可。npm 上却标 MIT,二者直接矛盾。
影响:
- 企业/商业团队按 GitHub 仓库采用将受 PolyForm Noncommercial 约束,商业使用需联系作者授权;
- 按 npm
@openspoon/subtask2(MIT)采用则宽松,但该包是否为官方授权发布存疑; - 选型时务必先与作者确认包名与许可一致性,避免合规风险。
10. 评估与落地建议
优势
- 概念清晰:把"模型下一步动作"从隐式变显式,降低会话熵、提升可观测性;
- 原语完备:顺序/并行/循环/上下文/结果捕获五件套齐全,对应成熟编排模式;
- 与 opencode 深度贴合,不引入新运行时,学习成本低(会 /command 即上手);
- 测试计划与文档质量高。
风险
parallel强依赖未进主线的 PR #6478,稳定性与时点不确定;- 会话状态 Map 无清理,长会话内存泄漏,1.0 前不宜重度依赖;
- 许可与包名不一致带来合规隐患;
- PolyForm Noncommercial 限制商业场景,团队需评估或购买商业许可;
loop的until由 LLM 评估,条件写得模糊会失效(作者已警示别用until: "DONE"这类魔法关键字)。
适用场景
多模型 A/B 计划对比、fix-until-pass 测试循环、上下文感知命令、设计→实现→测试→文档多步工作流——这些是 README 强示例,也是收益最高的场景。
落地建议
- 个人/研究/非商业:可直接采用 GitHub 版,先只用
return/loop/$TURN/{as}等不依赖 PR #6478 的特性; parallel待 PR #6478 进主线后再上;- 商业团队:先与作者确认许可与包名,优先评估
@openspoon/subtask2(MIT)的官方性,或取得商业许可; - 长会话部署前关注内存泄漏修复进展;
until条件写成可机器验证的语义(如tests pass而非DONE),并设合理max兜底。
use_autoprompt 参数暂不可用,已用等效工具替代)。数据截至 2026-06-23,涵盖 GitHub 仓库 README/PLAN.md/TODO.md/CONTRIBUTING.md/package.json、opencode 官方命令/代理/插件文档、npm 页面、上游 issue/PR 及编排模式公开资料。代码引用需人工审查后用于生产。