
先说说我自己的经历。去年我接手一个内部文档问答的需求管理层给的目标是“让 AI 能回答员工手册里的问题”。我第一反应是直接塞 Prompt结果模型一本正经地胡说八道。后来换成 RAG才真正把回答准确率从“听天由命”拉到“可交付”。所以我特别理解为什么“初学者的 RAG”这个标题下面会挂那么多热搜词——因为大家都想找一个靠谱的切入点而不是再看一遍概念科普。这篇东西我会按自己真实跑通项目的顺序来写先从原理上搞明白 RAG 解决了什么问题再把整个链路拆开看然后带你在 Mac 上搭一个能用的本地知识库最后聊聊 RAG 的瓶颈、不同知识库的选型以及那些新手必踩的坑。全文尽量不堆术语遇到必须用的概念我会用生活化的方式解释。1. RAG 到底是什么先别急着写代码1.1 为什么大模型需要外挂知识库大模型的训练数据是有截止时间的它只能根据训练时见过的内容来回答问题。这意味着两件事第一你在 2025 年写的内部规章制度模型根本不知道第二即便它“知道”一些通用知识也无法针对你公司的具体流程给出准确答案。有人会说那我微调一个模型不就行了微调确实能让模型“学会”某种风格或特定技能但它本质上是把知识压缩进模型参数里。这个过程成本高、周期长而且当你新增一份文档时又得重新训练一次。更麻烦的是微调后的模型依然可能把两个相似文件里的细节搞混。RAG 的思路很直接检索增强生成先到知识库里把相关片段捞出来再把这些片段和用户问题一起交给大模型让它基于给定的材料来回答。打个比方微调是让一个员工把制度背下来RAG 是直接给他一份带目录的资料遇到问题先翻资料再作答。后者显然更适合信息频繁更新的业务场景。1.2 RAG 和长上下文的区别大模型的上下文窗口越做越大有人就问了既然模型能一次读几十万字为什么还要搞 RAG直接把文档全塞进去不就行了这个想法乍看合理实际跑过就知道不行。首先是成本长上下文的 Token 消耗是线性增长的一次问答吃掉几十万 Token按商用模型的价格算很快就会心疼。其次是注意力问题模型在处理超长文本时对中间部分信息的关注度会下降你塞一万页文档进去它反而抓不住关键信息专业说法叫“中间丢失”现象。在金融、医疗这些对准确率要求高的场景这是不可接受的。所以 RAG 的真正价值不是“塞更多”而是“找更准”。它先用检索手段把范围缩小到几段最相关的材料再让模型精读这几段既省钱又提高准确度。理解这一点后面学所有组件都不会跑偏。2. 一个 RAG 项目的骨架从零搭建的全流程拆解2.1 数据接入与文本拆分任何 RAG 项目的第一步都是把各种格式的资料变成干净的纯文本。这部分的工作量往往占整个项目的六成以上却最容易被新手忽略。文档类型五花八门PDF、Word、Markdown、HTML甚至扫描件。我的建议是不要一上来就用重型工具先把文件类型摸清楚。PDF 用 PyMuPDF 或 pdfplumber 提取Word 用 python-docx纯文本直接用 Python 读取。扫描件需要先做 OCR这一步建议用 PaddleOCR 这类开源方案能识别中英文混排。提取完文本后下一步就是拆分。这是 RAG 的“地基”拆分质量直接决定检索效果。新手最常见的错误是贪心觉得块越大保留的上下文越完整于是把整个章节塞成一块。这种块放进向量数据库后检索时很难被精确命中因为用户的问题往往只对应块里的某一小段。反过来说块太小又会丢失上下文模型拿到一个孤零零的句子根本没法回答。2.2 向量化与存储拆好的文本块要变成计算机能算相似度的东西这就用到 Embedding 模型。Embedding 做的事情是把一段文字映射成一个几百到几千维的向量语义相近的文本在向量空间里距离也近。这是 RAG 能“理解”语义的关键。我刚开始做的时候对向量维度没有概念后来对比才发现维度越高理论上能表达的信息越多但存储和计算成本也越高。中文场景下常见的开源 Embedding 模型有 BGE 系列、M3E 系列它们会把文本映射成 768 维或 1024 维的向量对一般业务足够了。向量有了要找个地方存。市面上的向量数据库很多初学者不用纠结Chroma 和 FAISS 是最容易上手的两个。Chroma 是纯 Python 实现代码侵入性低适合做原型FAISS 是 Meta 开源的性能好适合数据量上来之后迁移。我个人的建议是先拿 Chroma 跑通流程等数据量超过几十万条再考虑 Milvus、Qdrant 这类服务化方案。2.3 检索与生成RAG 的第三步是检索也就是把用户的问题向量化然后去向量库里找最相似的几个文本块。这个环节有两个关键参数一个是返回多少条结果另一个是相似度阈值。我默认会取 top-k4也就是找回 4 块最相关的文本。阈值的作用是过滤“看似相关、实则无关”的结果具体数值要看 Embedding 模型的分布我在项目里一般先设为 0.3再根据实际效果调。检索到文本块后把它们和用户问题拼成一条 Prompt交给大模型。这里的 Prompt 值得多花心思。我推荐的结构是先声明“你是一个问答助手只能根据以下资料回答”然后列出检索到的文本块最后给出用户问题。同时必须加上限制条件“如果资料中没有答案直接说不知道不要编造”。这样写能显著减少幻觉。3. Mac 上搭一个本地 RAG实操记录3.1 环境准备与工具选型“怎么在 Mac 上搭建 RAG 知识库”是热搜里的高频词我当初就是在 MacBook 上一步步跑通的。先说结论M1 或更高版本的 Mac 完全够用不需要 GPU 也能跑本地模型只是速度慢一点。环境准备分三步。第一步是安装 Python 3.10 以上版本建议直接用 Homebrewbrew install python3.11。第二步创建虚拟环境避免依赖冲突python3 -m venv rag_env然后source rag_env/bin/activate。第三步安装核心依赖包我给一份最简清单pip install chromadb pip install sentence-transformers pip install langchain pip install pypdf pip install ollama这里面的 Ollama 是用来跑本地大模型的工具它支持在 Mac 上加载 Llama、Qwen 等开源模型而且不需要自己配置复杂的运行环境。安装好之后拉一个适合中文的模型比如ollama pull qwen2.5:7b内存 16G 的 Mac 就能跑得动。有人会问为什么不直接用 OpenAI 的 API我的看法是如果只是学习本地模型免费且没有数据隐私风险足够用来跑通全流程。等真正要做商用项目再换成 API 或部署在服务器上的模型。3.2 关键参数怎么定新手搭 RAG 的时候最迷茫的就是参数怎么调。我把前几次实验中让人抓狂的几个参数列出来给你一个可复用的起点。第一个是文本块大小 chunk_size。我默认设为 300 到 500 个字符这个范围来自一个朴素的考量中文一句话大概 20 到 50 个字符300 字左右的块大概能包含一个完整论点检索命中后模型的上下文也够用。第二个是重叠区 chunk_overlap我设为 50 到 80 个字符。重叠区的作用是防止一个完整知识点被硬生生拆到两块里给检索留一点缓冲。第三个是检索数量 top_k上面说过默认 4。如果你发现答案总是信息不足可以把 top_k 提到 6如果发现模型经常被无关内容干扰就降回 3。第四个是相似度阈值这个更依赖你的 Embedding 模型我习惯先跑几条真实问题看相似度分数分布再决定阈值。比如大多数正确命中的分数是 0.35 到 0.45那阈值就设在 0.3 左右。3.3 从写代码到能问答跑通一个最小可用的 RAG 只需要三处代码加载文档并拆分、生成向量并写入数据库、检索并调用模型。为了方便新手参考我贴一段我在 Mac 上验证过可以跑的索引代码from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma loader PyPDFLoader(员工手册.pdf) documents loader.load() splitter RecursiveCharacterTextSplitter( chunk_size400, chunk_overlap60, separators[\n\n, \n, 。, , , , , ] ) docs splitter.split_documents(documents) embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) vectordb Chroma.from_documents(docs, embeddings, persist_directory./db) vectordb.persist()检索和问答的代码更短question 年假制度是怎么规定的 retrieved vectordb.similarity_search_with_score(question, k4) context \n\n.join([doc.page_content for doc, score in retrieved]) prompt f你是一个问答助手只能根据以下资料回答。 如果资料中没有答案请直接说不知道不要编造。 资料 {context} 问题{question} 把构造好的 prompt 发给 Ollama 里的模型就能得到答案了。第一次跑通的那一刻你会觉得 RAG 不过如此但后面会有无数细节等着你。4. RAG 知识库、知识图谱和结构化知识库到底怎么选4.1 三种知识库的核心区别热搜里有“KG知识库、RAG知识库和结构知识库区分以及应用场景”这个关键词说明很多人在做技术选型时不知道该用哪种。我用一个例子说清楚它们的差异。假设你有一批车险理赔的文档。结构化知识库的做法是把案件编号、出险时间、赔付金额提取出来存成一张数据库表。它可以精确回答“2024年3月赔付总额是多少”但回答不了“哪些案例说明验伤环节容易出问题”因为这类语义信息没有被结构化提取。RAG 知识库的做法是把理赔文档切成若干块存进向量库针对问题检索相关段落。它能回答前面那个语义问题但对“统计类”问题无能为力因为检索到的几块文本无法做全局聚合。知识图谱 KG 的做法是把文档里的实体和关系抽出来比如“张三-投保于-平安保险”“平安保险-理赔-李四事故”构建成一张图。它擅长回答多跳关系问题比如“张三的车险是哪家公司的理赔员处理的”这是 RAG 很难做到的因为 RAG 需要两跳甚至三跳才能把信息串起来。4.2 不同场景怎么选型我的经验是三类知识库不是互斥的而是按问题类型组合使用。如果你的用户主要是自然语言查找比如“帮我找找报销流程”RAG 最合适。如果要做精准筛选和统计分析比如“上月销售额超过十万的客户”结构化数据库最合适。如果问题是复杂的关系推理比如“A 公司和 B 公司之间的股权控制链”知识图谱最合适。“ontology rag”这个词也经常出现在相关讨论里。Ontology 是知识图谱的概念指一套定义好的概念和关系模型。在传统 RAG 里加入本体约束可以让检索在特定领域内更精准但实现成本偏高新手不需要从这一步开始。“wiki和rag”也是很多人混淆的。Wiki 是一种内容组织方式RAG 是一种技术架构。你可以把 wiki 页面喂给 RAG也可以把 RAG 做成一个 wiki 系统的问答入口。两者不在同一个纬度别把它们对立起来。5. RAG 的瓶颈为什么效果不如预期5.1 检索召回不足“RAG瓶颈”这个话题我深有体会。很多新手按教程搭好项目一测发现效果很差于是怀疑自己的代码有问题。其实大部分情况都出在检索环节。检索的核心问题可以用“召回率”来描述排在前面的结果里到底有多少是真正相关的。如果检索器找回来的 4 块都不相关那模型拿到的就是一堆垃圾再强的模型也回答不好。召回不足的典型原因是文本块切得不合适或者 Embedding 模型和你的语料领域不匹配。比如你的文档全是法律条文却用了通用领域的 Embedding 模型语义匹配效果会打折扣。解决召回不足我的第一招不是换模型而是先检查文本块质量。我会随机挑几类问题去检索把前 10 条结果打出来看如果发现相关文本被切碎或混入了大量噪音就去调 chunk_size 和 overlap。这个笨办法比盲目换模型有效得多。5.2 重排被大多数人忽略的一步检索出来 top_k 条结果后直接全部塞给模型是一种新手做法。更好的做法是加入重排 rerank 环节。重排的意思是先用向量检索粗筛出比如 20 条候选再用一个专门的重排模型对它们做精细排序最后取前 4 条。为什么要多此一举因为向量检索的“相似”是语义近似不是“回答问题所需信息”的精确匹配。重排模型会同时看问题、答案片段和上下文打一个更贴合实际需求的分数。我做过一次对比实验在同样的数据集上加入重排后回答的准确率提升了大概 15%。这个投入产出比非常高强烈建议你在项目跑通后立刻加上。重排模型的选型也很简单中文场景我用过 BGE-reranker-base效果稳定直接用 sentence-transformers 就能加载。计算成本比向量检索高但因为它只处理粗筛后的 20 条不会拖慢整体响应。5.3 评估与迭代RAG 项目最大的隐形瓶颈是没有建立评估机制。很多人调了一下参数觉得回答“好像好了一点”就继续往下写最后上线了都不知道系统在真实数据上的表现。我建议每做一个知识库都要准备一份评估集。这份评估集至少包含 30 到 50 个真实用户问题并为每个问题标注预期答案的要点。每次改动参数就用这份评估集跑一遍统计回答中包含预期要点的比例。没有评估集的调优都是玄学有这个数字在你才能判断到底是检索的问题还是生成的问题还是 Prompt 的问题。6. 常见问题与排查技巧实录6.1 RAG 知识库能存储图片吗“rag知识库能存储图片嘛”这个热搜词我猜很多人是被“多模态”这个概念带偏了。直接给结论常规的文本型 RAG 不能直接存储图片的内容语义因为 Embedding 模型只处理文字。但这不是死路。第一种做法是给图片建立文本描述比如用 OCR 提取图片里的文字或者写一段图片说明文字把这段描述作为图片的“文本块”存进向量库。用户问问题时检索到的是描述文本再去调用图片本身展示。这种方案工程上最简单。第二种做法是用多模态 Embedding 模型比如 CLIP把图片和文本都映射到同一个向量空间图片就能直接被检索。但多模态索引和检索的复杂度会高一个档次不建议初学者作为第一个项目尝试。6.2 有没有本地的 RAG 文本拆解工具这个问题的答案是有而且不止一个。很多人搜索这个关键词是因为在线拆分工具存在数据隐私问题想把整个流程放在本地跑。我试过几套方案给你一个优先级排序。最推荐的是 LangChain 的RecursiveCharacterTextSplitter上面代码里已经用过。它足够灵活可以指定分隔符优先级还能通过len_function控制块长度。第二个是 unstructured这个库能批量解析各种格式且支持本地运行适合文档格式复杂的场景。第三个是 LlamaIndex 内置的SentenceSplitter它按句子边界切分语义完整性更好但响应速度慢一点。如果你需要处理非常规格式比如扫描 PDF我建议用 OCR 工具先转文本再用上述拆分器。不要试图用拆分器直接处理扫描件那是无效的。6.3 常见报错与排查速查表我整理了一份新手最常见的报错速查表帮你少走弯路。这些坑我在实战里几乎都踩过。现象可能原因排查方法检索结果完全无关Embedding 模型与语料语言不匹配确认模型是否为中文优化换 bge-small-zh-v1.5 试试回答内容过于简短chunk_size 太小调到 400 到 600 字符模型答非所问top_k 拉入了太多噪音调低 top_k 到 3并加入阈值过滤回答出现明显编造Prompt 缺少“不知道”约束在 Prompt 里加上“资料中没有答案时直接说不知道”索引速度极慢未使用批量写入向量库用add_documents批量写入而非逐条插入PDF 解析出乱码扫描件没有走 OCR对扫描件先 OCR再进入文本拆分流程这个速查表是我的经验浓缩但它不能替代“看日志”这个动作。我的习惯是每次改完参数就打印检索结果的实际文本肉眼确认问题出在哪个环节。你输入的 prompt 再好看如果检索到的内容不对一切都是白搭。6.4 从 RAG 往 RAG Agent 走热搜里有“rag智能体”我最后多说一句。传统 RAG 是“检索一次、生成一次”的直线流程适合简单问答。但真实场景往往需要多轮交互比如先查文档再查数据库或者根据中间结果决定下一步动作。这时可以把 RAG 封装成 Agent 的一个工具让大模型自主决定何时检索、检索几次。新手不用一上来就搞 Agent先把单轮 RAG 跑稳再去研究工具调用和记忆机制。我见过不少项目单轮检索都还很烂就开始搭复杂的 Agent 流程最后问题定位无从下手。先把地基打牢再往上盖房这是我在这个项目里学到的最重要一课。