dsh-alert-inject
在模型正式回答之前,先把"你可能已经忘了/看漏了"的东西重新摆到它眼前。
一个 DeepSeek Harness(DSH)插件。零第三方依赖、零常驻服务、不调 LLM。
📖 想先了解它为什么存在、解决了什么问题? → 读这篇
(那篇是"为什么你会想要它";本文件是"怎么让它跑起来"——两者目标不同)
⚠️ 它只在你【给它参照物】时才真正有用。 装完不配任何东西,它只会安静地说"无异常"。
→ 想 5 分钟看到效果,直接跳到 三、五分钟看到效果。
一、环境要求(务必先看)
必需
| 项 | 要求 | 说明 |
|---|
| 运行环境 | DeepSeek Harness(DSH) | ⚠️ 不是通用插件:它注册工具用的是 @deepseek-ai/dsh-tools,读的是 DSH 的会话日志格式(~/.dsh/sessions/*/session.jsonl.zstd)。Claude Code / Cursor / 通用 LLM 直接用不了。 |
| Node | ≥ 18(用到了 node:zlib 的 zstd) | 实测于 Node 24 |
| 第三方依赖 | 无 | package.json 的 dependencies 为空 |
可选:Ollama + bge-m3(只影响 3 项功能,不装照样能用)
⚠️ 读到这一节请先记住一句话:
不装 Ollama,它不会报错,也不会"坏掉" ——
只是有 3 项功能不工作,而且它会在输出里告诉你"哪项没跑、为什么"。
这不是故障,是设计如此。
装了会多出哪 3 项
| 功能 | 效果 |
|---|
| 语义检索 | 「这个之前好像发生过」—— 找到与当前内容语义相近的较早位置 |
| 知晓感(FOK) | 「我好像知道这个」 |
| 新颖性 | 「这是新东西」 |
不装会怎样(具体到你看到什么)
| |
|---|
alert_check 会不会失败? | ❌ 不会。它照常返回。 |
| 你会看到什么? | 输出里会有一行:
(另有 1 项未运行:语义检索: 无索引缓存(未建索引,跳过 → 可用 librarian.js CLI 建)) |
| 其余功能? | ✅ 全部照常(判据扫描、重复检测、引用检查、锚点、语境分块、知识层遍历、知识抽取都是纯程序) |
怎么装(两步)
# ① 安装 Ollama(Windows:官方安装包,装完会自动在 11434 端口起服务)
# https://ollama.com/download
# ② 拉模型(约 1.1 GB)
ollama pull bge-m3
# ③ 验证(应当返回 JSON,含 "models")
curl http://localhost:11434/api/tags
⚠️ 装完之后还需要【建一次索引】(否则那 3 项仍不可用,因为库里是空的):
# 建索引(约 15 秒 / 120 条;全量约 8 分钟 —— 这是唯一的重活)
node lib/librarian.js ~/.dsh/sessions/<工作区>/<会话>/session.jsonl.zstd
# 之后直接查
node lib/librarian.js <会话文件> "查询文本"
⚠️ 索引有上限(LIBRARIAN_MAX,默认 120 条) —— 覆盖不足时它会主动告警,
例如:(⚠️ 本次检索只覆盖了会话的 22%——更早的内容未参与检索)。
宁可暴露局限,不给虚假的完整感。
性能实测(供你预估)
| 操作 | 实测 |
|---|
一次 alert_check(不装 Ollama) | 约 0.3~0.9 秒 |
一次 alert_check(装了 Ollama,含 embedding) | 再 +0.1~1.2 秒(取决于文本长度) |
| 首次建索引 | ⚠️ 120 条约 15 秒;全量(约 1100 条)约 8 分钟(约 450ms/条,Ollama 串行) |
| 索引之后每次检索 | 约 0.1 秒(纯 CPU 余弦比对只要 3.4ms) |
⚠️ 首次建索引是唯一的"重活" —— 见第四节。
二、它是什么
三类声音:
| 声音 | 例子 |
|---|
| 「你本来在干什么」 | 目标、子目标、以及"这个目标被动过" |
| 「这个之前好像发生过」 | 与当前内容语义相近的较早位置(只给位置,不给原文) |
| 「这不对吧」 | 引用了不存在的文件/正在自我重复/当前语境内有可疑痕迹 |
它不是什么(这三条同样重要):
- 不是记忆数据库 —— 那是 OpenViking/Mem0 那一类的定位(存得下、取得到)。它的定位是"没人问的时候也要响"。
- 不是拦截器 —— 它只提醒,不阻断。
- 不做语义判断 —— "这两句是否矛盾"这类判断,本项目两次实测失败(NLI 三版全败;向量比对方向差 −2.8pp 且方向相反)。别让它做它做不到的事。
与既有方案的差别(作者实测过 OpenViking 后总结)
| 记忆库(OpenViking 等) | 本插件 |
|---|
| 形态 | 工具/数据库(实测 15 个 MCP 工具占约 1.7k token 常驻) | 2 个工具,静态指令落缓存前缀 |
| 触发 | 模型主动调("加载按需来") | 回答前自动被提醒去调 |
| 缓存 | 自动召回会打断前缀缓存 | 不打断(静态文本) |
| 目标 | 存得下、取得到 | 没人问的时候也要响 |
三、五分钟看到效果(最省事的验证路径)
⚠️ 先读这四行,否则你会以为它坏了:
| 你可能以为 | 实际情况 |
|---|
| "装完没反应,是不是坏了?" | 不是。它需要【参照物】才有的可比。 装完不配任何东西 ⇒ 它只会安静地说"无异常" |
| "输出里说『1 项未运行』,是 bug 吗?" | 不是。那是没装 Ollama。 见第一节——它只影响 3 项功能,且会明确告诉你 |
| "大部分检查没在跑?" | 只少了 3 项(语义检索/知晓感/新颖性)。其余 7 类全是纯程序,装了就能用 |
| "它什么都没查到?" | 很可能确实没东西可查(你还没给判据表)。这是正确行为,不是失败 |
最短路径:
① 装
# 在 DSH profile 的 node_modules 下放好本包,然后在 profile 的 cordis.patch.yml 里加:
- insert:
- id: dsh-alert-inject
name: dsh-alert-inject
⚠️ 必须重启 DSH(若 dsh-super-injector 被禁用,则没有热注入通道)。
② 让它在你的项目里"有得可比"
它按约定在这些相对路径下找参照物(第一个命中的生效):
| 参照物 | 候选路径 | 缺了会怎样 |
|---|
| 判据表 | ※世界模拟/回想/.警报判据.json、回想/.警报判据.json、.dsh-alert/judges.json、alert-judges.json | 「这不对吧」的判据扫描关闭 |
| 术语表 | 同上目录下的 _术语表.md | 判据的"与门"失效(会变宽松) |
| 知识图 | 同上目录下的 _graph.json | 「相关的档」召回关闭 |
⇒ 最小可行配置: 只要放一个 alert-judges.json 到项目根,形状如下:
{
"meta": ["(自指防护词,命中则不报,防止讨论机制本身时刷屏)"],
"judges": [
{ "id": "我常犯的错", "sources": ["正文"],
"signals": ["应该已经", "都改好了", "肯定能跑"],
"anomalies": [], "requireTermHit": false, "enabled": true }
]
}
⇒ 然后让模型在其间写一句含"应该已经"的话,再调 alert_check —— 它会报出来。
③ 确认它到底有没有在工作(用诊断,别靠猜)
import { diagnose, renderDiagnose } from './lib/config.js'
console.log(renderDiagnose(diagnose({ cwd: '你的工作区路径' })))
它会逐条告诉你:哪个路径没找到、哪个工作区不匹配、缺哪一项会关掉哪个功能。
为什么强调这个:作者装过的记忆库"感觉不好、没能实现",最常见的原因就是
"不知道它到底在不在工作"。所以本插件必须能自报状态。
④ 想用语义检索(可选)
⚠️ 前提是先装 Ollama + bge-m3 —— 完整步骤见 第一节。
装了之后,还需要建一次索引(否则库里是空的,仍不可用):
node lib/librarian.js <会话文件> # 建索引(约 15 秒 / 120 条)
node lib/librarian.js <会话文件> "查询文本" # 直接查
⚠️ 索引有上限(LIBRARIAN_MAX,默认 120 条) —— 覆盖不足时会主动告警,
例如:(⚠️ 本次检索只覆盖了会话的 22%——更早的内容未参与检索)。
—— 宁可暴露局限,不给虚假的完整感。
四、怎么用(两种)
① 作为工具(推荐)
模型在正式回答前主动调用一次 alert_check,拿到一份简短报告再回答。
另有一个 knowledge_extract,用来从文档里抽知识(见第五节)。
为什么是"工具 + 静态指令"而不是"自动注入"(三代试错换来的):
| 版本 | 做法 | 结果 |
|---|
| v1 | 注入 system prompt,每轮跑 | 打断前缀缓存 + 读 reasoning 通道 ⇒ 招来瘟疫 |
| v2 | 插一条消息 | 插在数组最前 ⇒ 前缀缓存全废 |
| v3 | 工具 + 静态前置指令 | ✅ 静态文本落缓存前缀,不打断 |
② 作为库
import { buildReport } from './lib/report.js'
const r = await buildReport(cwd) // { sections, text, meta }
console.log(r.text)
| 模块 | 做什么 |
|---|
index.js | 判据扫描(alertText)/指针回取(peek)/工具注册 |
report.js | 总装:把各检测器汇总成一份报告(按预算裁剪) |
anchor.js | 锚点:目标/子目标/约束(含"目标被动过"提醒) |
blocks.js | 语境分块(按用户每次发言切)—— 降误报的关键 |
knowledge.js | 知识层:遍历设定图(cooccur 优先,向两侧递减) |
librarian.js | 会话内语义检索(bge-m3) |
repetition.js | 重复检测(纯统计,误报实测 0%) |
reference-check.js | 引用存在性(查文件系统) |
fok.js / novelty.js | 知晓感/新颖性(复用检索结果,不另建索引) |
learning.js | 学习循环(发现→求证→入待审) |
extract.js | 知识抽取(提示词 + 防幻觉校验) |
samples.js | 样本层("能长起来"的前提) |
config.js | 配置 + 诊断 |
五、从文档里长知识(knowledge_extract)
插件不调 LLM(它没有那个能力),所以分工是:
① knowledge_extract(mode="prompt", doc=路径)
→ 返回【抽取提示词 + 原文】
② 模型自己按提示词产出 JSON
③ knowledge_extract(mode="verify", json=..., source=原文)
→ 程序【逐字核对】每条的证据(evidence/quote 必须真的出现在原文里)
· 对不上 ⇒ 判定为编造,丢弃并留痕
· 对得上 ⇒ 转入【待审记】(**不直接入图**)
⇒ 关键在③:模型可以编,但编的会被子串比对拦下来。 实测混入一条违规的 gate 边,
被正确拒绝并给出理由(gate 是"读者的门",按规矩只能手写)。
实测一次:候选 5 条 → 原文可核 4 条 → 入待审 6 条(含此前累积)。
六、它报出来的东西长什么样
核心尺度:像一个声音,不像一份报告。 实测演进(这条曲线本身是设计的一部分):
| 版本 | 长度 | 读者感受 |
|---|
| 初版 | 608 字,76% 是列表 | 像仪表盘 |
| 中间版 | 207 字,但无所指 | 礼貌的空话("我查了没找到") |
| 现在 | 60 字左右 | 像提醒 |
🧩 已换过 59 段话题 —— 主线目标值得回头确认一次(当前在谈:…)
两条硬规矩:
- 没值得说的就不出声(借
dsh-plugin-save-token 的 never-worse 门控:
不值得占位的东西不发出去)。"我查了,没找到"不构成信息。
- 一旦出声,必须有所指:要么指向具体位置(
pos=…),要么指出某个模式("已换过 N 段话题")。
七、⚠️ 五条设计红线(改代码前必读)
① 输出绝不回显触发词
报一次 = 把命中词写进上下文 = 下一轮又被匹配 = 自我喂食(REPORT.md §二·v4 的病因)。
改任何对外文本后,必须跑:
node leak-check.mjs <projectRoot>
这个检查已累计抓出 5 次真实泄漏(包括写检查工具的人自己踩的)。
实测教训:用正则从源码里"猜"文本是不可靠的 —— 它会漏(曾漏掉一个多行拼接的前置指令)。
⇒ 现在的做法是直接 mock ctx 调用 apply(),抓真实文本(单一真相源)。
② 只给指针,不给内容
要原文,用 peek(pos) 按指针取。这是 déjà vu 的 decoupled familiarity:
只发"这事发生过"的信号,不把内容一起送上来。
③ 不注入到会被回传的通道
reasoning 是强制回传通道(且只在带工具调用的轮次回传)。
往里写东西 = 下一轮必然被自己看见。(读取无害;注入才是病因。)
④ 有界容器超限时【归档】,绝不【静默丢弃】
一切结果留痕 —— 不留痕的结果等于没发生。
samples.js 是轮转(对);learning.js 曾错误地 slice(-500) 静默截断(已修为归档)。
参考实现在 learning.js 的 archiveOverflow():归档失败 ⇒ 不裁剪(宁可文件变大,也不丢数据)。
⑤ 工具返回值必须是无损 JSON(挂载后才发现的那类)
实测坑(挂载后首次调用才发现):返回 { note: undefined } ⇒ DSH 拒收整个返回值
(value is not lossless JSON)。离线调用测不出来(本地不经 DSH 序列化)。
⇒ 现已统一过 toLosslessJson() 闸门,并有专门断言。
八、可调参数(都有合理默认,不改也能跑)
DSH_ALERT_MAX_LINES=8 # 判据段最多几行
DSH_ALERT_SCAN=0 # 0=全量扫痕迹
ALERT_BUDGET=2400 # 报告总字数预算
ALERT_NO_RETRIEVAL=1 # 关掉需要 embedding 的部分
ALERT_LEARNING=1 # 打开学习循环(**有副作用**:写样本+入待审)
KNOW_GATE_W=0.3 # 图遍历里 gate 边的权重折扣
KNOW_LAYER_DECAY=1.0,0.6,0.35,0.2,0.15 # 按 rank 距离的层衰减
LIBRARIAN_MAX=120 # 索引上限(⚠️ 越高越贵:embed 约 450ms/条)
LIBRARIAN_MIN_DIST=3 # 近处排除半径
SAMPLES_MAX_MB=64 # 样本文件轮转阈值
配置文件(可选):~/.dsh/alert-inject.json
{ "workspaces": [], "projectRoot": "D:\\你的项目", "dataDir": "C:\\Users\\你\\.dsh" }
workspaces 为空数组 = 对所有工作区生效(这是发布后的正确默认)。
九、自测:四层,重点在第四层
node selftest.mjs <projectRoot>
| 层 | 测什么 |
|---|
| 单元 / 集成 / 系统 | 组件对不对、拼起来对不对、整份报告能不能生成 |
| 工具契约 | 工具注册、返回值可序列化(只有挂载后才发现的那类) |
| ★ 验收 | "端上来的,是不是他点的那盘" |
第四层是重点:前三层只能证明"代码能跑",而"代码能跑"和"这东西有用"是两件事。
那个差距就是「在酒馆点炒面」:菜单写的是炒面,端上来的不是那回事。
实测:这一层抓出过三个问题 —— "608 字的报告"、"207 字的礼貌空话"、"测试自己判据写错
(608 字 vs 判据 400,竟显示通过)"。都是前三层测不出来的。
十、当前状态(诚实清单)
已经能跑:
| 项 | 状态 |
|---|
| 挂载 | ✅ 已挂(cordis.patch.yml 生效),实测两个工具都可用 |
| 两个工具 | ✅ alert_check(自查)/knowledge_extract(知识抽取) |
| 知识抽取 | ✅ 完整闭环实测通过(提示词 → 校验 → 入待审) |
| 自测 | ✅ 27/0 通过;30 处对外输出无泄漏 |
还没有的:
| 项 | 状态 |
|---|
| 样本标注 | ⚠️ 机制就绪、供给量够(605 判断 + 184 纠正),但没人标注 ⇒ 这是"能长起来"的唯一缺口 |
| Laya(特化判断引擎) | ⚠️ 已实测可行(零样本 36% → 校准后 76.6%),未接入 |
| 跨环境验证 | ⚠️ 只在开发者的机器上跑过。换项目/换机器会怎样,未经检验 |
十点五、最近新增(v0.4.0)
完整清单见 CHANGELOG.md —— ⚠️ 它把「还没做」也列出来了(只写做了的,会让人以为它完整了)。
| 新增 | 一句话 | 怎么试 |
|---|
| 影子模式 | 新判据先只记录、不提醒 —— 判据表自己记着「首轮 5 条警报全是噪声」,所以不敢直接上线 | node tools/shadow.mjs |
| 可替代层 | 嵌入/生成不绑死 Ollama:云端或本地别的推理服务,只改配置不改代码 | 见 PROVIDERS.md |
| 三个库(骨架) | knowledge 累积/cases 整理稿的语境块投影/profile 覆盖+旧版留痕 | node tools/init-db.mjs |
| 整理稿接线 | 原先那块代码没有任何调用点(孤岛);现在有 CLI,还能把语境块灌进库 | node tools/context.mjs --project <你的项目> --to-cases |
| 索引自动重建 | 过期后以前是静默失效("回忆"直接消失),现在会自动后台重建 | 索引过期时自查会明说 |
| 纯测试环境 | 不碰你的数据:隔离数据目录+假项目,跑完当场核对真实数据没被动过 | node tools/sandbox-test.mjs |
| 接线审计 | 问一句「这个导出真的有人在用吗」——本项目的孤岛就是这么抓出来的 | node tools/audit-wiring.mjs |
⚠️ 还没做的:训练那一环(用你自己的纠正,长成你自己的判据)没接 ——
缺负例、缺分类、缺训练环境。详见 CHANGELOG.md 的「还没做」一节。
十一、希望得到什么反馈
这个项目最需要的不是"点赞",而是下面这几类具体信息。
★★★ 最想要:它在你那儿「报了什么没用的话」
这是最关键的反馈。 请贴出:
alert_check 的实际输出
- 你当时在做什么
- 那句话对你有用吗(有用/没用/看不懂)
为什么最想要这个:本项目的核心矛盾是误报。
开发者测出来的结论是「全库命中 45 处、当前语境 0 处 ⇒ 100% 是跨语境误报」,
但那是单一项目的结论。你的项目里是不是这样,我不知道。
★★ 想要:你在什么场景下想到它
它现在的用法是"回答前自查"。但开发者一开始设想的是「一个声音在你思考时提醒你」。
⇒ 你实际会在什么时候希望它响? 这个答案会决定下一步往哪走。
★★ 想要:没有参照物时,它是不是等于没用
它需要判据表/术语表/知识图才能发挥作用。 装上不配,就只会说"无异常"。
⇒ 这个门槛是不是太高了? 你愿意为它写一份判据表吗?还是希望它自己从会话里长出判据?
★ 想要:性能实测
| 场景 | 你的实测 |
|---|
一次 alert_check 耗时 | ? |
| 首次建索引 | ? |
开发者的机器上是 0.6~0.9 秒/120 条约 15 秒。你的机器/你的会话规模下是多少?
★ 想要:明确的失败
崩溃、报错、卡住、装不上 —— 请带上完整报错与环境(DSH 版本、Node 版本、有没有 Ollama)。
⇒ 尤其是这一类:开发者踩过一个「离线测了 27 项全绿,一挂上就报错」的问题
(value is not lossless JSON)。那类问题只有真实运行才会暴露 —— 你的环境很可能还有别的。
不需要的反馈
- "看起来很酷" —— 那不构成信息(本项目的原话:"我查了,没找到"不构成信息)
- 功能建议(除非你已经在用,并且说得出哪个具体场景下缺它)
十二、最需要你知道的一件事
这套东西的价值不在"零件多",而在"长起来"。
- 规则冷启动(今天就能用)
- 攒样本(机制已就绪)
- 训练特化模型(Laya 已实测过这条路:36% → 76.6%)
⇒ 而当前唯一缺的动作是「标注」 —— 每一次纠正,都是一条免费的高质量标注。
也就是说:你现在就能帮到它 —— 当你纠正它的时候,那就是一条训练样本。
作者注
这个是我发现 agent 老是会忘掉上下文才做的。
我的计算机水平不高,所以是在 agent 帮助下做的 ——
它比我更了解这个项目,所以介绍让它写。
本说明书随实现更新。如果说明书与代码不符,以代码为准,并回来改本文件。