Skip to content

Goal 机制差距分析:cabbage vs OpenChamber ​

日期:2026-08-09 背景:承接 docs/dev/research/openchamber-goal-mechanism.md(OpenChamber Session Goals 深度调研),本次对照本项目(opencode-cabbage)的 goal 设计做逐维度差距分析。 基线:OpenChamber main 2026-08-08 / v1.16.x;cabbage 当前工作区(version 1.1.0)。

结论摘要 ​

两个系统的 goal 机制是同一范式(服务端事件驱动续跑循环 + session metadata 状态 + 独立终止权威)下的两种实现,但存在 4 个实质性差距和 2 个反超项:

级别差距影响建议
严重无子会话(child)活动检查idle 续接可能压过正在运行的 subagent(OpenChamber 为此修过专门 bug)续接前查 /session/:id/children,任一 busy 则跳过
严重无"卡住/blocked"检测与状态卡点空转续接直到 50 次上限,烧 token 无告警加无进展检测(连续 N 轮无工具调用/无 diff → pause),或 continuationPrompt 要求显式报告卡点
中等无 token 记账与预算长跑成本不可控可选:GoalData 增加 tokenBudget,用消息 tokens 快照记账
中等续接先发后写(计数)崩溃窗口:prompt 已发、计数未写 → 重启 autoResume 双发镜像 OpenChamber 先写后跑,或 autoResume 跳过"刚续接过"的会话
轻微无静默窗口与尾部 quiescence 检查假 idle(工具间隙)可能过早续接续接前检查尾部消息(最后是 user 或 assistant 未完成 → 跳过)
轻微无目标编辑操作中途改目标只能 cancel 重建增加 edit op(保留 id/计数,参考 OpenChamber 语义)
—反超:autoResume 启动扫描OpenChamber 无此机制(第三方 teardown 点名批评的持久性缺口)保持,注意双写一致性与双发风险
—反超:goal-verify 证据型验证终止权威带测试/文件证据,优于 OpenChamber 的纯文本审计保持,建议补"周期性自动派发"

一句话:cabbage 的"终止权威"(goal-verify 带证据验证)比 OpenChamber 的小模型审计更可靠,但循环的"过程保护"弱于 OpenChamber——缺子会话闸、缺 blocked 检测、缺预算,这三项是续接循环最容易翻车的地方。


1. 双方机制对照表 ​

维度OpenChamber Session Goalscabbage goal 机制差距
循环位置OpenChamber server(独立进程,SSE hub 订阅)OpenCode 插件内 event hook(agent 进程内)等价(都在 agent 之外)
状态存储metadata.openchamber.goal(JSON,含 id/记账/streak)metadata.goal = {parentIssueNumber, status, continuationCount, sessionProfile}cabbage 更轻;OpenChamber 更富
目标来源metadata 内联 ≤5000 字符 或 文件 <data-dir>/goals/<sid>.md,可中途编辑Flow Record(Parent Issue body),外部单一事实源,不可经 goal 编辑设计不同:cabbage 更强外部化,弱可编辑性
触发idle + 15s 静默窗口;kickoff 3s/250ms;abort 立即暂停idle 立即续接;abort 标记 → 续接时暂停cabbage 无静默窗口
尾部 quiescence 检查有(尾部 user 消息/未完成 assistant → bail)无(只有 abort 二次检查 + inFlight 去重)cabbage 缺失
子会话活动闸有(a56584b 专门修复:children busy → 跳过审计)无(只跳过有 parentID 的会话本身)cabbage 缺失,严重
终止权威独立小模型审计(continue/complete/blocked,3 连败 blocked)goal-verify 子 agent(读文件/跑测试/证据分类 SATISFIED)哲学不同:cabbage 证据更强但需派发,OpenChamber 全自动但凭文本
blocked 语义blocked(3 连败 / turn error / 审计失败 2 连)/ budgetLimited无 blocked;仅 pause(abort/50 次上限)cabbage 缺卡点检测
turn error 处理assistant turn error → blocked未处理(仅 MessageAbortedError → paused)cabbage 缺普通错误停
token 记账快照记账(input+cache.read+output − baseline)+ budget无cabbage 缺预算护栏
续接上限MAX_AUTO_TURNS=20(Resume 重置)MAX_CONTINUATIONS=50(resume/user 消息重置)数值差异,cabbage 更宽松
compaction识别 summary:true 分段记账,跳过审计续跑每 20 次续接主动发 [compact] prompt主动 vs 被动,各有取舍
重启恢复无启动扫描(事件驱动,teardown 批评的缺口)autoResume 扫描 session-index 并 promptAsynccabbage 反超
持久化单一事实源(metadata),无索引metadata + .opencode/opencode-cabbage/session-index.json 双写双写一致性风险 vs 单写恢复缺口
幂等/并发stale-write guard by goal id;先持久化后 prompt(先写后跑)KeyedMutex 串行;先 prompt 成功才计数(先发后写)顺序相反,各有崩溃窗口
续接参数最后非-summary assistant 消息的 provider/model/agent/variantsessionProfile 快照(用户最后选择)设计意图不同
目标/审计输入目标+最后回复,≤6000 字符,语言锁定,JSON 容错解析goal-verify 从头读 Flow Record + 文件证据cabbage 输入更全,成本更高
完成通知settle 通知(desktop/web-push/APNs),抑制 ready 通知无插件环境无 push 通道
UIstrip/靶心按钮/侧栏 glyph/评估模型展示/对话框工具文本返回(无 UI)平台差异,低优先
目标编辑支持(edit objective/budget,保留 id 与记账)无(仅 cancel 重建)cabbage 缺 edit
权限goal 由 UI/服务端写,audit 强制 restrictToPreferredProvidergoal 工具仅 dev-lifecycle/goal-verify;子 agent 禁 lifecycle;complete 仅 goal-verifycabbage 更严(agent 授权模型不同)
测试runtime.test.js(4)/create.test.jsgoal-tool.test.ts(254 行)/server-permissions.test.ts均有覆盖

2. 详细差距分析(按影响排序) ​

2.1 严重:无子会话活动检查 —— 续接会压过运行中的 subagent ​

证据:server.ts:301-315,idle 事件处理只做 parentID 检查(跳过子 agent 会话本身),然后直接 queueContinuation;queueContinuation(server.ts:69-136)全程不查 children。而 OpenChamber 的 DOCUMENTATION.md 明确:"A background subagent leaves its parent idle, then injects its result into the parent when done; that parent busy → idle cycle re-arms the loop"(后台 subagent 运行时父会话保持 idle!),并为此提交 a56584b "gate session goal audits on live child activity" 修复了"父 idle 时子会话仍在工作却去审计/续接"的问题。

影响:cabbage 在"主会话派了 @developer/@reviewer 子代理、等待结果"的空窗期(父会话 idle),会立即对父会话发续接 prompt —— 与子代理结果注入父会话产生竞争,轻则噪音/重复工作,重则续接把会话带向另一个方向。这是本项目中实际会发生的高频场景(Flow 编排大量依赖子代理)。

建议:在 queueContinuation 的 mutateGoal 回调内(或之前),调用 client.session.children({ sessionID }) 查询直接子会话状态,任一 busy/retry 则跳过本次续接(等待子代理结果注入父会话后产生的下一次 idle 事件重新触发)。成本:一次 API 调用 + 5 行代码。

2.2 严重:无"卡住"检测 —— 卡点空转续接直到 50 次上限 ​

证据:GoalData 状态只有 active/paused/complete(goal.ts:12);续接条件只有 3 个硬停止(abort、50 次上限、无 goal)(server.ts:80-97)。OpenChamber 有:3 连败 blocked、turn error → blocked、审计失败 2 连 → blocked、budgetLimited;且审计 prompt 强制 agent"无法推进时必须陈述阻塞条件"。

影响:agent 卡在"需要用户决策/凭据缺失/外部失败"时,cabbage 会继续注入续接 prompt,agent 每轮重复同样的话,直到 50 次上限(空转成本)或用户注意到。Codex /goal 社区已反复踩过同类坑(#22362"goal 一直续跑但等审批"、#23177"blocked 时死循环烧 token")。

建议(两档):

  • 低配:continuationPrompt 增加"若无法推进,必须在回复开头输出 [BLOCKED: <原因>];插件检测到该标记 → 将 goal 置为 paused 并提示用户"。成本:prompt 一行 + 事件/消息内容检测 ~10 行。
  • 高配:无进展检测 —— 每轮续接后对比最新 assistant 消息(无工具调用、无文件变更、文本高度重复)→ 连续 N 次(如 3 次)→ paused。OpenChamber 用独立审计达成同样目的,但 cabbage 场景下"无进展启发式"更便宜且够用。

2.3 中等:续接先发后写 —— 崩溃窗口会双发续接 ​

证据:server.ts:121-127,先 promptAsync 成功后才 continuationCount++(注释:"失败不消耗配额")。OpenChamber 相反(先持久化 accounting + turnsUsed,再 promptAsync,注释:"a crash after the write just waits for the next idle tick; the reverse could double-send")。cabbage 的 autoResume(server.ts:138-164)在插件重启时扫描 index,凡 status=active 的 flow 都会 promptAsync —— 若崩溃发生在 prompt 已发、计数未写之间,重启后 autoResume 会再发一次续接。

影响:重复续接概率低(窗口毫秒级),但一旦发生会直接污染会话(agent 收到两条相同续接)。可回滚性:低危害但难察觉。

建议:改为先递增计数再 promptAsync(失败回滚计数),与 OpenChamber 对齐;或 autoResume 对"最近 30s 内更新过 continuationCount"的 flow 跳过。成本:3 行。

2.4 中等:无 token 记账与预算 ​

证据:GoalData 无任何 token 字段;OpenChamber 有快照记账(input + cache.read + output − baseline,compaction 分段)+ tokenBudget → budgetLimited。

影响:长 goal(50 次续接 × 大模型)成本无护栏。OpenChamber 自己承认"预算只是护栏不是账单",cabbage 连护栏都没有。

建议:可选。若做,直接复用 OpenChamber 的快照法(利用 OpenCode 消息 tokens 的 cache.read 语义,不跨消息求和),在 message.updated(assistant 完成)事件上记账,GoalData 增加 tokenBudget/tokensUsed,达限 → paused + 提示。成本:1-2 天。

2.5 轻微:无静默窗口与尾部 quiescence 检查 ​

证据:server.ts:301 idle 立即续接。OpenChamber 15s 静默 + tick 内检查"尾部是 user 消息或 assistant 未完成 → bail"(runtime.js:529-530)。cabbage 有 abort 二次检查(server.ts:113-118)与 inFlight 去重,但没有检查消息尾部。

影响:idle 事件与"用户刚发了消息/agent 流式输出未完全落盘"竞态时,可能过早续接。真实概率低(OpenCode idle 语义较稳),但 OpenChamber 之所以加 15s 窗口正是踩过此坑。

建议:在 queueContinuation 前拉一次最近消息,若尾部是 user 或 assistant 无 time.completed → 跳过。成本:~10 行。

2.6 轻微:无目标编辑操作 ​

证据:goal 工具 op 只有 create/get/complete/resume/cancel/pause(goal.ts:152)。OpenChamber 支持编辑 objective/budget(保留 id 与记账,statusReason='resumed' 语义)。

影响:用户中途想调整目标/预算只能 cancel 重建,丢失 continuationCount 与绑定。低频场景。

建议:加 edit op(仅改 parentIssueNumber 与可选 budget,保留计数),若同时做了 2.4 才需要 budget 字段。成本:半小时。

2.7 轻微:无 turn error 处理(已由 [BLOCKED:] 报告机制覆盖) ​

证据:server.ts 事件处理只有 MessageAbortedError → abortedSessions;普通 assistant turn error(如 provider 500)不单独触发暂停。

影响:连续失败的会话会反复续接直到 50 次。修复口径(2026-08-09):卡点检测统一由 [BLOCKED:] 报告机制承担(agent 在续接回复中显式声明无法推进 → 插件置 paused),不单独监听 error 事件暂停——error 与"需要用户输入"是不同语义,前者可能自愈,后者必须停。若实测发现 error 反复空转,再补 error 事件 pause。


3. 本项目反超项(保持,勿回退) ​

3.1 autoResume 启动扫描 —— 解决 OpenChamber 最大短板 ​

OpenChamber 被 OpenAgents teardown(2026-07-12)点名批评:"纯事件驱动、无启动扫描/回填,定时器与 in-flight 全内存,重启后已 idle 的 active goal 永远不会重新武装,除非有新事件"。cabbage 通过 session-index.json(parentIssue → sessionID 磁盘索引)+ 启动扫描 autoResume 直接解决了这个问题。这是架构级领先,差距为负。

代价是双写一致性:metadata.goal 与 index 各自独立更新,存在不一致窗口(如 goal cancel 时 removeFlowSession 失败被 catch 吞掉,goal.ts:240-244 → index 残留 active → 重启 autoResume 恢复一个已取消的 goal)。建议:把 index 清理失败改为告警日志(当前 silent catch),或在 autoResume 时以 readGoal 实际状态为准(已有此逻辑:server.ts:144-147 会检查 goal 是否 active,不 active 则置 paused —— 已正确兜底)。

3.2 goal-verify 证据型验证 —— 终止权威质量更高 ​

OpenChamber 的审计器只看"目标 + 最后回复文本"(无历史、无类型化证据),误判风险靠 3 连败兜底;teardown 批评"model prose must not be sole acceptance authority"。cabbage 的 goal-verify 是只读子代理,读完整文件、跑测试/构建/lint、逐项 SATISFIED/NOT SATISFIED/UNCERTAIN 分类后才 complete —— 证据强度显著更高。

代价:完成判定不是每轮自动做的(需要 dev-lifecycle 派发或 agent 自觉),存在"目标实际已完成但无人验证 → 继续续接"的窗口。建议(中等价值):在续接循环里每 N 次(如 5 次)自动派一次 goal-verify 检查可完成性,与 2.2 的无进展检测配合,形成"过程防空转 + 终点防漏判"。

实现注(2026-08-09):验证 SDK 后确认插件无"创建子 agent 会话"的 API(fork+promptAsync 可建子会话,但结果不会自动注入父会话,需自建轮询/解析/判定循环,且与「仅 goal-verify 可 complete」授权模型冲突)。故 P2 采用对称轻量方案:[DONE:] 完成报告协议——continuationPrompt 要求 agent 在验证(测试/构建)通过时输出 [DONE: 摘要],插件检测到即置 paused,由 dev-lifecycle/用户派 goal-verify 做最终独立验证(complete 授权不变)。完成侧空转窗口由「agent 主动报告 + 暂停提示」覆盖,代价是依赖 agent 自觉(与 blocked 机制同一信任模型)。若实测发现 agent 频繁漏报,再评估重型自动验证循环。


4. 可执行改进清单(按优先级) ​

更新(2026-08-09):P0/P1 六项已实现并测试通过(TDD 9 cycles,211 tests 全绿),见 commit 86edf08。P3(edit op)与 P2 轻量替代([DONE:] 完成报告协议)已实现,见 commit 81fe89d + 审查修复 2620fda。

#改进对齐对象成本风险状态
P0queueContinuation 前查 children,任一 busy 跳过OpenChamber a56584b~5 行低(纯跳过)✅ 已实现(server.ts hasWorkingChildren)
P0continuationPrompt 加"无法推进必须输出 [BLOCKED: 原因]"指令 + 检测后 pauseOpenChamber blocked 语义~15 行低✅ 已实现(isBlockedReport + resume 豁免)
P1续接顺序改为先计数后 prompt(或 autoResume 去重)OpenChamber 先写后跑~3 行低✅ 已实现(两阶段 + 失败回滚)
P1尾部 quiescence 检查(最后消息 user/未完成 → 跳过)OpenChamber tick~10 行低✅ 已实现
P1turn error(非 abort)事件 → pauseOpenChamber blocked~5 行低✅ 已实现([BLOCKED:] 报告覆盖卡点检测)
P2完成侧闭环:[DONE:] 完成报告协议(agent 验证通过 → pause 等待 goal-verify)无对应(反超方向)~20 行低✅ 已实现(轻量替代"自动派发",见 §3.2 注)
P2tokenBudget/tokensUsed 快照记账(可选)OpenChamber 记账1-2 天中✅ 已实现(create baseline + tick 记账 + 预算 pause + reasoning 计入)
P3goal edit opOpenChamber半小时低✅ 已实现(改预算/传 0 移除/换 Flow 重置记账+rebind;complete 只读)
P4autoResume 重启双发防护(30s 窗口跳过)自研~3 行低✅ 已实现

不建议照搬:小模型每轮审计(成本高、凭文本、与 goal-verify 证据验证重复)、15s 静默定时器(OpenCode idle 语义足够稳,加尾部检查即可)、通知体系(插件无 push 通道)、stale-write by goal id(单 goal 场景 + KeyedMutex 已够)。


5. 不确定性(元评审) ​

  • children API 可用性未验证:OpenCode SDK v2 是否有 session.children 接口、返回结构如何,需在实现 P0 时用 SDK 类型确认(OpenChamber 用的是 HTTP /session/:id/children,SDK 大概率有对应方法)。
  • "后台 subagent 使父会话 idle"在 cabbage 的 OpenCode 版本上是否同样成立未实测——若 OpenCode 已把子代理期间父会话置为 busy,则 P0 无必要;建议实现前用一次真实子代理运行 + 事件日志验证。
  • 无进展检测阈值为经验值,需实测调参,避免误杀"慢但有效"的长任务。
  • 本分析基于源码静态阅读,未运行任何端到端场景验证差距的实际触发频率。

参考来源 ​

  • OpenChamber 机制调研:docs/dev/research/openchamber-goal-mechanism.md(2026-08-09)
  • 本项目源码:src/plugin/goal.ts、src/plugin/server.ts(queueContinuation/autoResume/事件处理)、src/kernel/session-index.ts
  • 本项目文档:docs/guides/architecture.md("Goal 状态机""事件驱动""弹性设计"节)、assets/agents/team/goal-verify.md
  • OpenChamber 反方证据:OpenAgents teardown(2026-07-12)、commit a56584b、DOCUMENTATION.md