RAG实践中的关键模块解析
RAG(检索增强生成)这个概念,最早是Meta在2020年那篇著名的《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》里提出来的。简单说,就是让大模型不局限于自己脑子里那点东西,能从外部知识库里现查现用。在大模型时代,这招儿简直是解决幻觉问题、知识过时、处理超长文本这些系统级问题的“标配”技术了。
RAG的挑战
不过,理想很丰满,现实很骨感。RAG真正落地的时候,挑战主要集中在三个环节:检索够不够准、增强过程够不够顺、生成质量够不够好。
检索质量
- :最典型的例子,“苹果”到底是指水果还是那个科技公司?向量表示有时候就是会犯这种糊涂,把概念搞混,结果就是搜出一堆不相干的东西。
语义歧义
- :现在用户的提问不再是几个关键词了,而是整段整段的自然对话,还可能带着多轮上下文,口语化很严重,这让传统的关键词搜索逻辑直接失效。
用户输入变复杂
- :这是个很有意思的技术活。是按标点符号、段落切,还是按语义来切?切完的“块”怎么变成机器能懂的向量?这些细节直接决定了后面匹配的准头。
文档切分
- :现实世界里的文档,谁还没个表格、图表、公式啊。怎么把这些非纯文本的内容也提取出来、表达清楚,是个很现实的问题,尤其是在处理一些模糊、否定的查询时,影响特别大。
多模内容的提取及表征
增强过程
- :查到的资料和当前要生成的任务怎么无缝衔接?弄不好,生成出来的内容就跟拼接似的,前言不搭后语,缺乏连贯性。
上下文的集成
- :要是搜出来的几段话都讲着差不多的信息,那生成的时候很可能就会车轱辘话来回说,内容冗余。
冗余和重复
- :搜出来一堆东西,到底哪个对当前任务最重要?怎么给它们排个优先级?增强过程得学会给每段信息“掂量掂量”分量。
排名和优先级
生成质量
- :模型有时候会走到另一个极端,就是太“信”搜到的资料了,自己反而没了主见,甚至会把检索结果里的错误也一并“消化”了,加剧幻觉。
过度依赖检索内容
- :更让人头疼的是,生成的答案根本解决不了用户问的问题,答非所问。
无关性
- :这就不用多说了,生成的内容有害或者带有偏见,是绝对要避免的。
毒性或偏见
整体架构
面对这么多挑战,一个健壮的RAG系统应该是什么样的?我们来看看一个典型的分层架构。
产品架构
放眼望去,整个产品架构大致分四层:
- :这是地基,要能屏蔽掉不同模型(比如自研的序列猴子、各种开源模型、第三方API)的差异。同时,为了搞定向量的跨语言检索问题,还得专门搞出一个跨语言的Embedding模型。
模型层
- :这是“后勤部”,核心是做两件事。一是搞个智能知识库,把非结构化的文本(PDF、表格、图片里的文字等)进行解析、打标、入库。二是做搜索增强,通过问句改写、重排这些小技巧,先把检索的精准度提上来。
离线理解层
- :这是“前线”,要直接面向用户,必须支持多文档、多轮对话、多模态问答,还得做好安全性和拒识,用户体验和竞争力都靠这一层了。
在线问答层
- :针对不同行业(比如金融、医疗、法律),预制好相应的角色和模板,降低用户的使用门槛。
场景层
技术架构
如果从技术实现的角度来看,RAG的核心可以拆成三个部分:Query理解、检索模型和生成模型。
- :这个模块的使命是搞清楚用户到底想问什么,然后把问题“翻译”成系统能高效执行的结构化查询。它涵盖了Query改写、扩写和意图识别等子模块。
Query理解
- :它的任务是从知识库里把最相关的信息“捞”出来,技术核心就是文档加载、文本转换、Embedding和向量检索。
检索模型
- :给定Prompt和检索到的上下文,生成最终的、连贯且有价值的答案。这里面包括聊天系统(短期/长期记忆管理)、Prompt优化等。
生成模型
说白了,RAG就是让检索的“准确”和生成的“创意”互补,用知识库给大模型“打底”,让它说话更有根有据。
Query理解
很多RAG系统效果不好,根子就在Query理解上。用户提问的方式可能不适合检索,或者需要从问题里提取出结构化的查询条件。所以,我们得在这里下功夫。
意图识别
简单说,就是判断用户想干什么。比如,用户是想要一段摘要,还是要一个具体的答案?这通常可以用LLM来做决策(把各种选择都塞到Prompt里让它选),也可以单独训练一个传统的分类模型(比如基于Bert的意图分类器)。它的应用场景很广,比如决定从哪个数据源查、该用摘要还是搜索策略、要不要同时试好几个方案。
Query改写
原始的用户问题,往往不是最优的检索词。所以,我们要利用LLM来“润色”一下。
- :这个想法很巧妙。它先让LLM根据问题生成一个“假设的”完美答案,然后用这个假设答案的向量去搜真实文档。因为假设答案的语义空间和文档更接近,所以往往能搜到更相关的东西。
HyDE
- :这个框架更直接,就是拿LLM来改写查询。它还训练了一个小模型(比如T5)专门干这个活儿,甚至用强化学习来优化改写的策略,目标是让改写后的query在后面的检索和生成环节表现更好。
Rewrite-Retrieve-Read
Query扩写
遇到复杂问题怎么办?“分而治之”是个好办法。把一个大问题拆成几个小的、更具体的子问题,分别去搜,最后再把结果组装起来。
- :这个方法鼓励LLM“后退一步”,先抽象出问题的更高层级概念。比如问你“水温是多少”,它可能先抽象成“水的物理性质”,然后用这个抽象概念和原问题一起去搜,效果往往更好。
Step-Back Prompting
- :Chain of Verification。它让LLM自己给自己“挑错”。先生成一个初步答案,然后围绕这个答案生成一系列的验证问题,去搜知识库来验证,最后修正答案。这就好比让模型有了自我纠错的能力。
CoVe
- :一个简单粗暴但有效的方法。让LLM生成多个不同角度的子查询,然后并行搜索,最后用一个叫“倒数排名融合”的算法把结果排序合并,能覆盖更广的知识面。
RAG-Fusion
- :这是把推理和行动结合起来的经典范式。模型可以一边推理(CoT),一边执行动作(比如搜索知识库),根据搜索回来的信息再更新推理,循环往复,直到解决问题。
ReAct
Query重构
在实际的pipeline里,我们把这些思想综合起来,自研了一个“Query重构”模块。只需要一次请求,就能把用户的复杂问题同时完成改写、拆解和拓展,挖掘出更深层次的子问题,一举解决复杂问题检索不准的痛点。
检索模型
检索模型的挑战
检索这个环节,挑战也很直接:Embedding模型向量化得准不准?文档切分得合不合理?还有,当把搜到的内容拼接成Prompt送给大模型时,它能不能在长上下文中“一眼”就定位到真正有用的信息?《Lost in the Middle》那篇论文就专门指出过,大模型对长上下文中间位置的信息很“迟钝”,放在开头或结尾的信息才容易被它采纳。
文档加载器
这个模块负责把各种来源(.txt文件、网页、甚至YouTube视频的字幕)的文档数据加载进来,变成文本和元数据。它还支持“懒加载”,避免一下子把所有东西都塞到内存里。
文本转换器
加载进来的文档不能直接用,得先“切块”。这个切块是个技术活,理想情况是把语义相关的片段放在一起,还要控制块的大小,确保能塞进模型的上下文窗口。常见的切分方式有:按字符递归切分(最推荐)、按HTML/Markdown标签切分(能保留结构化信息)、按代码语言切分、甚至按Token数量切分。有个叫Chunkviz的开源工具可以帮你可视化这个过程,调整参数。
文本嵌入模型
这是将文本转化为向量表示的核心。一个理想的嵌入模型,应该具备跨语种关联能力(比如“苹果”和“Apple”),能把长原文和短摘要关联起来,能把不同表述但语义相同的文本关联起来,能把问题和可能的答案文本关联起来。更重要的是,检索出来的结果,越靠前的越有用,最好还能自动过滤掉低质量的片段。
向量数据库与索引
数据处理好后,就要建立索引来支持快速检索了。常见的索引结构有:摘要索引(按顺序存)、树索引(构建层级摘要树,便于高层检索)、关键词表索引(建立关键词到文档块的多对多映射)、以及目前最主流的向量索引(用向量相似度来匹配)。
排序和后处理
搜出来一堆结果,总得排个队吧。常用的策略有:按相似度分数过滤、按关键词过滤、让LLM自己给结果打分重排、按时间过滤、或者按时间对相似度加权排序等等。
生成模型
回复生成策略
拿到检索出来的文本块,怎么喂给大模型生成回答?有两种常见思路:一种是“吃一口,想一步”,每拿一个块就让大模型修正一次答案;另一种是“吃饱再想”,一次性把所有块都塞进Prompt,让大模型一次性生成。实践中也可以组合使用。
Prompt拼接策略
如何把系统指令、检索到的文档、历史对话和用户问题组装成一个有效的Prompt?常用的有字符串提示(直接把所有模板拼起来)和聊天提示(按消息列表来组织,比如SystemMessage, HumanMessage, AIMessage等)。
基于混合演示检索的上下文学习
这个听起来有点绕,其实就是在做RAG的时候,不仅检索知识库,还检索“过往的成功问答案例”(示范性示例)。把和当前问题最相似的几个问答对也一起塞进Prompt,让大模型模仿着回答。但问题来了,怎么找到最合适的示例?单靠一种检索方法(比如纯语义检索或纯文本检索)效果不够好。
我们的解法是搞了一个“混合检索+重排”的插件模块。
检索模块
采用多路召回。语义检索用双塔模型(比如OpenAI的embedding-ada),文本检索用BM25。这样既能捕捉语义相似,又能确保关键词匹配,互补短板。
重排模块
多路召回的结果怎么融合?不同算法的分数区间不一样,不能直接比。我们引入了一个很有效的融合算法——倒数排名融合。它不看分数,只看排名,把各个算法给候选示例的排名位置综合起来,算出个新分数。然后,考虑到大模型“偏食”开头和结尾的特点,我们不是简单地按新分数排序,而是把结果“两头填”。比如排好的12345,重排后变成13542,确保最重要的信息被放到大模型最容易看到的两端。
生成模块
最后,把重排好的示例、系统Prompt、长期/短期对话记录、以及用户当前问题,一起组装成一个Prompt,送给大模型生成最终的答案。
引用或归因生成
让模型“说人话”还不行,还得让它“有理有据”。归因,就是让生成的答案能指向知识库中的具体来源,这不仅能提高用户的信任感,也是减少幻觉的有效手段。
如何实现?目前主要有两种做法。一种是
模型生成
动态计算
评估
做开发的同学都听过TDD,做AI应用也得有MDD(指标驱动开发)的意识。理想情况是先把场景、数据、指标、目标分值都定好,然后一步步实现。但现实往往是场景不清、数据难洗、指标没想过。
那该怎么量化证明你的RAG系统比别人的牛?核心要关注这几个方面对系统性能的影响:位置偏见(大模型偏爱开头和结尾),检索内容的相关性(不相关的内容就是噪音)。
评测指标
- :生成的回答是否忠实地基于检索到的上下文?这是避免幻觉的关键。
Faithfulness
- :生成的答案是否真正解决了用户提出的问题?
Answer Relevance
- :检索到的上下文是否足够精炼,尽可能少地包含无关信息?冗余信息越少,相关性越高。
Context Relevance
评测方法
- :系统地评估LLM在噪声鲁棒性、拒答能力、信息整合和反事实鲁棒性这四项基本能力上的表现。
RGB
- :提供一个无需参考答案的评估框架,从检索、利用和生成质量三个维度打分,已经开源。
RAGAS
- :LlamaIndex提供了衡量生成和检索质量的模块,非常实用。
Llamalindex-Evaluating
总结
大模型这波浪潮,催生出来的技术细节实在太多了。从Query理解到检索,再到生成和评估,每个环节想要做到极致、符合企业级应用,都需要花很长时间去研究、去实践、去打磨。这篇文章就是我们团队在过去一年RAG实践中的一些核心模块总结,希望能给你带来一些启发。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名