RAG进阶指南:深入了解每个组件
第一课:复杂文档处理与解析
我们要解决的核心问题是计算机领域的一句老话:Garbage in,garbage out。如果喂给系统的文档内容本身就是乱码、错位的,那么后面的切块再精细,Embedding模型再强大,找出来的也只会是一堆垃圾。
现实中的文档陷阱
真实世界的文档通常是PDF,他们天生是为了人眼排版设计的,而不是为了机器读取设计的。如果你用最基础的提取工具(Python)去去读,会遇到这些问题:
- 双栏排版灾难:很多论文与报告是左右两栏排版的,基础工具会从左到右,从上到下去死板的读取,导致左边栏一半句子直接拼接在右边栏的一半句子,语义完全被毁。
- 表格搅碎机:财报里密密麻麻的表格,如果直接提取纯文本,原本对齐的列名和数据会变成一堆毫无逻辑的数字和汉字,大模型根本看不懂谁对应谁。
- 页眉页脚噪音:每一页都有的“机密文件”、“第几页”等无关信息会被频繁的切入你的chunk中,成为干扰检索的噪音
- 图表变黑洞:重要的趋势图、架构图在纯文本提取中会直接消失,丢失大量核心信息。
解决办法
为了解决这些问题,我们要引入更聪明的“解析流水线”
- 版面分析
- 不再盲目提取文字,而是先引入视觉模型(比如layout或开源工具Unstructured)给文档拍个片子,系统会识别出哪里是标题,哪里是正文,哪里是表格,识别清楚构造后,再按区块有逻辑地提取文字。
- 表格地特殊对待
- 表格是RAG的重灾区,通常有两种做法
- 把表格转为结构化的格式(如Markdown或HTML表格),保留行列关系后再进行切块
- 表格摘要法:用一个大模型先把这个表格看一遍,生成一段文字(例如:该表格展示了2003年Q1到Q4的营收,XXX),然后把这段摘要也存入向量库
- 表格是RAG的重灾区,通常有两种做法
- 图文多模态
- 遇到扫描版的PDF或者包含重要图片,我们会调用ORC(光学字符识别)技术提取文字,或直接使用具备视觉能力的大模型为图片生成详细的文本描述,让图片的知识也能被检索
- 清洗与过滤:写代码用正则表达式或专门的清洗逻辑,自动干掉那些毫无意义的页眉、页脚、水印和乱码
总结
在进阶 RAG 中,“解析”的地位甚至比大模型本身更重要。 一个花了很多心思把 PDF 里的表格和双栏排版完美还原的系统,用一个普通的开源小模型,其回答效果往往能秒杀那些文档解析做得烂、却用了最顶级昂贵大模型的系统。
第二课:高级切块策略
业界目前最流行的高级切块策略主要有两个,他们分别解决了不同的痛点
语义切块
- 痛点:传统切块到了尺寸就切,会导致相关的上下文被强行分开
- 做法:
-
- 语义切块不再数多少个字,而是看意思有没有转变
- 系统先把文章拆成一个个独立的句子
- 计算相邻句子的语义相似度
- 如果相邻句子语义相似度很高,说明在聊同一个话题,合并到一个chunk里
- 一旦发现相似度很低,说明切换话题了,在这里进行切割
- 效果:切出来的chunk长度不一,但每一个都保证了内部话题的高度一致性
父子块策略
- 痛点:为了让向量库找的准,chunk越小越好,但是为了让大模型回答全面,chunk越大越好,产生了矛盾
- 做法:
-
- 切出父块:先把文档按较大颗粒度(段落或章节)切分成大块,包含了丰富的上下文
- 切出子块:把每一个父块继续细切成若干个很小的子块
- 存入与检索:把大块和小块都在后台关联起来,用户提问时,系统只拿问题去和子块进行向量匹配。因为子块小,匹配精准
- 偷梁换柱:一旦命中了某个子块,就把这个子块对应的父块提取出来,作为context与问题拼接成prompt
- 效果:既实现了小块的高精度检索,又实现了大块的丰富上下文
第三课:用户意图理解与查询转换
RAG的核心思想是永远不要相信用户的原始提问,在去向量库搜索之前,先让大模型把问题洗一遍。 在真实的业务场景中,用户往往非常“懒”,或者他们自己都不知道该怎么精准提问。他们可能会只敲两个字:“病假”,或者紧接着上一个问题没头没脑地问一句:“那年假呢?” 如果系统直接拿“那年假呢?”去做 Embedding 搜索,找出来的可能全是废话,因为丢失了“员工”、“材料”等核心关键词。
主要有以下三种策略洗问题
查询重写
这是最常见的、最基础的操作,系统会在后台偷偷调用一次大模型,结合聊天历史,把用户含糊不清的口语化提问,重写成适合向量检索的标准结构化提问。
- 用户原问题: “那年假呢?”
- 大模型重写后: “员工请年假需要提供哪些材料以及审批流程是什么?”
- 效果: 补全了上下文,大幅提高了命中准确率。
多路召回/多查询生成
有时候,用户的问题非常复杂或者非常宽泛,单个向量很难完全捕捉他的多重语义,
比如用户问:公司对于新员工入职培训和转正考核有什么规定?
如果直接搜,可能搜到培训和考核二者其中之一
做法:
为了让大模型从不同视角看问题,把这个问题拆分成或平行扩展成3-5个不同的子问题。
- 子问题 1:“新员工入职培训流程是什么?”
- 子问题 2:“新员工转正考核的标准有哪些?”
- 子问题 3:“新员工试用期管理制度”
执行:系统会拿着这3个问题分别去向量库里搜,将所有找出来的chunk合并、去重、最后投喂给大模型
退一步提问
这是一个非常巧妙地思想,专门对付那些钻牛角尖的问题
用户问了一个及其细节的问题: “2024年3月修订的报销制度第三版中,关于高铁一等座的报销额度是多少?” 向量库里可能刚好没有这么精准的对应片段,导致检索失败。
- 做法: 让大模型先“退一步”,生成一个更宏观、更基础的问题。
- 退一步问题: “公司最新的差旅交通报销标准是什么?”
- 执行: 系统同时去搜“原问题”和“退一步问题”。往往宏观问题能召回包含答案的完整段落,从而曲线救国,回答出细节问题。
第四课:路由查询
好的RAG会思考一个根本的问题:用户问的所有问题,都必须去查向量数据库吗?
在基础RAG的思维里,流程是单向的:用户提问->Embedding->查向量库->大模型回答。
但这在真实的业务里会引发巨大灾难:
场景 A(闲聊): 用户输入“你好,你是谁?”。系统居然也拿这句话去做 Embedding,去公司规章制度的向量库里找了几个最相关的 Chunk(比如“新员工问候礼仪”),然后一本正经地回答。这显得非常蠢。
场景 B(精确数据): 用户问“帮我查一下员工编号 10086 的上个月真实报销金额”。正如你提供的基础资料里指出的,普通数据库更擅长精确查询和条件筛选,而向量数据库擅长的是语义和模糊搜索。如果你去向量库里搜,根本算不出准确的数字,大模型大概率会胡编乱造(幻觉)。
解决方法:路由与分发
优秀的RAG系统会在大模型与数据库之间建立一个分发中心,当用户的问题(甚至是被重写过的问题)过来时,系统会先判断他的意图,然后把它引流到最合适的通道去。
大模型逻辑路由
这是最灵活的方式,系统会先发一个非常简短的Prompt给大模型,让大模型做选择题。
- 提示词示例: “你是一个路由助手。现有三个工具:1.
Vector_DB(用于查询公司政策、流程、概念等文字资料);2.SQL_DB(用于查询具体员工数据、财务金额、考勤打卡记录);3.Chitchat(用于日常打招呼)。请判断用户的提问应该走哪个工具,仅输出工具名称。” - 用户问:“婚假怎么休?” → LLM 输出
Vector_DB。 - 用户问:“张三昨晚加班到几点?” → LLM 输出
SQL_DB。
语义路由
大模型路由虽然聪明,但每次都要调用大模型,会浪费token和算力,为了追求极致性能,可以使用语义路由:
- 系统会在本地预先定义好几个意图空间(比如几十个闲聊的句子算出一个闲聊向量聚类,用几十个查数据的句子算出一个查数据的向量聚类)
- 用户提问一进来,立刻做Embedding,然后用数学方法算一下这个向量离哪个意图聚类最近,瞬间完成分发。
多路并行与融合
有些问题及其复杂,连路由都无法只选一条路。比如用户问: 最近咱们公司关于新能源汽车的销量数据如何?另外,针对新能源销售的提成政策是什么?
这时候,我们可以用Prompt让大模型将问题拆分,让模型去选择每个子问题对应的工具,再把各自的子问题发给SQL_DB去查销量,发给Vector_DB去查政策,再把两个结果聚合发给大模型。
第五课:混合检索
我们知道Embedding只是把文本转为向量,并不是真的像人类一样理解全文真相,他只是在压缩语义,他对精确的专有名词、特定编号、生僻字极度不敏感。
- 场景举例: 假设用户问:“帮我查一下产品型号为
XJ-998-Pro的操作手册。” - 向量库的灾难: Embedding 模型在把这句话变成数字向量时,可能会觉得它跟
XJ-997-Max或者XJ-888-Pro的“语义”都差不多(因为它们都在聊某款科技产品)。结果,向量检索可能会把其他型号的手册排在最前面返回给你。 - 我们在入门阶段的时候说了,普通数据库擅长精确查询,而向量数据库擅长语义查询,但这种情况既会丢失精确度,又无法找到正确的资料。
为了解决这个问题,RAG不再在一棵树上吊死,而是采用双管齐下的策略,混合检索的核心就是:
字面精确匹配+语义模糊匹配=全面且精准
- 一手传统文本检索:系统会引入基于关键词频率的搜索引擎技术(比如BM25算法,就是Elasticsearch底层用的)。他的逻辑很死板,但及其精准:用户搜索XJ-998-Pro,他只会找到原文文本里包含这个的chunk。
- 另一手向量语义检索:把问题做Embedding,去向量库里找到最相似的Chunk,负责兜底那些意思差不多但字不一样的情况。
当用户提问后,按照以下流程:
- 双线并发:系统把用户的问题同时发给传统的关键引擎和向量数据库
- 各自召回Top-k:关键词引擎找出了他认为最相关的k个chunk,向量库也找出了他认为最相关的k个chunk
- 合并去重:系统把这两份合在一起,这样一来既不会漏掉准确的型号,又不会因为用词不同而错过相关政策
倒数排序融合
既然我们要把关键词检索和向量检索找出来的结果混合在一起,那么就要面临一个问题:关键词检索找出来的第一名和向量检索找出来的第一名,谁要排在前面喂给大模型?这就是一个度量衡问题,为此引入了RRF(Reciprocal Rank Fusion,倒数排序融合)
为什么不能直接比较?
关键词引擎的打分:他的分数是没有上限的,如果一个词在文档里出现了很多次,他的得分可能是15,也可能是120
向量检索的打分:他是计算余弦相似度,分数永远在0-1之间,比如0.85
如果把第一名120.5和第一名0.85放在一起比,向量的结果会瞬间垫底,完全无法相比。
RRF倒序排序融合算法
既然分数没法比,那就用一个忘记分数,只看排名的方法,这就是RRF的核心思想:不管原来得分多少,我只看排名,越靠前,分数越高,如果你同时在两个检索结果都排名靠前,那你的总分会很高。如果只在一个出现,那么它在另一个的分数就是0.
![]()
:指的是这个文档在各自检索结果中的排名(比如第 1 名就是 1,第 3 名就是 3)。
:是一个平滑常数(业界一般约定俗成取 60)。加这个常数是为了防止排名前几的文档分数差距过大。
假设我们用混合检索找一份资料,两边都返回了前 3 名:
- 资料 A:在向量检索排第 1 名,在关键词检索排第 4 名。
- 资料 B:在向量检索排第 3 名,在关键词检索排第 2 名。
资料 A 的 RRF 得分:
资料 B 的 RRF 得分:
最终结果: 资料 A(0.03201) > 资料 B(0.03199)。资料 A 险胜,排在第一位喂给大模型!
但如果只有一个
- A(偏科生): 关键词匹配极其精准,在关键词检索排第 2 名;但是向量检索那边觉得它意思不对,根本没把它捞进前 50 名(或者没找到)。
- 总分 =
- B(全能王): 关键词检索觉得它还行,排第 20 名;向量检索也觉得它意思挺搭,也排第 20 名。
- 总分 =
B(0.0250)以绝对优势碾压了 A(0.0161),排在了A前面。
第六课:重排技术
无论是之前的纯向量检索,还是混合检索,他们都有一个共同的特点:追求极致的速度,但这种为了追求快而盲目的对比,容易产生高分低能的错觉。
- 用户问题: “如何取消我的高级会员订阅?”
- 初次检索找到的 Chunk: “如何升级到高级会员订阅?” 这两个句子在向量空间里极度相似(相似度可能高达 0.9),因为它们包含的词汇几乎一样。初次检索会把它排在第一名,但对于用户来说,这完全是答非所问!
解决方法
引入Cross-Encoder模型
为了过滤掉这些高分低能的伪劣结果,RAG引入了一个全新的大模型机制,叫Cross-Encoder。
- 工作原理:他不再把问题和文档分开处理,相反,他把用户+找出来地每一个具体chunk拼接成一段完整的话,同时喂给模型(比如BERT)。模型会逐字逐句地去探索问题与文档之间的逻辑关系、因果关系和矛盾点。
- 能力:当他看到取消和升级这两个词放在一起时,凭借模型本身地语言逻辑能力,他会给这个chunk打很低分。就能将这k个chunk进行二次重排。
两阶段检索架构
既然Cross-Encoder这么聪明,为什么不一开始就用它呢?
因为他太慢!太贵了!如果让他去阅读向量库里100万个chunk和答案拼在一起,会消耗几十分钟与海量地token,所以RAG采用了两阶段检索的黄金架构
- 第一阶段:粗排召回
-
- 用快速的混合检索或向量检索,从100万chunk里快速海选出m个候选
- 第二阶段:精排重排
-
- 用Cross-Encoder对这m个入围的chunk进行深度阅读与二次打分,然后根据评分将这m个chunk进行排名,将top-k个结果形成context与问题进行组合,形成prompt交给大模型
第七课:上下文压缩(可选)
假设我们找出来的Top-5 chunk,每个有500字,加起来就是2500字。
- 大模型是按Token收费的,每次问答都带有2500字的冗长背景,不仅成本高,而且大模型阅读和生成的速度会明显变慢
- 大模型有一个著名缺点:Lost in Middle(迷失在中间),当给大模型的参考资料太长时,他往往只能记住开头和结尾,中间的细节反而会被忽略,如果答案正好在2500字中间,大模型可能会忽略答案。
解决方法:压缩与提取
RAG系统在把这2500字扔给大模型之前,会加上一道压缩机工序
大模型信息提取
我们先派出一个及其便宜、速度极快的小模型,给他下达指令: “用户的问题是:婚假需要提前几天申请?请阅读以下 500 字的片段,只提取出能回答这个问题的句子。如果这段话里没有答案,请输出空。”
然后向小模型发送5次独立请求。将每个chunk压缩成很短。
提示词自动压缩算法(LLMLingua)
它不用大模型去总结,而是用一种小巧的语言模型,从数学概率的角度去计算文档中哪些是废话,将文档里的”的、地、得、虽然、但是“以及一些重复的形容词全部剔除,保留精简核心名词,虽然人看不懂,但是大模型看得懂。
工业系统选择
上下文压缩不是必须的,在经过Cross-Encoder重排后,我们选前几的chunk合并就可以作为context与问题合并了。尽管我们前面提到了父块的字数比较多,但现实生产中,我们有很多方法可以解决,上下文压缩是一种,对父块的距离子块比较远的子块进行截断也是一种,生产中的方法是多种多样的,我们没法说出哪种方法是最好的,也没法说出标准方法是怎样的。
第八课:RAG系统的评估
到此为止,我们已经把RAG的进阶知识学完了,我们在入门基础上对每一个环节具体面临的方法进行了深入研究。但是当你准备上线这个系统时,老板或者客户一定会问你:你这套新系统,到底比原来准了多少?幻觉少了多少?
光凭感觉说是没用的,必须用数据证明。
用模型作为裁判
业界目前最主流的 RAG 评估框架(比如 RAGAS 或 TruLens),核心思想是:请一个更聪明、更严厉的大模型(比如 GPT-4 级别),来当裁判。
这个裁判不关心你用了什么花哨的检索技术,它只盯着 RAG 流程中产生的三样东西:
- 用户提问 (Question)
- 检索到的上下文 (Context)
- 最终生成的答案 (Answer)
基于这三样东西,从三个核心维度(RAG三端评估)给出打分
- 上下文召回率与精度:看用户提问与检索到的上下文,计算找出来的资料是否包含了问题的所有关键信息,是否有混入无关联的垃圾信息。得分低说明你的chunk切块策略与重排可能做的不好。
- 忠实度/无幻觉率:将检索到的上下文与最终生成的答案进行逐句对比,看答案的每一个事实能否在上下文中找到依据。得分低说明你的提示词可能出现问题
- 答案相关性:看用户提问于最终生成答案是否匹配,不去看检索到的上下文,仅看答案是否解决了问题。如果得分低,说明系统很多方面可能出问题了。
总结
回顾这 8 节课,我们从杂乱的文档一路走到精准的评分:
1-2课(数据底座): 摒弃死板提取,用版面分析解决复杂图文表格解析;用语义切块和“父子块策略”替代机械的定长切块,保全上下文逻辑。
3-4课(意图与路由): 永远不轻信用户原话。通过查询重写补全隐藏意图,利用智能路由将复杂问题拆解并精准分发到向量或关系型数据库。
5-6课(精准检索): 打出“混合检索(词频+向量)搭配 RRF 融合算法”的组合拳保证全量召回;再引入 Cross-Encoder 主考官进行硬核重排质检,精准踢出答非所问的干扰项。
7-8课(提效与验收): 喂给大模型前先做上下文脱水压
- 复杂文档解析 -> 2. 高级语义切块 -> 3. 查询重写 -> 4. 路由分发 -> 5. 混合检索 -> 6. 重排质检 -> 7. 上下文脱水 -> 8. 量化评估。
恭喜你,正式从基础 RAG毕业,跨入生产级进阶 RAG!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)