dsh-plugin-opencode-route:为 OpenCode Go 会话自动挑选更快网关路由

前言

在 DeepSeek Harness(DSH)里用 OpenCode Go 做模型推理时,同一会话会通过 x-opencode-session 请求头固定到某条上游路径。不同 id 相当于不同「抽签」结果,快慢并不一致。遇到首轮流式输出偏慢,常见做法是手动开新会话再试——费时,且对话里会留下中断痕迹。

dsh-plugin-opencode-route 把这一过程自动化:仅在会话的第一次模型请求上监测解码速率,过慢则中止当前尝试、换新的路由 id 重试;用户只看到最终被接受的那一次流式输出,后续轮次不再换路,以免破坏已积累的 prompt cache。

这是什么

  • 定位:DSH 插件,面向 opencode-go 提供商的网关路由实验与优化。
  • 维护者:neil-ji(GitHub 仓库)。
  • 分类:模型推理;当前仓库星标为 0。
  • 许可证:MIT(package.json 标明版本 1.10.9,运行需 Node.js >= 20)。

插件不保管凭证,每次调用仍经 ctx.llm,由提供商自行解析密钥。

核心机制

只动路由 id

OpenCode Go 用 x-opencode-session 把对话钉在某条后端链路上。本插件只改这个头里的 id,不碰模型名、prompt 内容或其他供应商逻辑。

首请求:缓冲、判速、重试

对处于处理组(treatment)的会话,第一次请求流程如下:

  1. 先按常规模型调用放行。
  2. 缓冲首轮流式输出的前 windowMs(默认 2000 ms)token。
  3. 计算这段窗口内的运行速率;若低于 thresholdTps(默认 150 tok/s),丢弃本次尝试,生成新的 x-opencode-session id 重发。
  4. 最多重试 maxRetries(默认 3)次。被中止的窗口不会展示给用户;接受的流按原始节奏回放(约一个窗口滞后),以便 DSH 自身的 token 速率统计仍反映真实时间线,而不是 2 秒突发。

仅首请求会触发上述逻辑:一旦会话已经调用过模型,后续轮次一律直连,以保护 prompt cache。若首条 prompt 超过 maxPromptTokens(默认 60 000),也不再重试——每次重试都要完整 prefill,代价过高。

默认参数来自对 323 次真实首请求的调参(仓库内 research/tune_reroll.mjs):2000 ms / 150 tok/s / 3 次重试时,约 95% 会话达到 ≥150 tok/s,平均约 1.75 次尝试,误杀率约 7%。动态重调参已设计但未启用,见 research/TODO-tuning.md。

A/B 实验

会话按 sha256(salt:sessionId) 分桶:treatment 启用重试,control 完全不动。双方均记录带 arm 字段的 turn 事件,便于对比效果。分析脚本为 research/analyze_ab.mjs,可用 --since <timestamp> 限定 cohort。

安装与配置

安装

仓库随附 cordis.patch.yml 默认配置。安装命令如下(须使用资料原文,勿自行拼接 github:owner/repo 形式):

dsh plugin add dsh-plugin-opencode-route --profile <name>

将 <name> 换成你的 DSH profile 名。插件在当前 dsh 进程权限下运行;安装前建议阅读源码与 MIT 许可证,确认符合你的安全策略。

配置项

下列字段均在 README 中列出,均可选覆盖:

config:
  enabled: true
  dryRun: false              # true = 只观测,不重试
  providers: [opencode-go]   # 精确白名单;其他提供商不受影响
  models: []                 # 空列表表示该提供商下全部模型
  reroll: { enabled: true, windowMs: 2000, thresholdTps: 150, maxRetries: 3, maxPromptTokens: 60000 }
  experiment: { enabled: true, armRatio: 0.5, salt: route-v1 }
  • dryRun: true 时只记录行为,不实际换 id。
  • 关闭 reroll.enabled 或顶层 enabled 可去掉所有重试副作用;A/B 中的对照组本就不走重试。

观测与排错

界面与命令

  • 会话标题栏徽标(Header capsule):只读展示会话加权速率(总输出 token / 总解码时间,与 DSH Composer 页脚定义一致);数据不足时回退为最近三次可用请求的中位数。A/B 期间显示 实验组 或 对照组,悬停可见最近三次请求的时间、速度、首 token、cache 等信息。
  • /route:当前实验臂、参数、计数器与近期 turn。
  • /route stats:重试相关计数。
  • /route test:受控检查路由 id 是否影响速度(四个 id 交错重复,报告组间与组内方差)。

事件与 HTTP

  • 事件日志:%DSH_HOME%\opencode-route\events.jsonl,含 plugin、turn、reroll 等类型;约 8 MB 轮转至 events.1.jsonl。
  • 只读 HTTP:GET /opencode-route/status?session=<id>。

效果评估可配合 research/analyze_ab.mjs 读取事件日志;早期实验曾测过旧的 probe/pin 方案(无显著效果),当前 shipped 行为以 README 所述 reroll 为准。research/README.md 记录的是更早的基线研究(446 sessions / 44k calls),属历史背景,不代表现行逻辑。

适用场景与副作用

适合谁

  • 主要通过 DSH 使用 OpenCode Go,且首轮流式解码速度波动明显、愿意用首 token 延迟换稳定吞吐的开发者。
  • 需要做路由策略 A/B、并用事件日志与 /route 工具链量化对比的团队。

不适合指望「零成本加速」的场景:重试只针对处理组会话的第一次请求,且仅在判定过慢并实际中止时产生代价。

首请求重试的成本(README 明示)

仅 treatment 首请求、且发生中止时:

  • 首 token 更晚:每次尝试各有 TTFT,外加 2 s 观测窗;按调参后约 1.75 次尝试,首个可见 token 可能约 10 s(相对单次约 4 s);四次均慢时可达约 25 s。接受后的流仍按原节奏回放,后续正文不慢于该次尝试本身。
  • 额外输入 token:每次中止会在新 id 下冷 prefill;约 1.75 次尝试意味着约 0.75 次额外 prefill(例:15k 首 prompt 约多 11k input tokens)。maxPromptTokens 限制最坏情况。
  • 额外输出 token:中止前最多已生成约一个窗口的输出(约 200–300 token)。
  • 上游可能短暂继续生成:插件停止消费流,未必取消提供商侧任务。
  • DSH 原生 TTFB 含浪费的尝试;插件 turn 事件中的 TTFB 仅来自被接受的那次。
  • 接受尝试使用新 id:该次起 prompt cache 变冷;原 id 下已温热的 cache 在换路后需重建。

对照组与关闭 reroll 的会话无上述开销。

结尾

dsh-plugin-opencode-route 在不动后续对话缓存的前提下,把「这条会话太慢,换个 session id 再试」收进 DSH 插件层:首请求测速、静默重试、只展示接受的那条流,并配套 A/B 与观测面方便验证是否值得长期开启。

DSH 生态强调「一切皆插件」;SkillHub 等目录为社区站点,与 DeepSeek / 幻方无官方从属关系。是否安装请结合本机权限与提供商策略自行判断。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜