R
深度调研报告 · 2026.06.23
调研对象:github.com/spoons-and-mirrors/subtask2
深度调研 · 2026-06-23

subtask2 深度调研:为 opencode 命令注入确定性的智能体编排循环

一个把 opencode 单发式 /command 扩展成链式、循环、并行、可传上下文的编排系统的插件——核心主张是"降低会话熵",把控制权交还给用户。

OpenCode Research ·2026-06-23 ·15 分钟阅读

1. 定位与背景:opencode 的 /command 与 subagents

subtask2 是一个 opencode 插件(opencode-plugin),通过 opencode 的插件机制加载。理解它必须先理解 opencode 的三层扩展模型。

opencode 是开源的终端 AI 编码代理(anomalyco/opencode,约 17 万 star)。它的可扩展性主要由三块构成:

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..."。这条消息带来了两个问题:

  1. 行为不透明:用户看不到这条驱动模型下一步动作的指令,却要承担它的后果。
  2. 会话熵增高:默认的"摘要"行为把控制权交给模型的自由发挥,容易跑偏、重复总结、偏离用户意图,长会话中尤其明显。

subtask2 的处理方式很激进——无论是否定义 return,都把这条合成消息从历史中移除:

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 里也能触发命令,且被触发的命令拥有自己的 parallelreturn(可嵌套):

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

并行命令的约束:

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)SequentialAgentreturn 数组按序触发
并行(fan-out/gather)ParallelAgentparallel + 等全部完成
循环(iterative refinement)LoopAgentloop + 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 合并了三项相关改进:

  1. 并行子任务执行 — slash 命令可经 Promise.all() 同时 spawn 多个 subagent;
  2. 模型继承修复 — 子任务完成后正确恢复父会话的 agent/model 上下文(模型解析链改为 Command Frontmatter → Agent → Session → Default);
  3. 新插件钩子command.execute.before,供插件在命令执行前介入。

涉及的文件与改动要点:

文件改动
session/message-v2.tsSubtaskPart 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 调用串行执行":

结论:subtask2 的 parallel 与上游这些 issue 是同一问题的不同解法。上游在修基础设施(session loop 的并发调度),subtask2 在命令层提供声明式 DSL。两者互补但耦合——PR #6478 未进主线前,subtask2 的 parallel 仍是 pending 状态。

· · ·

7. 同类编排方案对比

方案形态控制流与 subtask2 关系
subtask2opencode 插件命令层 DSL + orchestrator-decides
xxcoderClaude 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 ADKSDKSequentialAgent/ParallelAgent/LoopAgent 确定性工作流代理subtask2 三原语的理论原型
orchagent编排框架managed loop(LLM 工具调用)或 code runtimemanaged 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/sdkyaml。入口 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 暴露了三个值得关注的工程债:

  1. 会话状态内存泄漏(中高优先级):src/core/state.ts 用多个 Map 存会话状态(returnStatependingReturnspipedArgsQueuesubtaskResults 等),无 session 结束清理机制。长期运行的 opencode 实例会累积内存。建议加 session.end/session.close 事件清理或 TTL/LRU。1.0 前必修。
  2. auto mode 重构:/subtask --auto prompt 让 LLM 动态生成并执行工作流(解析 <subtask2 ...> 标签提取工作流),是实验特性。
  3. model aliases:{model:opus} 代替冗长的 {model:github-copilot/claude-opus-4.5},以及考虑改用 -- 行内语法。

CONTRIBUTING.md 要求版权转让:提交 PR 即把版权不可撤销地转让给维护者,以便其在 PolyForm Noncommercial 之外另签商业许可。这对贡献者是实质条款。

9. 许可与分发的混乱

这里存在一个需要警惕的不一致:

来源包名版本许可
GitHub package.json@spoons-and-mirrors/subtask20.3.5PolyForm-Noncommercial-1.0.0
npm @openspoon/subtask2@openspoon/subtask20.3.9(2026-01-31)MIT
README 安装示例@spoons-and-mirrors/subtask2@latest
每周下载(npm)@openspoon/subtask2196

两个不同的 npm 包名、两种许可、两个发布账号(spoons-and-mirrors vs openspoon)。PolyForm Noncommercial 1.0.0 的含义:仅授权"非商业目的"使用——个人研究/实验/测试、慈善/教育/公共研究/政府机构等可任意使用;但商业用途需另购商业许可。npm 上却标 MIT,二者直接矛盾。

影响:

10. 评估与落地建议

优势

风险

适用场景

多模型 A/B 计划对比、fix-until-pass 测试循环、上下文感知命令、设计→实现→测试→文档多步工作流——这些是 README 强示例,也是收益最高的场景。

落地建议

  1. 个人/研究/非商业:可直接采用 GitHub 版,先只用 return/loop/$TURN/{as} 等不依赖 PR #6478 的特性;
  2. parallel 待 PR #6478 进主线后再上;
  3. 商业团队:先与作者确认许可与包名,优先评估 @openspoon/subtask2(MIT)的官方性,或取得商业许可;
  4. 长会话部署前关注内存泄漏修复进展;
  5. until 条件写成可机器验证的语义(如 tests pass 而非 DONE),并设合理 max 兜底。
· · ·
🔍
关于本报告
由 OpenCode + article-magazine 自动生成 · 2026-06-23。调研采用 OpenCode 内置 exa 检索与网页抓取(gptr-mcp 因 Exa retriever 版本不兼容 use_autoprompt 参数暂不可用,已用等效工具替代)。数据截至 2026-06-23,涵盖 GitHub 仓库 README/PLAN.md/TODO.md/CONTRIBUTING.md/package.json、opencode 官方命令/代理/插件文档、npm 页面、上游 issue/PR 及编排模式公开资料。代码引用需人工审查后用于生产。