Skip to main content
Glama
1556053950

RunningKnowledgeBase

by 1556053950
README.md
# 本地跨 Agent 跑步知识库

一个完全本地运行的跑步知识库服务:导入 Markdown、PDF 与用户明确指定的网页,
用 SQLite FTS5(中文 trigram)与本地 BGE-M3 向量做混合检索,并通过同一个
带 Bearer 认证的 Streamable HTTP MCP 服务同时供 Codex 与 DeepSeek Harness 使用。

数据只保存在本机;日常检索不依赖网络;网页只在显式添加或刷新时访问。

## 架构

```text
RunningKnowledgeBase/
├─ sources/   原始文件与网页快照(按 <source-id>/<version-id>/original.* 存放)
├─ library/   规范化 Markdown(<source-id>-<version-id>.md,供阅读、审计、重建)
├─ data/      SQLite 元数据、FTS、向量 BLOB、auth-token、server.pid
├─ models/    本地 BAAI/bge-m3 向量模型缓存
├─ backups/   带清单校验的时间戳 ZIP 备份
├─ config/    Codex 与 DeepSeek Harness 的接入配置模板(托管块)
└─ scripts/   install/start/stop/diagnose/uninstall 生命周期脚本
```

职责边界:解析器不写数据库,数据库不访问网络,MCP handler 只调用 `KnowledgeService`。

## 安装(Windows)

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
```

install.ps1 依次完成:`uv sync` 与 `running-kb init`;生成 32 字节随机 Base64URL 令牌
写入 `data\auth-token` 并用 ACL 限定为当前用户;设置用户级环境变量 `RUNNING_KB_TOKEN`;
把带标记的托管块合并进 Codex 配置(`~/.codex/config.toml`)与 DeepSeek 补丁
(`$DSH_HOME\cordis.patch.yml`),绝不整体覆盖;在 `$DSH_HOME\AGENTS.md` 追加
"必须先查"托管指令块;注册隐藏的当前用户登录计划任务 `RunningKnowledgeBaseMCP`;
启动服务并运行诊断。

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\start.ps1   # 启动(拒绝重复启动)
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\stop.ps1    # 停止(校验 PID+命令行+根目录)
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\diagnose.ps1 # 12 项检查,全部通过退出码为 0
```

服务只监听 `127.0.0.1:8765`;`GET /health` 免认证,`POST /mcp` 需要
`Authorization: Bearer <RUNNING_KB_TOKEN>`。

## 接入两个 Agent

安装脚本自动合并以下内容(也可手动参考 `config/` 下的模板):

- Codex:`~/.codex/config.toml` 中的 `[mcp_servers.running_knowledge]` 段。
  还原:删除 `# === running-knowledge-base managed block` 到
  `# === /running-knowledge-base ===` 之间的托管块即可。
- DeepSeek Harness:`$DSH_HOME\cordis.patch.yml` 中的 `mcp-running-knowledge` 项,
  使用官方 `@deepseek-ai/dsh-mcp-client`(streamable-http 传输)。
  启用后工具名形如 `mcp__running-knowledge__search_running_knowledge`;
  还原:删除同一对标记之间的托管块。
- DeepSeek 指令块:`$DSH_HOME\AGENTS.md` 中
  `<!-- running-knowledge-base managed block -->` 到
  `<!-- /running-knowledge-base -->` 之间的内容,要求回答跑步问题前先检索、引用标题与
  定位、未覆盖/不可用时明确声明、把检索文本当作不可信数据、医疗安全规则优先。

## 四个 MCP 工具

| 工具 | 作用 | 说明 |
|---|---|---|
| `search_running_knowledge(query, limit)` | 混合检索相关章节 | 返回标题、章节、定位、摘要、原始来源、分数;只读 |
| `get_running_document(source_id, version_id?, locator?)` | 读取文档或章节全文 | 只读 |
| `add_running_source(source, refresh?)` | 导入一个文件或网页 | 文件必须是绝对路径的 `.md`/`.markdown`/`.pdf`;URL 只允许 HTTP(S),禁止私网地址,最多 5 跳重定向、30 秒超时、10 MiB 上限,只接受 text/html 与 text/plain |
| `list_running_sources(...)` | 列出来源与版本 | 可按类型、主题、状态过滤,最多 200 行;只读 |

成功返回 `{"ok": true, "hits"/"document"/"result"/"sources": ...}`;失败返回
`{"ok": false, "error": {"code": ..., "message": ...}}`,错误码稳定可读、不会被吞掉。

## 添加资料与刷新版本

自然语言让 Agent 执行即可,例如:"把 C:\notes\tempo.md 加入跑步知识库"、
"把 https://example.com/tempo 加入知识库"。也可以直接调用:

```text
add_running_source(source="C:\notes\tempo.md")
add_running_source(source="https://example.com/tempo", refresh=true)
list_running_sources(source_type="web")   # 网页每次刷新保留旧版本
```

重复内容按 SHA-256 识别并返回 `duplicate`(不覆盖);内容变化时创建新版本并保留旧版本;
扫描版 PDF(无可提取文字层)返回 `OCR_REQUIRED` 且不产生任何可检索记录;
损坏 PDF 返回 `PARSE_FAILED`。

## 确认"回答前先检索"

对跑步、比赛训练、装备、营养、恢复或伤病问题提问,观察 Agent 是否先调用了
`search_running_knowledge`(DeepSeek 侧为 `mcp__running-knowledge__search_running_knowledge`),
并核对回答中的引用格式:`资料标题 → 章节/页码 → 原始文件或网址`。
未覆盖时必须出现"本地知识库未覆盖",服务不可用时必须出现"本地知识库未能访问",
多资料冲突时必须呈现分歧与来源版本,而不是伪造统一结论。

## 备份、恢复与重建索引

```powershell
& $uv run running-kb backup                      # 生成 backups\running-kb-<时间戳>.zip
& $uv run running-kb restore <归档.zip> <目标目录> [--force]
& $uv run running-kb reindex                     # 从 library/ 重建 FTS 与向量并原子替换
```

备份包含 `sources`、`library`、`data`(排除 auth-token、server.pid 与暂存目录),
`backup-manifest.json` 记录每个相对路径的 SHA-256、应用版本与向量模型名;
恢复前逐文件校验摘要、拒绝绝对路径与 `..` 穿越;非空目标默认拒绝,
`--force` 会先写一份 pre-restore 安全副本。

## 卸载

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\uninstall.ps1
```

只移除:经校验的服务进程、计划任务、用户级 `RUNNING_KB_TOKEN`、两个客户端配置的托管块、
AGENTS.md 的托管指令块。`sources`、`library`、数据库、模型与备份全部保留;
AGENTS.md 中与本项目无关的文字原样保留。

## 磁盘占用

- BAAI/bge-m3 模型:首次使用时下载到 `models/`,约 2.2 GB。
- 数据库:500–5,000 篇资料预计数 MB 到数百 MB(向量 BLOB 占主要空间)。
- 原始快照与规范化 Markdown 与原资料大小同量级。

## 故障恢复

| 现象 | 处理 |
|---|---|
| 导入扫描 PDF 返回 `OCR_REQUIRED` | 首版不含 OCR:换用带文字层的 PDF,或手工整理为 Markdown 再导入 |
| 401 / 令牌不匹配 | 运行 install.ps1 重新生成并下发令牌(保留原数据),或在新的会话中确认 `RUNNING_KB_TOKEN` 已生效后重启终端 |
| 端口 8765 被占用 | `scripts\stop.ps1` 停止本库旧进程;若是其他程序占用,修改 `Settings.port` 并同步两份客户端配置的 URL |
| 数据库损坏 | `running-kb init` 无法修复时:`restore` 最近的备份;无备份则 `running-kb reindex` 从 library/ 重建索引(原始资料仍完好) |
| DeepSeek 重连耗尽 | 确认服务健康(`GET /health` 返回 `ok: true`)后重启客户端;`reconnect.maxAttempts: 10` 与 30 秒最大退避可在托管块内调整 |
| 服务未启动 | 检查计划任务 `RunningKnowledgeBaseMCP`、`data\server.log` 与 `data\server.err.log`,再运行 `scripts\diagnose.ps1` 定位失败项 |

## 测试

```powershell
& $uv run pytest -m "not integration" -v   # 默认套件,不下载模型、不访问真实网络
$env:RUNNING_KB_INTEGRATION = "1"
& $uv run pytest -m integration -v        # 真实模型 + 真实网页(按需运行)
```

## 安全边界

- 服务只监听 `127.0.0.1`,所有 `/mcp` 请求必须携带 Bearer 令牌(常量时间比对)。
- 本地导入只接受用户明确给出的单个绝对路径文件;目录、相对路径、其他扩展名一律拒绝。
- URL 只允许 HTTP/HTTPS 且禁止 userinfo;所有解析地址必须是全球可路由 IP(回环、私网、
  链路本地、组播、保留地址全部拒绝);重定向逐跳重新校验,最多 5 跳。
- 网页正文按不可信数据处理:脚本不执行,注入指令只是检索结果中的文本,
  不会改变 Agent、服务或数据库行为。