local-kb
by wynet
README.md
# Local KB · 本地知识库
一个只跑在你自己电脑上的**检索型**知识库:资料留在本地、不往外发,任何支持 MCP 或 HTTP 的 AI 工具都能连同一个库。
它只做两件事——**入库**和**检索**。不跑生成模型,所以不用显卡,一台普通笔记本就能跑;答案由你正在用的那个 AI 负责组织。
> **想直接动手?** 看 **[使用文档.md](使用文档.md)** —— 从启动、灌资料、接到 AI 工具,到日常维护和排查,一步步来。这份 README 讲的是它是什么、为什么这么设计。
## 为什么这么设计
很多知识库方案会往里塞一个大模型,让它直接回答。这里没这么做,原因有三个:
- **轻**。只做向量检索,不加载生成模型,普通机器跑得动,也没有推理开销。
- **不锁死工具**。知识库是个独立的服务,谁都能连。今天用这个 AI 工具,明天换那个,资料不用跟着搬家。哪天某个工具改版、限流甚至不用了,知识还躺在你自己硬盘上。
- **数据不出域**。嵌入模型也是本地跑的,全程不调任何外部 API。
代价是:它只会"把相关的资料片段捞给你",不会替你总结。要答案,得靠调用方那个 AI 照着片段来写——这恰好也是它不容易编的原因(见下文「怎么问」)。
## 特点
- 中文语义检索,本地嵌入模型 `BAAI/bge-small-zh-v1.5`,无联网、无 API 费用
- 一个 MCP Server 同时给 WorkBuddy / Trae 等工具用,共享同一份知识
- Docker 一条命令起,浏览器打开就是管理看板
- 入库自带三道防脏数据守卫:JSON 噪声过滤、反爬验证页识别、空正文拦截
> ⚠️ 本版**未内置鉴权**,默认只适合本机自用。要放到局域网或公网,请自己在前面加一层带鉴权的反向代理(Nginx Basic Auth、Cloudflare Access 之类都行)。
## 快速开始
```bash
cd local-kb
cp .env.example .env # 默认值开箱可用,一般不用改
docker compose up -d --build
```
起来之后:
- 看板:<http://localhost:8000>
- 健康检查:<http://localhost:8000/health>
首次启动会下载嵌入模型(几十 MB,已配国内镜像 + 关闭 xet 传输);如果卡在下载或报 401,检查 `.env` 里的 `HF_ENDPOINT` 和 `HF_HUB_DISABLE_XET=1` 是否生效。
塞第一批资料进去,两种方式都行:
```bash
# 命令行
curl -X POST http://127.0.0.1:8000/ingest/text \
-H 'Content-Type: application/json' \
-d '{"text":"这里放你的笔记正文……","source":"我的第一篇笔记"}'
```
或者直接打开看板,粘贴文本 / 上传文件 / 抓网页 URL。`samples/` 下有几份示例文本,可以先拿去试手感。
检索:
```bash
curl -X POST http://127.0.0.1:8000/search \
-H 'Content-Type: application/json' \
-d '{"query":"我第一篇笔记讲了什么","top_k":3}'
```
## 让 AI 工具连上它
### MCP(推荐)
后端跑起来后,各工具都连同一个 MCP Server(`mcp_server/kb_mcp.py`),配置是同一份:
```json
{
"mcpServers": {
"local-kb": {
"command": "python",
"args": ["/绝对路径/local-kb/mcp_server/kb_mcp.py"],
"env": { "KB_API_URL": "http://127.0.0.1:8000" }
}
}
}
```
- **WorkBuddy**:填进 `~/.workbuddy/mcp.json`,保存后在连接器管理页面对 `local-kb` 点「信任」启用。
- **Trae**:设置 → MCP → 添加 Server,内容一样。
连上后 AI 手里会多五个动作:`kb_search`(检索,可按来源限定范围)、`kb_ingest_text`(写入文本)、`kb_ingest_url`(抓网页并写入)、`kb_list_documents`(列文档)、`kb_delete`(删文档)。
**入库建议直接交给 AI**:内容已在对话里(你贴的正文、它整理的复盘)走 `kb_ingest_text`,最可靠;只有一个链接就走 `kb_ingest_url`,被反爬拦下时它会说明原因,改贴正文即可。唯独"几十份文件还要逐份改造格式再入库"这类批量活,写脚本比在对话里逐份处理更合适。
### 直接 HTTP
任何能发 HTTP 请求的工具都能接,接口见下表。
## API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查 + 当前模型 |
| POST | `/ingest/text` | 写入文本 `{text, source}` |
| POST | `/ingest/file` | 上传文件(multipart) |
| POST | `/ingest/url` | 抓取网页 `{url}` |
| POST | `/search` | 检索 `{query, top_k, source?}`。`source` 为**子串**匹配(`reports/` 能命中 `reports/2026-09/monthly.md`),过滤在服务端本地完成 |
| GET | `/documents` | 列出已入库文档 |
| DELETE | `/documents/{doc_id}` | 删除某文档 |
| DELETE | `/documents?source=或contains=` | 按来源批量删除(重灌时先清后灌) |
## 三道防脏数据守卫
这三条都是踩过坑之后加的,也是这套东西最该讲的部分——**进得干净,比进得多重要**。
1. **JSON 噪声块过滤**(`app/ingest.py` 的 `is_json_noise`)
分块后逐块检查,把「AI 对话记录 / 结构化日志」那种 JSON 对象整段剔除。这类内容如果当纯文本入库,会变成满屏 `"intent"`/`"actions"` 的字段碎片,检索时把真正相关的内容全顶掉。自然语言、Markdown、代码块不会被误删(代码块里虽然有 `{`,但不是合法 JSON)。
2. **反爬 / 验证页识别**(`ingest_url` 的 `looks_blocked`)
抓网页时命中「环境异常」「请完成安全验证」「扫码登录」这类特征,直接返回 **422 拒收**,绝不把验证页当成正文存进去。微信公众号、知乎等站点常见。
3. **空 / 短正文守卫**
抓到的正文少于 20 字符(多半是抽取失败或抽空)同样拒收,不留空壳文档。
已经混进库里的历史噪声,可以这样清:
```bash
python clean_json_noise.py # 先预览,只统计不删除
python clean_json_noise.py --apply # 确认无误后实际删除
```
## 目录结构
```
local-kb/
├── app/
│ ├── config.py # 配置(环境变量覆盖)
│ ├── embedding.py # 本地中文嵌入 (fastembed)
│ ├── store.py # Chroma 向量库封装
│ ├── ingest.py # 解析 / 分块 / 入库流水线
│ └── main.py # HTTP API + 看板
├── mcp_server/
│ └── kb_mcp.py # MCP 服务(各 AI 工具统一入口)
├── static/index.html # Web 管理看板
├── samples/ # 示例文本,用来试手感
├── tests/ # 回归测试(不依赖框架,直接 python 跑)
├── 使用文档.md # 上手到维护的完整操作指南
├── clean_json_noise.py # 清理历史 JSON 噪声
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env.example # 配置模板,复制成 .env
```
## 两个容易踩的坑
**一、别在服务运行时用脚本直连数据库文件写数据。**
底层用的是嵌入式 Chroma + SQLite,同一时刻只允许一个写者。入库脚本和运行中的服务同时写同一个数据目录,会抢文件锁,导致运行中的服务返回截断的响应——现象很像"数据丢了",其实一个都没丢。正确顺序是:**先停服务 → 跑入库脚本 → 再重启服务**。或者干脆让入库走 HTTP 接口,别绕开服务去动文件。
**二、别把数据目录放在网络共享盘上。**
NAS / SMB 上的 SQLite 文件锁不可靠,多台机器挂载同一个数据目录有数据损坏风险。要多人一起用,建议换成分离式的向量库(Chroma server / Qdrant / pgvector),而不是共享文件。
## 换嵌入模型 / 调参
编辑 `.env`:
- `KB_EMBED_MODEL` —— fastembed 支持的中文模型
- `KB_CHUNK_SIZE` / `KB_CHUNK_OVERLAP` —— 分块大小与重叠
换模型后旧向量不兼容,记得清空 `data/chroma` 重新入库。
> 如果你想把已经建好的库拷给别人用:目标机器必须使用**完全相同的嵌入模型和分块参数**,否则向量空间对不上,检索会返回一堆无关内容。这是最容易被忽略的一点。
## 怎么问才准
库是检索型的,两个习惯能让效果差很多:
- 让 AI 先查再答。接进工具不等于它每次都会去用,重要的问题在开头加一句「先翻一下我的知识库再答」。
- 管住它别编。固定加一句「只根据你找到的资料回答,找不到就说没找到,别自己补」。检索型知识库只负责把料捞出来,答得准不准,取决于你有没有要求它照着料说。
## 已知限制
- 未内置鉴权,默认只适合本机
- 单写者存储,不适合多人同时写入
- 对强反爬站点,抓取基本无效,请改用「粘贴文本」手动入库
## 授权
本仓库暂未指定开源协议。若希望读者可以自由使用、修改、再分发,建议补一份 MIT;若要保留商用限制,可换成 CC BY-NC 或自定义协议。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues