Repository navigation
DSH Tavern 的错题库 #150
XGUIMAX
started this conversation in
Show and tell
Replies: 2 comments 1 reply
|
好东西,我只有一个Skill |
0 replies
|
请问桌面版 是在哪个插件市场里面安装? |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
自己搓的错题库
https://github.com/XGUIMAX/dsh-wrongbook
dsh-wrongbook
DSH Tavern 的错题库:按人物卡分类记下调试中踩过的坑,下次先查本卡、再跨卡。
调一张卡踩过的坑,隔一周换一张卡会再踩一遍 —— 因为是同一类问题:同名的状态字段、同一段正则、同一个脚本钩子。这个插件把每个坑记在出问题的那张卡名下,并在调试时给出一个固定顺序:先查出问题那张卡自己的错题库,再跨卡查。
可以做什么
MVU版本互相认得,能把一条结论一键复制过去。界面
两个入口,指向同一个页面:
(早先只有前者,那是当时的取舍;后来按"和卡片更新器保持一致"补了侧栏入口。)
页面里六个页签:
lib/,这里用来核对补丁还在不在、一键重打。顶部筛选会同时作用于左侧列表:选了「未解决」或某个范围之后,左侧只留下还有命中条目的分类。没选筛选时不过滤(否则一进页面就像库读坏了)。
错题页顶部还有一个**「回流到 Skill」**按钮:把调出来的结论写进某个 skill 的参考资料,下次做同类事情时 Agent 会读到。见「回流到 Skill」。
与卡片更新器联动
设置页右上角有一个 「● 卡片更新器 · 已连接」 按钮,用来显示另一个插件在不在。
它有三种状态:
任何时候点它都会打开卡片更新器的仓库页(https://github.com/XGUIMAX/dsh-card-updater):未连接时那是补装入口,已连接时那是查更新和看文档的地方。鼠标悬停会写明当前状态。
探测方式是往对方插件自己的路由发一次
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/,备份不成功就不删。每条按现象 / 根因 / 修法 / 证据铺开,带上分类、范围和状态。同一个目标可以反复回流,不会写出重复的东西。
条目会跟着错题库更新,但手工改过的段落归你。 每段标题下面有一行
<!-- wrongbook:… -->标记,记着它来自哪条错题、写的时候什么状态、以及正文的指纹。同步时按它分三种:所以想给某条加上自己的注解,直接改那一段就行;从改的那一刻起,那一段就不再被同步碰。
老文件里那些没有标记的段落会被认领一次:结构对得上(有元信息行 + 至少一个小标题)就补上标记、纳入管理;对不上的当手工段落护起来。认领之后照上面的规则走。
新建 skill 时把简介写实。
description是 Agent 判断「要不要自动加载这个 skill」的唯一依据 —— 光有名字加一句"这是个错题库",它不知道自己该在什么时候想起来。三个要件:是什么 + 什么时候用 + 症状词。不知道怎么下笔就点「示例」:它按当前范围和错题库里的实际条目算一句真的出来,不是留个空模板。全部范围会生成类似这样一句,可以直接用或改:
两条硬边界:目标文件不许跑出 skill 目录(
..会被拦),skill 名也不许带路径。自动同步
手动点回流的结果通常是:记完错题就忘了点,skill 停在几天前,下次调试读到的还是旧的那份。所以在弹窗里勾上**「以后新条目自动同步到这里」**之后,每次记错题、改错题、批量导入都会顺手跟一次。
回流只改「我们自己写的、且没人动过」的段落,所以自动跑是安全的 —— 没变化时是 0 条写入、文件一个字节不动。实测:打开自动同步时
unchanged48、文件大小不变;之后改一条错题的状态,只有那一段被重写。是否真的在跑要能看见,所以:
自动同步已开 → ziyongskill(上次 2026-09-25 17:43,写入 0 条);弹窗里同样能看到这套状态,复选框就在目标 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 又没有全局脚本槽:脚本随卡加载,换一张卡就换一套。所以排查任何一张卡之前,先看清它带了什么。
「卡脚本」页签把这件事摊开。这一页是懒加载的,切过去才扫,不拖慢打开面板:
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 在后台生成资料、把状态栏渲染交给脚本、跑回归测试、做结果回填。
装到哪些卡
面板只列「可装脚本的卡」,判据是三条任一:
… MVU版本;dsh_card_marker.kind = hand-tuned-mvu(自带 MVU,见「卡型标记」);只作参照的原版卡不在此列,也不提供装卸入口。
怎么用
库里每个脚本有三个动作:导入脚本(在页面右上角,进新脚本)、更新(换掉这一份,并自动推到已经装了它的卡上)、删除(只删库文件,不动已装到卡上的)。
dsh_meta.category(没有就归「未分类」),标题行可以按分类筛。助手agent_v0.15只看装了它的卡。脚本本身就是分类单位,不再另外给卡设一套分类。三个建议入口
库里每个脚本右侧的按钮,按"精准度"分三层:
@引用 + 要求逐张读卡、点名具体功能),复制到剪贴板,对话页开着就顺便填进输入框为什么「生成建议」不直接发出去:它会让工作台那边的模型逐张读卡、说清"这张卡的哪个功能能被脚本接管"(比如「龙娘回廊的橱窗」)。
这一步要消耗一次完整的全卡分析,所以做成"生成 + 给你看 + 你决定发不发"。
关于"精准"的边界:宿主没有给插件暴露模型调用接口,所以插件自己做不到"模型读完整卡再判断"。
「检测适合的卡」和「后台分析」给的是技术层面的匹配(脚本用到的能力 × 卡具备的特征),
理由全部引用卡里实际存在的东西(正则名、脚本名、世界书条数),可以照着核对 ——
但它们判断不了题材是否合适,那需要真的读过卡。
后台分析
逐张读卡的深度分析:读
description、正则名、脚本名、世界书标题、MVU 机制,然后和脚本能力做匹配。刻意不读全文(有卡 11MB)。
关掉面板也会继续跑,靠四点:
data/tools/wrongbook/fit-<脚本名>.json),所以中途重启也能看到进度到哪了;setTimeout(r, 0)),不然会占满宿主进程、界面卡死。前端只负责启动和轮询,并用
localStorage记住上次看的脚本名,重开面板接着显示进度。装卸只动 scripts 数组
装/卸只改卡文件里的
extensions.tavern_helper.scripts,其余字段逐字节不变(做过逐字段实测)。注意:脚本本身可能用
indexedDB/localStorage、挂setInterval、往window上挂东西、用
eventOn订阅事件 —— 卸下只是停止注入,运行期已经写下的缓存不会自动清掉。更新
库里的脚本用「更新」换新版。它和「导入」是两个动作,不是同一个:
dsh_metacommon-script-backups/<时间戳>/为什么
dsh_meta要保住:分类 / 用途 / 建议可能是你在界面上改过、或者插件攒下来的,不能因为新版文件里没带这一段就被一份自动草稿盖掉。
内容与库里那份完全相同时直接返回「没有改动」,不白写一次盘。
更新会自动同步到卡上
更新库里的脚本时,已经装了它的卡会一起更新 —— 这一步是自动的,不需要再点一次。
理由是「库里是新的、卡上还是旧的」属于最容易漏掉的一种不一致:
它不会报错,只会让某张卡在某个时刻行为与预期不符,而人很难想到去核对"卡上那份是什么时候装的"。
同步走的是同一个安装流程(
installCommon),所以备份、写盘、只动scripts数组这几件事都一致;用的内容就是刚落盘的新版。更新完面板会列出同步了哪几张卡。
同步失败不影响库的更新:库已经换好了(那是主操作),卡这边没跟上属于部分完成 ——
面板会把失败原因写出来,让人知道哪一步没成。
脚本自带的持久状态
有的脚本自己会写
localStorage/indexedDB/ 世界书条目 / MVU 变量。换掉库里的文件不会清掉那些数据 —— 新版可能读到一份旧版留下的状态。
所以更新时会先认一遍这个脚本会写什么,命中就在面板上列出来:
顺带会对比新旧两份的版本常量(脚本自己写了
VERSION之类的话)并显示旧 → 新,降级时能一眼看出来。
这一条不阻止更新,只是提示 —— 判断该不该清状态需要知道脚本具体怎么用那些数据,那是读过它才说得准的事。
导入时自动生成说明
导入脚本时,后端会扫一遍内容,生成一份
dsh_meta草稿(分类 / 用途 / 建议)写进库,免得回头再补。已经带了
purpose的不会被覆盖 —— 作者的说明优先。它给的是能力清单,不是内容判断:
刻意不写"建议装到哪些卡":脚本能不能装跟卡无关,硬编一个卡名单只会误导。
卡型标记
有一条例外值得单独说。「来当小男友爆管人的米吧!」是作者原制、自带 MVU的卡,
不是 DSH 转换出来的 —— 转 MVU 会丢失大量内容(21 条正则含 583K 字正文美化、7 个脚本、170 条世界书),
代价大于收益。
所以它不走文件名约定,而是在卡里写一个显式标记:
面板读这个标记,把它的每张卡都当 MVU 卡对待(可装通用脚本、进统计)。
标记是手动打的,只用于这类例外 —— 正常情况下文件名
… MVU版本就够了。调试时的检索顺序
这是这个插件存在的理由,所以单独说。
① 本卡错题库。 同一张卡上的问题是复发概率最高的一类:还是那个状态字段、还是那段正则。先看它,是为了避免把已经调出来的结论重新调一遍。
② 其它错题。 本卡没有答案时先来这一段:归档位置可能有偏差 —— 卡的问题被记进了「其它错题」、或者插件上的坑其实是某张卡的坑 —— 而归类偏了不该让检索漏掉它。它排在跨卡之前,因为插件与工具链上的坑影响的往往不止一张卡。
③ 跨卡查询。 最后才是别的卡片。这时候「这也是个新坑」本身就是信息,而别处的相似记录开始有参考价值。
三段的范围是固定的:① 当前分类,② 「其它错题」那一组(当前就在这一组时是组内其余分类),③ 其余卡片。从哪一头进来顺序都一样。
三段共用同一套打分:标题 > 标签 > 现象 > 根因/修法 > 引用,越靠前权重越高。没给关键词时,工具输出里的 ② ③ 只印前 8 条并标出总数 —— 全库几十条铺开会把 ① 淹掉;带上关键词就是有目的地在找,那时整段列出来。
给卡片 Agent 的三个工具
工具在插件加载时注册到宿主,同时会往 system prompt 注入一段说明。只有前者不够:工具列表里多几个名字,模型不会因此去用它们 —— 真发生过一次,让 Agent 归档时它把错题写进了
materials/错题库.md,因为它只知道那儿。注入的说明要求 Agent:
wrongbook_lookup,按三段顺序看;wrongbook_record归档,现象、根因、证据、处理、防坑都写进去;wrongbook_update补根因或标记已修复;wrongbook_lookupcard(卡名、cards/xxx.json,或其它错题里的主题名)、query、status、scope、limitwrongbook_recordcard填一个还不存在的名字会当场建出这个分类。落库到 Tavern 数据根下wrongbook_updatecard的解析是分层的:完整路径 → 文件基名 → 分类显示名完全相等 → 名字包含。模糊命中多个分类时不猜,落到「通用 / 未归类」并把候选带回来。这条对原版卡和它的
MVU版本同样成立 —— 它们是两个不同的分类,坑往往只出在其中一张上(调试对象通常是 MVU 版)。所以card: "道渊"会同时命中两张,此时:wrongbook_lookup照常返回三段,但在开头警告「对上了 2 个分类:…」,让你补全名字;wrongbook_record直接拒绝,要求写全名字。替你在两者之间挑一个,就是把结论记到不该记的地方,比让你多写几个字贵得多。面板上这两类也各有标记:原版卡标「原版」,MVU 版标「MVU」。
安装
前置条件
先装 DSH Tavern。 这个插件是它的伴生工具:人物卡、卡片工作台对话都由 Tavern 提供,插件只负责把这些坑记下来、并在调试时按顺序翻出来。
Tavern 地址:https://github.com/flizzywine/dsh-tavern
此外需要已经在用的 DSH,且有一个 profile(本说明中默认叫
tavern,Tavern 项目用的就是它)。插件是 DSH 插件,不是独立程序。用 dsh 命令安装
dsh plugin是 DSH 自带的插件管理命令:把参数转给 profile 目录下的 pnpm,装完之后再按已安装状态核对dsh.profile.bundles—— 声明了dsh.bundle的依赖自动进入层栈。所以不必手工改 profile 的package.json。从 GitHub 装,不必先 clone:
在项目目录里装本地副本(改源码立刻生效):
装完重启 DSH,
设置 → 错题库出现即装好。卸载
重启 DSH 后入口消失。数据留在
~/.dsh/profile-data/tavern/data/tools/wrongbook,要一并清掉就手动删该目录。更新
GitHub 装的:
本地目录装的:在项目目录里
git pull,重启 DSH。数据与备份
单文件 JSON:
~/.dsh/profile-data/tavern/data/tools/wrongbook/data.json,结构是{ version, cards, entries, config }。每次写盘前把旧文件复制到备份目录,文件名形如
<毫秒>__<起因>__data.json,一眼能看出这份备份是哪个动作之前留下的。自动清理保留最近 60 份;面板上可以按份数手动清理,也可以删掉指定的某一份。还原之前会先把当前内容备份一次,所以还原本身也是可回退的。备份目录可以自己选
默认在
tools/wrongbook/backups,想换盘或丢进同步盘就在备份页上点「选择文件夹」。选择目录走的是自己的浏览弹窗,不依赖宿主的目录选择器 —— 那个能力在不在、能不能用都说不定,而「点一下什么也没发生」是最难查的一类故障。做法参照卡片更新器:host 出一个接口列一层目录,面板渲染一层。空路径列出所有驱动器(逐个探
A:\到Z:\,Windows 上 Node 没有列盘的 API),所以换到别的盘也走得到;路径框也接得住粘贴进来的东西(两边带引号、正反斜杠、D:只写盘符、相对名都认)。保存时会先建目录、再写一个探针文件试一下,路径有问题在按下去的那一刻就报出来 —— 而不是等某次备份静默失败、回头看才发现这几天的都没落地。
切换只影响之后写入的备份:旧目录里已有的文件不搬动也不删除,所以换回去随时还能看到。选空路径则恢复默认。
清理只动自己写的文件
自定义目录很可能是你挑的一个通用备份文件夹,里面躺着别人的 JSON。保留策略是会删东西的,所以列表、清理、删除都先按文件名认人:只有
<毫秒>__<起因>__data.json这种形状才算本插件的备份,别的一律不列、不动、也删不掉。插件自检
面板右上角的「检测更新」比对两件事:
package.json、lib/index.js、client.js三个文件的 sha256,跟上次检查的记录比。手改过源码就会被标成「本地有改动」。config.remoteUrl指向一个返回{ "version": "..." }的地址,有配置时会去比版本号,没配置就只报本地指纹结论。结果落在
tools/wrongbook/selfcheck.json,保留最近 20 次。不受 DSH 更新影响
~/.dsh/plugins/dsh-wrongbook,不在程序安装目录里。<Tavern dataRoot>/tools/wrongbook。dependencies里一条link:(或 GitHub 装的版本号),dsh.profile.bundles里一条名字。这两处为什么动不到,不是猜的。Tavern 更新 profile 清单走
bin/profile-configuration.mjs的mergeProfileManifest,是读-改-写加精确排除:它只删自己上次托管的、只覆盖自己这次托管的,用户自己加的条目原样留在里面。这个插件既不在这份
managedBundles/managedDependencies里,也不在previousManaged*里,所以一个分支都碰不到它。面板上有一行实时自检(备份与还原页底部),查三件事:
dependencies里那条还在不在、dsh.profile.bundles里那条还在不在、node_modules的链接是否指回插件目录。全绿即正常;缺了会直接给出补回命令(dsh plugin add <插件目录>,或手工补那两处),不用去翻这份 README 猜。性能
数据量上去以后,最常走的几步都该是一次线性扫描。
verify/perf.mjs用 300 张卡 + 8000 条记录实测(同一台机器上的数字,量级参考):GET /state)有几处曾经会随规模恶化,都改成了单遍:
unshift,是 O(n²);现在一次拼好再赋值。stat);现在两个目录各列一次建索引,之后全是查表。statSync取尺寸和时间 —— 那两样没人用,只是顺手拿的。写入路径的开销主要在备份:每次写盘前把旧文件整份复制走,这是拿磁盘换「随时能回退」。8000 条时一次约 43 ms,换来的是任何一次误操作都能找回上一份。
常见问题
「选择文件夹」点下去什么也没发生? 不会。它走的是插件自己的浏览弹窗,只依赖插件自己的 HTTP 路由,跟宿主提供什么能力无关。
「打开数据目录 / 打开备份目录」没反应? 两级依次尝试,每一级的失败原因都留着,全失败时连同 PATH 一起报回面板:宿主解析器 /
SystemRoot拼出的绝对路径 →cmd /c start。成功时消息末尾会带上走通的层级(比如· C:\WINDOWS\explorer.exe),所以这台机器上到底是哪种方式有效不用猜。面板里为什么没有系统弹窗?
window.prompt在 Electron 的渲染进程里没有实现,调用后直接返回null而且不报错 —— 用它做的「导入 JSON」「改分类名」会表现成按钮点了没反应。window.confirm的行为在各版本之间也不一致。所以导入用面板内的文本框、改名用内联输入框、删除和还原用二次确认按钮。验收脚本里有一条静态检查,防止这两个 API 再被引进来。卡片改名或被移走了,记在上面的错题会丢吗? 不会。已经不在卡片目录里的分类只标成「文件已不在卡片目录」,记录保留。删分类也一样,记录会先迁到「通用 / 未归类」。
为什么有两组分类? 分类带
group字段:card来自卡片目录扫描,other是手建的。两组共用同一套列表与条目区渲染,只是换数据源。开发
验收
verify/host.mjs在系统临时目录里现造一个沙盒数据根,不碰真实数据;verify/client.mjs用最小 React 运行时渲染面板,不发网络请求。套件不打开任何外部程序。 曾经有一条实跑
openBackupDir的用例,已经删掉:它会真的弹出一个资源管理器窗口,而窗口指向的目录一旦在事后被删(沙盒会),资源管理器就弹「位置不可用」——比不测还烦。「打开目录」是人工验证项:重启后在面板上点一下,看窗口落到哪儿、via是哪一级回退。发版
改
package.json的version,打同名 tag:git tag v1.0.1 && git push origin main --tags面板右上角的版本号读的就是
package.json。许可
MIT
All reactions