dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
基于 DeepSeek Harness 的文字游戏宿主:每次模型请求都产出逐字节可验的上下文装配单(SillyTavern 人物卡/世界书迁移 + 三 Agent 职责互斥 + 原子回退)
运行在 DeepSeek Harness 上的模型测试台:把「一个模型 + 一套上下文编排」变成可复算的读数。
它测的是模型在给定上下文下的真实表现——这次请求发出去多少字节、缓存命中多少、一轮回吐
多少字、结束原因是什么。每一次请求都由纯函数 assemble() 产出装配单,落盘后从磁盘回读
校验(判据 A1:实际发出的 body 逐字节等于装配单重建的 body)⇒ 上下文编排的每次变化都是一笔
可复核的字节账,不是黑箱。任何「换了编排,模型表现变好」的说法,都必须先过这一关。
功能分区(主人 2026-09-22 定调,原话):「那他那边就主要满足我的游玩需求,那本地的功能 是用来迭代预设,还有就是研究上下文内容对模型表现的影响」。 ⇒ 游玩归
dsh-profile-tavern;本插件的主场是「迭代预设」与「研究上下文内容对模型表现的 影响」——即「最优上下文」命题的度量装置。 它技术上仍能跑完整对话(modeltest_play),但那不是它的存在理由,而是它取数的手段。
与上游
flizzywine/dsh-tavern的关系:独立实现,不 fork(上游为 AGPL-3.0)。 只借鉴接口、行为与文件格式,不复制其源码片段。许可:MIT(自持)。
1) 装依赖(在目标 profile 的 package.json 加 link 依赖):
"dsh-model-test": "link:E:/alice/self-plugins/dsh-model-test"
2) 挂组合:
- id: dsh-model-test
name: dsh-model-test
3) 30 秒验证:modeltest_cards → 应列出可用卡片;modeltest_assemble → 应给出逐字节可验的装配单
每一次模型请求都由纯函数 assemble() 产出一份装配单(assembly manifest):每个片段来自哪个槽位、
命中哪个关键词、占多少字节、sha256 是多少、最终 hash 是多少。装配单落盘后从磁盘回读,
用于校验「实际发出的 body 逐字节等于装配单重建的 body」(判据 A1)。
因此上下文编排的每次变化都是一笔可复核的字节账,不是黑箱。这条判据同时是本项目的 研究价值所在:任何「换了编排、模型表现变好」的说法,都必须先过这一关。
| 工具 | 意图 |
|---|---|
modeltest_cards | 卡库清点(只读) |
modeltest_card | 读一张卡的字段与原文键集 |
modeltest_worldbooks | 世界书清点/按 ST 格式导入(只读源) |
modeltest_assemble | 装配一轮并落盘装配单(不调模型),返回字节账与 A1 自检 |
modeltest_play | 玩一轮:装配 → 真调模型 → 落盘 → 回报 token/缓存读数 |
modeltest_rollback | 原子回退(正文与状态同生共死;无完整快照则响亮拒绝) |
面板(依赖 dsh-panel 宿主,缺席时工具面不受影响):/panel → 「模型测试」。
面板动作:开局 / 玩一轮 / 行动候选 / 后台结算 / 装配单读数 / 回退。
| Agent | 产出 | 写权限 |
|---|---|---|
正文 Agent(runTurn) | 叙事正文 | 对状态只读 |
候选 Agent(runCandidates) | N 个行动选项 | 对状态只读,不写历史 |
结算 Agent(runSettlement) | 结构化状态 | 唯一状态写者 |
三者共用同一装配路径(判据 A7 用行为级测试:工具路径与面板路径产出同一 manifest hash)。
代码里的默认值一律为空——部署相关的取值全部走配置(宿主公约:插件不得硬编码可调项; 默认值若写死某台机器的目录,公开仓库还会连带泄漏路径)。
| 字段 | 默认 | 说明 |
|---|---|---|
dataDir | '' | 我们写的一切(会话/装配单/快照)。空 ⇒ 解析为 <DSH_HOME>/model-test-data |
cardDirs | [] | 只读卡库(ST characters/ 目录)。空 ⇒ 卡库为 0,工具如实报 0 并告警 |
worldbookDirs | [] | 只读世界书库(ST worlds/ 目录) |
provider / model | '' | 模型路由。空 ⇒ modeltest_play 响亮失败(不猜默认模型) |
maxTokens | 1600 | 单轮输出上限 |
temperature | 0.9 | 采样温度 |
budgetChars | 24000 | 单次请求字符预算(超出按优先级确定性裁剪并记账) |
部署示例(放在 profile patch 里,不要写进源码):
- id: model-test
name: dsh-model-test
config:
dataDir: <你的数据目录>
cardDirs: [<你的 ST characters/ 目录>]
worldbookDirs: [<你的 ST worlds/ 目录>]
provider: <provider>
model: <model>
⚠ 迁移源只读:插件不写回酒馆目录;ST 的
secrets.json一律不读。
node scripts/acceptance.mjs # 一条命令:判据表 + 真实数据读数 + A7 旁路面
node scripts/preset-cost.mjs --preset "E:/alice/tavern/色欲之罪.json" # 迭代预设第一步:块级成本表(不调模型)
npm test # 仅判据测试
每一轮的四类证据都在盘上,且 turns/<轮>.json 把实验条件(卡 / 预设 / 路由 / maxTokens /
temperature / budgetChars)与读数(finishKind / truncated / usage / A1 / 字数)钉在一起
——否则换过参数之后两轮读数不是一回事,失败也事后查不到当时的结束原因。
# 逐轮摊开:条件 → 请求(逐 entry)→ 响应(正文/思维链)→ 读数 → 落点
node scripts/turn-report.mjs --session <会话id> --dataDir <dataDir> [--turn N] [--entries]
# 导出可度量稿:思维链落 drafts/reasoning/ 子目录(同层会被度量器当语料),
# --prose-only 只取 <dream_body> 内的散文(协议包装约占输出 40%,混进去读数不成立),原始另存 drafts/raw/
node scripts/export-draft.mjs --session <会话id> --dataDir <dataDir> --reasoning --prose-only
modeltest_play 的返回值同样带 finishKind / reasoningChars / reasoningPath / turnRecordPath
——失败时也带(空正文判失败但思维链仍落盘:它是唯一诊断证据)。
退出码:0 全量通过 | 1 判据失败 | 2 判据通过但真数据不可达(PASS 降级)。
判据:A1 装配单逐字节可重建 | A2 同输入同 hash | A3 原子回退(字节级 + 行为级重放) | A4 缓存命中读数 | A5 ST 卡往返不丢字段且不改卡 | A6 世界书匹配为纯函数 | A7 各路径共用装配器(无旁路)。
bash scripts/cache-experiment.sh <会话名> 6 <卡 id> # 需 web 在线且面板宿主已挂载
判据跑之前写死:① 每轮 A1 为真 ② requestChars 单调不减 ③ cacheRead 命中比不塌。
实测(2026-09-22 · 修复「世界书重复注入」后重跑,读数为干净值):
| 轮 | 请求字数 | 新增 tok | 缓存读 tok | 命中比 | A1 |
|---|---|---|---|---|---|
| 2 | 3986 | 219 | 896 | 80.4% | ✓ |
| 4 | 4477 | 425 | 1024 | 70.7% | ✓ |
| 6 | 4935 | 367 | 1408 | 79.3% | ✓ |
| 8 | 5474 | 494 | 1664 | 77.1% | ✓ |
| 10 | 6018 | 497 | 2048 | 80.5% | ✓ |
⇒ 三条判据全过:命中比在 70–81% 波动、无下降趋势(requestChars 与 cacheRead 均单调不减)。
⚠ 命中比按服务端 usage 原样计算(cacheRead / (cacheRead + input)),未核对该 provider 的字段口径。
node scripts/context-report.mjs --data <dataDir> <session>
把一个会话的装配单聚合成「钱花在哪」的账:逐轮总账与增量、来源构成(按片段自身字数)、 槽位构成、世界书触发、增长与裁剪。
实测样例(会话 a4fixed,5 轮,修复重复注入后的干净读数):
| 来源 | 字数 | 占比 |
|---|---|---|
| card | 14220 | 57.2% |
| history | 8885 | 35.8% |
| preset | 1650 | 6.6% |
| input | 75 | 0.3% |
| state | 10 | 0.0% |
⇒ 卡定义一项就占 57.2%——它既是最大的可控自变量,也是预算裁剪时最先该被讨论的对象。
(记账按片段自身 text.length 累加;均分会把 system 块的字节错配到别的来源,
实测曾把 2 字的 state 报成 2655 字。)
dataDir 仍是
E:/alice/tavern/dream-tavern-data,里面是 11 份实验报告与 tqb-* 质量基准,共 86 MB / 1625 文件。
迁移收益低于风险——patch 里是显式路径,名字跟不跟得上都不影响运行。
源码默认值已改为 model-test-data ⇒ 新部署用新名,老部署不动(不必以为这是漏改)。maxTokens:实测上限 1600 时 usage 报满 1600,而正文只剩 195 字
就断在句子中间(预算被思维链吃掉)。modeltest_play 与面板 play 动作会返回 truncated 标记,
不静默(判据:outputTokens >= maxTokens)。建议 maxTokens ≥ 3000。<response><now_plot>),
模型会照它写——这是酒馆语义下正确的行为,但意味着模板类卡不能靠预设纠正格式。dsh-panel 的 actionTimeoutMs 默认 10 000 ms,而酒馆一轮
(装配 + 模型调用)常规 10–20 s,卡越大越慢 ⇒ 必须把它提到 ≥60000,否则长卡的动作会被
宿主判超时(实测:一张 24 条卡内世界书的卡在默认值下返回 动作超时(>10000ms))。
⚠ 且超时 ≠ 撤销:服务端回合仍会跑完并落盘,客户端只看到超时。
这是跨插件契约问题(宿主决定超时、贡献方决定耗时),不是酒馆自身的缺陷。raw 里,modeltest_worldbooks 会逐项报出,不假装支持。data.alternate_greetings:目前只取第一个作为回落开场,尚未做多开场切换。<VariableEdit>{...}</VariableEdit>)。
这是卡自带的 MVU 风格协议,本插件不消费它——状态走独立的结算 Agent(面板「后台结算」/
runSettlement),职责互斥(正文不写状态)。因此这类卡的变量不会自动同步;要支持需显式建模该协议。dsh-panel;未挂载该宿主时没有图形界面(工具面仍可用)。cd self-plugins/dsh-model-test && npm test
131 条用例(跑一次得到)。
docs/semantic.md —— 权威契约(定位/反定位 · 验收表)