2、RAG系统架构:RAG整体流程拆解、索引阶段、检索阶段、生成阶段、各模块职责
好,咱们直接进入正题。RAG 这个架构,说白了就是把「检索」和「生成」这两件事串起来。你想想看,大模型再厉害,它也有知识截止日期,也有幻觉问题。RAG 就是给它配了个「外挂知识库」,让它能查着资料再回答。
我个人习惯把 RAG 拆成三个阶段:索引、检索、生成。这三个阶段各司其职,缺一不可。下面我一个个给你拆开讲。
一、索引阶段——先把知识「存好」
索引阶段是 RAG 的地基。地基没打好,后面检索和生成全是空中楼阁。我见过太多人一上来就调模型,结果发现召回的内容乱七八糟,最后查了半天——原来是分块策略有问题。
索引阶段的核心任务就四个字:清洗入库。具体来说,包含这几步:
- 文档加载:把 PDF、Word、网页、数据库里的内容读进来。不同格式有不同的解析器,PDF 尤其坑多,我后面会专门讲。
- 文本分块:把长文档切成小段。切多大?切太短语义不完整,切太长检索不精准。我个人习惯用 256-512 tokens 作为一个块,重叠 10-20%。
- 向量化嵌入:用 embedding 模型把文本块转成向量。这一步决定了「语义相似度」的质量。
- 存储到向量库:把向量和原始文本一起存进向量数据库,比如 FAISS、Milvus、Chroma 等。
核心要点:索引阶段是离线完成的,不涉及用户请求。你可以在服务器上慢慢跑,但一定要跑得准。
我的经验:分块策略没有银弹。我曾经在做一个法律文档项目时,发现 512 tokens 的块总是把「法条」和「案例」切到一起,导致检索时语义混淆。后来改成按段落分块 + 固定大小兜底,效果好了很多。
二、检索阶段——把相关的「找出来」
检索阶段是 RAG 的核心竞争力。说白了,就是用户问一个问题,你去知识库里把最相关的几段内容捞出来。
这个过程分三步:
- 查询向量化:用户的问句也要转成向量。注意,这里用的 embedding 模型要和索引阶段保持一致,否则向量空间不对齐,检索结果会跑偏。
- 相似度搜索:在向量库里做最近邻搜索,常用的度量是余弦相似度或欧氏距离。我一般用余弦相似度,因为它对向量长度不敏感,更适合语义匹配。
- 召回 Top-K:返回最相似的 K 个文本块。K 值怎么设?太小可能漏信息,太大又塞给大模型太多噪音。我通常设 3-5 个,然后根据实际效果微调。
避坑指南:我曾经遇到过一个问题——检索出来的结果看似相关,但语义上完全不对。后来发现是 embedding 模型选错了。通用领域的用 text-embedding-ada-002 没问题,但如果是医疗、法律等垂直领域,一定要用领域微调过的 embedding 模型。
三、生成阶段——让大模型「说人话」
生成阶段是 RAG 的最后一公里。检索回来的文本块,要和大模型的原始能力结合起来,生成一个流畅、准确、有依据的回答。
这一步的核心是 Prompt 工程。你需要把检索到的上下文和用户的问题拼成一个结构化的 Prompt,然后喂给 LLM。
一个典型的生成 Prompt 长这样:
你是一个知识问答助手。请根据以下提供的上下文内容,回答用户的问题。
如果上下文中没有相关信息,请直接说「我不知道」,不要编造答案。
上下文:
{检索到的文本块1}
{检索到的文本块2}
{检索到的文本块3}
用户问题:{用户输入}
请给出回答:
嗯,这里要注意几点:
- 上下文要按相关性排序:最相关的放前面,LLM 对开头的内容更敏感。
- 明确告诉模型「不知道就说不知道」:这是减少幻觉的关键。
- 控制上下文长度:别把整个知识库都塞进去,LLM 的上下文窗口是有限的。
核心要点:生成阶段不是让大模型「自由发挥」,而是让它「带着镣铐跳舞」。检索到的上下文就是镣铐,限制了它的想象空间,但也保证了回答的准确性。
四、各模块职责一览
为了方便你理解,我把三个阶段的模块职责整理成了一张表:
| 阶段 | 模块 | 核心职责 | 常见工具/技术 |
|---|---|---|---|
| 索引阶段 | 文档加载器 | 从不同来源读取原始文档 | PyMuPDF、Unstructured、LangChain 文档加载器 |
| 文本分割器 | 将长文本切分成语义完整的块 | RecursiveCharacterTextSplitter、SentenceSplitter | |
| Embedding 模型 | 将文本转换为向量表示 | text-embedding-ada-002、BGE、M3E | |
| 向量数据库 | 存储向量并支持高效检索 | FAISS、Milvus、Chroma、Pinecone | |
| 检索阶段 | 查询编码器 | 将用户查询转为向量 | 与索引阶段相同的 Embedding 模型 |
| 相似度计算器 | 计算查询向量与文档向量的距离 | 余弦相似度、欧氏距离、内积 | |
| Top-K 选择器 | 选出最相关的 K 个结果 | K 值通常设为 3-5 | |
| 生成阶段 | Prompt 组装器 | 将上下文和问题拼接成结构化 Prompt | LangChain PromptTemplate、自定义模板 |
| LLM 推理引擎 | 基于 Prompt 生成最终回答 | GPT-4、Claude、文心一言、ChatGLM |
五、三个阶段的协作关系
你想想看,这三个阶段其实是一条流水线:
- 索引阶段是「备料」——把原材料加工成半成品(向量 + 文本块)。
- 检索阶段是「拣货」——根据用户的需求,从仓库里挑出最合适的几件。
- 生成阶段是「组装」——把挑出来的零件和用户的需求组装成最终产品。
任何一个环节出问题,最终的回答质量都会打折扣。我见过一个项目,检索阶段召回率只有 60%,结果生成阶段再怎么调 Prompt 也没用——巧妇难为无米之炊嘛。
我的建议:刚开始做 RAG 时,别急着调模型。先把索引阶段的数据质量搞上去,再优化检索的召回率,最后才去调生成阶段的 Prompt。这个顺序反了,你会走很多弯路。
好了,这一章的内容就到这里。RAG 的整体架构其实不复杂,但每个阶段都有很多细节值得深挖。后面我们会逐一展开,把每个阶段的坑和技巧都讲透。
公众号:蓝海资料掘金营,微信deep3321