XGUIMAX/dsh-tavern-wrongbook

dsh-wrongbook

DSH Tavern 错题库:按人物卡分类记下调试中踩过的坑,先查本卡、再跨卡。 / Per-card defect ledger for DSH Tavern.

待分类character-carddebugdsh

dsh-wrongbook

DSH Tavern 的错题库:按人物卡分类记下调试中踩过的坑,下次先查本卡、再跨卡。

调一张卡踩过的坑,隔一周换一张卡会再踩一遍 —— 因为是同一类问题:同名的状态字段、同一段正则、同一个脚本钩子。这个插件把每个坑记在出问题的那张卡名下,并在调试时给出一个固定顺序:先查出问题那张卡自己的错题库,再跨卡查。

可以做什么

  • 按卡分类:卡片目录里有几张卡就自动建几项,带卡名与头像。原版卡和它的 MVU版本 互相认得,能把一条结论一键复制过去。
  • 固定的检索顺序:① 本卡错题库 → ② 其它错题 → ③ 跨卡查询。本卡命中永远排在前面 —— 同卡的问题复发概率最高,先看它是为了避免把已知答案重查一遍。
  • 其它错题:不挂在人物卡上的问题单独一组,预置了「卡内故障」「卡片更新器」「通用 / 未归类」,也可以自己建分类。记一个谁也没听说过的分类名时会自动建一个新分类,而不是默默塞进兜底。
  • Agent 能查:注册了三个工具,卡片 Agent 在调试时按同一个顺序翻 —— 和面板上看到的是同一套打分与排序。
  • 备份与还原:每次写盘前自动备份,独立页面里按份数清理、删单份、还原;备份目录可以自己选。
  • 插件自检:面板右上角标着版本号,点一下「检测更新」比对插件自身文件的指纹与版本。
  • 通用脚本:一份跨卡共用的脚本库(异步助手、状态栏渲染器之类)。库里有什么、每张卡装没装、装哪些卡 —— 一个页面里看完;可以按分类筛、勾选多张卡批量装卸。
  • 卡型标记:像「米吧」那种自带 MVU 的卡,可以打一个显式标记,让面板的"可装脚本的卡"与统计认得它。见「卡型标记」。
  • 左侧跟随筛选:顶部选了状态或范围,左侧卡片列表只留下还有命中条目的卡 —— 条目一多时,一眼看出哪几张还有活儿没干完。

界面

四个入口,指向同一个页面:

  • 设置 → 错题库 —— 设置面板里的一个页面。
  • 左下角侧栏底部 —— 与「插件市场 / 卡片更新器」同一层的一个按钮,点了弹出覆盖层显示同一个面板。
  • Tavern 右侧栏面板(装了 Tavern 才有)—— 游玩界面右侧栏「插件」一组里的一页。
  • 每条助手消息下面的「记进错题库」(装了 Tavern 才有)—— 游玩时把这一轮直接抓成一条底稿:归类按 Tavern 给的卡文件位置走(原版卡和它的 MVU 版卡名通常一模一样,按名字会被判成「对上了两个分类」而记不下来),并自动把前面两轮正文放进「证据」那一栏。

(前两个是卡片工作台里的入口;后两个来自 Tavern 的插件接口,装不装 Tavern 都不影响别的功能。侧栏面板用的是接口第 2 版的 registerPanel,装在旧版 Tavern 上只会少这一块。)

页面里六个页签:

  • 人物卡错题:卡片分类列表 + 本卡错题库 + 跨卡查询。分类由卡片目录自动生成,左边每行显示头像、卡名、未解决/观察中/已修复的条数。
  • 其它错题:不挂在人物卡上的问题。这一组的分类是手建的,可以新建、改名、删除;删分类时它下面的记录会迁到「通用 / 未归类」,不会跟着一起消失。记一条不存在的分类名会当场建出来,所以「某个插件出的问题」天然就能各占一格。
  • 卡脚本:卡内自带的自定义脚本与机制条目。见下一节。
  • 宿主补丁:宿主源码上打的补丁清单与重打入口。宿主升级会覆盖 lib/,这里用来核对补丁还在不在、一键重打。
  • 备份与还原:当前数据概览、备份目录设置、备份列表(每条带时间和条目数)、还原、删除单份、按份数清理、导入导出。
  • 通用脚本:跨卡共用的脚本库。见「通用脚本」。

顶部筛选会同时作用于左侧列表:选了「未解决」或某个范围之后,左侧只留下还有命中条目的分类。没选筛选时不过滤(否则一进页面就像库读坏了)。

错题页顶部还有一个「回流到 Skill」按钮:把调出来的结论写进某个 skill 的参考资料,下次做同类事情时 Agent 会读到。见「回流到 Skill」。

来源失效标记(装了 Tavern 才有)

从「记进错题库」抓下来的底稿,出处不只是写进 refs 里那句话,另外存了一份结构化的 { gameId, turn, textVersion }。理由是来源会失效:回退、重新生成、编辑正文之后, 那一轮的正文已经不是当初抓下来的那段,refs 里那句「第 N 轮」就成了指向空气的指针。

Tavern 的插件接口第 2 版会通知剧情线变化(onTimelineChanged),错题库据此给受影响的 底稿盖一枚标记:

变动 · 对底稿的处置 回退到来源轮次或更早 · 标脏(那一轮及之后都读不到了) 撤销回退 · 自动清掉因回退盖的标记 重新生成 / 编辑来源轮次 · 标脏(轮次还在,正文换了) 分叉 · 原局的底稿不动 没带轮次的通知 · 退化成按整局标,不去编一个轮次号

被标脏的条目在面板上多一个「来源已失效」角标,点开详情写明是哪一轮、怎么变的,并说明 底稿本身一个字没改。确认问题仍然存在就点「确认仍有效」把标记清掉。

条目自己存的 source 是可选的:早期条目(以及手工新增的条目)没有它,永远不会被标脏 — 那时候只有一句人读的 refs,没有可比对的东西。

与卡片更新器联动

设置页右上角有一个 「● 卡片更新器 · 已连接」 按钮,用来显示另一个插件在不在。

它有三种状态:

  • 绿点 + 已连接 —— 卡片更新器装着,而且它的宿主半正在跑
  • 灰点 + 检测中 —— 刚打开面板,第一次探测还没回来。单独留这一档是为了不把「还没测完」误报成「未连接」
  • 红点 + 未连接 —— 没装,或者被停用了

任何时候点它都会打开卡片更新器的仓库页():未连接时那是补装入口,已连接时那是查更新和看文档的地方。鼠标悬停会写明当前状态。

探测方式是往对方插件自己的路由发一次 GET /dsh-card-updater/state,每 15 秒复查一次。这用的是对方的公开接口,不需要两边事先约定协议,也不要求两个插件同时升级 —— 对方没装、版本老、或者路由还没就绪,这里只会显示未连接,不会有报错弹出来。

对端状态

面板右上角有一个对端状态按钮:显示卡片更新器的连接状态(绿点「已连接」/ 未连接), 并在它挂着「专属预设」时把预设名带过来。两个插件互不读写对方的数据文件,只走 HTTP。

「专属预设」标记

卡片更新器认得出作者在帖子里写的「另附专用预设」,错题库把这一条读过来显示:人物卡列表里,带专属预设的卡会多一个虚线边框的「专属预设」小标签,鼠标悬停能看到来源链接。

判据是卡片更新器状态里的 config.cards[].primary.gates 含 preset 项 —— 也就是它的面板上那个「另附专用预设」条件。能不能对上号取决于这张卡有没有配原卡路径;对不上就安静地不显示,不会报错、也不会卡住。

这里只做展示,不往错题库里写任何东西。 专属预设是卡片的客观属性,不是「踩过的坑」;自动写成条目会把病历本变成资料库,查故障时反而更吵。需要记录时「回流到 Skill」那条路更适合。

回流到 Skill

错题库是病历,skill 是诊疗手册。这一栏搭的就是两者的桥:错题页顶部点「回流到 Skill」,把选中的条目写进某个 skill 的 references/ 下。

只写你自己的 skill —— 这条写在面板的提示行里,不用点进来才知道。内置 skill(card-to-mvu、debug-card 这些)在程序目录里,Tavern 更新会整份覆盖它们,写了也留不住,所以弹窗里只列能写的;要往那个语境里沉淀,先新建一个自己的。

弹窗里填三件事:目标 skill、范围(全部 / 当前分类 / 未解决的)、目标文件(默认 references/错题库回流.md)。选中某个 skill 时会显示它的简介、参考资料和条目数 —— 建重了、或者建完一直没用(条目数 0)一眼就能看出来。旁边的「删除这个 skill」删之前会把整个目录复制到 /skill-backups/,备份不成功就不删。

每条按现象 / 根因 / 修法 / 证据铺开,带上分类、范围和状态。同一个目标可以反复回流,不会写出重复的东西。

条目会跟着错题库更新,但手工改过的段落归你。 每段标题下面有一行 `` 标记,记着它来自哪条错题、写的时候什么状态、以及正文的指纹。同步时按它分三种:

  • 有标记、指纹对得上 → 是我们写的、没人动过 → 状态变了原地更新(错题库里标了「已修复」,这里也跟着变);
  • 有标记、指纹对不上 → 你改过 → 不动,并在回执里报「另有 N 条你手工改过、已跳过」;
  • 没有标记 → 不是我们写的 → 一律不动。

所以想给某条加上自己的注解,直接改那一段就行;从改的那一刻起,那一段就不再被同步碰。

老文件里那些没有标记的段落会被认领一次:结构对得上(有元信息行 + 至少一个小标题)就补上标记、纳入管理;对不上的当手工段落护起来。认领之后照上面的规则走。

新建 skill 时把简介写实。 description 是 Agent 判断「要不要自动加载这个 skill」的唯一依据 —— 光有名字加一句"这是个错题库",它不知道自己该在什么时候想起来。三个要件:是什么 + 什么时候用 + 症状词。

不知道怎么下笔就点「示例」:它按当前范围和错题库里的实际条目算一句真的出来,不是留个空模板。全部范围会生成类似这样一句,可以直接用或改:

已解决故障库:DSH Tavern 调试中踩过的 40 个坑(工具链、MVU、前端UI、正则、世界书),涉及 Tavern 本体、龙娘回廊!5.3 MVU版本、卡片更新器、卡内故障。用户排查卡片异常、转 MVU、或调试插件与宿主时,先翻一遍看有没有同一类 —— 每条含现象、根因、修法与实测证据。

两条硬边界:目标文件不许跑出 skill 目录(.. 会被拦),skill 名也不许带路径。

自动同步

手动点回流的结果通常是:记完错题就忘了点,skill 停在几天前,下次调试读到的还是旧的那份。所以在弹窗里勾上「以后新条目自动同步到这里」之后,每次记错题、改错题、批量导入都会顺手跟一次。

回流只改「我们自己写的、且没人动过」的段落,所以自动跑是安全的 —— 没变化时是 0 条写入、文件一个字节不动。实测:打开自动同步时 unchanged 48、文件大小不变;之后改一条错题的状态,只有那一段被重写。

是否真的在跑要能看见,所以:

  • 面板上「回流到 Skill」按钮前面有个绿点,说明自动同步开着;
  • 那一行的提示写着上次什么时候跑的、写进去几条:自动同步已开 → ziyongskill(上次 2026-09-25 17:43,写入 0 条);
  • 记完一条错题回来,时间戳会变 —— 这就是「生效了」的证据,不用点进弹窗;
  • 万一某次没跑成,提示行直接显示失败原因(skill 被删了、文件被占用……),而不是安静地不工作。

弹窗里同样能看到这套状态,复选框就在目标 skill 下面。

和宿主「改卡记忆」的分工

DSH Tavern v2.3 起内置了自己的改卡记忆(data/card-memory/,配 tavern_memory_search / _preference / _experience 三个工具,底层是 memon 记忆引擎)。它和错题库记的是同一类东西 —— 改卡时踩过的坑 —— 但两者的长处不同,所以并存、按长处分工,而不是合并成一个。

宿主改卡记忆 → 跨卡、可复用的经验。

它的 scope: shared 语义明确:一条经验要么属于当前这张卡、要么是全库通用,没有含糊地带。而且带强制约束 —— status 只要不是 unverified(即 static-validated / runtime-verified / user-confirmed),就必须填 evidence,不填直接报错。这让「声称这条已验证」这件事留得下凭据,比错题库那套纯靠自觉的 status 有力。

错题库 → 按卡归因的具体故障。

per-card 分桶让它天然回答「这张卡出过什么问题」;面板能一览全部条目、按状态过滤、原版卡与 MVU版本 互认;而且回流出的 skill 能在别的会话里被搜到 —— 这是目前唯一一条能跨会话把结论带走的链路。

检索时两处都查。 两个系统互不读取:只查错题库会漏掉跨卡经验和存档的用户偏好,只查宿主记忆会漏掉按卡归因的存量。任何一处查不到,都不代表「没踩过这个坑」。

一处有意的例外。 v2.3 的说明里写着「将错误经验写入改卡记忆,不再默认生成 Skill」,而错题库保留自动回流 —— 这不是没跟上更新。回流是错题库的核心机制,它的产物能被别的会话检索到,这条链路目前没有替代品。所以这条分歧是刻意留的。

卡脚本

作者经常随卡附带自定义脚本 —— 悬浮状态栏、地理数据加载、变量更新器、自动化流程 —— 它们不在任何通用清单里,却决定了卡能不能正常跑。DSH 又没有全局脚本槽:脚本随卡加载,换一张卡就换一套。所以排查任何一张卡之前,先看清它带了什么。

「卡脚本」页签把这件事摊开。这一页是懒加载的,切过去才扫,不拖慢打开面板:

  • 每张卡一块,标题上直接标出「N 个脚本 / 特化 N / 控制器 N」,带特化内容的不用挨个点开就知道;
  • 块内逐条列脚本:名字、字符数、启用还是停用、import 指向的外链、脚本自己的 data 键。常见的几个(外置状态栏、小手机脚本、创意工坊、格式修复、辅助计算脚本)算通用,压暗显示,不跟特化脚本混在一起;
  • 另外挑出两类会左右卡行为的东西:控制器条目(世界推演、副本推进那类要求模型输出特定协议的)和含 `update` / `json_patch` 的条目;
  • 页面下方单独列 data/tools/ 下的脚本 —— 那些不属于任何一张卡,是从卡里导出来的、或者为卡自制待命的那批,卡出问题时就靠它们适配。

脚本的判据是从 data/tools/card-scripts/scan.mjs 搬进来的:读 data.extensions.tavern_helper.scripts,并且要先剥掉卡文件外层可能套着的那层 `raw` —— 少这一层,脚本数会永远是 0。

通用脚本

一份跨卡共用的脚本库。这些脚本不挑卡,装到哪张卡就在哪张卡用;库目录是 data/tools/wrongbook/common-scripts/,不属于任何一张卡。

典型用途:让副 API 在后台生成资料、把状态栏渲染交给脚本、跑回归测试、做结果回填。

装到哪些卡

面板只列「可装脚本的卡」,判据是三条任一:

  1. 文件名是 … MVU版本;
  2. 卡自己声明了 dsh_card_marker.kind = hand-tuned-mvu(自带 MVU,见「卡型标记」);
  3. 已经装了库里的任何脚本 —— 装了的当然要统计。

只作参照的原版卡不在此列,也不提供装卸入口。

怎么用

库里每个脚本有三个动作:导入脚本(在页面右上角,进新脚本)、更新(换掉这一份,并自动推到已经装了它的卡上)、删除(只删库文件,不动已装到卡上的)。

  • 分类筛选:库项读脚本自带的 dsh_meta.category(没有就归「未分类」),标题行可以按分类筛。
  • 卡列表用脚本名当标签:点 助手agent_v0.15 只看装了它的卡。脚本本身就是分类单位,不再另外给卡设一套分类。
  • 批量装卸:每张卡前面有勾选框,配「全选 / 清空 / 装上选中 / 卸下选中」。没选中时批量按钮是灰的 —— 否则点下去会退化成全量操作。

三个建议入口

库里每个脚本右侧的按钮,按"精准度"分三层:

按钮 · 做什么 · 快慢 检测适合的卡 · 即时关键词匹配:从脚本内容认出它调用了哪些宿主能力,与每张卡的特征对着看,对得上就给一条理由 · 秒出 生成建议 · 生成一段文本(脚本路径 + 全部候选卡的 @ 引用 + 要求逐张读卡、点名具体功能),复制到剪贴板,对话页开着就顺便填进输入框 · 秒出 后台分析 · 逐张读卡的静态分析,带进度条 · 慢,但关界面也继续

为什么「生成建议」不直接发出去:它会让工作台那边的模型逐张读卡、说清"这张卡的哪个功能能被脚本接管"(比如「龙娘回廊的橱窗」)。 这一步要消耗一次完整的全卡分析,所以做成"生成 + 给你看 + 你决定发不发"。

关于"精准"的边界:宿主没有给插件暴露模型调用接口,所以插件自己做不到"模型读完整卡再判断"。 「检测适合的卡」和「后台分析」给的是技术层面的匹配(脚本用到的能力 × 卡具备的特征), 理由全部引用卡里实际存在的东西(正则名、脚本名、世界书条数),可以照着核对 —— 但它们判断不了题材是否合适,那需要真的读过卡。

后台分析

逐张读卡的深度分析:读 description、正则名、脚本名、世界书标题、MVU 机制,然后和脚本能力做匹配。 刻意不读全文(有卡 11MB)。