Administrator
发布于 2026-09-07 / 4 阅读
0
0

基于 Python + AI 搭建本地知识库问答框架(BM25 实战)

基于 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·章节】 格式,让引用可以还原到原文位置。

四、中文分词的两个坑

  1. jieba 处理中文没问题(pip install jieba,唯一依赖),但会把英文命令和函数名切烂——netif_receive_skbESP32 这类 token 必须原样保留。解法:jieba 之外再加一遍正则 [a-zA-Z_][a-zA-Z0-9_-]{2,}|\d{2,} 提取原始英文/数字 token,两路合并。
  2. 过滤长度 <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:让模型先写一个假想答案、用答案去检索(答案与答案的词重叠大于问题与答案)。只在升级到向量检索后值得做。

七、落地流程与验证方法

  1. 盘点语料(篇数/格式/字符量),检查环境:有没有 numpy?embedding API 通不通?——这一步直接决定 BM25-only 还是上向量。
  2. 写 build_index.py,跑完抽读 2 个块 sanity check。
  3. 先用 --only-chunks 测检索:拿 3 个真实问题看命中是否精准,再组装提示词。坏的材料列表会让模型"自信地基于错误的块回答"。
  4. 接入 AI 助手:助手内部跑检索后按规则回答,用户在聊天里直接提问即可。新笔记入库后重跑 build_index(这个规模秒级完成)。

八、踩坑清单

  • 别跳过环境检查:本机无 numpy、HuggingFace 不可达,jieba 是 BM25 路线的唯一依赖。
  • jieba 首次运行会写 /tmp/jieba.cache,别把首次慢当成卡死。
  • BM25 分数是语料内相对值,阈值不可跨语料移植;先肉眼检查 top 命中再信答案。
  • 先测检索再测生成——提示词规则只在材料正确时才有效。

评论