dsh-cvm —— 给 DSH agent 加一层「防卡住 / 防假完成 / 防超支」的运行时监督
数据截至 2026/10/9 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
dsh plugin --profile web add qlheric/dsh-cvm状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 04:18:49
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
查看扫描证据(共 50 处,展示前 48 条)
- 写入本地文件eval/batch-eval.mjs:21
mkdirSync(OUTDIR, { recursive: true }); - 读取本地文件eval/batch-eval.mjs:24
if (reset && existsSync(outFile)) rmSync(outFile); - 删除本地文件eval/batch-eval.mjs:24
if (reset && existsSync(outFile)) rmSync(outFile); - 写入本地文件eval/batch-eval.mjs:30
mkdirSync(fix, { recursive: true }); - 读取本地文件eval/batch-eval.mjs:31
for (const name of readdirSync(fix)) { - 删除本地文件递归eval/batch-eval.mjs:33
rmSync(join(fix, name), { recursive: true, force: true }); - 读取本地文件eval/batch-eval.mjs:35
for (const name of readdirSync(scen)) { - 写入本地文件eval/batch-eval.mjs:36
copyFileSync(join(scen, name), join(fix, name)); - 读取本地文件eval/batch-eval.mjs:40
const done = existsSync(outFile) - 读取本地文件eval/batch-eval.mjs:41
? readFileSync(outFile, 'utf8').split('\n').filter((l) => l.trim() !== '').length - 执行外部命令来自配置/变量eval/batch-eval.mjs:50
const r = spawnSync(NODE, [join(__dirname, 'drive.mjs'), '--case', caseId], { - 写入本地文件eval/batch-eval.mjs:64
appendFileSync(outFile, `${JSON.stringify({ i: i + 1, pass, detail, metrics })}\n`); - 读取本地文件eval/batch-eval.mjs:68
const lines = readFileSync(outFile, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l)); - 读取本地文件eval/cost-run.mjs:14
const CASES = JSON.parse(readFileSync(join(ROOT, 'eval', 'cases.json'), 'utf8')).cases; - 写入本地文件eval/cost-run.mjs:28
mkdirSync(OUTDIR, { recursive: true }); - 写入本地文件eval/cost-run.mjs:38
mkdirSync(FIXTURES, { recursive: true }); - 读取本地文件eval/cost-run.mjs:39
for (const name of readdirSync(FIXTURES)) { - 删除本地文件递归eval/cost-run.mjs:41
rmSync(join(FIXTURES, name), { recursive: true, force: true }); - 读取本地文件eval/cost-run.mjs:43
for (const name of readdirSync(SCEN)) copyFileSync(join(SCEN, name), join(FIXTURES, name)); - 写入本地文件eval/cost-run.mjs:43
for (const name of readdirSync(SCEN)) copyFileSync(join(SCEN, name), join(FIXTURES, name)); - 执行外部命令来自配置/变量eval/cost-run.mjs:60
const r = spawnSync(NODE, args, { - 读取本地文件eval/cost-run.mjs:81
const done = existsSync(outFile) - 读取本地文件eval/cost-run.mjs:82
? readFileSync(outFile, 'utf8').split('\n').filter((l) => l.trim()).length - 写入本地文件eval/cost-run.mjs:88
appendFileSync(outFile, `${JSON.stringify({ i: i + 1, ...rec })}\n`); - 读取本地文件eval/cost-run.mjs:90
const lines = readFileSync(outFile, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l)); - 读取本地文件eval/cost-summary.mjs:17
if (!existsSync(f)) return null; - 读取本地文件eval/cost-summary.mjs:18
return readFileSync(f, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l)); - 读取本地文件eval/drive.mjs:13
const CASES = JSON.parse(readFileSync(join(ROOT, 'eval', 'cases.json'), 'utf8')).cases; - 执行外部命令固定命令eval/drive.mjs:134
const r = spawnSync('python', [script, ...(j.args ?? [])], { - 读取本地文件eval/drive.mjs:147
const same = existsSync(cur) && existsSync(tpl) && readFileSync(cur, 'utf8') === readFileSync(tpl, 'utf8'); - 读取本地文件eval/drive.mjs:147
const same = existsSync(cur) && existsSync(tpl) && readFileSync(cur, 'utf8') === readFileSync(tpl, 'utf8'); - 执行外部命令来自配置/变量eval/drive.mjs:170
const r = spawnSync(NODE, args, { - 读取本地文件eval/parallel-eval.mjs:11
const CASES = JSON.parse(readFileSync(join(ROOT, 'eval', 'cases.json'), 'utf8')).cases; - 执行外部命令来自配置/变量eval/parallel-eval.mjs:22
const child = spawn(NODE, args, { cwd: FIXTURES, env: { ...process.env, DSH_HOME } }); - 读取本地文件install.mjs:51
if (!existsSync(dir)) return []; - 读取本地文件install.mjs:53
return readdirSync(dir, { withFileTypes: true }) - 执行外部命令来自配置/变量install.mjs:60
return spawnSync(cmd, args, { - 读取本地文件src/domain/ledger-validator.mjs:208
stat = statSync(filePath); - 读取本地文件src/domain/ledger-validator.mjs:219
raw = readFileSync(filePath, 'utf8'); - 写入本地文件src/domain/test-ledger-validator.mjs:155
writeFileSync(p, JSON.stringify(ledger(), null, 2), 'utf8'); - 删除本地文件递归src/domain/test-ledger-validator.mjs:159
} finally { rmSync(dir, { recursive: true, force: true }); } - 写入本地文件src/domain/test-ledger-validator.mjs:165
writeFileSync(p, '{ not json', 'utf8'); - 删除本地文件递归src/domain/test-ledger-validator.mjs:169
} finally { rmSync(dir, { recursive: true, force: true }); } - 执行外部命令来自配置/变量src/domain/test-ledger-validator.mjs:183
const runCli = (args) => spawnSync(process.execPath, [CLI_PATH, ...args], { encoding: 'utf8' }); - 写入本地文件src/domain/test-ledger-validator.mjs:194
writeFileSync(p, JSON.stringify(ledger(), null, 2), 'utf8'); - 删除本地文件递归src/domain/test-ledger-validator.mjs:198
} finally { rmSync(dir, { recursive: true, force: true }); } - 写入本地文件src/domain/test-ledger-validator.mjs:204
writeFileSync(p, JSON.stringify(ledger([unit({ reviewed_paths: [] })]), null, 2), 'utf8'); - 删除本地文件递归src/domain/test-ledger-validator.mjs:208
} finally { rmSync(dir, { recursive: true, force: true }); }
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
@deepseek-ai/dsh-llm@deepseek-ai/schemastery给这个插件打分还没有人投票↓
功能亮点
- 防卡住与假完成
- 四道认知护栏
- 预算超支提醒
防止 DSH 智能体卡壳、假装完工或超支:4 道认知护栏(任务契约/循环检测/完工前举证/单一注入点)+ 1 道成本护栏(预算提醒)。不改核心,只用官方扩展点。
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
dsh-cvm —— 给 DSH agent 加一层「防卡住 / 防假完成 / 防超支」的运行时监督
五个正交的 Cordis 插件(4 认知监督 + 1 成本监督),不改 DSH 内核,只用官方接缝。
不提升模型能力——模型已经够强了;它管的是偶尔会出现的几种「跑偏」。
实测结论:几乎零成本地把「打转」削掉一半(−48%),单任务成本仅 +2%。
一、它解决什么问题(先看症状,对号入座)
强模型在长任务里偶尔会停在一个低于自己水平的状态。三种症状,中了哪条装哪个插件:
| 你会看到的症状 | 它为什么坏 | 插件 | 做什么 |
| 第 1 轮说了硬约束,第 6 轮忘了——把「不许动」的文件也改了 | 全局目标被最近的局部信号挤掉 | dsh-contract | 把首条消息落成任务契约(含约束原话)钉进 system prompt |
| 反复读文件、就是不推进——烧掉几十步,最后啥也没改 | 轨迹塌进「打转」,没有信息增益 | dsh-convergence | 数连续只读,到阈值提示「停止读,直接改/验证」 |
| 说「已完成」,其实一次没跑过——改了文件就宣布成功 | 模型被自己的结论锚住 | dsh-evidence | 检测「声称完成 + 改了文件 + 没有一次成功验证」 |
上面三个只检测,真正「开口」的是第四个:
| 插件 | 职责 |
| dsh-intervention | 唯一注入点:读前三个状态 → 注入提示,或在轮次收尾 agent.steer 强制再走一步 |
检测(写状态)与行动(读状态)分开 → 每个都能单独开关、单独调参,互不耦合。
B 组|成本监督(一个):dsh-budget —— token 不知不觉烧掉一大截时,到阈值软提醒一次(不中断、不做硬熔断)。与 A 组完全正交,可只装 A 不装 B。
一句话定位:给强模型加的过程保险。风险不是「模型做错」,而是「模型偶尔停住或自我说服」。适用于长任务、要它真跑起来、无人看管防打转、控成本;不适用于一句话问答。
二、装(一条命令)
node install.mjs # dry-run:只打印命令
node install.mjs --apply --profile <你的profile> # 真装(5 个包 + 自动自检)
node install.mjs --check --profile <你的profile> # 只自检
⚠️ 不要用 add github:qlheric/dsh-cvm(不带 #path:)——本仓库根是 workspace 包,那样只装到根包、一个插件都不生效。
三、配置(都能单独关)
所有阈值/关键词/提示文本都在各插件 Config(schemastery),可在 cordis.patch.yml 覆盖(跨升级存活):
- id: convergence
config:
threshold: 5 # 连续只读 5 次判定打转
- id: intervention
config:
steerAtTurnStop: false # 关掉「收尾强制再走一步」
- id: evidence
config:
enabled: false # 整个终局门禁关掉
- id: budget
config:
maxTokens: 1000000
softRatio: 0.8
四、实测效果(诚实版)
一句话结论:削打转 −48%,单任务成本 +2%(基本打平)。 价值在「过程」(少打转、少假完成),不在「结果」——强模型本来就不怎么掉,任务成功率测不出差异。
完整数据台账在 C1-AB数据汇总.md,下面的数字都来自各 20 次的 A/B(eval/batch-eval.mjs)。
4.1 关键归因:成本一度 +28.7%,真凶不是缓存,是「轨迹变长」
早期实测打转确实被削了(−33%),但 token 成本反而 +28.7%。最初怀疑是「动态注入 system prompt 破坏前缀缓存」,于是加了两项观测来验证:
- 缓存命中率:cacheReadRatio 基线 96.4% → 装插件 95.2%,几乎没动——缓存根本没坏。
- 成本加权 token(cache_read ×0.1、cache_write ×1.25,区分「命中 vs 未命中」):这才是真实成本。
结论:成本上升不是缓存失效,而是「轨迹变长」——干预把「一次长打转」拆成了「多次短探索/验证」,模型被提醒后反而更谨慎、多读多验,总工作量增加。
4.2 关键改进:把干预从「发散」改成「收敛」
对症下药:不是改注入机制,而是改文案方向。原来提示「换一种方法」是发散指令(诱发更多探索);改成收敛指令后成本一路压下来:
| 版本(各 20 次) | 打转 maxReadStreak | 真实成本 costTokens |
| 旧文案「换一种方法」 | −33% | +28.7% |
| 收敛文案「停止读 + 直接改」 | −48% | +12.7% |
| +「验证一次就够」✅ | −48% | +2.2% |
| 滞回冷却(边沿只注入一次)❌ | −34% | +8.6%(已回退) |
最终定案的干预文案(收敛版,也是默认):
⚠️ 运行时检测:已连续 N 次只读、无推进。停止读文件,直接改最可疑处并运行验证——验证一次就够,不要反复验证;不确定就运行最小复现命令;信息不足就用一句话说明卡点。
4.3 边界(别把数字当承诺)
- 样本 20 次/组,方向性证据,未到生产级(隔离 DSH_HOME + headless)。
- 结果指标测不出:强模型在 C5 场景基线就 20/20,价值只在过程指标(打转长度/步数),且要看 sd / 极值,不能只看均值。
- 阈值有倒 U 型最优窗口:T=5 最优,T=3(过早打断探索)和 T=8(已陷深打转)都比不装还差。
- 留着一个被撤回的假阳性当标本:差异 10pp 时 p≈0.34,样本不够就下结论会翻车(详见台账 §11)。
五、已知代价(风险是「误伤」,不是「没效果」)
- dsh-evidence 早期把 tool/result.error 当成「工具失败」,在调试场景误判正常报错,退化率反涨到 37.5%。修正为「验证工具成功执行才算验证过」后消除。
- dsh-convergence 的 threshold 过低(T=3)会过早干预、恶化过程。
- dsh-budget 的团队聚合是「下界」:只计当前活跃子 agent(已结束的无法归属)。
⇒ 每个插件都可配、可单独关;调参按场景标定,不照搬默认值。
六、评测方法学(写给要复现的人)
1. 场景文件每轮复位,复位源不能是 git HEAD(模型产物会被 git add -A 一起提交)——用独立模板目录 eval/scenarios/。
2. A/B 数据并存:batch-eval.mjs --label A|B,否则互相覆盖。
3. 别只看均值:强模型的过程指标均值可能不动,差异在 sd / 极值 / p90。
4. 单测 mock 必须对齐真实事件结构(user/message 的 payload 是 UserMessage,assistant/message 才是嵌套 {turn,step,message})。
5. 归因要分项 token:totalTokens 简单相加看不出缓存问题,要用 cacheReadRatio + costTokens(成本加权)拆「缓存失效 vs 轨迹变长」。
七、目录
packages/dsh-{contract,convergence,evidence,intervention,budget}/ # 五个插件
install.mjs # 一键安装器 eval/ # 评测集 + 驱动 + cost-summary src/domain/ # 单测 *.md # 知识底座、调研、数据台账
License
MIT预览分享卡片长什么样 ↓
dsh-cvm —— 给 DSH agent 加一层「防卡住 / 防假完成 / 防超支」的运行时监督