RAG完整链路拆解:离线阶段和在线阶段到底做了什么
离线阶段
原始文档
-
RAG系统的知识来源可以多种多样:PDF文档、网页、Markdown文件、数据库记录、数据库记录、邮件...不同格式的文档需要不同的解析方式,这一步通常叫做文档加载
-
值得注意的是,这一步的质量直接影响整个系统的上限。如果原始文档本身是扫描或排版混乱的PDF,解析出来的文本就会充满噪声,后续所有环节都会受损。"垃圾进,垃圾出"在RAG里体现的非常明显
文档处理(清洗与预处理)
- 解析出来的原始文本往往不能直接使用,需要做一轮清洗:去掉页眉页脚、无意义的格式符号、重复内;识别并保留文档的标题结构;过滤表格乱码、图片占位符等
- 这一步看起来琐碎,但是在实际项目里面,文档预处理往往是工程量最大、最容易被低估的部分
切片(Chunking)
- 清洗好的文档不能整篇进向量库,需要切成更小的片段。这是RAG系统设计决策最多的一个环节,直接影响后续检索的准确度
- 为什么要切?原因很直接:一篇20页的文档,用户的问题可能只和其中的某一段相关,如果把整篇文档作为一个单元存储和检索,那么检索粒度太粗(命中了整篇但是相关内容被淹没),要么上下文太长(放不进模型或者注意力被稀释)
- 切太多合适吗?这没有通用的答案,需要根据文档类型、模型的上下文窗口、业务问题的颗粒度来决定。
向量化(Embedding)
- 切好的每个chunk,都需要通过Embedding模型转换成一个向量(一个高维浮点数数组),这个向量代表了这段文字的"语义"
- 向量化的关键点:用户问题和文档chunk必须用同一个Embedding模型来处理,这样两者的向量才处于同一个语义空间,相似度计算才有意义
- 同时还需要存储对应的元数据:这个chunk来自那份文档、原文在那一页、文档的创建时间等。元数据在过滤检索结果时非常重要,比如:只看最近三个月的文档这类需求,就需要依赖元数据来实现
存入向量数据库
- 向量和元数据分别存入向量数据库和普通数据库,向量数据库的核心能力是近似最近邻搜索,能在百万向量中毫秒级找到与查询向量最相似的top-kj结果
在线阶段
Query处理
- 用户的原始问题不一定适合直接用来检索
- Query改写:把口语化的问题转成更适合检索的形式,或者把一个复杂问题拆解成几个子问题分别检索
- Query扩展:对问题做同义词扩展,提高召回覆盖面,避免因为用词差异漏掉相关文档
检索
- Query向量化之后,和向量库里面存储的所有chunk向量做相似度计算(通常用余弦相似度),召回相似度最高的top-K 个chunk,k的值通常在3-10之间
- 更完整的实现会做混合检索:同时跑向量检索(语义相似)和关键字检索(精确匹配),然后把两路结果合并,这样能兼顾"语意理解"和"关键词精确匹配"两种优势
Rerank(精排)
-
初步召回的top-K结果,相关性不一定都高。Retrank是在召回之后加一道精排:用一个专门的Cross-Encoder模型对(Query.chunk)对打分,按新分数重新排序,只保留最相关的几条
-
Rerank就是RAG里面最常见也是最有效的手段之一,代价是多一次模型推理的延迟
上下文构建
- 把最终筛选出来的chunk,加上元数据,按照一定格式拼装成上下文,连同用户的原始问题一起构建出最终的Prompt,送给生成模型
生成
- 生成模型接收完整的Prompt,基于提供的上下文生成回答,关键点是Prompt里面有明确的引导指令---让模型优先依据资料回答,而不是依赖自身参数知识,并要求在答案里标准来源
常见误区
RAG = 向量检索
向量检索只是在线阶段一个步骤,完整的RAG系统话包括文档解析、Chunking策略、Embedding选型、元数据管理、Rerank、Prompt设计等一系列工程工作,缺少任何一个环节都会拖累整体的效果
只要模型够强,Chunking随便切换就行
Chunking是RAG里面最底层的基础设施,模型再强,如果检索到的chunk要么太短要么太长,生成质量都会大打折扣,模型能力无法弥补检索质量的缺陷
Rerank一定要加
Rerank是由代价的:多一次模型调用意味着更高的延迟和成本,对于实时性要求高、或者文档量较小的场景,精确的Embedding+合理的top-K往往已经足够,先评估是否真的需要,再决定是否加