Bridge 跨模式迁移

September 10, 2026 · View on GitHub

0.3.5 更新:DSH 0.1.5-rc.1 的 V4.1-Flash 在同样的迁入 minimal 探针条件下与 V4-Pro 打平,默认档位已改为 current,见档位对比。下文仍是 2026-08 基于 V4-Flash 的原始结论。

日期:2026-08-17 | 宿主:本地 dsh web(默认 127.0.0.1:3080) | 模型:deepseek-v4-flash / deepseek-v4-pro 脚本:eval/run.mjs | 原始数据:reports/benchmark-2026-08-17.raw.json(含逐 run token 记账)

1. 实验设计

被测对象:TotoroPilot 的 Bridge 迁移全链路(buildBridgeSource 取材 → 工人模型压缩成交接摘要 → 目标 preset 新会话挂 goal + kickoff → 继续执行)。

因子与水平

因子水平备注
压缩档位 tierflash / pro工人会话显式 selectModel
目标 presetstandard / code / minimal / cordis迁移目的地
源 presetstandard / code / minimal / cordis仅 C 组变化

控制变量:同一真实工作区、同一埋点模板、每 run 恰好 5 个事实点(端口/数据库/禁令/路径/提交语言)、同一探针问题集、同一漂移任务、单轮超时统一(埋点 150s / 工人 360s / 目标 240s,超时即 session.cancel 记 caps)。

数据集分离

  • 测试集 T16:tier(2) × 目标(4) × 固定题材(2:电商后端/数据管道),源统一 minimal(排除源干扰)
  • 源对照 C4:同题材同档位(pro),目标统一 code,只变源 preset
  • 验证集 V6:3 个 held-out 题材(物联网采集/游戏存档/日志分析),方向与档位抽样,含 1 条真实 cordis 源

四层指标(每层按 5 事实点计命中率)

  1. 摘要层:工人摘要本身含多少事实(压缩保真上限)
  2. 复述层:kickoff 后新会话自述理解含多少事实
  3. 探针层:对新会话提问能答出多少事实(用户真实可用性
  4. 漂移层:让新会话"执行下一步"(写启动命令+首行代码),输出携带多少约定

26 条 run 全部成功(无 error,2 条触发过重试)。本轮实验总消耗 ≈ 13.6M 输入 / 0.42M 输出 tokens(作为对照,早期无限速试跑 6 条即烧 ~70M)。

2. 总体结果

测试集 T验证集 V
摘要保真97.5%96.7%
复述38.8%33.3%
探针(可用性)87.5%83.3%
漂移45.0%50.0%
摘要结构合规(5 段标题)100%100%

验证集与测试集差距 ≤4.2pp,结论可外推。

3. flash vs pro 压缩(测试集 T,核心对比)

档位摘要保真复述探针漂移工人成本(均值)单 run 耗时
flash95.0%22.5%80.0%40.0%1,626 in / 543 out348s
pro100%55.0%95.0%50.0%1,605 in / 707 out353s

结论:这是一个方差结论,不是均值结论。

两者输入相同(同一份取材),pro 只多输出 ~30% tokens(数百个),耗时持平。但那 15pp 的均值差距全部来自一次全灭——把 8 个 run 拆开看:

档位run 级探针命中率均值标准差
flash1.0, 1.0, 0.8, 0.8, 0.0, 0.8, 1.0, 1.00.800.32
pro0.8, 1.0, 1.0, 1.0, 1.0, 0.8, 1.0, 1.00.950.09

去掉那条 0/5,flash 是 0.91。事实级 Fisher 精确检验 p = 0.087(不显著),而且事实级计数本身高估了精度——一个 run 里的 5 个事实并不独立,全灭是 5 个一起丢的。

所以数据支持的说法不是「pro 更准」,而是:同样的价格下 flash 的离散度大一个数量级,且它的失败是整条全灭而不是少一两个事实。压缩环节本身极便宜(约 2K tokens/run),花同样的钱买掉这条尾巴是划算的——这才是「默认 pro」的理由。

4. 目标 preset 影响(测试集 T)

目标摘要保真复述探针漂移
cordis100%45.0%100%55.0%
code100%60.0%95.0%55.0%
standard95.0%45.0%90.0%40.0%
minimal95.0%5.0%65.0%30.0%

迁入 minimal 是最弱环节;且失败有组合特征:flash→minimal 三次 run 两次全灭(探针命中 4/15),pro→minimal 正常(9/10)。推测 flash 摘要信息密度低 + minimal 目标上下文引导弱,叠加后目标会话"接不住"。

5. 源 preset 对照(C 组,控制变量验证)

摘要复述探针漂移
standard / code / minimal / cordis5/55/55/52-3/5

四种源 preset 下保真度完全一致——迁移准确率与源模式无关(摘要取材自消息文本,与源工具集无关),源 preset 无需作为风险因子。执行漂移(2-3/5)也与源无关,见 §6。

6. 执行偏移分析

漂移任务 = "写出启动命令(含端口)+ 核心文件首行"。漂移层 45-50% 的口径偏严:答案天然只携带端口/路径两类事实,数据库/禁令/提交语言不出现是正常的。按此口径,端口与路径两个关键约定的实际漂移率约 20-35%,主要形态:

  • 路径对但端口"合理化"成 3000/8080 等常见值(摘要里有正确端口但模型补全习惯压过事实)
  • 首行代码语言/框架与约定文件后缀不符(.ts 写成 .py 风格)

复述层普遍低(39%)要区分解读:kickoff 只要求"一段复述",模型倾向概括目标而非罗列参数;探针层(87.5%)证明事实在上下文中可用。复述层低分还受 240s 限速截断影响(25/26 的 kickoff 轮触顶,长思考被 cancel 后按已产出文本计分)。

7. 失败模式清单(开源 FAQ 素材)

模式频次表现缓解
flash→minimal 全灭2/3 run探针 0/5,goal 形同未注入默认 pro 压缩;GUI 可对该组合告警
端口合理化漂移多次7101→3000 等摘要中数字类事实可加粗/单列
单轮限速截断25/26 kickoffcordis/standard 目标 kickoff 超 240sGUI 已在跑批外,不影响结果但见 §8
摘要丢 1 事实3/26(全 flash)禁令类事实最易丢pro 压缩

8. 附带的工程发现(超出准确率本身)

  1. cordis 会话单轮可跑飞:无约束提示下 cordis 埋点轮会进入工具循环,单轮 >100K 事件、>10 分钟;杀掉客户端进程不会终止 host 侧轮次(本次烧掉的 ~70M tokens 的主因)。建议 GUI 侧加"单轮看门狗"。
  2. host 无会话删除 RPC:只有归档;物理删除需停 host 后移出 ~/.dsh/sessions/<workspace>/ 目录。本轮 测试会话已归档并隔离,host 重启后彻底清除。
  3. 压缩成本极低(~2K tokens/run),token 消耗的大头永远是 agentic 会话本身,"用 flash 省钱"在 bridge 场景不成立。

9. 结论

Bridge 迁移在 pro 压缩 + 任意目标 下达到 95-100% 探针可用性,可以发布;默认档位应保持 pro——理由是尾部风险而非均值(见 §3)。执行偏移存在但集中在"数字合理化"一类;0.2 已在压缩指令里加了「端口、版本号、数量上限必须原样单独成行抄写,不得改写成常见值」的规则,但尚未重测

读这份报告前请先读 §10:这批数据有几处口径上的局限,其中两处会系统性地抬高所有数字。

可复现:node eval/run.mjs 3(需 host 在线;BRIDGE_ONLY 可筛子集)。

10. 方法学局限(以及 0.2 做了什么)

这批数据是在 0.1 上跑的。下面几条是它已知的局限,按「会不会影响结论」排序。0.2 修掉了工具层面的部分,但没有重测——所以上面的数字仍然是 0.1 的数字。

10.1 打分口径有非零下限(会抬高所有臂)

score() 是大小写不敏感的子串匹配,而 5 个事实里至少有 1-2 个是语义可猜的:lang 的期望值是字面量「中文」/「English」,而探针本身就是中文提问,回答里出现「中文」二字几乎必然;db = PostgreSQL 对一个电商项目、ban = Kafka 对一个数据管道,都是模型的默认猜测。

证据:裸重开进 minimal(无工具、无历史、真失忆)的两条 run 都恰好得 1/5,不是 0/5。

0.2 的做法:评测新增 guess 臂(BRIDGE_ARM=guess)——不埋点,直接问探针,测出这个下限。所有命中率都应减去它再读。新题材进 datasets/ 前必须报告猜测基线得分(见 CONTRIBUTING)。

10.2 A/B 对照臂被工作区污染(会抬高对照臂)

0.1 的所有 run 共用 workspace.list() 的第一个工作区,源会话的日志、甚至本仓库的 datasets/test.json(里面明文写着 7101 / PostgreSQL / src/shop/orders.ts)都在 agent 的可读范围内。这正是 bare→code 能拿到 9/10 的机制(eval/inspect-bare.mjs 复现了它)。

0.2 的做法:每个 run 建一个空的临时工作区(workspace.create / workspace.delete),跑完即销毁注册。

10.3 样本量只够给方向

  • flash vs pro:每臂 8 个 run,结论已按方差重述(§3)。
  • A/B:4 对。按对拆开后,迁进 code 时两臂基本打平(5-5 / 5-4),全部差异来自 minimal 目标——把两种目标平均成一个数会盖住机制。README 的 A/B 表已按目标 preset 拆开。

统计口径建议:run 内的 5 个事实不独立,不要把 26×5 当成 130 个独立观测;按 run 级算均值与方差,或给出置信区间(探针可用性 T 组 87.5%,95% CI 78.5–93.1)。

10.4 外部效度:测的是最好情况

每个 run 的埋点只有一条用户消息,agent 回一句确认,然后立刻迁移。于是:

  • 26 组 run 的 truncated 全部为 false——预算与截断逻辑一次都没被触发;
  • reusedCompaction 从未为 true——复用 compaction 底稿这条核心设计路径零覆盖;
  • 摘要长度均值 635 字符,离 2400 字符 / 900 tokens 的预算都远,约束从未生效

而插件的目标用户是「聊了很久、已经压缩过、上下文很杂」的会话。0.2 的做法:预算路径补了语义单元测试(超预算时最新的用户消息与最近助手结论必须还在),但长会话数据集仍是待办——真跑起来的分数大概率低于 87.5%。

10.5 复述层这个指标目前不可解释

26 条 run 里 25 条的 kickoff 轮触到 240s 限速被 cancel,按已产出文本计分。这个数字衡量的是「240 秒内说没说完」,不是「记不记得」。README 已不再展示它;本报告 §2 的总表保留是为了留痕。

一个后来才查清的原因:0.1 的 goal.create 没有传 maxGoalRounds,用的是上游部署默认的 256,而 dsh-goal-round-driver 会在 agent 空闲时把目标渲染成 <goal_round> 提示反复跑。目标会话因此进入自主循环——这也解释了 §8.1 的「cordis 会话单轮可跑飞」与目标会话累计均值 53 万 tokens。0.2 默认只给 1 轮。

10.6 可复现性

0.1 的 run id 由全局下标生成(T01V26),往 datasets/ 加一个题材会让所有 id 重编号,README 里的复现命令就会选中另一批用例。0.2 改为由配置派生T-pro-minimal→code-电商后端)。历史报告里的旧 id 与新 id 不对应,这是一次性断裂。

10.7 下一批实验应该跑什么

  1. guess 三臂重跑 A/B(隔离工作区 + 猜测基线),n ≥ 12 对;
  2. 一个 long 数据集:20-30 轮真实工具调用、至少触发一次 /compact 再迁移;
  3. 0.2 的注入改动(摘要进首轮提示)与数字防四舍五入规则的 A/B;
  4. 复述层把 cap 提到 600s 重测,或彻底删掉这一层。