dsh-cvm
Keep DSH agents from stalling, fake-finishing, or overspending: 4 cognitive guards (task contract / loop detection / evidence-before-done / single injection point) + 1 cost guard (budget nudge). No core changes - official seams only.
- Stars
- 1
- Language
- JavaScript
- Created
- Oct 2, 2026
- Updated
- Oct 3, 2026
Introduction
dsh-cvm —— 给 DSH agent 加一层"防卡住 / 防假完成 / 防超支"的监督
四个认知监督插件(管模型跑偏)+ 一个成本监督插件(管花钱失控),共五个。 全是正交的 Cordis 插件,不改 DSH 内核,只用官方接缝。 不提升模型能力——模型已经够强了;它管的是偶尔会出现的几种"跑偏"。
一、它解决什么问题(先看症状,对号入座)
A 组|认知监督(四个,管"模型跑偏")
强模型在长任务里偶尔会停在一个低于自己水平的状态。下面三种症状,中了任何一条,就有对应的插件:
| 你会看到的症状 | 它为什么坏 | 对应插件 | 插件做什么 |
|---|---|---|---|
| 用户第 1 轮说了硬约束,干到第 6 轮忘了——把"不许动"的文件也改了 | 全局目标被最近的局部信号挤掉 | dsh-contract | 把首条消息落成任务契约(含约束原话)钉进 system prompt |
| 反复读同一堆文件、就是不推进——一轮烧掉几十步,最后啥也没改 | 轨迹塌进"打转",没有信息增益 | dsh-convergence | 数连续只读;到阈值就提示"你在打转,换个方法" |
说人话:"打转"有两种——反复看同一个地方(深度),和一直在读、读的东西换了一堆但就是不推进(广度)。 后者更常见也更贵:模型看着很忙,其实一步没往前走。 | 说"已完成",但其实一次都没跑过——改了文件就宣布成功 | 模型被自己的结论锚住 |
dsh-evidence| 检测"声称完成 + 改了文件 + 没有一次成功验证" |
上面三个只负责"检测",真正"开口说话"的是第四个:
| 插件 | 职责 |
|---|---|
dsh-intervention | 唯一的注入点:读前三个的状态 → 注入提示,或在轮次收尾时 agent.steer 强制再走一步 |
为什么 A 组拆成四个:检测(写状态)和行动(读状态)分开 → 每个都能单独开关、单独调参,互不耦合。contract / convergence / evidence 互不 import。
B 组|成本监督(一个,管"花钱失控")
| 你会看到的症状 | 对应插件 | 插件做什么 |
|---|---|---|
| token 不知不觉烧掉一大截——没有人在看总消耗 | dsh-budget | 到阈值软提醒一次(不中断;不做硬熔断) |
B 组与 A 组完全正交:它不管认知,只管消耗。可以只装 A 组不装 B 组(
enabled: false或干脆不挂)。 它读的是官方tokenUsage投影,自己的状态放cvmBudget。
一句话说清它是什么
是:给强模型加的过程保险。风险不是"模型做错",而是"模型偶尔停住或自我说服"。 不是:不是提示词技巧合集,也不是"让模型更聪明"——模型越强,"补能力"的空间越小。
适用:长任务、要它真跑起来、无人看管时防打转、要控成本。 不适用:一句话问答;或你不想让任何规则打断模型。
装上以后你会看到什么
它不刷屏、不弹窗,也不改模型说的话。只在这三种时刻,往上下文里注入一条提示(文案都能在 Config 里改):
打转时
⚠️ 运行时检测:你已经连续多次只读操作却没有推进,可能陷入了原地打转。请换一种方法——例如直接执行/运行看真实输出、换一个假设、或从另一个入口文件切入。注意:目标是让任务真正完成,而不是继续收集信息。
声称完成、但其实没验证过
⚠️ 运行时检测:你已声称完成,但修改了文件后从未成功验证过。请先运行脚本/测试确认改动真的生效,再交付;否则应视为未完成。
快超预算时
[预算提醒] 本次会话已用约 85%(token 850,000/1,000,000)。请收窄范围、少做无效探索;接近完成就直接收尾给出结论。
其余时间完全静默——没有信号时注入的是空串(这也是"守前缀缓存"的要求:提示只在有信号时出现)。
二、装(一条命令)
node install.mjs # dry-run:只打印将要执行的命令
node install.mjs --apply --profile <你的profile> # 真装(5 个包一条命令 + 自动自检)
node install.mjs --check --profile <你的profile> # 只自检(没装齐时退出码非 0)
脚本会:探测 DSH_HOME 与现有 profile → 打印命令 → --apply 时执行 → 用 --dump-config 自检 5 个层是否真的加载。
dsh 不在 PATH 时用 --dsh "node /path/to/dsh/lib/bin.js"。默认只做 dry-run(有些 profile 是生产环境)。
⚠️ 不要用
add github:qlheric/dsh-cvm(不带#path:)——本仓库根是 private 的 workspace 包, 那样装到的只是根包,dsh会警告declares no dsh.bundle,一个插件都不会生效(我们实测踩过)。
手工挂载则是往 dsh.profile.bundles 里加这五项:
{ "dsh": { "profile": { "bundles": [
"@deepseek-ai/dsh-base",
"@qlheric/dsh-contract", "@qlheric/dsh-convergence", "@qlheric/dsh-evidence",
"@qlheric/dsh-intervention", "@qlheric/dsh-budget"
] } } }
三、配置(都能单独关)
所有阈值/关键词/提示文本都在各插件 Config(schemastery),可在 cordis.patch.yml 覆盖(patch 层跨升级存活):
- id: convergence
config:
threshold: 8 # 连续只读 8 次才算打转
readTools: [read, glob, grep]
- id: intervention
config:
steerAtTurnStop: false # 关掉"收尾强制再走一步"
- id: evidence
config:
enabled: false # 整个终局门禁关掉
- id: budget
config:
maxTokens: 1000000 # token 上限(默认 100 万)
maxSteps: 200 # 步数上限
softRatio: 0.8 # 到 80% 先软提醒
四、实测(诚实版:有正结果,也有撤回的假阳性)
一句话结论:结果指标(任务成败)几乎测不出差异——强模型本来就很少掉;过程指标能测出,而且取决于"场景有没有给它发挥空间"。
下文里的 C1 / C5 / C8 是我们自己造的几个评测场景编号(C 系),完整台账在
C1-AB数据汇总.md。 读的时候只要记住一句话:场景越"容易打转",插件越有用。
4.1 有效的地方:打转(C5 跨文件排查)
场景:app.py → lib.py → data.csv,bug 藏在 lib.py,必须跨文件追——天然容易广度打转。各 20 次:
统计量(maxReadStreak=连续只读最长串) | 基线 | 装插件后 | 变化 |
|---|---|---|---|
| mean | 19.95 | 9.75 | −51% |
| median | 18 | 9.5 | −47% |
| sd | 9.15 | 2.75 | −70% |
| max | 42 | 16 | −62% |
| 步数 / 只读 | 44.3 / 38.3 | 26.5 / 20.9 | −40% / −45% |
| 结果 | 20/20 | 20/20 | 不变 |
两组结果都满分,但被打转耗掉的步数差一倍。 这是目前最硬的证据。
4.2 反例:C1(明确可修的任务)基线满通过、过程只削长尾
| 统计量 | 基线 | 装插件后 |
|---|---|---|
| 结果(各 20 次) | 20/20 | 20/20 |
maxReadStreak mean | 6.3 | 5.55 |
maxReadStreak median | 5 | 5(不变) |
maxReadStreak sd | 2.87 | 1.60(−44%) |
maxReadStreak max | 15 | 11 |
中位数完全一样——只看均值/中位数会得出"没效果"的错误结论;要看 sd 与极值。
4.3 干预参数:有一个最优窗口,不是越敏感越好
改 convergence.threshold(各 20 次,maxReadStreak 越小越好):
| 阈值 | C1(基线 6.30) | C5(基线 19.95) |
|---|---|---|
| T=3(早干预) | 8.45 | 11.4 |
| T=5(默认) | 5.55 | 9.75 |
| T=8(晚干预) | 12.65 | 14.6 |
只有 T=5 优于"不装插件";T=3 和 T=8 都比不装还差(早干预打断正常探索,晚干预时模型已陷深打转)。
4.4 一个被撤回的假阳性(留着当标本)
我们曾用 20 次样本得出"新 contract 把 C8 场景退化率从 35% 打到 15%"——补跑到 40 次后塌了(基线 25% vs 新 contract 28%)。该结论已作废。 教训:差异 10pp 时 p≈0.34,要达 p<0.05 需约 250 次/组——当置信区间宽度大于效应本身时,不要下结论。
4.5 边界(别把上面的数字当承诺)
- 样本 20 次/组(部分对照 8–12 次),方向性证据;同一配置 8 次与 20 次能差 50%。
- 有一个场景(用户施压要求改口)在 deepseek 上基线就不退化,测不出改善空间 —— 不是插件没用,是基线没这个毛病。
- 实验室级证据(隔离
DSH_HOME+ headless),未到生产级。
五、已知代价(这类插件的风险是"误伤",不是"没效果")
dsh-evidence早期把tool/result.error当"工具失败",在调试场景里把正常的"看报错再改"误判成失败,退化率反而涨到 37.5%(比不装还差)。修正为"验证工具成功执行才算验证过"后消除。dsh-convergence的threshold过低(T=3)会因过早干预恶化过程(见 4.3)。dsh-budget的团队聚合是"下界":只把当前活跃子 agent 计入(已结束的无法归属,child上没有 parent 字段);实测子 agent 往往在父会话收尾前就结束了,所以父会话常看到childTokens = 0。修过的一个真 bug:turn-stopping在子 agent 自己的会话里也会触发,不排除自己就会重复计算(实测团队量翻倍)。
⇒ 所以每个插件都可配置、可单独关闭;调参要按场景标定,不能照搬默认值。
六、评测方法学(我们踩过的坑,写给要复现的人)
- 场景文件必须每轮复位,且复位源不能是 git HEAD——评测会改场景文件,而
git add -A会把模型产物一起提交,git checkout就再也复位不到"原始 bug 版"了(我们踩过两次)。现在用独立模板目录eval/scenarios/。 - A/B 数据要并存:
batch-eval.mjs --label A|B,否则互相覆盖。 - 别只看均值:强模型的过程指标均值可能不动,差异藏在 sd / 极值 / p90。
- 单测 mock 必须对齐真实事件结构:
user/message的 payload 就是 UserMessage,assistant/message才是嵌套{turn,step,message}。我们曾因 mock 假设错误,让 41 项单测全绿而线上功能恒为 null。
七、目录
packages/dsh-{contract,convergence,evidence,intervention,budget}/ # 五个插件
install.mjs # 一键安装器(dry-run / apply / check)
eval/ # 评测集 + 驱动 + 模板(scenarios/)
src/domain/ # 单测(70 项)
*.md # 知识底座、调研、数据汇总
License
MIT