dsh-knowledge-sync
English | 中文
把 DeepSeek Harness 的每一轮对话固定成一份知识库文档,并让后来的对话能找到它。
一轮结束,问了什么、得出了什么结论、跑了哪些工具,就变成一份 Markdown 文件。这个过程不调用任何模型。之后,在同一目录下工作的 agent 会被告知"这里有一个知识库"——只有两句话,不是文档本身——由它自己决定要不要去看。
你会得到什么
| 一份文档一个文件 | 带 YAML front matter 的 Markdown,路径 <root>/<会话>/<id>-<标题>.md —— 可以是捕获的轮次(raw)、提炼后的发现,或 agent 主动记下的笔记 |
| 值得存才存 | 只有通过显著性过滤的轮次才会被记录 —— 「12 个通过」这种简短状态不是知识,不会落盘 |
| 写盘前脱敏 | 工具参数里的凭据(token、--password=、.env 值、PEM 私钥…)在文件写入前就被清除,并支持配置额外正则 |
| 什么都能读 | 可 grep、可编辑、可纳入版本管理 —— 文件就是唯一事实来源,没有需要同步的旁路索引 |
| 是召回,不是注入 | 系统提示里一段简短指引 + knowledge_search / knowledge_read 两个工具;文档绝不会不请自来地进入上下文 |
| 全文检索 | 内存 BM25 索引搜索文档正文(不只是标题),每条命中带匹配片段 |
| 按工作区隔离 | agent 只召回自己目录下记录的内容,别的项目的知识不会变成这里的噪音 |
| 有页面可看 | 设置里的知识库版块:筛选、打开、阅读,带类型与标签徽章 |
| 有页面可配置 | 同一版块的配置标签页可现场调整脱敏 / 捕获 / 召回,写入 settings 并立即生效 |
安装
dsh plugin --profile web add dsh-knowledge-sync文档默认写到启动目录下的 ./knowledge,也可以用 DSH_KNOWLEDGE_ROOT 指定。这个包是一个 Cordis bundle:它的 cordis.patch.yml 必须与它的构建输出保持一致——改完源码后先 pnpm run build,让 lib/ 带上 patch 引用的全部模块(capture、note、recall、http、distill、policy、redact、search),再把 dsh-knowledge-sync 加进 profile 的 dsh.profile.bundles。一条指向尚未构建的 lib/<模块>.js 的 patch 行会导致整个 profile 启动失败。 任何一行都能在 profile 自己的 cordis.patch.yml 里覆盖:
- id: knowledge
config:
root: D:/project/knowledge文档长什么样
---
id: "abc12345-t1"
title: "为什么构建挂了?"
session: "abc12345-0000-4000-8000-000000000000"
turn: 1
created: "2026-08-18T10:00:00.000Z"
outcome: "completed"
cwd: "D:/project"
tools: ["bash"]
---
# 为什么构建挂了?
## Question
为什么构建挂了?
## Answer
lockfile 过期了 —— 依赖升级之后没有重新跑 `pnpm install`。
## Tools
- `bash` — {"command":"pnpm install"}推理过程被刻意排除:文档记录的是结论,不是推导过程。没有产出答案的一轮根本不会写入 —— 一次中断的尝试记录的是"试过",不是"学到了什么"。
三类文档
kind | 来源 | 正文示例 |
|---|---|---|
raw | 通过显著性过滤的轮次 | ## Question / ## Answer / ## Tools |
note | agent 在工作中调用 knowledge_note | ## Finding / ## Evidence / ## Scope |
distilled | 被保留的轮次经一次小模型调用提炼(可选开启) | ## Summary / ## Finding / 后面附原始轮次 |
召回是怎么工作的
这个插件不会把知识库粘进对话。上一轮的内容对当前这轮通常无关,全量注入既浪费上下文窗口,又会在每写入一轮时让提示前缀失效(KV cache 全部作废)。
取而代之:在有记录的目录下工作的 agent,只会看到一小段:
> 知识库 —— 本工作区(/srv/project)有 12 份来自早前对话轮次的文档。它们没有包含在本次对话中。 > > 当当前任务像是这里已经做过的工作时——反复出现的构建失败、已经做过的决定、已经弄清用途的文件——调用 knowledge_search。检索会读取文档正文,所以结论里的措辞就能带回那一轮。然后对值得细看的用 knowledge_read 读全文。宁可先查一下也不要重复劳动;当文档与你自己的判断冲突时,以你的判断为准。
knowledge_search 通过内存 BM25 索引检索文档正文(不只是标题和工具名),返回标题、类型、标签和一段匹配片段。note 与 distilled 发现排在 raw 转录之前——因为主动记下的发现比恰好被记录的轮次更有价值。它还支持按 kind、tag、tool 过滤。knowledge_read 按 id 返回单份全文,且只对同一规则下可见的文档生效。
没有任何记录的工作区,这一段完全不出现。 指向空货架的指引,只会教会模型不再相信这个指引。
配置
| 配置行 | 字段 | 含义 |
|---|---|---|
knowledge | root | 文档写到哪里 |
redact.enabled | 凭据写盘前是否清除(默认开) | |
redact.mask | 脱敏后的替换值 | |
redact.patterns | 额外的脱敏正则 | |
knowledge-capture | enabled | 是否在每轮结束时固定 |
salience.minScore | 一轮如何挣得记录资格;0 表示保留一切有答案的轮次 | |
distill.enabled | 用一次小模型调用提炼被保留的轮次(默认关) | |
distill.provider / distill.model | 开启蒸馏时要调用的路由 | |
knowledge-note | enabled | 是否注册 knowledge_note 工具 |
knowledge-recall | announce | 是否告诉模型知识库存在 |
sameWorkspaceOnly | 召回是否限定在会话自己的目录 | |
searchLimit | 单次搜索返回条数 | |
knowledge-http | path | 页面从哪里读 |
各行彼此独立:没有 web server 的组合里可以去掉 knowledge-http,只记录不召回可以去掉 knowledge-recall,其余照常工作。
开发
仓库自包含 —— 针对已发布的 @deepseek-ai/* 包开发,和 dsh 安装实际带的是同一批版本:
pnpm install
pnpm run typecheck
pnpm run test
pnpm run builddevDependencies 必须精确钉版,不要用范围:registry 上若干 harness 包的 latest 标签仍指向远比 CLI 实际安装的更旧的版本,用范围会解析到那套陈旧版本。
测试跑的是真东西 —— 真实 agent loop 加脚本化模型 —— 因为会话日志的结构恰恰是这个插件唯一不能靠猜的东西。
已知限制
- 词法检索,不是语义检索。 内存 BM25 索引只做词的匹配(含 CJK 二元切分),无法命中从未见过的措辞。在当前数据规模下够用;dsh 的 LLM 接缝暂不支持 embedding。
- 召回按目录精确匹配。 已记录工作区的子目录里的会话看不到它。
- 暂不做关系追踪。 两份文档仍可能互相矛盾地躺着;计划第三期的
supersedes/时效信号尚未实现。 - 蒸馏是可选开启的。 默认关;开启后每个被保留的轮次多一次小模型调用。
- 页面上的文档只读。 知识库由产生它的对话写入;一个能改写历史的页面,会让文档的份量低于它所固定的那一轮。配置可编辑,文档不可编辑。
许可
MIT