先说个现象。这几年做 AI 应用,我身边不少人从“大模型真厉害”到“这玩意儿怎么老胡说八道”,中间只隔了一个生产环境。问它公司制度,它给你编一条;问它项目历史,它把别人的事安到你头上。有些团队第一反应是换更大的模型,结果成本翻了几倍,该编还是编。其实大部分业务场景压根用不着微调,也不需要换模型,你应该先把 RAG 搞清楚。
RAG 全称 Retrieval-Augmented Generation,检索增强生成。我习惯把它理解成“给大模型开卷考试”:模型不需要靠记忆硬答,而是先从你的知识库里查资料,再基于查到的内容作答。这篇我不打算写成学术论文,就按我从零开始做 RAG 项目的顺序,把原理、选型、实操、进阶、踩坑全捋一遍。适合准备做知识库问答的开发者、想用企业内部文档搭问答服务的工程师,以及被幻觉问题折磨到怀疑人生的产品经理。
1. 先理解问题:大模型的“胡说八道”到底从哪来
1.1 大模型的记忆不是数据库
先说一个很多人忽略的事实:大模型本质上只是一个“接龙游戏高手”,它的训练目标就是预测下一个 token,而不是建立一个事实数据库。你问它问题,它不是在“查档案”,而是在根据概率“续写一段看起来合理的文本”。
这就带来三个天生的问题。第一,知识有截止日期,2023 年之后发生的事情它一概不知道,除非有人帮它补上;第二,参数容量有限,训练语料里低频的知识它根本记不住,或者记成了模棱两可的样子;第三,训练数据本身就有噪声,网上的错误信息它一样会学进去。
所以你让大模型直接回答你的私有业务问题,它最好的情况是“记混了”,最坏的情况是“自己编一个”。这不是模型笨,是它压根没有你那些资料的入口。
1.2 RAG 的本质:让模型学会查资料
RAG 的思路很简单:既然模型不知道,那就先查,查完再答。整个流程可以拆成三个动作——先把你手里的文档切成小块、做成索引;用户提问时,从索引里找出最相关的几块内容;把这些内容塞进提示词,让模型“看着资料回答”。
这不是什么高深理论,跟你上班查 wiki 写方案一模一样。但正是这个朴素的思路,解决了大模型落地的两个关键痛点:一是知识可以实时更新,你不用重新训练模型;二是回答有出处,模型不能凭空编造,因为提示词里明确写了“仅基于以下内容回答”。
我在实际项目中经常跟团队说一句话:RAG 不是模型能力的替代品,而是模型能力和业务数据之间的桥梁。它让大模型从“凭记忆瞎猜”变成了“查资料作答”,这种转变在知识库问答、客服辅助、文档分析这类场景里,效果立竿见影。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 搞懂 RAG 的三个阶段:索引、检索、生成
2.1 一条完整链路在做什么
RAG 的标准流程分三段,听起来简单,但每一段都有大量细节。
第一阶段是索引构建(Indexing)。你要把 PDF、Word、Markdown、网页这些乱七八糟的原始文档,清洗成纯文本,切成适当大小的片段(chunk),用嵌入模型(Embedding Model)把每个片段转成向量,最后存进向量数据库。这个阶段有点像给一本书编目录——但比编目录难,因为切多细、怎么切,直接决定了后面能不能查到。
第二阶段是检索(Retrieval)。用户输入问题后,先把问题也转成向量,然后去向量库里找最相似的几个片段。这个阶段的目标不是“找到正确答案”,而是“找到可能包含正确答案的资料”,所以通常要放宽一点,多召回一些候选。
第三阶段是生成(Generation)。把检索到的片段和用户问题一起拼成一个结构化提示词,交给大模型。模型不能自由发挥,只能基于你给的片段回答。如果片段里没有答案,它必须承认不知道,而不是硬编。
你可以把这个过程理解成做菜:索引阶段是备菜,检索阶段是配菜,生成阶段是炒菜。前面任何一步做得不好,最后端上桌的菜都好不了。
2.2 文档切分:最容易被低估的环节
我在很多项目里都遇到过这种情况:Embedding 模型选得挺好,向量库也搭得没什么毛病,但检索结果就是一团糟。查到最后,问题往往出在文档切分上。
切分的核心矛盾是“尺度”问题。切得太细,比如一句话一切,检索时容易匹配到碎片,抓不住完整的上下文;切得太粗,比如整章一切,向量会被大量无关信息稀释,相似度计算出来的结果很不稳定。这两种情况都会让检索召回质量直线下降。
我常用的切分策略有这么几种:
- 固定字符切分:按固定长度硬切,代码简单,但会拦腰切断语义。
- 递归字符切分:按“段落 → 句子 → 标点”的优先级逐层往下切,是大多数场景的默认选择。
- 结构化切分:针对 Markdown、HTML、代码文件按标题、章节、代码块切分,能最大程度保留文档本身的层级结构。
- 语义切分:算相邻句子的向量相似度,相似度掉到某个阈值就切一刀,效果不错但计算成本高。
具体参数上,我习惯的中文场景起点是:块大小 500 到 800 字符,重叠长度 50 到 100 字符。为什么要有重叠?因为切分点在语义中间时,关键信息正好落在边界上的概率不小,重叠部分能起到“保险带”的作用。
这里有个容易被忽略的细节:你切出来的 chunk 最终是要被当成一段独立文本去做语义匹配的,所以每个 chunk 内部必须有完整的信息单元。表格数据要整行整行地保留,合同条款要按条款切,菜谱要把用料和步骤放在一起。拿合同举例,如果把甲方名称和违约条款切成两个 chunk,用户问“甲方违约了怎么办”,检索根本联系不到一块儿。
2.3 Embedding 和向量检索:匹配的核心引擎
切
