my-kb
by alexiuhubhiu
README.md
# my-kb 个人知识库
本地离线、可溯源、中文友好的 MCP 文档检索服务(个人 agent 生态知识底座)。
> **使用者请从这里开始 → [docs/USER_GUIDE.md](docs/USER_GUIDE.md)**
> 白话手册:这东西是什么、资料放哪儿、怎么入库、搜不到怎么办、怎么让 AI 带出处作答。
- **格式**:Markdown / txt / PDF(文本层)/ docx;扫描件 PDF 可选 RapidOCR 摄取通道
- **检索**:SQLite FTS5 trigram + bm25 + 学科术语扩展(弱语义);短查询/引擎不可用自动 LIKE 回退
- **溯源**:每块固化 `domain / doc_id / 文档标题 / 章节链(section_path) / 页码或偏移(page_or_offset)`
- **离线硬约束**:零网络调用、零 API key、零 embedding/向量依赖
- **接口**:MCP(FastMCP stdio,4 个 `kb_*` 工具)与 CLI(6 子命令)复用同一批 handler
## 快速开始
```bash
# 1) 安装(全新 venv;OCR 通道额外安装 [ocr] extra:rapidocr_onnxruntime,pip 装包自带模型、之后纯离线)
python -m venv .venv
.venv/Scripts/pip install -e ".[dev,ocr]"
# 2) 摄取(增量;可用 --glossary 指定术语表;默认读 <kb_root>/_glossary/<domain>.md)
my-kb ingest --domain ds --source D:\kb_root\ds --glossary D:\kb_root\_glossary\ds.md
# 2b) 扫描件(无文本层 PDF):显式 --ocr 触发 RapidOCR 摄取(溯源标注 第N页(OCR))
my-kb ingest --domain math --source D:\kb_root\math --ocr
# 3) 检索
my-kb search --domain ds "什么是平衡二叉树" -k 3
```
## CLI 命令
| 命令 | 用途 |
| --- | --- |
| `my-kb ingest --domain <d> --source <dir> [--glossary <f>] [--ocr] [--prune] [--force-ocr]` | 增量摄取:新增/变更;`--ocr` 对扫描 PDF 走 OCR;`--prune` 才允许废弃 source 内缺失文档;`--force-ocr` 强制重建已入库废块 PDF |
| `my-kb rebuild --domain <d> --source <dir> [--glossary <f>] [--ocr]` | 整 domain 权威重建 |
| `my-kb sync [--root <kb_root>] [--domain <d>] [--ocr] [--prune] [--dry-run]` | **批量增量摄取**:遍历 `<root>/<domain>` 逐域入库(跳过 `_`/`.` 前缀目录),术语表取 `<root>/_glossary/<domain>.md`;目录名与 domain 不同名时读 `<root>/_domains.md` 映射;`--dry-run` 只比对不写库 |
| `my-kb status [--domain <d>]` | 库/域统计 |
| `my-kb search --domain <d> "<query>" [-k 5] [--no-expand]` | 自测检索(与 kb_search 同引擎) |
| `my-kb terms --domain <d> [--file <f>]` | 打印/校验术语表解析 |
| `my-kb backup [--keep 10]` | 手动备份 db → `data/backup/` |
| `my-kb web [--host 127.0.0.1] [--port 8787] [--no-open]` | 启动本地 Web 控制台(见下一节) |
## Web 控制台(本地 GUI,v0.3)
不想敲命令时用这个:一个只监听 `127.0.0.1` 的本地页面,能看域健康状况、搜资料、跑同步/入库/废弃/备份,长任务日志实时滚动。
```powershell
# 方式一:双击(Windows 启动器;优先用仓库内 .venv,其次按 PATH 解析)
scripts\start_gui.bat
# 方式二:CLI 子命令(与 serve.py 共用同一份启动逻辑)
my-kb web --port 8787
# 方式三:直接起 ASGI
python serve.py --port 8787
```
- 默认地址 `http://127.0.0.1:8787/`,启动后自动开浏览器(加 `--no-open` 可关掉)。
- **只本机可访问**:Host/Origin 白名单仅 `127.0.0.1`/`localhost`;非 GET 请求要求 `Content-Type: application/json`(挡跨站表单)。用 curl 手测记得带 `-H "Content-Type: application/json"`。
- 启动器怎么挑解释器:`MY_KB_PYTHON` → 仓库内 `.venv\Scripts\python.exe` → `py -3` → `python`,每个候选都由 `scripts\check_env.py` 自检(能 `import fastapi, uvicorn` **并且**能真的构造 `FastAPI()`)。全都不合格时会把每个候选的失败原因打在屏幕上并暂停,不会一闪而过。
- 根目录清单存在 `data/gui_config.json`(首次启动自动生成,页面「设置」里改)。默认两个根:资料域 `D:\kb_root`、产出域 `D:\knowledge\subjects`;目录不存在的根会留空路径并提示去设置里配。
- 写操作全部走**单线程任务队列**(串行,避免抢 SQLite 写锁);任务日志用 SSE 推送——断线重连带 `Last-Event-ID` 续传,缓冲超过 2000 条会先提示「已截断」。
- `prune` 仍然只是**废弃**(`status=deprecated`,不删源文件);`rebuild`(整域重建)会物理清空该域索引,页面会二次确认。
- 前端是零构建原生 HTML/JS(无 CDN、无 node_modules),只有 `web/static/` 下三个文件。
- 术语表面板会诊断「只做了扩展词、没有独立词条」的词——直接查这些词是 0 结果(踩过两次的那个坑)。
## MCP 工具
| 工具 | 说明 |
| --- | --- |
| `kb_search(domain, query, top_k=5, expand=True)` | 词面检索 + 溯源片段;0 结果给 hint |
| `kb_domains()` | domain 统计(选域前探测) |
| `kb_ingest(domain, source, mode="incremental", glossary="", ocr=False)` | 摄取;mode=rebuild 重建该域;ocr=True 对扫描 PDF 走 RapidOCR |
| `kb_forget(domain, rel_path="", doc_id=0)` | 按 rel_path/doc_id 废弃文档 |
启动 MCP server(stdio):`python server.py` 或由 MCP 客户端运行器拉起。
## 知识库根目录约定(`D:\kb_root`)
资料根与代码库、学习产出目录三者分离:
```
D:\kb_root\ 资料根(教材/讲义/习题等第三方资料,不进代码库)
_glossary\<domain>.md 术语表(权威;每 domain 一个)
_raw\<domain>\... 暂不入库的原始件(如 OCR 效果差的板书),`_` 前缀 → sync 自动跳过
<domain>\... 一级子目录名即 domain(如 gaoshu / hcie)
D:\knowledge\subjects\ 产出根(笔记/思维导图/实验),与资料分离
_domains.md 目录名 → domain id 映射(中文/连字符目录名必需)
_glossary\<domain>.md 产出域术语表(与资料域同构,各自镜像)
<科目目录>\... 一级子目录名经 _domains.md 映到 domain(如 hcie-datacom → hcie_notes)
```
- **一条命令更新整个知识库**:`my-kb sync --root D:\kb_root --ocr`(逐域增量、幂等、默认不废弃)。
- 新增一门课 = 建 `D:\kb_root\<拼音小写 domain>\` + `_glossary\<domain>.md`,无需改代码。
- 摄取是**运维动作**(CLI),不暴露给 LLM;客户端只拿到只读的 `kb_ask` / `kb_coverage`。
### 资料域 / 产出域(双域分工)
同一科目的**第三方资料**与**自产产出**分成两个 domain,`kb_ask` 返回的溯源因此能区分
「教材原话」与「我自己的总结」。命名约定:**裸名 = 资料域,`_notes` 后缀 = 产出域**。
| 域 | 根目录 | 内容 | domain 示例 |
| --- | --- | --- | --- |
| 资料域 | `D:\kb_root` | 教材 / 讲义 / 习题 / 实验手册(只读) | `hcie` |
| 产出域 | `D:\knowledge\subjects` | 笔记 / 思维导图 / 实验记录(LLM 写入) | `hcie_notes` |
产出根的一级目录是人读的中文/连字符名,而 domain id 只允许 `[a-z0-9_-]`,
故用 `_domains.md` 解耦(一行 `目录名 = domain_id`;`#` 注释与空行忽略;
未映射的目录若名字本身非法则跳过该域并打印提示,不中断整批)。
```bash
my-kb sync --root D:\knowledge\subjects --domain hcie_notes
```
### Obsidian 兼容
产出域的笔记常由 Obsidian 编辑,摄取时按 md 做三项归一,避免编辑器语法污染索引:
- **YAML frontmatter 整块置空**(保留行号,溯源「行N」不错位);
- **围栏代码块内的 `#` 不当标题**(VRP / Python 配置注释极常见),围栏行本身不入正文;
- **`[[双链]]` / `![[嵌入]]` 归一化为可见文本**(`[[目标|别名]]`→别名、`[[目标#锚点]]`→目标)。
扫描时跳过路径中任一层以 `.` 或 `_` 开头的条目:
- `.` 开头是工具目录(`.git` / `.obsidian` / `.trash` / `.workbuddy`),尤其 `.trash` 装的是被删笔记;
- `_` 开头是「**存档但不索引**」约定(与根目录 `_glossary` / `_raw` 同源)。典型用法是产出域里的
`_索引/` 主题索引页——它们是 Obsidian 关系图谱的骨架,但入库只会跟正文抢检索排名。
索引页由 `scripts/gen_vault_index.py` 生成(`--dry-run` 预览、缺省刷新全部登记科目 +
`vault/_索引/知识库总览.md` 总览页)。原因:Obsidian 图谱只认 `[[双链]]`,不认目录结构,
没有枢纽页就是一地孤岛。脚本是纯增量产物,不改动正文;科目配置见其 `SUBJECTS` 登记表。
## 客户端挂载(任意 MCP 客户端)
my-kb-bridge 是标准 stdio MCP 服务,任何 MCP 客户端(Kun / Claude Code / Codex / …)都能挂,
差别只在各自的配置文件位置。最小配置(`command` 换成自己的解释器路径):
```json
{
"mcpServers": {
"my-kb-bridge": {
"type": "stdio",
"command": "<python>",
"args": ["D:/my_kb/server_bridge.py"],
"env": {
"PYTHONUTF8": "1",
"PYTHONIOENCODING": "utf-8",
"MY_KB_PERSONA_DIR": "D:\\my_agents_v7\\personas",
"MY_KB_BRIDGE_DEFAULT_PERSONA": "tutor",
"MY_KB_ROOT": "D:\\kb_root"
}
}
}
}
```
- `MY_KB_ROOT` 必须指向资料根:术语表默认路径与检索期术语扩展都靠它(不设则回退 DB 镜像,且术语表编辑不即时生效)。
- `MY_KB_PERSONA_DIR` 指向 my_agents 的人设目录,`kb_ask` 据此读 `## visible_domains` 做限域检索。
- 本机解释器为 `C:\Users\1249840596\.workbuddy\binaries\python\envs\default\Scripts\python.exe`
(editable 安装 my-kb,含 OCR 依赖);客户端挂载后**需重启客户端**才生效。
## 二期:my-kb-bridge(人设可见域编排,只读)
把检索能力按 **agent 人设可见域** 编排给上层 LLM(与 my-agents 记忆底座联动,`server_bridge.py`):
- **角色边界**:my-agents 管“记忆 + 人设即文件”;本服务只读消费 my_kb 库,按人设 `profile.md` 的
`## visible_domains` 声明限域检索;不写任何数据、不认识 agent 的记忆,纯本地离线。
- **人设声明**(在 my_agents 的 `personas/<name>/profile.md` 里加可选节):
```markdown
## visible_domains
- gaoshu
- ds
```
- **启动**(stdio,单独挂载,与 my-kb / my-agents 并列):
```bash
set MY_KB_PERSONA_DIR=D:\my_agents_v7\personas # 缺省即此值
set MY_KB_BRIDGE_DEFAULT_PERSONA=tutor # 缺省人设(可选)
python D:\my_kb\server_bridge.py
```
| 工具 | 说明 |
|---|---|
| `kb_ask(question, persona="", top_k=5, expand=True)` | 按人设可见域检索:命中返回带溯源 hits,无命中返回 `coverage=false` 提示 |
| `kb_coverage(persona="")` | 该人设可见域及各域在库统计(文档/块/词条),对话前探测用 |
- **行为约定**:domain 只能来自人设声明;未声明 = 知识库不可用(返回明确提示);绝不跨域检索。
## OCR 摄取通道(扫描件 PDF)
- **引擎**:`rapidocr_onnxruntime`(PaddleOCR 的 ONNX 移植,CPU 本地推理)。
安装即随包获得模型(“装包=首次获取”),识别全程纯离线、零 API key。
- **触发**:OCR 是显式能力。无文本层 PDF 不带 `--ocr` 仍报“OCR 不支持”;带 `--ocr` 逐页识别,
产物与文本层 PDF 同构进入同一 chunks 表,溯源标注 `第N页(OCR)`(如 `第3页(OCR)`、跨页 `第3-4页(OCR)`)。
- **质量取舍**:印刷体中文可读性高;数学公式/符号允许识别错误——溯源会指回原 PDF 页码供人工复核。
单页识别失败记日志跳过;整份全败进 ingest 的 failed 清单,不中断整批。
- **“有没有文本层”怎么判**(`core/parsers.text_layer_pages_valid`,四重门槛):
文档总字符 ≥ 40、有效正文页 ≥ 1、有效页正文合计 ≥ 30、**有效页占比 ≥ 10%**。
只管页码戳的 PDF、以及**「扫描件 + 文字封面/目录」**(323 页书只有封面目录带字,
总字符量大但占比 1.5%)都会判为无文本层 → 走 OCR;否则入库的只有目录、正文全丢。
实测真教材占比 34%~99%,门槛留了足够余量。代价:一份已按文本层入库的 PDF
不会自动改判,需 `rebuild --ocr` 重跑该域。
## 删除与安全(废弃策略)
- **默认只增改、不废弃**:incremental ingest 永不把缺失文件标为 deprecated。
- `--prune`:仅当显式开启时才废弃 **本次 source 目录内** 扫描缺失的 active 文档
(按 `source_path` 前缀判定);目录外文档绝不触碰——只 ingest 讲义子目录不会误废弃
板书/习题等其他子目录的文档。废弃前报告给出 `预检:…废弃 N 篇` 提示。
- `rebuild` 语义不变:整 domain 权威重建。
- `--force-ocr`:对已入库但内容为废块(PDF 只有页码/页眉戳被误判为文本层)的文档,
即使文件 hash 未变也强制以 OCR 通道重建(溯源标注 `第N页(OCR)`)。
## 目录约定
```
D:\kb_root\ 资料根(用户原文,不进代码库)
_glossary\ds.md 术语表(每 domain 一个)
ds\... 一级子目录即 domain
D:\my_kb\ 本仓库
data\my_kb.sqlite3 索引库(单库+domain 列过滤)
data\backup\ 迁移/手动备份
data\logs\ 日志
data\gui_config.json Web 控制台的根目录清单(首次启动生成,gitignore)
core\ 解析/切块/摄取/检索核心
web\ Web 控制台(FastAPI 应用 + 零构建前端)
serve.py Web 控制台唯一启动入口(my-kb web 转发到这里)
tests\ pytest 与回归集
```
## 术语表格式
```markdown
# ds.md
# 以 # 开头为注释;词条 = 扩展词1, 扩展词2, ...
二叉树 = 二叉树, binary tree, 二叉排序树, BST
平衡二叉树 = 平衡二叉树, AVL树, AVL tree, 高度平衡树
```
检索 query 子串命中“原词”时,自动扩展为“原词 + 扩展词”合并进 FTS MATCH(可用 `--no-expand` 关闭)。
## 数据一致性
- 文档级 SHA-256 整文件哈希判变更;增量更新单文档事务,删除 = `deprecated` 留档(保留行、不检索)
- `rebuild` 整 domain 单事务权威重建;chunks 的一切写路径经 `core/fts.py` 代码同步 FTS
- 迁移版本化(`PRAGMA user_version`),迁移前自动 backup(保留最近 10 份)
## 回归与测试
```bash
python -m pytest tests/ -q
```
回归集 `tests/regression_queries.py` 含 22 条概念题,G3 Top-1 命中率 ≥ 80%、P95 < 3s(CI 宽松上限)。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues