1. 为什么我要自己搭一个知识库
先说结论:我搭这套东西的起因特别朴素——受够了。受够了收藏夹里躺着几百篇“稍后再读”结果再也没打开过,受够了每次写方案都要重新翻聊天记录找半年前同事发的那份参数表,更受够了把公司内部文档传到各种在线服务里时心里那点不踏实。市面上现成的知识库产品不少,功能也花哨,但要么按人头收费、要么按调用量计费,要么数据存在别人服务器上,用着用着就开始焦虑。所以当我看到 MoreLogic RAG 个人免费版这个方案时,第一反应是:这东西能不能让我在本地跑起来,数据不出门,还不用掏钱?
答案是能。这套方案的核心思路很清晰:用Ollama在本地跑大模型,用FAISS做向量检索,用Python把整个流程串起来,最后套一个 MoreLogic RAG 的壳子,形成一个完全离线的个人知识库。你不需要显卡,不需要服务器,一台普通的笔记本就能跑。我实测下来,一台 16G 内存的 Windows 笔记本,跑一个 7B 参数的量化模型,检索响应在 2 秒以内,生成回答在 5 到 15 秒之间,日常查资料完全够用。
这篇文章适合谁看?如果你是那种“想把散落在各处的笔记、文档、网页存档统一管起来”的人,或者你对 RAG 这个概念好奇但一直没动手,又或者你只是想找个理由学一下 Python 和 Ollama,那这篇内容就是为你写的。我会从整体设计思路讲到具体操作步骤,再到踩过的坑和排查技巧,尽量把每个环节的“为什么”说清楚。你不需要是程序员,但需要有一点折腾的耐心——毕竟本地部署这件事,第一次总会遇到几个报错。
提示:本文涉及的所有工具和模型均为本地运行,数据全程留在你自己的硬盘上,不涉及任何外部服务。
2. 整体设计思路与方案选型
2.1 为什么是 RAG 而不是直接问大模型
很多人第一次接触本地大模型时,会直接装一个 Ollama,然后对着命令行窗口问问题。这当然可以,但你会发现两个问题:第一,模型不知道你的私人文档里写了什么;第二,模型的训练数据有截止日期,新东西它一概不知。RAG 要解决的就是这两个问题。
RAG 的全称是检索增强生成,拆开看就是“先检索,再生成”。你把文档切碎、转成向量、存进数据库;用户提问时,系统先把问题也转成向量,去数据库里找最相似的几段文本,然后把这几段文本和问题一起塞给大模型,让模型基于这些材料来回答。这样一来,模型不需要记住你的文档,它只需要会“阅读理解”就行。
我选择 MoreLogic RAG 个人免费版作为框架,原因有三个。第一,它把文档解析、切分、向量化、检索、生成这几个环节都封装好了,我不需要从零写代码。第二,它支持本地模型接入,Ollama 的 API 地址填进去就能用。第三,个人免费版没有功能阉割,只是限制了商用场景,对个人用户来说完全够用。
2.2 为什么用 Ollama 而不是其他本地推理方案
本地跑大模型的选择其实不少,比如 llama.cpp、text-generation-webui、LM Studio 等等。我最终选 Ollama,理由很实际:它把模型下载、量化、推理服务化这几件事做得最省心。你只需要一行命令ollama run qwen2.5:7b,它就会自动下载模型并启动一个本地 API 服务,默认监听 11434 端口。MoreLogic RAG 只需要配置这个地址,就能调用模型。
另一个原因是 Ollama 对中文模型的支持比较友好。我试过 qwen2.5 系列和 glm4 系列,在中文问答场景下表现都不错。7B 参数的量化版本大概占 4 到 5 G 硬盘空间,推理时内存占用在 6 到 8 G 左右,普通笔记本扛得住。如果你机器配置更低,可以选 3B 或 1.5B 的版本,速度更快但回答质量会下降一些。
2.3 为什么用 FAISS 做向量检索
向量数据库的选择也很多,比如 Chroma、Milvus、Qdrant、Weaviate 等等。FAISS 是 Facebook 开源的一个库,严格来说它不是一个完整的数据库,而是一个向量相似度搜索库。我选它的原因很简单:轻量、快、跟 Python 集成方便。
FAISS 不需要你启动额外的服务进程,它就是一个 Python 库,你 import 进来就能用。索引文件就是一个本地文件,复制走就能迁移。对于个人知识库这种规模——几千到几万条文本块——FAISS 的检索速度完全够用,而且内存占用很低。相比之下,Milvus 和 Qdrant 更适合团队级、百万级向量的场景,个人用属于杀鸡用牛刀。
2.4 整体架构与数据流向
把这几个组件串起来,整个系统的数据流向是这样的:
- 你把 PDF、Word、Markdown、TXT 等文档放进指定文件夹。
- MoreLogic RAG 调用文档解析器,把文档转成纯文本。
- 文本被切分成固定长度的块,每块大概 500 到 1000 字。
- 每个文本块通过嵌入模型转成一个向量,存进 FAISS 索引。
- 你提问时,问题也被转成向量,FAISS 找出最相似的几个文本块。
- 这些文本块和问题一起发给 Ollama 里的模型,模型生成回答。
整个流程里,嵌入模型和生成模型是分开的。嵌入模型负责把文本转成向量,我推荐用nomic-embed-text或者bge-m3,这两个在 Ollama 里都能直接拉取。生成模型负责最后回答问题,用 qwen2.5:7b 或 glm4:9b 都可以。两个模型都跑在本地,互不干扰。
注意:嵌入模型和生成模型是两回事,不要试图用生成模型来做嵌入,效果差很多。
3. 环境准备与核心组件安装
3.1 Python 环境搭建与依赖管理
Python 是这套方案的粘合剂,MoreLogic RAG 本身也是 Python 写的。我的建议是不要用系统自带的 Python,而是用 conda 或者 venv 创建一个独立环境。这样做的好处是依赖冲突不会污染全局,出了问题直接删掉环境重来就行。
我习惯用 conda,命令如下:
conda create -n morelogic-rag python=3.10 conda activate morelogic-rag选 3.10 而不是最新版,是因为很多向量库和文档解析库对 3.11 以上的支持还不完善,3.10 是目前最稳的版本。创建好环境后,安装核心依赖:
pip install faiss-cpu pip install ollama pip install langchain pip install langchain-community pip install pypdf pip install python-docx pip install markdown这里解释一下每个包的作用。faiss-cpu是 FAISS 的 CPU 版本,如果你有 NVIDIA 显卡并且装了 CUDA,可以换成faiss-gpu,检索速度会快很多。ollama是 Python 客户端,用来调用本地模型。langchain和langchain-community提供了文档加载器和文本切分器,省得自己写。pypdf和python-docx分别用来解析 PDF 和 Word 文档。markdown用来处理 Markdown 文件。
提示:如果你在国内下载 pip 包速度慢,可以在命令后面加
-i https://pypi.tuna.tsinghua.edu.cn/simple指定镜像源。
3.2 Ollama 安装与模型拉取
Ollama 的安装很简单,去官网下载对应系统的安装包,双击安装即可。Windows 版安装后会默认在后台启动服务,监听 11434 端口。你可以在浏览器里访问http://localhost:11434看看有没有响应,如果有,说明服务正常。
安装完成后,打开终端,拉取模型:
ollama pull qwen2.5:7b ollama pull nomic-embed-text第一个是生成模型,第二个是嵌入模型。下载速度取决于你的网络,7B 模型大概 4.7G,嵌入模型大概 270M。如果下载太慢,可以设置环境变量OLLAMA_MODELS把模型存储路径改到空间大的盘符,但下载速度本身还是取决于网络。
拉取完成后,测试一下:
ollama run qwen2.5:7b如果能看到命令行提示符变成>>>,说明模型加载成功。输入一个问题试试,比如“你好,请介绍一下你自己”,看看有没有正常回复。确认没问题后,按Ctrl+D退出。
3.3 FAISS 索引的创建与持久化
FAISS 的核心对象是索引。对于文本检索,我们通常用IndexFlatL2或IndexFlatIP。前者用欧氏距离,后者用内积。因为我们的向量会做归一化,所以两者等价,我习惯用IndexFlatIP。
创建一个索引并保存的代码大概长这样:
import faiss import numpy as np dimension = 768 # 嵌入模型的向量维度 index = faiss.IndexFlatIP(dimension) # 假设 vectors 是一个 numpy 数组,形状为 (n, 768) vectors = np.random.random((100, dimension)).astype('float32') faiss.normalize_L2(vectors) index.add(vectors) # 保存到本地文件 faiss.write_index(index, "my_knowledge.index")读取的时候用faiss.read_index("my_knowledge.index")就行。这里的关键点是维度必须和嵌入模型输出的维度一致。nomic-embed-text的输出维度是 768,bge-m3是 1024。如果你换了嵌入模型,索引必须重建,否则会报维度不匹配的错误。
注意:FAISS 索引文件不包含原始文本,只包含向量。你需要另外用一个列表或数据库来存储文本块和向量的对应关系。
3.4 MoreLogic RAG 个人免费版的部署
MoreLogic RAG 个人免费版通常以 Docker 镜像或源码包的形式提供。我选择用 Docker 部署,因为依赖问题最少。确保你的机器上装了 Docker Desktop,然后拉取镜像并启动:
docker pull morelogic/rag-personal:latest docker run -d -p 8080:8080 -v /path/to/your/data:/data morelogic/rag-personal:latest启动后访问http://localhost:8080,应该能看到管理界面。第一次进入需要配置模型地址,填http://host.docker.internal:11434,这是 Docker 容器访问宿主机服务的地址。如果你是在 Linux 上直接跑源码,那就填http://localhost:11434。
配置完成后,创建一个知识库,选择嵌入模型为nomic-embed-text,生成模型为qwen2.5:7b,向量存储选 FAISS。保存后就可以上传文档了。
4. 文档处理与知识库构建实操
4.1 文档收集与格式统一
在往知识库里塞东西之前,先花点时间整理文档。我的经验是:格式越统一,解析效果越好。PDF 是最麻烦的,尤其是扫描版 PDF,纯文本提取经常乱码。如果你有大量扫描版 PDF,建议先用 OCR 工具转一遍,或者干脆放弃,只保留文字版 PDF。
我自己的文档来源主要有四类:Markdown 笔记、Word 文档、网页存档、纯文本。Markdown 和纯文本解析最稳,Word 次之,PDF 最差。对于网页存档,我习惯用浏览器的“打印为 PDF”功能,但这样出来的 PDF 也是文字版,解析没问题。如果你用 Obsidian 记笔记,直接把整个 vault 文件夹拖进去就行,MoreLogic RAG 支持批量导入。
提示:文档文件名尽量用英文或拼音,避免特殊字符,否则在某些系统上会出现路径编码问题。
4.2 文本切分策略与参数选择
文本切分是 RAG 里最容易被忽视但影响最大的环节。切得太碎,上下文丢失,模型回答不完整;切得太大,检索精度下降,噪音太多。我的经验值是:中文文本每块 500 到 800 字,英文文本每块 800 到 1200 字符,块与块之间重叠 100 到 200 字。
MoreLogic RAG 默认用的是递归字符切分器,它会优先按段落切,段落太长再按句子切,句子太长再按字符切。这个策略对大多数文档都适用。你可以在知识库设置里调整块大小和重叠长度。我试过把块大小设成 300 字,结果检索出来的片段经常缺头少尾;设成 1500 字,又经常混入无关内容。最后定在 600 字,重叠 150 字,效果最平衡。
对于代码文件,切分策略要另外考虑。代码的逻辑单元是函数或类,按行切会破坏结构。我建议对代码文件单独建一个知识库,用按函数切分的策略,或者干脆不切,整个文件作为一个块。不过代码检索本身是个难题,本文不展开。
4.3 嵌入模型的选择与对比
嵌入模型决定了检索的准确性。我对比过三个模型:nomic-embed-text、bge-m3、mxbai-embed-large。在中文场景下,bge-m3的表现最好,尤其是对长文本的语义捕捉更准。nomic-embed-text胜在速度快、体积小。mxbai-embed-large英文强但中文一般。
如果你主要处理中文文档,我推荐bge-m3。它的向量维度是 1024,比nomic-embed-text的 768 高,检索精度更好,但索引文件也更大,检索速度稍慢。对于个人知识库这种规模,这点性能差异可以忽略。
切换嵌入模型后,必须重建索引。MoreLogic RAG 里有一个“重建索引”按钮,点一下就会重新处理所有文档。重建时间取决于文档数量,我的一千多篇笔记大概花了 15 分钟。
4.4 批量导入与增量更新
MoreLogic RAG 支持监控文件夹,你把新文档放进监控目录,它会自动解析并加入索引。这个功能很实用,我设置了一个inbox文件夹,平时看到好文章就丢进去,系统自动处理。但要注意,自动监控只对新文件生效,修改已有文件不会触发更新。如果你改了某个文档,需要手动删除对应的索引记录再重新导入。
批量导入时,建议分批进行。一次导入太多文件,嵌入模型会排队处理,内存占用会飙升。我试过一次导入 500 个 PDF,结果内存直接爆了。后来改成每次 50 个,稳得很。
5. 检索与问答的调优经验
5.1 检索参数调优:Top-K 与相似度阈值
检索时有两个关键参数:Top-K 和相似度阈值。Top-K 是返回最相似的几个文本块,默认是 4。我试过设成 2,结果模型经常说“根据已知信息无法回答”;设成 8,又经常把不相关的内容塞进去,导致回答跑偏。最后定在 5,兼顾召回率和精度。
相似度阈值是过滤低质量匹配的。FAISS 返回的是距离分数,MoreLogic RAG 会把它转成相似度。我一般设 0.7 作为阈值,低于这个值的直接丢弃。这样能避免模型被无关内容干扰。但阈值也不能设太高,否则有些边缘相关的内容会被误杀。0.7 是我试出来的平衡点。
5.2 提示词模板的调整
MoreLogic RAG 允许自定义提示词模板。默认模板大概是“基于以下材料回答问题,如果材料中没有相关信息,请说不知道”。这个模板对大多数场景够用,但如果你希望模型回答更详细,可以改成“基于以下材料,用不少于三句话回答,并引用原文出处”。
我自己的模板是这样的:
你是一个知识库助手。请根据以下参考材料回答用户问题。 如果材料中没有相关信息,直接说“知识库中没有找到相关内容”,不要编造。 回答时尽量引用原文,并在末尾标注来源文档名。 参考材料: {context} 用户问题:{question}这个模板的好处是明确告诉模型不要编造,并且要求标注来源。实测下来,加了来源标注后,我更容易判断回答是否可信。
5.3 多轮对话与上下文管理
MoreLogic RAG 支持多轮对话,但要注意上下文长度限制。qwen2.5:7b 的上下文窗口是 32K token,听起来很大,但如果你每轮都塞 5 个文本块,每个块 600 字,再加上对话历史,很快就满了。我的做法是:只保留最近三轮对话历史,更早的自动丢弃。这样既能保持对话连贯,又不会撑爆上下文。
如果你发现模型回答开始胡言乱语,大概率是上下文超了。这时候开一个新对话就行。
6. 常见问题与排查技巧实录
6.1 模型加载失败与内存不足
这是最常见的问题。现象是 Ollama 日志里报out of memory,或者模型加载到一半卡住。原因通常是内存不够。7B 模型量化后大概需要 6 到 8 G 可用内存,如果你同时开着浏览器、IDE、聊天软件,内存很容易被吃光。
解决办法有三个:第一,关掉不必要的程序;第二,换更小的模型,比如 qwen2.5:3b;第三,设置 Ollama 的OLLAMA_MAX_LOADED_MODELS=1,确保同时只加载一个模型。我试过同时加载生成模型和嵌入模型,内存直接飙到 14G,后来改成用完就卸载,稳多了。
6.2 检索结果不准确
检索不准的原因很多,按优先级排查:第一,检查嵌入模型是否匹配,换了模型必须重建索引;第二,检查文本切分是否合理,块太大或太小都会影响;第三,检查相似度阈值是否合适,太高会漏,太低会混;第四,检查文档本身是否清晰,扫描版 PDF 提取的文本质量很差,检索自然不准。
我遇到过一次检索完全失效的情况,排查了半天发现是索引文件损坏了。删掉索引重建就好了。所以建议定期备份索引文件,或者保留原始文档,随时可以重建。
6.3 Ollama 服务连接超时
MoreLogic RAG 连不上 Ollama,通常是因为地址填错了。如果你用 Docker 部署 MoreLogic RAG,Ollama 跑在宿主机上,地址要填http://host.docker.internal:11434,而不是localhost。因为容器里的localhost指向容器自己,不是宿主机。
另外检查防火墙是否拦了 11434 端口。Windows 上第一次启动 Ollama 时,系统会弹窗询问是否允许网络访问,一定要点允许。如果误点了拒绝,去防火墙设置里手动放行。
6.4 文档解析乱码与格式丢失
PDF 解析乱码是老大难问题。我试过 pypdf、pdfplumber、pymupdf 三个库,pymupdf 的效果最好,但 MoreLogic RAG 默认用的是 pypdf。如果你有大量 PDF 要处理,可以考虑在 MoreLogic RAG 的配置里把解析器换成 pymupdf。不过换解析器需要改源码,稍微麻烦一点。
Word 文档的表格解析也容易出问题。表格内容经常被拆成零散的文本块,导致检索时找不到完整信息。我的做法是把重要表格单独导出为 Markdown 或 CSV,再导入知识库。
6.5 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 模型加载失败 | 内存不足 | 关程序、换小模型、限制并发加载 |
| 检索结果不相关 | 嵌入模型不匹配 | 重建索引 |
| 检索结果不相关 | 切分参数不合理 | 调整块大小和重叠 |
| 连接 Ollama 超时 | 地址填错 | Docker 用 host.docker.internal |
| 连接 Ollama 超时 | 防火墙拦截 | 放行 11434 端口 |
| PDF 解析乱码 | 扫描版或编码问题 | 换 pymupdf 或先 OCR |
| 回答编造内容 | 提示词不够严格 | 修改模板,强调不要编造 |
| 上下文超限 | 对话历史太长 | 开新对话或减少 Top-K |
7. 我踩过的坑与实操心得
第一个坑是模型存储路径。Ollama 默认把模型存在 C 盘,7B 模型加上嵌入模型,轻松占掉 10G。我的 C 盘是固态但容量小,很快就红了。解决办法是设置环境变量OLLAMA_MODELS=D:\ollama\models,把模型挪到 D 盘。注意这个变量要在启动 Ollama 之前设置,设置完重启服务才生效。
第二个坑是 Docker 卷映射。我一开始把文档放在容器内部,结果容器一删,文档全没了。后来改成把宿主机目录映射到容器里,-v /my/docs:/data,这样文档始终在宿主机上,容器随便删。索引文件也一样,映射出来,方便备份。
第三个坑是嵌入模型的维度。我一开始用nomic-embed-text建了索引,后来换成bge-m3,忘了重建,结果检索一直报维度错误。排查了半天才想起来。所以换嵌入模型后第一件事就是重建索引,没有例外。
第四个坑是中文标点。有些文档里的中文引号和英文引号混用,切分器处理时会把句子切得乱七八糟。我的做法是导入前先用脚本统一标点,把中文引号替换成英文引号,效果立竿见影。
最后一个心得是关于模型选择的。不要迷信大参数模型。我试过 14B 的模型,回答质量确实好一点,但速度慢了一倍,内存占用翻倍。对于知识库问答这种场景,7B 模型完全够用,关键是检索要准。检索准了,小模型也能给出好答案;检索不准,再大的模型也是胡扯。
这套系统我用了大半年,存了大概两千多篇文档,日常查资料、写方案、找参数,基本告别了“翻聊天记录”和“搜收藏夹”。它不是什么高大上的东西,就是一个老老实实干活的本工具。如果你也想搭一个,照着上面的步骤走,遇到报错别慌,大概率是内存或地址问题,排查一下就能解决。