qw2007/dsh-incident-journal:把踩过的坑写成可复习、可审计的条目

前言

做智能体开发的人大多攒过「踩坑笔记」,常见的结局有三种:写成了流水账,只有「我做了什么」,三个月后重读学不到东西;只写不复习,记了一百条一条没再看;或者笔记只装得下坑,真正值得留下的技术点和设计立场无处可放,最后流失掉。

下面介绍的 qw2007/dsh-incident-journal 就是冲着这三个问题来的。它是 DeepSeek Harness(DSH)的一个插件,维护者是 qw2007,分类为「记忆」。和「让 Agent 顺手记一下」的做法不同,它把条目按三类拆开,并且强制每条写出反事实——写不出来的不收。插件以五个 skill 的形式提供:记录(incident-journal)、复习(incident-review)、体检(incident-audit)、资料(incident-resource)、布局(incident-layout)。

这是什么

一句话定位:给 DSH 用的工程日记,把踩过的坑、留下的技术点、认下的设计立场写成可复习、可审计的条目,而不是让它烂在聊天记录里。

GitHub 仓库 qw2007/dsh-incident-journal,2026-10-12 创建,最近一次推送 2026-10-21,语言 JavaScript,许可证 MIT,当前 2 个星标。仓库已发布到公共 npm,包名 dsh-incident-journal,零运行期依赖(只用 Node 内置模块),声明的最低 DSH 版本是 >=0.1.0-rc.6。

它解决的核心矛盾是:笔记的价值不在于「记下来」,而在于「三个月后还读得回来、并且能当证据用」。

核心功能

三类条目,各配一把反事实筛子

条目分三类,每类的收录标准是各自的反事实问题:

kind 收什么 硬筛子(反事实)
坑 一个被推翻的判断 我原来以为是什么,凭什么
技术点 一个值得留下的做法 没有它我会怎么做,错在哪
思想风格 一个设计立场 它在什么约束下是对的,换什么条件不成立

每条必须能写出反事实。「今天我学了 Redis」写不出「没有它会怎样」,「他们用了分层架构」写不出「什么条件下它会错」——这类内容不收。README 里的原话是:没有反事实的条目是摘抄,不是条目。此外还有一条可迁移判据:如果一条经验只对当时那个报错有用,它不该进这个库。

记录时先查重,命中就加厚

跟 Agent 说「把刚才那个坑记下来」后,它会按固定流程走:检查开关状态;按三类筛子判断够不够格;先 grep 全库查重,命中且新判断是原条目没顾到的就加厚而不新建;拿关键词 grep 一遍 资料/,命中就加一节 ## 与资料的关联;按骨架落一条;最后同步四处索引(根 README.md、career.log、REVIEW.md、<项目名>/README.md)。

加厚被写成硬动作,是因为「想起来再去补」这个前提本身就不成立——想得起来的时候,你本来就不需要这条规则。坑 类从不加厚,因为它绑在一个项目的代码位置上。

复习按 kind 换角度,答错会回写

说「考考我」,它会挑 5–8 条出题,问题角度随 kind 变化:坑 考你凭什么做出那个错误推断;技术点 考你没有它打算怎么做、会怎么坏;思想风格 考加上什么条件之后结论就不成立了。答案用 <details> 折叠,出完追加到 REVIEW.md。答错的条目会回去补一条「追问会问什么」——README 的解释是,答错说明笔记缺了这个角度,不是记性的问题。

体检:把「笔记坏了」变成可读的报告

说「审一遍笔记」,incident-audit 输出形如:

[断链]   career.log:18 引用 of-0007,但身份行和 `## of-0007` 小节里都没有声明它
[悬空]   项目视图 my-project/README.md 指向 技术点/xxx.md,该文件不存在
[流水账] 坑/foo.md 缺「我原来以为」;技术点/bar.md 说不出不用会怎样
[污染]   技术点/baz.md 把 [资料] 写成了事实
[退化]   坑/qux.md 只写了行号没写符号名
[重复]   同一条判据在 坑/a.md、坑/b.md、坑/c.md 各写了一遍
[提升]   坑/jwt-expiry-a.md、坑/jwt-expiry-b.md 分属两个项目、同一判据 → 建议提成一条 技术点
[失衡]   库里有 9 条坑、0 条技术点、0 条思想风格
[缺记]   commit abc1234 (revert: ...) 没有对应条目

这九类问题里,前六类是条目质量,后两类是库的结构健康度。

布局:目录名可以改,改完引用一起改

默认目录名是 坑、技术点、思想风格、资料。说一句「把坑改成 trouble」,incident-layout 会把每个项目下的 坑/ 一起改名,把这次覆盖写进 <库根>/.layout.yml,再把库里所有引用一起改掉。覆盖表放在库根而不是机器配置里,这样 git clone 到新机器名字还在。不要手改目录名,链接会一起断。

安装与启用

这个插件提供两条路:方式 A 是零安装,把 skill 目录复制到用户技能目录;方式 B 是作为插件包注入 profile,适合分发。

方式 A:把 skill 目录复制过去(零安装)

skill 就是普通目录。先把五个目录复制到 Harness 的用户技能目录:

mkdir -p ~/.dsh/skills
cp -r skills/incident-journal skills/incident-review skills/incident-audit skills/incident-resource skills/incident-layout ~/.dsh/skills/

DSH 的 filesystem skill provider 会自动发现 ~/.dsh/skills/<name>/SKILL.md,不需要装插件、不需要重启,改 SKILL.md 会热重载。Windows 对应目录是 C:\Users\<你>\.dsh\skills\。

方式 B:作为插件包注入 profile

方式 A 是「DSH 自己去技能目录里发现」,方式 B 是「这个包自己把五个 skill 注册进 ctx.skills」,意义在于分发——换台机器装一次这个包,五个 skill 一起到位。包已发布到公共 npm,先在一个空目录里确认它装得出来:

npm i dsh-incident-journal

然后让 Agent 用 plugin_manager 的 install_bundle 把它装进当前 profile,target 传包名 dsh-incident-journal@latest。如果只接受本地目录,就先取 tarball 再传绝对路径:

npm pack dsh-incident-journal@latest

装进去的是三个文件:lib/index.js(Cordis provider,name = 'dsh-incident-journal'、inject = ['skills'],在 apply(ctx) 里调 ctx.skills.registerProvider())、dsh.plugin.json(插件清单)、cordis.patch.yml(一行 loader 配置,install_bundle 会把它并进 profile 的 composition)。

不要手改 profile 的 package.json 或 cordis.patch.yml,那两步由 install_bundle 完成,手写的每一处都要单独授权,而且很容易写漂。

代价按影响程度排序:每个 plugin_manager 动作都要一次授权;改动影响该 profile 下所有会话;启动期 profile 要重启一次才生效。只想自己用就走方式 A,这些代价一个都不会遇到。

装完要自检

仓库自带一个不需要装任何东西的自检脚本:

node scripts/verify-provider.mjs

它会走一遍 list() / get(),检查每个 SKILL.md 里用反引号引用的 references/... 路径是否真的存在,外加一条 frontmatter 是否 YAML 安全。单行 description: 里出现 ": " 会让宿主静默丢弃整个技能——README 说这个坑真踩过,所以现在有机器检查。

同名 skill 的优先级

如果你同时留着方式 A 的副本,副本会赢。skill 注册表把候选按 rank 升序排序后取同名里的第一个,数字越小优先级越高:

项目目录 100/200  <  运行时 250  <  自定义目录 300
     <  ~/.dsh/skills 400  <  ~/.agents/skills 500
     <  本包 550  <  dsh 自带 600

装完包之后 ~/.dsh/skills/incident-journal 仍然是生效的那个,包里那份被静默忽略,只在日志里留一行 skill "incident-journal" from custom ignored because a higher-priority skill already exists。这条分层是有意的:你自己的副本 > 第三方包 > dsh 自带。所以用方式 B 就不要再复制 skills/。

典型用法

建库、指定库、启动

先建目录骨架:

mkdir -p my-journal/资料 my-journal/{技术点,思想风格} my-journal/my-project/坑
my-journal/
  .layout.yml                  目录改名后的覆盖表(可选)
  资料/                        外部参考资料(面经、文档)
  技术点/  思想风格/           跨项目共享 —— 跟人走
  my-project/
    README.md                  该项目的条目清单(只存链接,不存内容)
    坑/                        只属于这个项目
  README.md   career.log   REVIEW.md

坑 跟项目走,技术点 / 思想风格 跟人走。 后两类放在一级目录,靠条目里的 projects: 标签关联项目。<项目名>/README.md 只存链接,不复制内容,所以不会出现指针和正文不一致。

跟 Agent 说「开始记录」,它会问一次库的绝对路径,写进 ~/.dsh/incident-journal.yml:

journal_root: /abs/path/to/my-journal
active_workspaces:
  - /abs/path/to/the/project/you/work/in

之后在每个工作区里说一次「开始记录」,那个工作区就一直记,直到说「停止记录」,跨会话有效。没启动的时候什么都不会记,但也不会静默——如果那一刻本来该记,Agent 会回一句「(journal 未启动,这次没记)」。

库在工作区之外时

条目先写到 <工作区>/.incident-journal-staging/,说「落盘」时一次搬完。暂存目录是库根的影子,相对路径一一对应;授权按调用计、不按文件计,所以搬 20 个和搬 1 个一样贵。

条目编号必须在文件里声明

四处索引按编号引用条目,所以 of-NNNN 不能只活在索引里。合法位置只有两个:

  • 一个文件一个条目——写在身份行上:`kind: 坑` · `of-0007` · `status: 需复习`
  • 一个文件多个条目——每个条目一个 ## of-NNNN <子标题> 小节,身份行不写编号

两处都没有、却被索引引用的,incident-audit 报 [断链]。

适用场景与注意

适合谁:在 DSH 里做工程实践、希望把判断过程沉淀下来而不是依赖聊天记录的人。条目需要能回溯到证据,所以接受「每条都要写反事实」这个约束的人会更顺手。

注意事项:

  1. 插件以当前 DSH 进程权限运行。用方式 B 时,每个 plugin_manager 动作都要一次授权,改动影响该 profile 下所有会话,启动期 profile 需要重启一次才生效。
  2. 安装前应检查源码与许可证。仓库许可证是 MIT,可以在安装前自行审阅 lib/、skills/ 与 cordis.patch.yml 的实际内容。
  3. 想核对包没被篡改,可以拿 npm view dsh-incident-journal dist.shasum 打印当前版本的 tarball SHA-1,和本地 npm pack 出来的 .tgz 对一下。哈希每次发版都会变,所以这里不写死。
  4. 方式 A 与方式 B 不要同时用。如果同时留着方式 A 的副本,副本优先级更高,包里那份会被静默忽略。

结语

这个插件的价值不在于「又多了一个能记笔记的 Agent」,而在于它把「记笔记」拆成了三个可检查的动作:写的时候过反事实筛子,复习的时候按 kind 换角度提问,体检的时候把库的结构问题列成报告。三个动作缺一个,笔记还是会烂在聊天记录里。

目录页:https://www.skillhub.cn/plugins/qw2007/dsh-incident-journal

GitHub:https://github.com/qw2007/dsh-incident-journal

npm:https://www.npmjs.com/package/dsh-incident-journal

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜