dsh-kb
本地长期记忆库 for DeepSeek Harness. 把每次会话里"想明白的事"沉淀成不可变的事实卡,让下一次会话不必从零开始。
零外部依赖:只用 node:sqlite 和 node:zlib。库是一个 SQLite 文件,随你备份、迁移、直接 sqlite3 打开看。
当前源码版本 0.9.1,包含前轮完整性修复及复审 R01–R10 的连续流程修复。229 项隔离测试通过;修复行为、schema v5 迁移和兼容性见 最新修复记录。实际宿主安装与真实库迁移尚未执行。
结论按作用域 + 完整标题和正文的 SHA-256精确去重;可变 session/compaction 原料按来源独立绑定,提炼结论与原料不共用身份。同标题不同内容保留独立卡,需要补充时用 kb_update,保留 id 和旧版本。重扫按来源基线合并更新,不恢复人工设置的失效状态,正文冲突保留人工版本并报告。kb_add 默认使用会话工作目录,通用记忆显式设 scope=global;工具检索和开场提示默认只取当前目录作用域及 global,跨范围检索用 allScopes=true。没有 cwd 的旧卡按项目标签归作用域,无法判定项目的归 global;面板和 CLI 仍可浏览全库。
Markdown 格式 2 用导出 base 检查冲突;导入成功后确认文件的新基线,支持连续编辑。未编辑的镜像往返不会修改数据库正文或版本。只改 path 会替换主归属,只改 paths 使用首项为主归属;两者同时修改且矛盾会报错。提炼逐条校验证据,详情 API、工具和面板可查看引用版本与原文;derived_from 仅代表引用,片段处理进度另存账本。阅读/引用不算采纳,质量分只使用明确反馈。开发验证运行 npm test,测试进程会自动使用临时数据目录。
fact 账本(唯一真相)
一行 = 一条不可变事实卡
│ 投影(全部可重建)
┌────────────────┼────────────────┐
▼ ▼ ▼
node 骨架树 edge 关系图 FTS 索引
多归属、摘要=缓存 1跳扩展、演化链 bigram + 覆盖率精排
为什么
长期记忆会以三种方式失败,多数方案只防第一种:
| 失败 | 表现 | 防它的机制 |
|---|---|---|
| 遗忘 | 问"上次那个怎么弄的",答不上 | 索引 + 检索 |
| 失真 | 记得,但记错了;摘要层层压缩后偏离原意 | 卡不可变 + 回指原文 + 摘要可重算 |
| 腐烂 | 旧结论还在,被当成现行方案 | status + supersedes 边 + 时间衰减 |
这套设计的权重是 失真和腐烂 > 召回率:召回差了你换个词再查一次就行,记错了你会照着错的干。
为什么不是纯树、也不是纯向量
纯树的三种死法:单父挂载在跨主题会话上必错(一个会话常常横跨四个主题);分类成本随规模线性上涨,写到第 500 条时你花在"该归哪"的时间超过知识本身;表达不了"这条已被那条推翻"。
纯向量/纯图的三种死法:返回一堆语义像但没用的碎片;没有层级 = 无法做范围限定("只看 dsh 相关的");没有锚点去重,无限膨胀,不知道哪些过期。
所以真问题不是选哪个,是 谁做真相、谁做索引。这里 fact 表是唯一真相,树和图都是它的投影,随时可重建。
安装
# 在 dsh profile 目录
cd ~/.dsh/profiles/desktop
pnpm add dsh-kb
插件自带 cordis.patch.yml,装完重启 dsh 即生效。也可以手动插入:
- insert:
- id: dsh-kb
name: dsh-kb
config:
enabled: true
dbPath: "" # 留空 → $DSH_HOME/kb/kb.db
maxResults: 6
promptSection: true
工具
| 工具 | 作用 |
|---|---|
kb_search |
检索。用户说"之前/上次/我们说过/继续"时先调 |
kb_add |
写一条卡。一个可独立陈述的结论 = 一条卡 |
kb_tree |
看骨架树:每枝多少卡、最近更新、代表卡 |
kb_stats |
健康度:卡数、类型分布、路径分布、边数 |
kb_ingest |
增量吸收本机会话(幂等,可反复跑) |
插件还会向系统提示注入一段记忆纪律,规定什么时候必须查、什么时候必须写。
界面
两套,共用同一份数据:
- 侧边栏「中枢」页 —— 一屏六块:记忆库 / 检索 / 星图 / 会话 / 工具 / 状态。回答"这台机器现在整体什么样",是入口页。
- 侧边栏「记忆」面板 —— 树 / 搜索 / 卡片画布 / 星图 / 时间线,直接在 dsh GUI 里用。
- 静态 HTML ——
npx dsh-kb export,产出单文件页面,双击就开,不依赖服务。适合快照、分享,或者面板出问题时确认数据本身是对的。
图谱是 canvas 力导向,边只画 edge 表里真实存在的包含/取代关系,不臆造关联。
CLI
插件工具给模型用,CLI 给人和脚本用。
npx dsh-kb init
npx dsh-kb ingest [--limit N] [--rescan] # 扫 ~/.dsh/sessions/*.jsonl.zstd
npx dsh-kb search "中文分词 方案" -k 6
npx dsh-kb search "zstd" --path topic/dsh --json
npx dsh-kb add --title "结论" --body "正文" --path topic/dsh --kind decision
npx dsh-kb tree topic --summary
npx dsh-kb stats
npx dsh-kb show f_0d06e6d4155a92
npx dsh-kb forget f_0d06e6d4155a92 # 标记失效,不删
npx dsh-kb reindex
环境:DSH_HOME(默认 ~/.dsh)、DSH_KB_HOME(默认 $DSH_HOME/kb)。
素材从哪来
kb_ingest 读 $DSH_HOME/sessions/<cwd-tag>/<session-id>/session.v*.jsonl.zstd。两个非显然的事实:
- 文件是多帧 zstd(一个会话几十帧)。Node 的
zstdDecompressSync一次性只解第一帧,你只会拿到一个 session header —— 这是最容易被误判成"文件损坏"的坑。本插件按 magic 扫帧边界逐帧解。 - 事件流里真正有用的是四种:
sessionheader、session/title、compaction/summary、user/message。其余(tool/call、step/start)是过程,不是知识。
抽取只做"已经写在会话里的事实"。"为什么"不从这里推 —— 那必须由模型在当场用 kb_add 写下,否则抽出来的是幻觉。这条分工是整套系统可信度的地基。
实测:219 个会话 / 225 MB 压缩 / 6.9 秒 / 676 张卡 / 零错误。
检索怎么打分
召回:FTS5(bigram) AND → 不足退 OR → 子串 LIKE 兜底 → 图 1 跳扩展
精排:0.50·词覆盖率 + 0.22·标题命中 + 0.16·路径对齐 + 0.12·新颖度
× status × confidence × kind_bonus
不吃 LLM,纯算。中文必须做 bigram 预分词:FTS5 的 trigram tokenizer 只给 ≥3 字符建索引,「记忆」「材质」这类双字词直接检索不到,而中文里双字词占绝大多数。
维护纪律
一条卡好不好,只有一个测试:下次会话的我在不知道上下文的情况下读到它,能不能直接照做? 不能就重写。
一个库好不好:问"是什么"能答(卡)、"为什么"能答(decision 卡 + 图)、"什么时候放弃的、换成什么"能答(superseded + supersedes 边)、"继续上次"能找回原话(source_ref 回指原会话)。
许可
MIT
No comments yet. Be the first to write one.