基于 Python + AI 搭建个人学习笔记知识库(BM25 RAG 实战)
语料:36 篇 Markdown 学习笔记(约 100 万字符);环境:无 GPU、无 numpy、HuggingFace 被墙的普通云服务器。本文记录一套零向量数据库的本地知识库方案,全部组件实测通过。
一、核心结论:个人语料规模不需要向量数据库
RAG(检索增强生成)的标准教程都会让你装 embedding 模型 + 向量数据库,但在个人笔记场景下这是过度设计:
| 对比项 | BM25 关键词检索 | 向量检索 |
|---|---|---|
| 依赖 | jieba(纯 Python,~80 行 BM25 代码) | embedding 模型 + 向量库 |
| 适用语料 | 几百篇以内、术语密集(技术笔记) | 语义改述差距大、跨语言查询 |
| 国内网络可行性 | 完全离线 | HuggingFace 被墙,本地模型难下载 |
技术笔记的查询有一个天然优势:用户的提问词和笔记里的术语是同一套词("netfilter"、"veth pair"、"梯度下降"),BM25 命中率极高。实测 3 个测试问题全部精准命中正确单元。
检索决定上限,提示词决定下限——这是整个系统的设计原则:检索阶段保证材料找得对,生成阶段用提示词规则保证模型不瞎编。
二、系统架构
笔记目录/*.md → 切块(按 ## 章节,上限约1200字符,段落二次切分)
→ 挂元数据(文件名/日期/单元号/章节标题)
→ BM25 倒排索引(jieba 分词 + 英文数字原样 token)
→ index.json(1170 块仅 2.8MB)
用户提问 → BM25 Top-K 检索 → 提示词组装(回答规则 + [n] 编号材料块) → LLM 回答(带引用)
文件结构:
| 文件 | 职责 |
|---|---|
| build_index.py | 切块 + 元数据 + BM25 索引构建,笔记更新后跑一次 |
| rag_query.py | 查询入口:检索 + 提示词组装(--only-chunks 只看检索结果) |
| index.json | 索引产物 |
三、切块规则(决定检索质量的第一步)
- 按
##章节切分;超长章节在段落边界(\n\n)二次切分,上限约 1200 字符。章节标题("直觉理解"、"动手练习")本身承载语义,切块保持语义完整。 - 每个块必须带元数据(来源文件、日期、单元号、章节标题)。后面提示词里的"优先新笔记""引用标注"规则全靠它落地。
- 索引文本里预置文档标题——标题词是最强的匹配信号。
- 元数据同时写进索引文本:
【文件名·单元N·章节】格式,让引用可以还原到原文位置。
四、中文分词的两个坑
- jieba 处理中文没问题(
pip install jieba,唯一依赖),但会把英文命令和函数名切烂——netif_receive_skb、ESP32这类 token 必须原样保留。解法:jieba 之外再加一遍正则[a-zA-Z_][a-zA-Z0-9_-]{2,}|\d{2,}提取原始英文/数字 token,两路合并。 - 过滤长度 <2 的 token 和纯标点。
BM25 本体(k1=1.5, b=0.75)是标准实现:文档词频表 + 文档长度表 + IDF 字典(log(1 + (N-n+0.5)/(n+0.5)))+ 平均文档长度,查询时遍历 query token × 倒排表打分。纯 stdlib,无任何第三方检索库。
五、生成端提示词:RAG 真正干活的部分
检索结果再准,提示词不约束照样幻觉。实测验证过的模板七条规则,每条都有明确目的:
【回答规则】
1. 答案必须基于【参考材料】,禁止用你自己的知识补充事实内容 ← 知识边界锁(反幻觉)
2. 每个关键结论标注来源编号 [n] ← 引用机制
3. 材料不足时明确说"笔记中未覆盖",并指出最接近的单元 ← 拒答出口(防强行编造)
4. 多个笔记讲过同一主题时合并说明并指出侧重差异 ← 冲突处理
5. 引用命令/代码时原样保留,不要改写 ← 保真
6. 末尾附"复习建议" ← 任务附加值
7. 末尾附"延伸问题":基于笔记内容提2个该问未问的问题 ← 把问答变成学习伙伴
材料块格式 [n] 【文件名·单元N·章节】\n正文,让引用能还原到用户可打开的位置。两个细节:
- 最匹配的块放在开头或结尾(模型对上下文边缘注意力最强,中间易丢失);
- 个人语料 Top-K 控制在 3~5。
六、检索升级路径(按需启用)
- 查询改写:口语化提问改写成 2~3 个书面/术语化/多语言查询再检索(笔记词库偏技术、提问偏口语时收益最大)。
- 多轮独立化:"那内存要多少"这种追问直接检索必然落空,先结合对话历史改写成独立问题。
- HyDE:让模型先写一个假想答案、用答案去检索(答案与答案的词重叠大于问题与答案)。只在升级到向量检索后值得做。
七、落地流程与验证方法
- 盘点语料(篇数/格式/字符量),检查环境:有没有 numpy?embedding API 通不通?——这一步直接决定 BM25-only 还是上向量。
- 写 build_index.py,跑完抽读 2 个块 sanity check。
- 先用
--only-chunks测检索:拿 3 个真实问题看命中是否精准,再组装提示词。坏的材料列表会让模型"自信地基于错误的块回答"。 - 接入 AI 助手:助手内部跑检索后按规则回答,用户在聊天里直接提问即可。新笔记入库后重跑 build_index(这个规模秒级完成)。
八、踩坑清单
- 别跳过环境检查:本机无 numpy、HuggingFace 不可达,jieba 是 BM25 路线的唯一依赖。
- jieba 首次运行会写 /tmp/jieba.cache,别把首次慢当成卡死。
- BM25 分数是语料内相对值,阈值不可跨语料移植;先肉眼检查 top 命中再信答案。
- 先测检索再测生成——提示词规则只在材料正确时才有效。