RAG 核心流程拆解:索引、检索、生成
RAG 系统说白了就三步:先存好、再找到、最后生成。听起来简单,但每一步都有不少坑。我刚开始搭 RAG 时,以为把文档扔进向量库就完事了,结果检索出来的东西驴唇不对马嘴。后来才明白,这三步每一步都得精心设计。
咱们把这三个模块拆开看看,每个模块到底在干什么。
1. 索引阶段:把知识变成机器能懂的结构
索引阶段的目标很明确——把原始文档变成可检索的结构化数据。我个人习惯把这个阶段分成四个子步骤:
- 文档解析:把 PDF、Word、HTML 等格式转成纯文本
- 文本分块:把长文本切成合适大小的片段
- 向量化:用 Embedding 模型把文本块转成向量
- 存储:把向量和元数据存入向量数据库
核心要点:索引质量直接决定检索效果。你想想看,如果分块策略不对,后面再怎么优化也白搭。
这里有个常见的误区。很多人觉得分块就是简单按字符数切,比如每 512 个字符一刀切。我在项目中遇到过这种做法的后果——切出来的块语义不完整,检索时经常返回半句话。后来我改用语义分块,按段落、句子边界来切,效果好了很多。
| 分块策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 固定长度分块 | 实现简单,速度快 | 语义不完整,边界生硬 | 日志、短文本 |
| 语义分块 | 保留完整语义 | 块大小不统一 | 技术文档、论文 |
| 递归分块 | 兼顾大小和语义 | 实现复杂 | 长文档、书籍 |
| 基于模型分块 | 最精准 | 速度慢,成本高 | 高精度场景 |
我的经验:对于大多数技术文档,我建议用 256-512 个 token 的块大小,配合 20% 的重叠。这样既能保证语义完整,又不会让检索结果太冗余。
2. 检索阶段:从海量知识中找到最相关的
检索阶段是 RAG 的核心。用户问一个问题,系统要从索引里找到最相关的几个片段。嗯,这里要注意,检索不是简单的「找相似」。
我常用的检索方式有三种:
- 向量检索:用余弦相似度或欧氏距离找最近邻
- 关键词检索:用 BM25 等算法做精确匹配
- 混合检索:把向量检索和关键词检索的结果融合
为什么会需要混合检索?我举个例子。用户问「苹果公司的创始人是谁」,向量检索可能找到「乔布斯创立了苹果」这个片段,但关键词检索能确保「创始人」这个词被精确匹配。两者结合,召回率更高。
避坑指南:我曾经只用了向量检索,结果用户问「2024 年的财报」时,系统返回了「2023 年的财报」,因为向量太相似了。后来加了关键词权重,才解决了这个问题。
检索阶段还有一个关键参数——Top-K。K 值太小,可能漏掉重要信息;K 值太大,又会引入噪声。我一般从 K=5 开始调,根据实际效果上下浮动。
3. 生成阶段:把检索结果变成流畅的回答
生成阶段就是把检索到的片段喂给大语言模型,让它生成最终答案。这一步看似简单,但有个大坑——上下文窗口限制。
LLM 的上下文窗口是有限的。比如 GPT-4 的 8K 上下文,你不可能把所有检索结果都塞进去。所以需要做重排序和压缩。
# 一个简单的重排序示例
def rerank_and_generate(query, retrieved_chunks, llm, top_n=3):
# 1. 用交叉编码器重排序
scores = cross_encoder.predict([(query, chunk) for chunk in retrieved_chunks])
# 2. 取 Top-N
top_chunks = [chunk for _, chunk in sorted(zip(scores, retrieved_chunks), reverse=True)][:top_n]
# 3. 构建 prompt
context = "\n\n".join(top_chunks)
prompt = f"基于以下信息回答问题:\n\n{context}\n\n问题:{query}"
# 4. 生成
return llm.generate(prompt)
生成阶段还有个容易被忽略的点——引用来源。我建议在生成时让 LLM 标注每个答案片段来自哪个文档块。这样用户能追溯来源,也方便调试。
整体流程:一张图看懂
下面这张图展示了 RAG 系统的完整流程。从原始文档到最终答案,三个模块环环相扣。
三个模块的职责对比
最后用一张表总结一下三个模块的核心职责:
| 模块 | 核心职责 | 关键组件 | 常见问题 |
|---|---|---|---|
| 索引 | 把原始文档转成可检索的结构 | 解析器、分块器、Embedding 模型、向量库 | 分块不合理、向量质量差 |
| 检索 | 从索引中找到最相关的片段 | 检索器、重排序器、混合检索策略 | 召回率低、噪声多 |
| 生成 | 基于检索结果生成最终答案 | LLM、Prompt 模板、上下文压缩 | 上下文超限、幻觉 |
这三个模块不是孤立的。索引质量影响检索效果,检索质量又直接影响生成质量。我见过不少团队只优化其中一个模块,结果整体效果上不去。嗯,记住一句话:RAG 系统的瓶颈往往在最短的那块板。