dsh-company-kb
给 DeepSeek Harness 用的本地文件夹知识库插件:把公司资料目录建成一个中文可用的检索索引, 写方案 / 投标 / 核政策时按需调用;只在用户明确点名时检索,只在用户手动触发时同步。
- 索引对象:本地任意目录(在面板「设置」页填
roots),只读,插件永不写入 - 检索方式:中文分词 BM25 + 中文二字组合 + trigram 子串 → RRF 融合(全离线,零外部服务)
- 文档预览:
.docx/.xlsx/.pptx由插件自己解析成带排版的 HTML(Word 保留标题/表格/图片, Excel 是真表格网格,PPT 是分页卡片)——不启动 Office、不转 PDF、不写临时文件 - 图里的字也能搜:文档内嵌图片(架构图、界面截图)走 OCR,按 sha1 缓存,重建不重复识别
- 同步方式:纯手动(面板按钮 / 明确要求 / 命令行),没有任何定时器
- 交付形态:5 个模型工具 + Web 面板(侧边栏底部面板入口 / 会话开关 / 设置页)+
company-kb技能
安装
环境要求:DSH 0.1.5 及以上,Node.js ^22.19.0 || >=24.0.0;OCR 与旧版 Office 抽取需要 Windows(可选,缺了不影响其它格式)。
# 方式一:从 GitHub 安装(pnpm 原生支持 git 源)
dsh plugin --profile <你的 profile> add github:wu81313-lab/dsh-company-kb
# 方式二:克隆到 profile 的 plugins/ 目录后按本地目录安装
cd <DSH_HOME>/profiles/<你的 profile>
git clone https://github.com/wu81313-lab/dsh-company-kb.git plugins/dsh-company-kb
dsh plugin --profile <你的 profile> add ./plugins/dsh-company-kb
# 确认 package.json 的 dsh.profile.bundles 含本行,并校验组合树
dsh --profile <你的 profile> --dump-config | findstr company-kb
# 重启 profile(宿主侧改动需要重启),然后刷新页面(客户端 bundle 需要刷新)
安装后打开「设置 → 插件 → 本地知识库」填好资料目录,再点面板「立即同步」建库。
卸载:dsh plugin --profile <你的 profile> remove dsh-company-kb,再从 dsh.profile.bundles 移除该行;
数据目录 ~/.dsh/local-kb 可整体删除,知识库原文件夹自始至终零改动。
快速上手
- 先在「设置 → 插件 → 本地知识库」填好资料目录(
roots),然后首次建库(二选一):- 打开左栏「本地知识库」→ 点「立即同步」(首次约 1–5 分钟,含扫描件 OCR)
- 或在终端:
node lib/cli.mjs index
- 在对话里点名使用:
用知识库查一下 XX 政策的条款要求按 模板/技术方案书模板.docx 写一份 XX 项目方案- 或点输入框右侧的「知识库 已关」开关
- 资料更新后手动同步:面板「立即同步」,或说"同步一下知识库",或
node lib/cli.mjs index。
不点名、不打开开关,检索工具会直接返回一段说明而不会去查资料。
目录结构
lib/
index.js Cordis 插件入口:装配内核、注册工具、注入状态提示段、挂载 Web API
core.js 内核:设置 + 索引库 + OCR 桥 + 同步引擎的组合,供工具/面板/CLI 共用
store.js SQLite(node:sqlite) + 双 FTS5 索引 + RRF 融合检索
segment.js 中文分词、二字组合、FTS 查询构造与转义
bigram.js 中文相邻二字组合(解决分词器把"袋线瓶线"切碎的问题)
chunk.js 分块:标题面包屑、页码、重叠、超长段落切分
scan.js 同步引擎:遍历 diff → 抽取 → 写库;手动触发,无定时器
gate.js 显式调用门禁(触发词 / 路径点名 / 会话开关 / 同步意图)
prompt.js 状态提示段(未启用 / 已启用 / 尚未建库 / 索引已过期)
tools.js kb_search / kb_read / kb_list / kb_status / kb_session
web.js 宿主 HTTP API(同源,供面板调用)
ocr.js OCR / Word 助手桥(临时文件传参,不用管道)
ocr-helper.ps1 Windows PowerShell 5.1 + WinRT:PDF 渲染、图片 OCR、Word COM
open.js 用系统默认程序打开 / 在资源管理器中定位(explorer /select + 前台提权)
focus-helper.ps1 把资源管理器窗口提到前台(后台进程默认不许抢前台,靠 AttachThreadInput 提权)
settings.js 运行时设置(settings.json,热生效)
client.js 客户端 bundle(手写 __ModuleLoader__ + React.createElement)
render/ 文档预览:docx → 排版化 HTML、xlsx → 表格网格、pptx → 分页卡片(零依赖)
cli.mjs 命令行:status / probe / index / search / read / list
selftest.mjs 验收自测:抽取覆盖率 + 15 条金标查询
extract/ zip / docx / xlsx / pptx / text / media(零第三方依赖)
test/ node --test 单测(门禁、分词、分块、索引、HTTP、手动同步语义、面板渲染、文档预览)
skills/company-kb/ 技能:检索流程与引用规则
数据目录(可整体删除):%USERPROFILE%\.dsh\local-kb\ → index.sqlite、settings.json
工具
| 工具 | 作用 |
|---|---|
kb_search |
混合检索,返回文件相对路径、标题层级、页码、docId 与高亮片段;可按 dir / ext / days 限定范围 |
kb_read |
读某个文件的抽取正文(分页);二进制文件只返回完整路径。传 open:true 则用本机默认程序打开原文件 |
kb_list |
浏览目录(名称/大小/字数/状态),不确定资料在哪时用 |
kb_status |
状态、失败清单、体检结果、面板接口状态;action=now/rebuild 手动同步,progress 看进度 |
kb_session |
本会话开关(on/off/status),只有在用户明确要求时才该调用 |
Web 面板
挂在 DSH 自己的 Web 服务下(同源路由 /company-kb-api/*,仅回环可访问,不开新端口):
- 侧边栏底部「本地知识库」(在「设置」按钮上方,独占一行):索引概况(文件数/字符/检索片段/上次同步/失败/待同步变化)、立即同步、重建索引、中止、进度条、搜索(含原文预览与失败清单)、同步记录
- 搜索框下面有筛选行:类型(Word / Excel / PPT / PDF / 图片 / 文本)、时间(近 30/90 天/一年)、目录, 比如"只看近 90 天的投标文件"。
- 「未抽取正文的文件」清单里每项都能单独重抽,也可以一次「全部重抽」—— 只重抽这几个文件,不是整库重建。
- 面板分「搜索」「同步记录」两个页签;
/company-kb-api/tree与kb_list工具仍可用于浏览目录,只是不再占用面板页签。 - 该入口挂在
sidebar.footer.action,与「设置」按钮同一列:插件把该列改成flex-direction:column,因此三个入口各占一行、不会重叠。
- 原文预览(点搜索结果或「同步记录」里的文件名即打开):
- 「用本机程序打开」= 交给系统默认程序,等价于双击:
.docx/.doc→ WPS/Word,.xlsx→ WPS/Excel,.pptx→ PowerPoint,.pdf→ PDF 阅读器;.dwg等图纸同理。打开后你可以直接在里面查看甚至编辑(原文件本身仍是你的,插件不参与写入)。 - 「在文件夹中显示」= 在资源管理器中定位该文件,并把资源管理器窗口提到前台(
focus-helper.ps1)。 - 图片与 PDF 在面板内直接渲染(
/raw直出原文件)。 .docx/.xlsx/.pptx走排版预览(/preview):插件解析文件结构渲染成 HTML —— Word 保留标题层级、加粗斜体、列表、表格、内嵌图片;Excel 渲染成真正的表格网格(行号列标、 合并单元格、多工作表、日期/百分比/千分位格式);PPT 渲染成分页卡片。可随时切到「抽取正文」看纯文本。 这一路不启动 Office、不转 PDF:79MB 的投标 docx 渲染 3935 段 + 38 表 + 287 图只需 210ms。 旧版.doc/.xls/.ppt是二进制格式,仍只显示抽取的文字。- 从检索结果点开的文档会自动滚到第一处命中并高亮,上方有「命中 N 处 / 上一处 / 下一处」; 高亮做在服务端渲染阶段(只作用于已转义的纯文本),不会碰坏文档结构。
- 预览区可自由调整:拖中间的竖条改左右宽度(26%–80%)、拖预览右下沿改高度(320–1200px), 也可以直接点「紧凑 / 标准 / 加高 / 满屏」;满屏会隐藏结果列表、预览占满窗口(Esc 退出)。 双击手柄或按 Home 恢复默认;手柄支持方向键微调。选择记在浏览器 localStorage,刷新后仍在。
- 预览右上角还有「新标签页」:把渲染好的整页丢给浏览器,用整屏看长文档。
- 「用本机程序打开」= 交给系统默认程序,等价于双击:
- 输入框右侧「知识库」开关:切换本会话是否允许检索(等价于点名)
- 设置 → 插件 → 本地知识库:根目录、同步方式、显式调用、路径点名、trigram、旧版 Office 处理、触发词(全部热生效,不用重启)
面板不可用时(例如客户端 bundle 没加载)工具与索引照常工作。
配置
行配置在 cordis.patch.yml(启动默认值),运行时覆盖在 ~/.dsh/local-kb/settings.json(面板写入,热生效)。
| 键 | 默认 | 说明 |
|---|---|---|
roots |
[] |
索引根目录,数组,可多个;为空时同步会提示"尚未配置索引根目录" |
autoSync |
'off' |
'off' 纯手动;'watch' 文件变化即增量同步(事件驱动,仍无定时器) |
stalenessHint |
true |
打开面板/查状态时做一次 stat 体检,只报"有多少文件变了",不动库 |
explicitOnly |
true |
未点名不许检索 |
pathTriggers |
true |
消息里出现库内文件名/目录名也算点名 |
triggers |
知识库 / 资料库 / 公司资料 / 公司知识库 / /kb … |
点名触发词,可改 |
include / excludeDirs / excludeGlobs |
常见文本与 Office 格式 | 决定抽取范围(不匹配的文件只登记元数据) |
maxFileBytes |
256MB | 本地解析保险丝;PDF/图片走 OCR 不受它限制 |
legacyDoc |
'word-com' |
旧版 .doc/.xls 用本机 Word 取正文;设 'skip' 则只按文件名检索 |
trigram |
true |
子串索引(更全,占用更大) |
chunkChars / chunkOverlap |
900 / 150 | 分块大小与重叠 |
topK / maxChunksPerDoc |
8 / 3 | 返回条数与单文件最大块数 |
ocrWidth / maxOcrPagesPerFile |
1600 / 60 | OCR 渲染宽度与单文件页数上限 |
ocrEmbeddedImages |
true |
识别文档内嵌图片里的文字(架构图/截图也能搜到) |
embeddedImageMinBytes |
20KB | 小于这个体积的图按 logo/图标跳过 |
maxEmbeddedImagesPerFile |
40 | 单文件最多识别多少张内嵌图 |
exposeWeb / webPath |
true / /company-kb-api |
面板 API 开关与路径 |
手动同步模型(本插件与"自动同步"的边界)
- 索引只在三种显式触发下变化:面板按钮 / 用户明确要求(
kb_status action=now,同样过门禁)/ CLI。 - 启动不扫描:只打开数据库、校验 schema,并做一次"体检"(stat 比对,只报告差异)。
- 检索结果与状态卡会提示"索引之后有 N 个文件变化",提醒你手动同步,但绝不偷偷重建。
- 每次同步按文件单事务提交:中止、断电、DSH 重启都不会留下半写的块,下次同步自然续做。
- 同步摘要(新增/更新/删除/跳过/失败/耗时)写入
sync_log,面板「同步记录」可查; 每条记录下面还会逐文件列出这次到底同步了哪些内容(新增 / 更新 / 删除 / 失败四组, 带相对路径;新增与更新的文件可一键在资源管理器中定位)。老记录没有明细,就只显示摘要。
「立即同步」与「重建索引」的区别
| 立即同步(平时用这个) | 重建索引 | |
|---|---|---|
| 做什么 | 遍历目录,按 大小 + 修改时间 找出变化,只抽取这些文件 |
先清空索引,再把所有文件重新抽取一遍 |
| 耗时 | 通常几十毫秒~几秒 | 全量重抽,含扫描件 OCR 与旧版 Office:本机 102 个文件约 3.5 分钟 |
| 适合 | 日常资料更新后 | 改过影响索引本身的设置(trigram 开关、分块大小/重叠、OCR 页数上限、旧版 Office 抽取方式);调整过 roots/排除规则后想让历史文件按新规则重来;升级插件后想把新的解析规则应用到所有文件;怀疑索引与磁盘不一致 |
| 副作用 | 无 | 重建期间检索结果会变少甚至为空(索引被清空后逐步补回),所以别在正查资料时点;可随时「中止」,下次同步续做 |
| 相同点 | 都只动插件自己的索引库(docs / chunks / 两张 FTS 表),从不碰知识库里的原文件;同步记录、会话开关、设置都不会被清掉 |
一句话:改了资料 → 立即同步;改了设置或想让老文件彻底重来 → 重建索引。
内嵌图片 OCR(图里的字)
- 有正文的 Office 文档也会抽内嵌图片做 OCR:架构图标签、界面截图、表格截图里的字本来完全搜不到。
- 三个刹车:小于
embeddedImageMinBytes(默认 20KB)的图当 logo 跳过;单文件最多maxEmbeddedImagesPerFile(默认 40)张;结果按图片内容 sha1 缓存,重建索引时同一张图不再重复识别。 - 第一次开启会慢一些(本机语料 34 个文件 / 668 张图,约 6 分钟),之后重建只需要几秒。
- 新开的开关不需要整库重建:插件给每个文档记了
media_ocr标记,下次普通同步会把还没做过的 Office 文件逐个补做一次。 - 识别出来的文字会带
【图片N】前缀单独成块,引用时能看出"这段来自图片"。
只重抽某几个文件
同步记录里失败的文件(needs_ocr / needs_conversion / 抽取报错)可以在面板上单独重抽,
不必整库重建;命令行等价动作是 POST /company-kb-api/resync {"rels":["某个文件.docx"]}。
日志里这次同步会记成 同步(retry),明细照常列出改了哪些文件。
为什么这样检索
| 技术选择 | 原因(均为实测) |
|---|---|
| 预分词后写入 FTS | SQLite unicode61 不切中文:原文直索 MATCH '追溯' 命中 0 条,分词后可命中并 BM25 排序 |
| 加中文二字组合 | Intl.Segmenter 把"袋线瓶线"切成 袋|线|瓶|线,单字被丢弃后检索失败;补二字组合后 2 字查询走正常词元 |
| trigram 子串路 | 覆盖分词切不准的场合;2 字查询 trigram MATCH 无效,因此 1 字查询才退化为 LIKE |
| RRF 融合 | BM25 是负分、子串路是名次,量纲不同不能直接相加;RRF 只看名次 |
| 每文件最多 3 块 | 避免一份文件刷满结果,保证结果覆盖多个来源 |
| 全离线 | 公司资料不出本机:不调用任何外部 API、不装服务、不开端口 |
抽取能力
| 格式 | 方式 |
|---|---|
.docx .xlsx .pptx |
自解析 ZIP + XML(表格按行、表名/幻灯片作标题),零第三方依赖 |
.md .txt .csv .json .log … |
直读,带 UTF-8/UTF-16/GB18030/Big5 编码探测 |
.pdf |
Windows.Data.Pdf 渲染到内存 → Windows 内置中文 OCR(zh-cn),逐页标记 |
.png .jpg … |
直接 Windows OCR |
| 纯图片 docx | 正文为空时抽出内嵌图片单独 OCR(架构图、截图型文档) |
.doc .xls |
本机 Word COM(晚绑定、禁用宏、只读、一份文件一批 + 超时隔离) |
.dwg .apk .zip … |
只登记元数据,结果里给出完整路径供其它工具打开 |
OCR / Word 都不可用时不会报错中断:相应文件标为 needs_ocr / needs_conversion 并在状态里列出。
故障排查
| 现象 | 处理 |
|---|---|
| 面板显示"面板连接失败" | 先跑 kb_status 看"面板接口"一行:已注册 = 接口正常(多半是没刷新页面);未注册:运行时没有 webServer 服务 = 宿主启动顺序问题(本插件会轮询等服务出现,最多 30 秒,若仍失败说明该 profile 没有 webServer 行);注册失败: xxx = 按错误信息定位 |
| 面板接口路径被占用 | 改 webPath(默认 /company-kb-api)后重启;重复注册会由 webServer 抛 duplicate 错误并记录在本插件的状态里 |
| 检索说"尚未建库" | 还没手动同步过:点面板「立即同步」或 node lib/cli.mjs index |
| 搜不到刚放进去的资料 | 这是手动同步模型:先同步(结果里会提示"索引之后有 N 个文件变化") |
| 某个文件没有正文 | kb_status 看失败清单:needs_ocr(扫描件/纯图片)、needs_conversion(旧版 Office)、metadata(二进制或超限) |
| 点「在文件夹中显示 / 用本机程序打开」没反应 | 四个已知坑:Node 给 /select,路径 自动加引号后 explorer 解析不了、detached 进程不弹窗、windowsHide 会把 explorer 自己创建的窗口藏起来、窗口开在浏览器后面(Windows 不允许后台进程抢前台,看起来就像没反应)。前三个已按"手动拼 /select,"完整路径" + windowsVerbatimArguments + 不 detach + 不隐藏"修掉;第四个由 focus-helper.ps1 在定位后把窗口提到前台(AttachThreadInput 提权)。可用 node test/reveal-probe.mjs "<文件路径>" reveal 复现,再用 PowerShell 对比前台窗口标题验证 |
| 排查时想确认窗口到底有没有弹出 | 用 PowerShell 枚举可见窗口对比前后即可;不要用 $js | node 把含中文路径的脚本从 stdin 喂进去——管道按本地代码页转码,中文路径会变乱码,容易误判"无效" |
| Word 提取偶发卡死 | 已按"一份文件一批 + 75s 超时"隔离;仍频繁出现可设 legacyDoc: 'skip',或在设置页关掉 |
| 排版预览和原件长得不一样 | 这是阅读还原而不是像素级排版:字体度量、分页、文本框/SmartArt/公式/图表不还原(内容不会丢,降级成普通文字)。要看原样请点「用本机程序打开」 |
| 预览里图片不显示 | 预览图片走 /media 从压缩包里直接读(不落盘)。若某张图是"链接式"引用(r:link 而非 r:embed)则取不到——这属于原文件本身引用了外部图片 |
| 预览渲染很慢或空白 | .docx/.xlsx/.pptx 才走排版预览;旧版 .doc/.xls/.ppt 是二进制格式,只看得到抽取的文字。可用 node test/render-probe.mjs "<文件路径>" 看渲染统计与告警 |
| 索引体积过大 | 设置页关掉 trigram 后重建;或删除 ~/.dsh/local-kb 重新建库 |
| 预览窗口太小 / 想记不住尺寸 | 拖竖条改宽度、拖预览右下沿改高度,或点「紧凑/标准/加高/满屏」;选择存在浏览器 localStorage(键 dslkb.layout.v1),清掉浏览器数据会回到默认。窄窗口(≤1100px)会自动改为上下堆叠,此时竖向拖拽不可用,高度仍可调 |
| 内嵌图片 OCR 太慢 | 设置页把「单文件最多识别」调小、或把「KB 以下跳过」调大;也可以直接关掉开关。缓存按图片 sha1 命中,重建不会重复识别 |
| 搜不到图里的字 | 确认「识别文档内嵌图片里的文字」是开着的,然后跑一次同步(插件会自动补做没做过的 Office 文件) |
| 从检索结果打开文档没滚到命中处 | 只有 .docx/.xlsx/.pptx 的排版预览支持高亮;旧版 .doc/.xls 只有纯文本,图片文字(【图片N】)也不在正文流里 |
验收
node --test test/*.test.mjs # 77 项单测:门禁/分词/分块/索引/HTTP/面板渲染/文档预览/内嵌图片 OCR/重抽/预览布局/回环校验
node lib/selftest.mjs --rebuild # 全量重建 + 抽取覆盖率 + 15 条金标查询
node lib/cli.mjs status # 状态与失败清单
node lib/cli.mjs search "关键词"
单测里 手动同步语义 一项是这套设计的核心保证:新增文件后直接检索必须搜不到,手动同步后才搜得到,删除后同步即消失。
安全边界
- 插件对知识库目录只读:只调用读操作,从不写入、移动或删除其中任何文件。
- /preview 只读解析已在索引中、且位于配置根目录之内的文件,渲染结果放进 iframe(沙箱 + CSP:禁止脚本、禁止外部请求),文档里的文本一律转义。
- 唯一的写入目标是
~/.dsh/local-kb/(索引库与设置)与系统临时目录(OCR 中间文件,用完即删)。 - Web API 挂在 DSH 自身站点下(默认
127.0.0.1:8080),能力仅"读索引 + 触发同步 + 读写插件设置- 打开/直出已在索引中且位于配置根目录之内的文件";接口只接受回环 Host(挡 DNS rebinding)。 若把 DSH 暴露到公网,该接口与 DSH 本身同权,请用反向代理加登录保护。
/open、/reveal、/raw都先经resolveOriginal()校验:文件必须在索引里、路径必须落在roots之内、且磁盘上确实存在,否则一律 403/404;打开动作是"只读打开"(把文件交给关联程序), 插件不写入原文件。- Word 提取以
AutomationSecurity = 3(强制禁用宏)只读打开文档。
License
MIT © wu81313-lab
No comments yet. Be the first to write one.