RAG搜索增强生产

1、RAG概念

检索增强生成(RAG,Retrieval-Augmented Generation) 作为前沿的人工智能技术框架,创造性地融合了大规模语言模型(LLM)与外部知识源检索功能,致力于提升模型在问答与内容生成方面的表现。它通过从外部知识库提取相关信息,为语言模型输出注入更精准、更具深度的上下文信息,使模型响应更贴合实际需求。从技术内核来看,RAG 的本质是 InContext Learning,可简单理解为 “检索技术 + LLM 提示” 的有机结合。

RAG 的核心价值,在于针对性解决 LLM 存在的知识局限、幻觉问题和数据安全三大痛点,显著增强模型性能。此外,RAG 无需复杂的模型微调流程,能够基于语义实现专有领域知识的快速动态更新,即在系统持续运行过程中,不更换模型,仅通过加载、更新或切换知识库,就能适配不同垂直领域的知识需求 。

2、RAG分类

相信通过前面的概念阐述,大家对 RAG 的功能已经有了清晰认知。随着大模型技术的持续演进,RAG 的关键要素也在不断革新。依据技术发展脉络,我们可以将 RAG 划分为三大类型,它们同时也代表了 RAG 的主要发展阶段:经典的 NaiveRAG、模块化设计的 ModularRAG,以及基于智能体架构的 RAG。具体框架详见下图展示。

image

 

结合图片内容,在此为大家梳理 RAG 的三大发展阶段:

Naive RAG:以文本处理为核心,遵循 “索引 - 检索 - 生成” 的标准流程

【核心就是检索和大模型两个模块,把问答内容输入到数据库中,给定query,可以直接去数据库中搜索,搜索完成后把查询结果和query拼接起来送给模型即可,就如下图所示】

索引
指的是在离线状态下,从数据来源处获取数据并建立索引的过程。具体而言,构建数据索引包括以下步骤:

数据索引: 包括清理和提取原始数据,将 PDF、HTML、Word、Markdown 等不同格式的文件转换成纯文本。
分块: 将加载的文本分割成更小的片段。由于语言模型处理上下文的能力有限,因此需要将文本划分为尽可能小的块。
嵌入和创建索引: 这一阶段涉及通过语言模型将文本编码为向量的过程。所产生的向量将在后续的检索过程中用来计算其与问题向量之间的相似度。由于需要对大量文本进行编码,并在用户提问时实时编码问题,因此嵌入模型要求具有高速的推理能力,同时模型的参数规模不宜过大。完成嵌入之后,下一步是创建索引,将原始语料块和嵌入以键值对形式存储,以便于未来进行快速且频繁的搜索。
检索
根据用户的输入,采用与第一阶段相同的编码模型将查询内容转换为向量。
系统会计算问题向量与语料库中文档块向量之间的相似性,并根据相似度水平选出最相关的前 K 个文档块作为当前问题的补充背景信息。
生成
将给定的问题与相关文档合并为一个新的提示信息。
随后,大语言模型(LLM)被赋予根据提供的信息来回答问题的任务。
根据不同任务的需求,可以选择让模型依赖自身的知识库或仅基于给定信息来回答问题。如果存在历史对话信息,也可以将其融入提示信息中,以支持多轮对话。
Naive RAG 的挑战
Naive RAG 系统在实际应用中主要面临以下三个方面的挑战:

检索质量
低精度问题:检索结果中的文档块可能与查询内容不完全相关,可能导致信息错误或不连贯。
低召回率问题:未能检索到所有相关文档块,影响大语言模型获取足够的背景信息来合成答案。
过时信息问题:数据的冗余或过时可能导致检索结果的不准确性。
回应生成质量
错误信息:模型可能在缺乏足够上下文的情况下虚构答案。
回答不相关性:模型生成的答案可能未能准确针对查询问题。
有害或偏见性回应:生成的回应可能包含有害或偏见性内容。
增强过程
上下文融合:如何有效将检索到的文段上下文融入当前生成任务,避免内容杂乱无章。
处理冗余和重复:多个检索到的文段包含相似信息时,需要避免内容重复。
评估文段价值:判断多个检索到的文段对生成任务的重要性或相关性。
保持输出一致性:检索到的内容可能风格或语调不同,需调和差异以确保输出的一致性。
避免过度依赖增强信息:生成模型不应仅重复检索信息,而应提供新的价值或综合信息。
然而现实情况可能远不如这个这么简单,例如数据源可能就不是问答对的形式,此时无法进行检索,需要分块或者预处理,例如检索效果不足需要调优等,此时就有了高级的RAG。

原始 RAG (Naive RAG) 代表了早期研究方法,在 ChatGPT 广泛应用后迅速崭露头角。

image

Modular RAG:相较于 Naive RAG,知识整合与检索策略更为灵活。在知识库构建阶段,需要对数据进行复杂的 Chunk 编排;检索过程中,更高级的 Modular RAG 还支持对检索结果进行预处理和后处理;

Advanced RAG

现在我们深入讲解高级 RAG 技术。包括所涉及的核心步骤和算法的方案,但是省略了一些逻辑循环和复杂的多步代理行为,以保持方案的可读性。

上图中绿色部分是我们接下来详细探讨的核心 RAG 技术。一张图并不能全部展示所有的高级 RAG 技术,比如我们这里省略了上文扩展技术。

Modular RAG

模块化RAG是对高级RAG的一种升级,这里面集成了大量优化策略,并将这些策略进行重组,形成完整的模块独立完成特定功能,其实很多内容和上面提及的很接近,只是更加模块化。论文里有提及很多模块,这些模块都是可以进行单独优化的。

搜索模块:相比于前面提及的RAG中的简单搜索,这里的搜索可以涵盖大量经典搜索的必要组件,如query改写、意图识别、实体检索等,还涉及多路召回等工程思路,再者,也要开始面对各种千奇百怪的文档形式,表格、数字公式等。
记忆模块:借助大模型本身的记忆功能来实现,寻找和大模型历史回复最接近的,来进行回复。
额外生成模块:提供除生成回复结果之外的其他生成能力,例如对文档的精简,删除检索噪音等。
任务适配模块:让RAG系统能够适配不同的下游任务,有些时候,类似用大模型进行分类,其实也能使用RAG的模式进行优化。
对齐模块:用于对齐query和doc,毕竟query和doc本身的语义空间就有巨大差异,直接进行相似度计算的难度其实很大,因此可以进行一定的调整。
验证模块:作为检索的后处理,验证query和检索结果的相关性,即确定检索得到的文档是否能够回答这个问题。
RAG本就是一个高度组织性的项目,在迭代过程中,是需要对这些模块进行优化和调整的,而往里面新增模块。



1:分块 (Chunking) & 向量化 (Vectorisation)

首先我们需要为文档内容创建向量索引,然后在运行时搜索与查询向量余弦距离最近的向量索引,这样就可以找到与查询内容最接近语义的文档。

1.1 分块 (Chunking)

Transformer 模型具有固定的输入序列长度,即使输入上下文窗口很大,一个句子或几个句子的向量也比几页文本的向量更能代表其语义含义,因此对数据进行分块—— 将初始文档拆分为一定大小的块,而不会失去其含义。有许多文本拆分器实现能够完成此任务。

块的大小是一个需要重点考虑的问题。块的大小取决于所使用的嵌入模型以及模型需要使用 token 的容量。如基于 BERT 的句子转换器,最多需要 512 个 token,OpenAI ada-002 能够处理更长的序列,如 8191 个 token,但这里的折衷是 LLM 有足够的上下文来推理,而不是足够具体的文本嵌入,以便有效地执行搜索。有一项关于块大小选择的研究。在 LlamaIndex 中,NodeParser 类很好支持解决这个问题,其中包含一些高级选项,例如定义自己的文本拆分器、元数据、节点/块关系等。

1.2 向量化 (Vectorisation)

下一步是选择一个搜索优化的模型来嵌入我们的块。有很多选项,比如 bge-large 或 E5 嵌入系列。只需查看 MTEB 排行榜以获取最新更新即可。

有关分块和向量化步骤的 end2end 实现,请查看 LlamaIndex 中完整数据摄取管道的示例

2. 搜索索引

2.1 向量存储索引

RAG 管道的关键部分是搜索索引,它存储了我们在上一步中获得的向量化内容。最原始的实现是使用平面索引 — 查询向量和所有块向量之间的暴力计算距离。

为了实现1w+元素规模的高效检索,搜索索引应该采用向量索引,比如 faissnmslib 以及 annoy。这些工具基于近似最近邻居算法,如聚类、树结构或HNSW算法。

此外,还有一些托管解决方案,如 OpenSearchElasticSearch 以及向量数据库,它们自动处理上面提到的数据摄取流程,例如PineconeWeaviateChroma

取决于你的索引选择、数据和搜索需求,还可以存储元数据,并使用元数据过滤器来按照日期或来源等条件进行信息检索。

LlamaIndex 支持多种向量存储索引,同时也兼容其他简单的索引类型,如列表索引、树索引和关键词表索引。关于这些索引,我们会在后续的融合检索部分详细介绍。

2.2 分层索引

在大型数据库的情况下,一个有效的方法是创建两个索引——一个由摘要组成,另一个由文档块组成,然后分两步进行搜索,首先通过摘要过滤掉相关文档,然后只在这个相关组内搜索。

2.3 假设性问题和 HyDE

另一种方法是让 LLM 为每个块生成一个问题,并将这些问题嵌入到向量中,在运行时对这个问题向量的索引执行查询搜索(将块向量替换为索引中的问题向量),然后在检索后路由到原始文本块并将它们作为 LLM 获取答案的上下文发送。

这种方法提高了搜索质量,因为与实际块相比,查询和假设问题之间的语义相似性更高。

还有一种叫做 HyDE 的反向逻辑方法——你要求 LLM 在给定查询的情况下生成一个假设的响应,然后将其向量与查询向量一起使用来提高搜索质量。

2.4 内容增强

这里的内容是将相关的上下文组合起来供 LLM 推理,以检索较小的块以获得更好的搜索质量。

有两种选择:一种是围绕较小的检索块的句子扩展上下文,另一种是递归地将文档拆分为多个较大的父块,其中包含较小的子块。

2.4.1 语句窗口检索

在此方案中,文档中的每个句子都是单独嵌入的,这为上下文余弦距离搜索提供了极大的查询准确性。

为了在获取最相关的单个句子后更好地推理找到的上下文,我们将上下文窗口扩展为检索到的句子前后的 k 个句子,然后将这个扩展的上下文发送到 LLM。

绿色部分是在索引中搜索时发现的句子嵌入,整个黑色 + 绿色段落被送到 LLM 以扩大其上下文,同时根据提供的查询进行推理。

2.4.2 自动合并检索器(或父文档检索器)

这里的思路与语句窗口检索器非常相似——搜索更精细的信息片段,然后在在LLM 进行推理之前扩展上下文窗口。文档被拆分为较小的子块,这些子块和较大的父块有引用关系。

首先在检索过程中获取较小的块,然后如果前 k 个检索到的块中有超过 n 个块链接到同一个父节点(较大的块),我们将这个父节点替换成给 LLM 的上下文——工作原理类似于自动将一些检索到的块合并到一个更大的父块中,因此得名。请注意,搜索仅在子节点索引中执行。查看 LlamaIndex 教程 递归检索器 + 节点引用 以更深入地了解。

2.5 融合检索或混合搜索

这是一个很早以前的思路:结合传统的基于关键字的搜索(稀疏检索算法,如 tf-idf 或搜索行业标准 BM25)和现代语义或向量搜索,并将其结果组合在一个检索结果中。

这里唯一的关键是如何组合不同相似度分数的检索结果。这个问题通常通过 Reciprocal Rank Fusion 算法来解决,该算法能有效地对检索结果进行重新排序,以得到最终的输出结果。

在 LangChain 中,这种方法是通过 Ensemble Retriever 来实现的,该类将你定义的多个检索器结合起来,比如一个基于 faiss 的向量索引和一个基于 BM25 的检索器,并利用 RRF 算法进行结果的重排。

在 LlamaIndex 中,这一过程也是以类似的方式 实现 的。

混合或融合搜索通常能提供更优秀的检索结果,因为它结合了两种互补的搜索算法——既考虑了查询和存储文档之间的语义相似性,也考虑了关键词匹配。

3、 RAG基本流程

介绍完RAG内容后,大家听的可能还是一头雾水,接下来介绍一下RAG的基本流程。RAG 的基本流程包含了整理知识库、使用嵌入模型、载入向量数据作索引、查询与检索和LLM生成回答的过程。

image

为了让大家更好理解这些专业术语,下面为大家拆解 RAG 的核心操作步骤:

构建文本库:把不同类型的数据,整理切割成合适大小的文本块(Chunks)。文本块的长度、排列方式,以及多源数据整合逻辑,都要根据实际需求进行设计。
生成向量表征:利用嵌入模型(Embeddings),提取文本块核心语义,去除冗余词汇,将其转化为向量形式,确定在向量空间中的位置。
建立向量索引:把转化后的向量数据存储到向量数据库中,搭建索引结构,方便快速检索。
检索匹配:用户输入的问题同样转化为向量,在数据库中匹配出最相关的 Top K 内容。
模型生成答案:将用户问题和检索到的内容,按照特定模板(ChatTemplate)组合成提示词(Prompt),输入到大模型中,输出最终答案。
简而言之,RAG 就是让大模型在作答时,通过实时检索相关知识,增强回答的准确性和专业性。

4、 RAG场景介绍

现在,针对医院智能问诊场景做出实践。智能问诊场景中RAG框架需要借助代表静态资产的医学知识(例如临床医疗指南NCCN、UpToDate和医学论文PubMed等)和代表动态资产的患者日志病历(例如患者电子病历EMR等)进行简单/推理性质的询问,其中主要技术链路如下图。

image

整个业务流程如下:用户提出问题后,DeepSeek-R1 作为 LLM 基座,会先对用户提问进行意图分析,判断是调用基于医学知识文档的 RAG,还是启用用于联表查询的 SQL Tools 等基础工具。随后,相应的业务工具开始运作,获取医学概念知识、文献数据或具体患者病历等相关信息。接着,将用户的 Query 与获取到的文档,按照已有的 Prompt 模板进行拼接,再输入到 LLM 基座中。这时,LLM 基座接收到包含参考资料的询问,便会激活预先微调好的 Adapter,输出推理过程与标准回答。由于该流程具备上下文保存功能,所以能够实现与患者的多轮问答交互。

5、RAG模块构建

在 RAG 模块的运行流程中,首要任务是对现有资料进行切分与嵌入操作。鉴于数据均为 Markdown 格式,采用 MarkdownHeadSplitter 工具,先针对一级标题和二级标题进行划分,随后依据内容长度执行滑窗切分。具体实现的核心代码如下:

headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3")
]
loader = TextLoader(filename, encoding='utf-8')
documents = loader.load()
text_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers=False
)
text_splitter = MarkdownTextSplitter(
chunk_size=200,
chunk_overlap=20
)
docs = text_splitter.split_text(documents[-1].page_content)
for idx, doc in enumerate(docs):
text = doc
inputs = tokenizer.encode(text, max_length=MAX_LENGTH, add_special_tokens=False)
text = tokenizer.decode(inputs)
metadata = {}
metadata["start_index"] = idx
documents.append(Document(page_content=text, metadata=metadata))

经上述步骤处理后,拼接得到的字段涵盖两级标题与具体内容。例如,当文章一级标题为 “RAG 医疗场景实践”,二级标题为 “RAG 模块构建” 时,格式化后的 Chunk 呈现为 “RAG 医疗场景实践 - RAG 模块构建 -(具体内容)” 。

这些切分后的 Chunk 需持久化部署至向量数据库,以便在线模型多次查询,本次实践选用易于启动的轻量级单机数据库 Milvus。在嵌入环节,针对中文内容,选用 BAAI/bge-large-zh 模型,将每个 Chunk 转化为 784 维的稠密向量,完成从 “text of document” 到 Vector (784) 的转变。

在初始化好持久化查询的向量数据库后,具体的检索流程如下:

用户输入的Query同样使用 BAAI/bge-large-zh 嵌入成查询向量
针对查询向量从Milvus向量数据库中查询到TopK个相似结果,其中相似度度量采用余弦相似度,具体公式如下( A,B代表两个向量)

image

考虑注意力对头尾的聚焦能力比中间文本更强,因此使用重排器FlagReranker(也是基于Bge系列的重排器)进行重排,得到更加符合问题的参考文档块
将文档块和Query使用Prompt模板规则化,输入微调后的LLM基座,即可得到回答

6、DeepSeek-R1微调适配

DeepSeek-R1是作为Reasoning模型对于RAG包括智能问诊适配的一个尝试,这边针对已有的资产进行简单询问或带有推理的询问,其中构造相关的问答对 (query, documents, response) 格式,具体如下

("问下这个布洛芬怎么吃?", ["布洛芬是一種非类固醇消炎止痛药...", "患者id-问诊日期-病症-药方..."], "口服成人一次1片,一日2次(早晚各一次)")
考虑模型的参数量、训练性能与具体指令功能的匹配,进行Lora微调即可,具体微调的过程和原理不做展开,这边给出Lora的部分关键参数。

LoraConfig(
lora_alpha=32,
lora_dropout=0.1,
r=16,
bias="none",
task_type="CAUSAL_LM",
target_modules=["k_proj", "q_proj", "v_proj", "up_proj", "down_proj", "gate_proj"])
后续将训练好的权重作为Adapter和DeepSeek-R1一块在vllm上进行部署。

7、 Agent集成与业务工具的拓展

使用LangGraph+LangSmith做一个智能体服务的集成,其中主要包含Start节点、意图识别节点与绑定BaseTool节点,具体智能体相关在此不做展开。同样地,能够基于LangGraph搭建类似RAG或者是别的知识图谱的基础工具,因为都包括三个组件:静态资料、持久化容器与暴露的API。关于LangGraph调用的节点流程如下图。

 

image

 

以上就是RAG的具体在医疗场景的实践,但是这个实践还是有很多可以优化的地方,因此,小编在文章最后也总结了相关RAG优化内容,大家一起来看一下吧~

8、 RAG评估

现在大家已经对RAG的使用场景已经熟悉了,那么如何评价下RAG的对于内容检索和整体生成精度的优劣呢?这边小编带大家来看一下关于RAG评估的知识。通常对RAG评估需要考量RAG检索、生成、意图等方面(其实这是一个比较困难的评估过程,因为并没有绝对客观的量化标准)

检索环节的评估

MRR(Mean Reciprocal Rank)平均倒数排名:用于评估信息检索的指标。记确定正确的检索条目Chunk,考虑Chunk在实际检索中的排名倒数(如果检索排名是n,则MRR(1) = 1 / n)
HR(Hit Rate)命中率:评估召回文档的比率,即TopK中正确的Chunk占比
生成环节的评估(借助Ragas)

BleuScore:基于精度做评分(n-gram匹配对数量 / 系统生成的翻译总n-gram数, 可能使用短文本惩罚)
ROUGE-N:基于召回做评分(n-gram匹配对数量 / 参考的翻译总n-gram数)
scorer = AspectCritic(
name="maliciousness",
definition="Is the submission intended to harm, deceive, or exploit users?",
)
scorer.llm = openai_model
await scorer.single_turn_ascore(sample)

同样地,答案性质评估还包括:Perplexity、时效性、拒答程度

9、RAG优化方向

RAG的优化方向可以根据检索优化、生成优化与RAG增强三个方面,其中检索优化主要针对文档准备、分块存储处理、索引检索策略的阶段,主要有如下优化方向

数据链路优化: 需要对表格数据,列表数据和流程图数据做额外Chunk切分(因此在考虑数据的时候也考虑分块,同样可以考虑元素嵌入);滑动窗口技术检索、摘要嵌入(TopK检索,并对文档给出完整的上下文)、图索引(匹配实体-关系对作查询,但是GraphRAG耗时耗成本);以及针对稠密的向量可以直接进行相似度的匹配,但是对于稀疏向量的检索,最佳匹配方式是BM25(基于TF-IDF)
Altas模式: 检索器基于Contriever设计,将模型与检索器基于同种损失函数共同训练

image

而生成优化主要强调模型对已有信息的感知能力和提升Response的事实准确度,主要有如下优化方向

Context 顺序优化: 由于通用注意力对越靠后的文本注意力越强,因此可以重新根据之前几轮的对话重新给出文献的排序,并且根据最新的Query相关性过滤已有的记忆;
模型微调: 如果数据并不是特别隐私,或者面向的用户都是私域内部,模型微调也能加强对指令感知(训练数据:专业问答、通用任务等),在处理数据时可以考虑针对原有的Response做人工标注来做RL增强。

 

 

原文链接:https://blog.csdn.net/weixin_72959097/article/details/149069661
原文链接:https://blog.csdn.net/mingzai624/article/details/137343216

转自:https://zhuanlan.zhihu.com/p/674755232

posted on 2025-09-10 21:07  duanxz  阅读(6)  评论(0)    收藏  举报