ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

本地RAG知识库搭建实战:Ollama+FAISS+Python从零到一

本地RAG知识库搭建实战:Ollama+FAISS+Python从零到一 1. 为什么我要在本地搭一套知识库而不是直接用在线笔记先说结论我搭这套东西的起因特别朴素——我受够了收藏了就等于学会了这种自欺欺人的状态。过去两年我的浏览器书签栏塞了四百多个链接微信收藏夹里躺着上千条稍后阅读Obsidian 里散落着几十个没写完的笔记文件。每次想找某个具体的技术细节比如FAISS 的 IndexFlatIP 和 IndexIVFFlat 到底该选哪个我依然得重新搜一遍。收藏这个动作本身给了我一种虚假的掌控感但知识从来没有真正沉淀下来。后来我意识到问题的核心不在于存而在于取。传统的文件夹式笔记是给人脑的树状结构设计的可人脑回忆东西的方式其实是联想式的——你想起一个概念往往是通过另一个不相关的线索牵出来的。这就是 RAG检索增强生成这套思路真正打动我的地方它不要求你提前把知识整理成完美的分类体系你只管把原始材料丢进去需要的时候用自然语言问它它自己去向量空间里找最相关的片段再交给大模型组织成答案。MoreLogic RAG 个人免费版吸引我的点在于它把文档解析、切块、向量化、检索、生成这一整条链路打包成了一个可以本地跑的东西。关键词里出现的 Ollama、FAISS、Python 这几个词基本就勾勒出了它的技术底座Ollama 负责在本地跑大模型FAISS 负责向量检索Python 负责把这两者粘起来。整套方案不需要联网调用任何外部 API你的文档不出本机这对处理工作资料、私人笔记、甚至一些内部技术文档来说心理负担小很多。这篇文章适合谁看如果你符合下面任意一条那接下来的内容应该对你有用手头有一堆 PDF、Markdown、Word 文档想统一管理对 RAG 感兴趣但被 LangChain 那一堆抽象概念劝退过想在自己电脑上跑个大模型但不知道从哪下手或者单纯就是想搞明白向量检索到底是怎么回事。我会从环境准备一路讲到踩坑排查尽量把每一步的为什么也讲清楚而不是甩一堆命令让你照抄。需要提前说明的是我下面给出的所有配置和参数都是基于个人电脑、单机、文档量在几百到几千份之间这个场景来设计的。如果你要处理的是企业级的海量文档那这套方案的取舍逻辑会完全不一样我会在相关章节里点出来。2. 动手之前先把这套 RAG 的零件认全2.1 Ollama 在这套方案里扮演什么角色很多人第一次接触 Ollama 会误以为它是个模型其实它更像是一个模型运行时的管理器。你可以把它理解成 Docker 之于容器——Docker 本身不是容器它是帮你拉取、运行、管理容器的工具Ollama 也一样它本身不是大模型它是帮你下载、加载、运行各种开源大模型的工具。在 MoreLogic RAG 这套方案里Ollama 承担的是最后一步生成的工作。当 FAISS 从你的文档库里检索出几段最相关的文本片段后这些片段会作为上下文连同你的问题一起被塞给 Ollama 里跑着的模型由它来组织出一段通顺的回答。所以 Ollama 里装什么模型直接决定了你最终答案的质量和速度。这里有个新手特别容易踩的坑一上来就拉一个 70B 参数的大模型结果发现自己 16G 内存的笔记本跑起来像幻灯片。我的建议是先用一个 7B 到 8B 级别的模型把整条链路跑通确认流程没问题了再根据自己机器的实际性能往上换。模型参数量和硬件需求的大致对应关系我整理成了下面这张表方便你对照自己的机器做选择模型参数量量化后大致显存/内存占用适合的硬件生成速度体感1.5B - 3B2GB - 4GB8G 内存的轻薄本很快但回答深度有限7B - 8B5GB - 8GB16G 内存或 8G 显存显卡流畅日常问答够用13B - 14B9GB - 12GB32G 内存或 12G 显存稍慢质量明显提升32B 及以上20GB 起步64G 内存 高端显卡慢但推理能力强需要强调的是这张表里的数字是量化后的占用。所谓量化简单说就是把模型原本用 16 位浮点数存储的权重压缩成 4 位或 8 位整数代价是精度略微下降收益是体积和内存占用大幅缩小。对个人使用来说4 位量化的模型质量损失在大多数场景下几乎感知不到但资源占用能砍掉一大半所以是性价比最高的选择。2.2 FAISS 为什么比用数据库存向量更合适FAISS 是 Facebook 开源的一个向量检索库全称是 Facebook AI Similarity Search。它的核心工作只有一件事给你一堆向量再给你一个查询向量快速找出最相似的那几个。你可能会问向量相似度计算不就是算个余弦距离或者欧氏距离吗我自己写个循环遍历一遍不就行了问题在于规模。假设你的知识库切出了一万个文本块每个块用一个 768 维的向量表示那你每次查询都要拿查询向量和这一万个向量逐一算距离每次算 768 次乘加运算。单次查询可能也就几十毫秒感觉还行。但如果你有十万个块、一百万个块呢线性遍历的时间会线性增长很快就没法用了。FAISS 的价值就在于它提供了多种近似最近邻索引结构能在牺牲极小精度的情况下把检索复杂度从线性降到接近对数级。它内置的索引类型很多对个人知识库来说最常用的其实就两三种我把它们的区别列出来IndexFlatL2 / IndexFlatIP暴力精确检索不损失任何精度适合一万条以下的向量。实现简单结果绝对准确是我推荐新手起步用的类型。IndexIVFFlat倒排文件索引先把向量空间聚成若干簇查询时只搜最近的几个簇。速度快很多但需要先训练索引且会损失一点点召回率。IndexHNSWFlat基于图的索引查询速度极快内存占用相对高一些构建索引比 IVF 慢但不需要训练步骤。对绝大多数个人知识库场景我的建议是文档块数量在一万以内直接用 IndexFlatIP别折腾。等你的库真的涨到几万条以上再考虑换 IVF。过早优化索引结构只会让你在调试阶段多出一堆莫名其妙的召回问题。2.3 文档切块这件事决定了检索质量的上限这是整套 RAG 里最容易被忽视、但对最终效果影响最大的环节。所谓切块就是把一篇长文档拆成一段段长度合适的文本片段每个片段单独做向量化。切得太粗一个块里混了好几个主题检索出来的内容就不精准切得太细一个完整的论述被拆散模型拿到的上下文就残缺不全。我踩过的坑是这样的一开始我图省事按固定 500 个字符硬切结果一份技术文档里的代码示例被从中间截断检索出来的片段前半段是文字说明后半段是半截代码模型看了半天也没搞明白在讲什么。后来我改成了按语义边界切优先在段落、标题、代码块结束的地方断开效果立刻好了很多。MoreLogic RAG 这类工具通常会提供几个切块参数让你调我一般会关注这几个块大小chunk size单个片段的字符数或 token 数中文场景我习惯设在 300 到 500 字之间。重叠长度overlap相邻两个块之间重复的部分防止关键信息正好卡在切割线上被切断一般设块大小的 10% 到 20%。分隔符优先级告诉切块器优先在哪些符号处断开比如先找双换行再找单换行最后才按句号切。提示如果你的文档里有大量表格或代码建议单独处理不要和普通正文混在一起切。表格被切碎之后基本就失去意义了。3. 从零把环境跑起来Python、Ollama、FAISS 的安装顺序3.1 Python 环境为什么我强烈建议用虚拟环境装 Python 本身没什么难度官网下载安装包一路下一步就行。但我要重点说的是虚拟环境这件事因为这是新手最容易忽略、后期最容易出问题的地方。假设你系统里全局装了一个 Python然后项目 A 需要 numpy 1.24项目 B 需要 numpy 1.26你全局只能装一个版本装了这个那个就报错。虚拟环境的作用就是给每个项目一个独立的依赖空间互不干扰。MoreLogic RAG 依赖的库不少包括向量化模型、文档解析库、Web 框架等等把它们关在一个独立的虚拟环境里将来想删掉整个环境直接删文件夹就行不会污染系统。创建虚拟环境的命令很简单在项目目录下执行python -m venv .venvWindows 下激活.venv\Scripts\activatemacOS 或 Linux 下激活source .venv/bin/activate激活之后你的命令行提示符前面会出现(.venv)字样这时候用 pip 装的所有包都只在这个环境里生效。我个人的习惯是每个项目单独建一个 venv绝不共用。关于 Python 版本我建议用 3.10 或 3.11。3.12 虽然更新但有些依赖库的预编译包还没跟上装的时候容易触发源码编译在 Windows 上编译环境没配好的话会直接报错。3.10 是目前兼容性最稳的版本没必要追新。3.2 Ollama 的安装与模型拉取Ollama 的安装包在官网可以直接下载Windows 和 macOS 都有图形化安装程序装完之后它会在后台常驻一个服务。Linux 下则是一条安装脚本命令搞定。装完之后第一件事是验证服务是否正常ollama --version能打印出版本号就说明装好了。接下来拉模型比如拉一个 8B 级别的通用模型ollama pull qwen2.5:7b这里有个现实问题模型文件动辄几个 G从官方源拉取在国内网络环境下可能非常慢甚至中途断掉。我的经验是如果拉取速度长期低于几百 KB/s可以考虑配置镜像源或者找离线的模型文件手动导入。Ollama 支持从本地文件导入模型具体做法是写一个 Modelfile然后用ollama create命令创建。这个方式适合网络条件实在不理想的情况。拉完模型之后用一条命令测试它能不能正常对话ollama run qwen2.5:7b进入交互界面后随便问一句能正常回答就说明模型跑通了。想退出输入/bye。还有一个容易被忽略的点Ollama 默认把模型存在系统盘的用户目录下如果你的系统盘空间紧张模型很快就能把盘塞满。这时候需要修改模型存储路径。Windows 下通过设置环境变量OLLAMA_MODELS指向一个新目录Linux 下则是在服务配置文件里改。改完之后记得重启 Ollama 服务并且把原来下载的模型文件手动挪过去否则它会重新下载。3.3 FAISS 的安装与一个最小验证FAISS 的 Python 包安装起来很简单pip install faiss-cpu如果你有 NVIDIA 显卡并且想用 GPU 加速可以装faiss-gpu但个人知识库这个量级CPU 版本完全够用没必要折腾 GPU 版本的驱动兼容问题。装完之后我建议写一个十几行的小脚本验证一下它到底在干什么这比看一百页文档都管用import numpy as np import faiss # 造 5 个 8 维向量模拟 5 个文本块的向量 data np.random.random((5, 8)).astype(float32) index faiss.IndexFlatIP(8) # 用内积做相似度 faiss.normalize_L2(data) # 归一化后内积等价于余弦相似度 index.add(data) # 查询向量 query np.random.random((1, 8)).astype(float32) faiss.normalize_L2(query) # 找最相似的 2 个 distances, indices index.search(query, 2) print(最相似的块索引:, indices) print(相似度分数:, distances)跑通这段代码你就理解了 RAG 检索环节的全部本质把文本变成向量存进索引查询时找最近的几个。剩下的所有工程复杂度都是围绕怎么把文本变成好向量和怎么把检索结果用好展开的。4. 把文档喂进去解析、切块、向量化的完整链路4.1 不同格式文档的解析策略差异你的知识库素材大概率不是单一格式的。PDF、Word、Markdown、纯文本、甚至网页剪藏每种格式的解析难度和坑都不一样。Markdown 和纯文本最好处理直接读进来就是干净的文本。Word 文档用 python-docx 之类的库也能拿到结构化的段落。真正麻烦的是 PDF尤其是扫描版 PDF 和排版复杂的 PDF。扫描版 PDF 本质上是图片必须走 OCR 才能提取文字这一步的准确率直接决定了后续所有环节的质量。排版复杂的 PDF 则容易出现文字顺序错乱、表格被拆散、页眉页脚混入正文等问题。我的处理原则是能拿到原始 Markdown 或 Word 的绝不用 PDF必须用 PDF 的优先找文字版而非扫描版扫描版 PDF 如果 OCR 质量差宁可手动整理关键内容也不要让垃圾进垃圾出。这一点在农业知识库、法律文档这类对准确性要求高的场景里尤其重要一个识别错的数字可能让整个答案跑偏。4.2 向量化模型的选择本地跑还是调 API把文本块变成向量需要一个嵌入模型embedding model。这里有个关键决策用本地嵌入模型还是调用外部 API。本地嵌入模型的优势是数据不出本机、没有调用次数限制、没有网络延迟。劣势是首次加载需要占内存且质量通常不如顶级商业模型。常见的本地嵌入模型有 BGE 系列、M3E 系列等中文场景下 BGE 的中文版本表现相当不错模型体积也不大几百兆而已。调用外部 API 的优势是质量高、省本地资源劣势是数据要发出去、有调用成本、依赖网络。对个人知识库来说如果你的文档涉及隐私或工作内容我强烈建议用本地嵌入模型。多花的那点内存换来的是心理上的踏实。这里有个技术细节值得说清楚嵌入模型和生成模型是两回事不要混淆。嵌入模型只负责把文本转成向量它不会回答任何问题生成模型也就是 Ollama 里跑的那个才负责根据检索结果组织答案。两者可以完全独立选择比如用 BGE 做嵌入用 Qwen 做生成这是很常见的组合。4.3 入库流程的实操步骤与参数记录把上面这些串起来一个完整的入库流程大致是这样的遍历文档目录按扩展名分派到对应的解析器提取纯文本。对提取出的文本做清洗去掉多余空行、页眉页脚、乱码字符。按设定的块大小和重叠长度切块记录每个块来自哪个文档、第几段。用嵌入模型把每个块转成向量。把所有向量加进 FAISS 索引同时把向量 ID 到原文块的映射存起来。把索引和映射持久化到磁盘下次启动直接加载。第 5 步那个映射特别关键很多人第一次做会忽略。FAISS 只存向量和 ID它不知道 ID 对应的是哪段文字。你必须自己维护一个字典把 FAISS 返回的 ID 翻译回原文才能把检索结果喂给大模型。这个映射通常用一个 JSON 文件或者轻量数据库存就行。我在实际操作中会记录每次入库的参数因为不同批次的文档如果用了不同的切块参数检索效果会有差异出问题时方便回溯。下面是我习惯记录的一张参数表参数项我的常用值调整依据块大小400 字中文技术文档兼顾上下文完整与检索精度重叠长度60 字块大小的 15%防止关键句被切断嵌入模型BGE 中文基座版中文语义理解好体积适中索引类型IndexFlatIP文档量万级以内追求召回准确相似度度量余弦相似度对文本长度不敏感更适合语义匹配5. 检索和生成让答案真正有用的那几个调优点5.1 Top-K 取多少才合适检索时你要指定取回最相似的 K 个块。K 太小可能漏掉关键信息K 太大一堆不相关的块混进上下文反而干扰模型判断还白白消耗上下文窗口。我的经验值是 K 取 3 到 5。这个区间在大多数场景下能覆盖到真正相关的内容又不至于引入太多噪声。如果你的问题比较复杂需要综合多个文档的信息可以适当提到 8 到 10但要配合重排序rerank来过滤掉不相关的块。说到重排序这是个提升效果很明显但常被忽略的步骤。它的逻辑是先用向量检索快速召回一批候选比如 20 个再用一个更精细的模型对这 20 个逐一打分排序最后取前几个。向量检索快但粗重排序慢但准两者结合能在速度和精度之间取得很好的平衡。对个人知识库来说如果检索结果经常答非所问加一个重排序环节往往能立竿见影。5.2 提示词模板决定了模型怎么用检索结果检索回来的片段怎么交给模型是有讲究的。最朴素的做法是把片段直接拼在问题前面但这样模型可能会忽略片段、凭自己的记忆瞎答。更好的做法是在提示词里明确约束你是一个知识库问答助手。请严格根据下面提供的资料回答问题。 如果资料中没有相关信息请直接说资料中未提及不要编造。 资料 {context} 问题{question}这个模板里有两个关键约束一是严格根据资料二是没有就说没有。第二点尤其重要它能大幅减少模型胡编乱造的情况。RAG 最大的价值之一就是让模型的回答有据可查如果模型还是靠自己的参数记忆瞎答那 RAG 就白做了。我还会在提示词里要求模型在回答时标注信息来自哪段资料这样我一眼就能看出它是真读了资料还是在编。这个习惯帮我抓出过好几次检索失效的问题。5.3 上下文窗口塞不下怎么办大模型的上下文窗口是有限的虽然现在动辄 32K、128K但塞太多内容进去一是慢二是模型对中间部分的注意力会下降业内叫迷失在中间现象。如果检索回来的片段总长度超过了预算有几个处理办法一是减少 K 值只留最相关的几个二是对片段做压缩用一个小模型先把每个片段摘要成一句话再拼进去三是分段提问把复杂问题拆成几个子问题分别检索。我个人最常用的是第一种简单直接效果也够用。6. 我踩过的那些坑以及怎么爬出来的6.1 检索结果全是无关内容先查向量归一化有一次我搭好库之后问什么问题检索出来的都是同一批不相关的块相似度分数还都挺高。排查了半天最后发现是忘了对向量做归一化。用内积做相似度时如果向量没有归一化向量的模长会直接影响内积大小导致某些长向量无论内容如何都排前面。加上faiss.normalize_L2之后问题立刻消失。这个坑的教训是用内积做相似度务必先归一化或者干脆用 L2 距离就不用操心这件事。两种方式在归一化之后其实是等价的选哪个看个人习惯。6.2 中文检索效果差嵌入模型选错了我一开始图省事用了一个英文为主的嵌入模型结果中文文档检索出来的结果惨不忍睹。原因是那个模型的训练语料以英文为主对中文语义的编码能力很弱。换成中文优化的嵌入模型之后检索准确率肉眼可见地提升。这件事说明嵌入模型的语言适配性比它的参数量更重要。一个专门为中文优化的小模型在中文任务上往往吊打一个通用的大模型。选嵌入模型时先看它的训练语料覆盖哪些语言再看它在中文语义相似度榜单上的表现。6.3 模型回答一半就断上下文超限了有次模型回答到一半突然截断我以为是模型的问题换了好几个模型都一样。后来才反应过来是上下文超限——检索回来的片段加上提示词总长度超过了模型的上下文窗口模型处理到一半就没空间了。解决办法前面提过控制检索片段的总长度。我现在的做法是在拼接上下文之前先估算 token 数超过预算就自动截断或减少 K 值。这个检查逻辑最好写进代码里别指望手动控制。6.4 入库速度慢得离谱向量化是瓶颈第一次批量入库几千份文档时我发现速度慢得让人抓狂。排查后发现瓶颈在向量化这一步因为我是逐条调用嵌入模型的没有批处理。改成批量向量化之后速度提升了好几倍。嵌入模型通常都支持批量输入一次传几十条文本进去比一条条传效率高得多。这个优化在文档量大时效果尤其明显值得一开始就做好。6.5 重启之后索引没了持久化没做对FAISS 的索引是存在内存里的程序一退出就没了。必须显式调用faiss.write_index把它写到磁盘下次用faiss.read_index读回来。我一开始忘了这一步每次重启都要重新入库白白浪费大量时间。除了索引本身前面提到的ID 到原文的映射也要一起持久化两者必须同步保存和加载否则会出现索引里的 ID 找不到对应原文的情况。7. 关于这套方案能走多远的一些个人判断搭完这套东西之后我最大的感受是RAG 的门槛其实比想象中低但要做好比想象中难。跑通一个 demo 可能一个下午就够了但要让它在你的真实文档上稳定好用需要在切块、嵌入、检索、提示词这几个环节反复调。我现在的用法是把它当成一个第二大脑的检索层。平时看到有价值的文章、技术文档、会议记录随手丢进去需要的时候用自然语言问。它不会替我做决策但能帮我把散落的信息快速聚拢到眼前这已经省下了大量翻找的时间。如果你也想搭一套我的建议是从小处着手先拿二三十份你最熟悉的文档试水把整条链路跑通感受一下检索质量。等你对各个环节的手感有了再逐步扩大文档规模。一上来就导入几千份文档出了问题你根本不知道是哪一环的锅。最后分享一个我自己的小习惯每次调整了切块参数或换了嵌入模型我都会留一组固定的测试问题重新入库后拿这组问题跑一遍对比检索结果的变化。这比凭感觉判断好像变好了靠谱得多。知识库这东西效果好不好最终还是要用具体的问题去验证。
返回列表