opencode 自动循环方案:拒绝事件驱动,TICK 单一驱动器
2026/9/1 4:47:59 网站建设 项目流程

opencode 自动循环方案:拒绝事件驱动,TICK 单一驱动器

本文基于 opencode-goal 插件(开源,SDK 1.18)真实实现编写。主题:自动续推循环为何不由 idle/error 事件直接触发,而由独立 TICK 驱动。

1. 问题域

自动续推循环的初版直觉实现:监听session.status(idle)与session.error事件,事件到达即发送下一条续推或处理错误。该方案在真实环境暴露四类缺陷,最终被整体替换为单一 TICK 驱动器。

2. 事件驱动的四类缺陷

2.1 事件不可靠

事件流经 WebSocket 推送,不承诺送达。idle 事件丢失后,循环静默停滞——无日志、无异常,仅表现为"不再驱动"。该故障形态排查代价最大。

2.2 乱序与重复

session.errormessage.updated两条通道可能先后携带同一中止(实测间隔在秒级)。状态迁移不得假设"仅到达一次"或"按序到达"。事件驱动实现必须自行承担去重与版本比对。

2.3 重入并发

idle 到达时若上一次续推尚未完成,直接发送即产生并发双发。必须引入互斥;互斥引入后,事件驱动事实上已演进为"以事件为触发器的状态机",但状态机主体仍分散。

2.4 副作用确认缺失

"续推是否成功发送"在事件语义下无天然锚点。SDK 超时、半发送、队列积压使错误处理分散且不一致。

3. 替代设计:单一 TICK 驱动器

自 2026-08-29 起,循环仅保留一个驱动器——后台 TICK(默认间隔 1000ms,envGOAL_TICK_INTERVAL_MS):

  1. 请求权威会话状态快照(client.session.status);
  2. 遍历全部带目标会话,依次经过门链(drive_session):
    • 无目标或目标 paused/complete/unmet → 跳过
    • user_abort_at非空 → 跳过(用户中止,仅显式恢复)
    • 会话未激活(重启门:本实例中用户未发言)且为 active → 跳过
    • 快照 busy/retry → 记录停滞起始时刻,超STUCK_RESCUE_STALL_MS(默认 90s)且无事件且无进行中工具则自动 abort 一次,其余跳过
    • 子会话 busy → 跳过
    • 速率窗口未到 → 跳过
    • 上下文超限 → 触发自动压缩并返回
    • 其余 →run_auto_continue(原子保留 → 发送)

事件在此设计中只记录状态,绝不触发发送。事件处理仅执行:滑动窗口盖章、中止指纹记入、忙闲跟踪。

4. 正确性论证(对应 2 节缺陷)

缺陷处理
事件丢失循环不依赖事件;决策基于磁盘状态与最新快照,idle 丢失后 TICK 仍依据窗口自然驱动
乱序/重复状态以持久化为准;相同事件的重复到达对状态写入幂等
并发双发active_continuations集合持有整个发送周期;每次 TICK 快照一次并复用,杜绝并发双发
恰好一次单调锚last_continuation_at+auto_turns在同一变更内自增(reserve_continuation原子)

4.1 no-progress 自愈的门序位置

paused 且stop_reason === "no progress"的目标由 TICK 轮询(get_comatose_goal_ids):首次检出记录时刻,冷却期(GOAL_NO_PROGRESS_RESUME_COOLDOWN_MS,默认 60000)内保持暂停;期满自动恢复 active 并继续驱动。检测器暂停非用户行为,循环不得因此永久停止。

5. 代价与边界

  • TICK 间隔引入感知延迟:空闲后至多等待一个间隔再发送;
  • 事件记录必须正确实现(漏盖章将使窗口失效);
  • 快照失败时保守跳过本轮,不做猜测式发送;
  • 多插件实例各自运行 TICK:同会话的跨实例双发由用户操作门与 CAS 写收敛,不在本文范围。

6. 验证

  • goal-tick-loop-test.mjs:TICK 驱动断言;
  • goal-gates-test.mjs/goal-guards-test.mjs:门链各分支;
  • goal-fault-injection-test.mjs:SDK 挂起/抛错/重放/乱序注入后目标保持 active、循环继续;
  • goal-state-exhaustive-test.mjs:状态迁移穷举。

7. 结论

自动循环的可靠形式是周期性地基于权威状态做决策,而非响应事件。opencode 事件是状态输入的传感器,不是驱动来源;将二者混用是"循环自行停止"类难以定位故障的来源。该结论适用于任何需要"恰好一次、限速、有状态"的后台驱动场景。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询