1. 为什么我要自己搭一个知识库
1.1 从“收藏夹吃灰”说起
我电脑里有个文件夹叫“待读”,里面躺着大概四百多个网页存档、PDF 和截图。每次想找一份半年前看过的技术方案,都得靠grep加肉眼扫描,效率低到令人发指。后来我试过用在线笔记软件,但很快发现两个致命问题:一是数据不在自己手里,二是搜索全靠关键词匹配,问它“上次那个关于向量检索的方案是怎么写的”,它只会返回一堆包含“向量”两个字的无关文档。
这就是我决定自己搭一个本地知识库的直接原因。我要的东西很明确:数据完全本地存储、支持自然语言提问、能理解语义而不是死磕关键词、最好还能免费。折腾了一圈之后,我最终落地的方案是MoreLogic RAG 个人免费版,底层用Ollama跑本地大模型,向量检索用FAISS,整个流程用Python串起来。
这套方案能做什么?简单说,你把 PDF、Markdown、TXT 甚至网页剪藏丢进去,它自动切分、向量化、建索引。之后你用大白话提问,它先从你的文档里检索出最相关的片段,再交给本地大模型生成回答。整个过程不联网、不花钱、不上传任何数据。适合谁?适合像我这样对数据隐私有洁癖、又不想每个月交订阅费的技术爱好者,也适合想入门 RAG 但不知道从哪下手的 Python 初学者。
1.2 为什么是 MoreLogic RAG 而不是别的
市面上做本地知识库的方案不少,我选 MoreLogic RAG 个人免费版有几个很实际的理由。第一,它对个人用户完全免费,没有文档数量限制,也没有“免费版只能存 100 个文件”这种恶心人的设定。第二,它的架构足够透明,底层就是 Python 脚本加 FAISS 索引,出了问题我能自己排查,不像某些封装得严严实实的商业软件,报错了只能干瞪眼。第三,它和 Ollama 的集成非常顺滑,基本上装完 Ollama 拉个模型就能跑,不需要额外配置什么 API Key。
当然,它也不是没有缺点。个人免费版没有 Web 管理界面,所有操作都得在命令行或者 Python 脚本里完成。但对我来说这反而是优点,因为我可以完全控制数据流向,想怎么改就怎么改。如果你想要的是开箱即用的图形界面,那 Dify 或者 LMStudio 可能更适合你。但如果你愿意花一个下午折腾一下,换来一个完全属于自己的知识库,那继续往下看。
2. 环境准备:Python、Ollama 和 FAISS 的安装踩坑记录
2.1 Python 安装:别用系统自带的
我踩过的第一个坑就是 Python 版本问题。macOS 和 Linux 系统自带的 Python 往往是 3.8 甚至更老,而 MoreLogic RAG 需要 3.10 以上。更麻烦的是,系统自带的 Python 你最好不要去动它,因为很多系统工具依赖它。正确的做法是装一个独立的 Python 环境。
Windows 用户直接去 Python 官网下载 3.11 或 3.12 的安装包,安装时务必勾选“Add Python to PATH”。这个选项不勾,后面在命令行里敲python会提示找不到命令,很多人卡在这一步。macOS 用户我推荐用 Homebrew 装:brew install python@3.12。Linux 用户可以用apt或者pyenv,但我建议用pyenv来管理多版本,避免污染系统环境。
装完之后验证一下:
python --version # 应该输出 Python 3.12.x pip --version # 应该输出 pip 24.x如果pip版本太老,先升级:python -m pip install --upgrade pip。这一步很重要,因为后面装 FAISS 和 Ollama 的 Python 客户端时,老版本 pip 可能会解析依赖失败。
注意:千万不要在 Windows 的 Microsoft Store 里装 Python,那个版本权限有问题,装包经常报错。我帮朋友排查过三次类似问题,最后都是卸载重装官网版本解决的。
2.2 Ollama 安装:国内网络环境的应对策略
Ollama 的安装本身很简单,官网下载对应系统的安装包,双击下一步就行。但真正的痛点在后面拉模型的时候。ollama pull qwen2.5:7b这种命令,在国内网络环境下下载速度可能只有几十 KB/s,一个 4GB 的模型要下好几个小时,甚至中途断连。
我的解决方案是找国内镜像源。Ollama 支持通过环境变量指定镜像地址,具体操作是在启动 Ollama 之前设置OLLAMA_HOST或者用代理工具。但这里我不能说得太细,你懂的。另一个办法是手动下载模型的 GGUF 文件,然后通过ollama create命令从本地文件导入。GGUF 文件在一些模型社区都能找到,下载下来之后写一个 Modelfile:
FROM ./qwen2.5-7b-instruct-q4_k_m.gguf PARAMETER temperature 0.7 PARAMETER num_ctx 4096然后执行ollama create mymodel -f Modelfile,这样就不用走 Ollama 的下载通道了。这个方法的缺点是得自己找模型文件,优点是下载速度取决于你的网盘或者下载工具,而且一次下载永久可用。
还有一个常见问题是 Ollama 默认把模型存在系统盘,C 盘空间不够的话会很痛苦。Linux 和 macOS 可以通过设置OLLAMA_MODELS环境变量来修改存储路径:
export OLLAMA_MODELS=/data/ollama/modelsWindows 用户可以在系统环境变量里添加这个变量,然后重启 Ollama 服务。改完之后记得把之前下载的模型手动迁移过去,否则 Ollama 会重新下载。
2.3 FAISS 安装:CPU 版就够了
FAISS 是 Facebook 开源的向量检索库,有 CPU 版和 GPU 版。很多人一看到 GPU 就兴奋,觉得一定要装 GPU 版才快。但实际情况是,对于个人知识库这种规模——几千到几万个文档片段——CPU 版的检索速度已经在毫秒级了,完全感觉不到延迟。GPU 版反而会带来 CUDA 版本兼容问题,装起来一堆坑。
所以我的建议很明确:直接装 CPU 版。
pip install faiss-cpu就这一行,完事。如果你用的是 Apple Silicon 的 Mac,FAISS 对 ARM 架构的支持已经不错了,faiss-cpu可以直接跑。Windows 用户如果遇到安装失败,大概率是缺少 Visual C++ 运行库,去微软官网下载最新的 VC++ Redistributable 装上就行。
验证安装:
import faiss print(faiss.__version__) # 输出类似 1.8.0如果这行代码能跑通,FAISS 就没问题了。
3. 核心流程拆解:从文档到问答的完整链路
3.1 文档加载与切分:切得好不好直接决定检索质量
整个 RAG 流程里,最容易被忽视但最关键的一步就是文档切分。很多人随便按固定字数切,结果把一段完整的论述拦腰截断,检索出来的片段前言不搭后语,大模型看了也懵。
我的做法是按语义切分,具体来说就是优先按段落切,如果单个段落超过 500 个字符,再按句子切。MoreLogic RAG 个人免费版内置了递归字符切分器,你可以这样配置:
from morelogic_rag import DocumentLoader, TextSplitter loader = DocumentLoader() documents = loader.load("./my_docs") # 支持 pdf, md, txt splitter = TextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""] ) chunks = splitter.split(documents)chunk_size=500意味着每个片段大约 500 个字符,chunk_overlap=50表示相邻片段之间有 50 个字符的重叠。这个重叠很重要,它保证了被切断的句子在前后两个片段里都能看到完整上下文。separators列表的顺序就是切分优先级,先尝试用双换行切,不行再用单换行,再不行用中文句号,以此类推。
实操心得:如果你的文档里有大量表格或者代码块,建议单独处理。表格按行切,代码块按函数或类切。我试过把一段 Python 代码按字符数硬切,结果检索出来的代码片段缺了缩进,大模型补全的时候直接语法错误。
3.2 向量化:选对 Embedding 模型比选大模型还重要
文档切好之后,下一步是把每个片段转成向量。这一步用的模型叫 Embedding 模型,它和大语言模型是两码事。Embedding 模型负责把文本映射到一个高维空间里的点,语义相近的文本在空间里距离就近。检索的时候,你的问题也被转成向量,然后找距离最近的几个文档片段。
MoreLogic RAG 个人免费版默认用的是BAAI/bge-small-zh-v1.5,这个模型对中文支持很好,体积也小,只有 100MB 左右,CPU 上跑毫无压力。如果你追求更好的效果,可以换成BAAI/bge-large-zh-v1.5,但体积会大到 1.3GB,检索速度也会慢一些。我的建议是先用 small 版,觉得效果不够再换。
from morelogic_rag import EmbeddingModel embedding_model = EmbeddingModel("BAAI/bge-small-zh-v1.5") vectors = embedding_model.encode(chunks) print(vectors.shape) # 输出类似 (128, 512),表示 128 个片段,每个片段 512 维向量这里有个细节要注意:Embedding 模型第一次运行时会自动从 HuggingFace 下载模型文件。国内网络环境下这个下载也可能很慢。解决办法是提前用huggingface-cli下载好,或者设置HF_ENDPOINT环境变量指向国内镜像。具体镜像地址我就不写了,你搜一下“HuggingFace 国内镜像”就能找到。
3.3 FAISS 索引构建:把向量存起来
向量算出来之后,需要存到一个能快速检索的数据结构里,这就是 FAISS 干的事。FAISS 提供了多种索引类型,对于个人知识库这种规模,用IndexFlatL2就够了。它是最简单的暴力检索索引,把所有向量存成一个矩阵,检索时计算问题向量和所有文档向量的距离,返回最近的 K 个。
import faiss import numpy as np dimension = vectors.shape[1] # 512 index = faiss.IndexFlatL2(dimension) index.add(vectors.astype(np.float32)) # 保存索引到磁盘 faiss.write_index(index, "./my_knowledge.index")IndexFlatL2用的是欧氏距离,距离越小表示越相似。如果你想让检索结果更偏向余弦相似度,可以在向量化之前先做归一化,然后用IndexFlatIP(内积索引)。这两种方式在效果上差别不大,但归一化之后内积等价于余弦相似度,更符合直觉。
索引文件的大小大概是片段数量 × 512 × 4 字节。一万个片段的话,索引文件大约 20MB,非常轻量。你可以把这个索引文件备份到网盘,换电脑的时候直接拷过去就能用。
3.4 检索与生成:把问题变成答案
前面三步都是准备工作,真正用起来的时候,流程是这样的:
- 用户输入问题,比如“MoreLogic RAG 支持哪些文档格式?”
- 用同一个 Embedding 模型把问题转成向量
- 在 FAISS 索引里检索最相似的 5 个文档片段
- 把这 5 个片段和问题一起拼成一个 Prompt,发给 Ollama 里的大模型
- 大模型根据这些片段生成回答
from morelogic_rag import RAGPipeline pipeline = RAGPipeline( index_path="./my_knowledge.index", embedding_model="BAAI/bge-small-zh-v1.5", llm_model="qwen2.5:7b", top_k=5 ) answer = pipeline.query("MoreLogic RAG 支持哪些文档格式?") print(answer)top_k=5表示检索最相似的 5 个片段。这个数字不是越大越好。太小了可能漏掉关键信息,太大了会引入无关内容,反而干扰大模型判断。我的经验是 3 到 7 之间比较合适,具体取决于你的文档密度。如果文档里每个片段都很短,可以适当调大;如果片段本身就很长,3 个就够了。
4. 实操全流程:从零开始搭建你的知识库
4.1 第一步:创建项目目录和虚拟环境
我习惯给每个项目建一个独立的虚拟环境,避免包版本冲突。MoreLogic RAG 依赖的包不少,如果和其他项目混在一起,很容易出现“装了这个坏了那个”的情况。
mkdir my-knowledge-base cd my-knowledge-base python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活虚拟环境之后,命令行前面会出现(venv)字样。然后安装依赖:
pip install morelogic-rag ollama faiss-cpu这里morelogic-rag是核心库,ollama是 Python 客户端,faiss-cpu是向量索引。三个包加起来大概 200MB 左右,取决于你的网络速度,几分钟到十几分钟不等。
4.2 第二步:拉取并测试 Ollama 模型
Ollama 装好之后,先拉一个中文能力不错的小模型。我推荐qwen2.5:7b,它在中文理解和生成上表现很均衡,7B 参数在 16GB 内存的机器上跑得动。如果你的机器内存只有 8GB,可以换成qwen2.5:3b或者qwen2.5:1.5b,效果会打折扣但至少能跑。
ollama pull qwen2.5:7b拉完之后测试一下:
ollama run qwen2.5:7b "你好,请用一句话介绍你自己"如果能看到模型正常回复,说明 Ollama 服务没问题。如果报错connection refused,检查一下 Ollama 服务有没有启动。Windows 和 macOS 安装包会自动注册服务,Linux 需要手动systemctl start ollama。
常见问题:
ollama run qwen3.5:2b error: 500 internal server error: llama-server process这个报错我遇到过,原因是模型文件下载不完整。解决办法是ollama rm qwen3.5:2b删掉重新拉。如果反复失败,检查磁盘空间是否充足,Ollama 拉模型需要至少两倍模型大小的临时空间。
4.3 第三步:准备你的文档
把你的 PDF、Markdown、TXT 文件都放到一个文件夹里,比如./docs。MoreLogic RAG 会自动遍历这个文件夹。我建议按主题建子文件夹,比如./docs/技术、./docs/产品,这样后面可以按目录过滤检索范围。
文档命名也有讲究。尽量用有意义的文件名,因为文件名本身也会被纳入检索。比如RAG架构设计说明.md就比新建文档1.md好得多。我试过把一堆截图丢进去,文件名全是IMG_001.png,检索效果惨不忍睹。后来我把截图里的关键信息手动写成 Markdown 文件,检索准确率立刻上来了。
关于图片,MoreLogic RAG 个人免费版本身不支持图片内容检索,但你可以用 OCR 工具把图片里的文字提取出来,存成 TXT 再放进去。或者用多模态模型生成图片描述,把描述文本作为文档内容。这两种方法我都试过,OCR 适合文字截图,多模态描述适合图表和流程图。
4.4 第四步:构建索引并测试检索
文档准备好之后,跑一个构建脚本:
from morelogic_rag import KnowledgeBase kb = KnowledgeBase( docs_dir="./docs", index_path="./my_knowledge.index", embedding_model="BAAI/bge-small-zh-v1.5", chunk_size=500, chunk_overlap=50 ) kb.build() print(f"索引构建完成,共 {kb.chunk_count} 个片段")这个过程会依次执行加载、切分、向量化、建索引。根据文档数量,耗时从几十秒到几分钟不等。构建完成后,先别急着接大模型,单独测试一下检索:
results = kb.search("MoreLogic RAG 的切分策略是什么", top_k=3) for i, r in enumerate(results): print(f"--- 结果 {i+1} (距离: {r.score:.4f}) ---") print(r.text[:200])看看返回的片段是不是真的和问题相关。如果返回的内容驴唇不对马嘴,说明切分或者 Embedding 模型有问题。这时候可以调整chunk_size或者换一个 Embedding 模型再试。
4.5 第五步:接入大模型完成问答
检索没问题之后,把 Ollama 接进来:
from morelogic_rag import RAGPipeline pipeline = RAGPipeline( index_path="./my_knowledge.index", embedding_model="BAAI/bge-small-zh-v1.5", llm_model="qwen2.5:7b", top_k=5, temperature=0.3 ) while True: question = input("\n你问:") if question.lower() in ["exit", "quit"]: break answer = pipeline.query(question) print(f"\n回答:{answer}")temperature=0.3表示让模型输出更保守、更贴近检索到的内容。知识库问答场景下,我不建议把 temperature 调高,否则模型容易自由发挥,编造出文档里没有的内容。这个现象叫“幻觉”,是 RAG 系统最常见的坑。
5. 常见问题与排查技巧实录
5.1 检索结果不相关怎么办
这是最高频的问题。排查思路按优先级来:
| 可能原因 | 排查方法 | 解决方案 |
|---|---|---|
| 切分粒度不合适 | 打印几个片段看看是否完整 | 调整 chunk_size 和 overlap |
| Embedding 模型不匹配 | 用简单问题测试检索 | 换 bge-large 或换多语言模型 |
| 文档本身质量差 | 检查原始文档是否清晰 | 清洗文档,去掉乱码和页眉页脚 |
| top_k 设置不当 | 观察不同 top_k 的结果 | 调整到 3-7 之间 |
| 问题表述太模糊 | 换一种问法试试 | 在问题里加入关键词 |
我遇到过一次特别诡异的情况:检索“如何配置 Ollama 模型路径”,返回的全是无关内容。后来发现是因为文档里有一张表格,表格被切分器切成了碎片,每个碎片只有几个字,向量化之后语义信息几乎为零。解决办法是在切分之前先把表格转成自然语言描述,比如“Ollama 模型路径通过 OLLAMA_MODELS 环境变量配置”,这样检索就正常了。
5.2 大模型回答“我不知道”或者答非所问
这种情况通常是 Prompt 没写好。MoreLogic RAG 默认的 Prompt 模板是英文的,对中文模型不太友好。你可以自定义 Prompt:
custom_prompt = """你是一个知识库助手。请根据以下参考资料回答用户问题。 如果参考资料中没有相关信息,请直接说“根据现有资料无法回答”,不要编造。 参考资料: {context} 用户问题:{question} 回答:"""把这个模板传给RAGPipeline的prompt_template参数就行。关键点是明确告诉模型“没有就说没有”,这能大幅降低幻觉率。我实测下来,加了这句话之后,编造回答的情况减少了八成以上。
另一个技巧是把检索到的片段按相关性排序,最相关的放最前面。大模型对 Prompt 开头的内容注意力更集中,把最重要的信息放在前面能提升回答质量。
5.3 索引构建太慢或者内存爆了
文档特别多的时候,一次性把所有向量加载到内存里可能会爆。FAISS 的IndexFlatL2需要把所有向量放在内存里,一万个 512 维向量大约占 20MB,十万个就是 200MB,一般机器扛得住。但如果你有上百万个片段,就得考虑用IndexIVFFlat这种带聚类的索引,它能减少内存占用但会损失一点精度。
构建慢的话,瓶颈通常在 Embedding 模型推理。CPU 上跑 bge-small 大概每秒能处理 50 到 100 个片段,一万个片段需要两三分钟。如果你有 GPU,可以装faiss-gpu和 GPU 版的 PyTorch,速度能快十倍以上。但如前所述,个人知识库规模下没必要折腾 GPU。
5.4 Ollama 模型加载失败或响应超时
Ollama 第一次加载模型时会把它读进内存,7B 模型大概需要 5GB 左右内存。如果你的机器内存不足,Ollama 会报错或者直接卡死。解决办法是换更小的模型,或者增加虚拟内存。Windows 用户可以在“高级系统设置”里把虚拟内存调到 16GB 以上。
响应超时通常是num_ctx参数设置过大。num_ctx是上下文窗口大小,默认 2048 或 4096。如果你把它设成 8192 甚至 16384,模型需要处理更长的上下文,生成速度会明显变慢。知识库问答场景下,4096 通常够用了,因为检索出来的片段加起来也就两三千字。
6. 进阶优化:让知识库更好用
6.1 混合检索:关键词加语义
纯向量检索有个弱点:对专有名词和精确匹配不敏感。比如你问“FAISS 的 IndexFlatL2 和 IndexIVFFlat 有什么区别”,向量检索可能返回一堆泛泛而谈的向量数据库介绍,而不是精确对比这两种索引的内容。解决办法是加一路关键词检索,用 BM25 或者简单的 TF-IDF,然后把两路结果融合。
MoreLogic RAG 个人免费版支持配置混合检索权重:
pipeline = RAGPipeline( index_path="./my_knowledge.index", embedding_model="BAAI/bge-small-zh-v1.5", llm_model="qwen2.5:7b", hybrid_search=True, semantic_weight=0.7, keyword_weight=0.3 )semantic_weight=0.7表示语义检索占七成权重,关键词检索占三成。这个比例可以根据你的文档类型调整。技术文档可以适当提高关键词权重,因为术语多;散文类文档可以降低关键词权重,因为表达方式灵活。
6.2 重排序:用 Cross-Encoder 精排
检索出来的 top_k 个片段,顺序未必是最优的。可以用一个 Cross-Encoder 模型对每个片段和问题的相关性重新打分,然后按新分数排序。Cross-Encoder 比 Embedding 模型慢,但它能同时看到问题和片段,判断更准确。
from morelogic_rag import Reranker reranker = Reranker("BAAI/bge-reranker-base") reranked = reranker.rerank(question, results, top_n=3)top_n=3表示精排后只保留 3 个片段送给大模型。这样既保证了相关性,又减少了 Prompt 长度,加快生成速度。我实测下来,加了重排序之后,回答准确率大概提升了 15% 到 20%,尤其是对于复杂问题效果明显。
6.3 定期更新索引
知识库不是建一次就完事了。新文档加进来之后,需要重新构建索引。MoreLogic RAG 支持增量索引,只处理新增或修改过的文件:
kb.update() # 只处理变化的文件它会在索引目录里维护一个文件指纹记录,通过对比修改时间来判断哪些文件需要重新处理。这个功能很实用,我每周把新写的技术笔记丢进./docs,跑一下kb.update(),几分钟就更新完了。
注意:如果你修改了切分参数或者换了 Embedding 模型,必须全量重建索引,增量更新会出问题。因为新旧向量不在同一个语义空间里,检索结果会混乱。
7. 我个人的一些使用体会
这套方案我用了大概三个月,目前知识库里存了六百多份文档,索引文件 80MB 左右,检索响应时间在 200 毫秒以内,大模型生成回答大概 3 到 5 秒。整体体验比我之前用过的任何在线笔记搜索都好。
最让我满意的是数据完全在自己手里。有一次我在外面用笔记本查资料,没连网,照样能打开知识库提问,因为所有东西都是本地的。这种安全感是在线服务给不了的。
当然也有不满意的地方。MoreLogic RAG 个人免费版没有图形界面,每次加文档都得敲命令。我后来写了一个简单的 Streamlit 页面套在上面,才算解决了这个问题。如果你也想做界面,Streamlit 是个不错的选择,几十行代码就能搞出一个能用的聊天界面。
最后分享一个小技巧:定期备份你的索引文件和原始文档。索引文件虽然可以重建,但重建需要时间。我设置了一个定时任务,每周把./docs和./my_knowledge.index打包压缩,存到移动硬盘里。这样即使电脑坏了,知识库也能在另一台机器上快速恢复。