导读
你是否还在用现成框架调包实现 RAG ?本文带你撕开技术黑箱,仅用 numpy 等 Python 基础库构建 RAG 系统,从零手撕 RAG 内核!从文本划分、向量化、相似度检索到生成优化,逐行代码解剖检索增强生成的核心逻辑,更深度解析 9 大实战技巧:从智能分块策略到动态上下文压缩,助你突破回答质量瓶颈。拒绝做调参工具人,这次彻底掌握 RAG 的底层基因!
一、从 0 开始:简易 RAG 实现
在构建更复杂的 RAG 架构之前,我们先从最基础的版本入手。整个流程可以分为以下几个关键步骤:
数据导入:加载并预处理原始文本数据,为后续处理做好准备。
文本分块:将长文本分割成较小的段落或句子,以提高检索效率和相关性。
创建 Embedding:使用嵌入模型将文本块转换为向量表示,便于进行语义层面的比较与匹配。
语义搜索:根据用户输入的查询内容,在已有向量库中检索出最相关的文本块。
响应生成:基于检索到的相关内容,结合语言模型生成最终的回答输出。
二、基于语义的文本分块
在 RAG 中,文本分块(Text Chunking)是一个至关重要的环节。其核心作用是将一大段连续文本划分为多个具有语义完整性的较小段落,从而提升信息检索的准确性和整体效果。
传统的分块方式通常采用固定长度的切分策略,例如每 500 个字符或每若干句子进行一次分割。这种方法虽然实现简单,但在实际应用中容易割裂完整的语义单元,导致后续的信息检索与理解受到影响。
相比之下,一种更智能的分块方法是语义分块(Semantic Chunking)。它不再依据字数或句数进行机械划分,而是通过分析句子之间的内容相似性来判断合适的切分位置。当检测到前后句子在语义上出现明显差异时,就在该位置断开,形成一个新的语义段落。
切分点的判定方法
为了找到合适的语义切分点,我们可以借助以下几种常见的统计方法:
<b>百分位法(Percentile)</b> 找出所有相邻句子之间语义相似度差异的“第 X 百分位数”,并在那些差异值超过该阈值的位置进行切分。
<b>标准差法(Standard Deviation)</b> 当句子间的语义相似度下降幅度超过平均值减去 X 倍标准差时,在该位置进行切分。
<b>四分位距法(IQR, Interquartile Range)</b> 利用上下四分位数之差(Q3 - Q1)来识别变化较大的位置,并将其作为潜在的切分点。
实际应用示例
在本次实践中,我们采用的是 <b>百分位法(Percentile)</b> 来进行语义分块,并在一个样本文本上测试其分块效果。
三、在 RAG 中引入 “上下文增强检索”
传统的方法存在一个明显的问题:它只返回一个个孤立的文本块,这些文本块之间缺乏上下文联系,有时会导致 AI 获取的信息不完整,从而出现回答错误或内容不全面的情况。
为了解决这个问题,我们提出了一种新的方法,叫做 “上下文增强检索”(Context - Enriched Retrieval)。 它的核心思想是: 不只是找出一个最相关的文本块,而是同时返回它的前一个和后一个文本块,帮助 AI 更好地理解上下文,从而生成更准确、更完整的回答。
整个流程主要包括以下几个步骤:
<b>数据导入(Data Ingestion)</b> 从 PDF 文件中提取原始文字内容。
<b>带上下文的分块(Chunking with Overlapping Context)</b> 将大段文字划分为多个小块,但每个文本块与前后内容有一定的重叠。 👉 这样做的目的是确保即使某句话被切分到两个文本块之间,在其中一个块中也能看到完整的上下文。
<b>创建嵌入向量(Embedding Creation)</b> 将每个文本块转换为一组数字表示(称为 “嵌入向量”),便于后续进行相似度计算。 👉 可以理解为给每个文本块打上 “语义标签”,这样就能快速找到语义相近的内容。
<b>上下文感知的检索(Context - Aware Retrieval)</b> 当用户提问时,系统不仅会找到最相关的那个文本块,还会一并返回其前后的文本块。 👉 这样 AI 在回答问题时能获得更丰富的背景信息,避免断章取义。
<b>生成回答(Response Generation)</b> 使用大语言模型(如 Llama、ChatGLM 等),基于包含上下文的检索结果生成自然、准确的回答。 👉 就像你在考试时可以翻书找答案,而且还能看到那一页的前后内容,自然就能答得更准确。
<b>评估效果(Evaluation)</b> 最后,我们会对 AI 的回答进行评估,判断是否因引入上下文而提升了回答的准确性与完整性。 👉 比如可以通过人工评分,或者让另一个 AI 来评估回答的质量。
四、添加 “块标题”(Contextual Chunk Headers,CCH)
RAG 通过在生成回答之前从外部知识库中检索相关信息,从而提升语言模型的事实准确性。然而,在传统的文本分块方法中,往往会丢失重要的上下文信息,导致检索效果不佳,甚至使模型生成脱离上下文的回答。
为了解决这个问题,我们引入了一种改进方法:上下文块标题(Contextual Chunk Headers, 简称 CCH)。 这个方法的核心思想是: 在将文本分成小块(chunk)时,将该段内容所属的高级上下文信息(如文档标题、章节标题等)一并加到每个文本块的开头,然后再进行嵌入和检索。 这样做可以让每个文本块都带有其背景信息,帮助模型更好地理解它属于哪个部分,从而提高检索的相关性,并避免模型基于断章取义的内容生成错误答案。
五、基于问题生成的 RAG
本节通过在文档处理阶段引入问题生成(Question Generation),对文档内容进行增强。
我们为每个文本块生成相关的提问,从而提升信息检索的效果,最终帮助语言模型生成更准确、更相关的回答。
这种方法的核心思想是: 在传统的 RAG(Retrieval - Augmented Generation)中,我们通常只将文本块嵌入后存入向量库。而在这一改进方法中,我们还为每个文本块自动生成一些相关的问题,并将这些问题也进行嵌入。这样,在用户提问时,系统可以更好地理解哪些文本块与问题最相关,从而提高检索效果和回答质量。
六、Query 改写
本节实现了三种查询转换(Query Transformation),以提升检索增强生成(RAG)系统的信息检索效果。
核心目标:
通过修改或扩展用户的原始查询,帮助系统更准确地理解用户意图,并从向量库中找到更相关的信息。
三大查询转换技巧
<b>查询重写(Query Rewriting)</b>
将用户的问题变得更具体、更详细,从而提高检索的精准度。
示例:
✅ 提升点:让搜索更精确,避免过于宽泛的结果。
用户原问题:“AI 是什么?”
重写后的问题:“人工智能的定义及其核心技术有哪些?”
<b>回退提问(Step - back Prompting)</b>
生成一个更广泛、更高层次的问题,用于获取更多背景信息,帮助系统更好地理解上下文。
示例:
✅ 提升点:有助于找到与问题相关但不直接匹配的重要背景知识。
用户原问题:“深度学习在医疗领域有哪些应用?”
回退问题:“人工智能在医疗行业的应用有哪些?”
<b>子查询拆解(Sub - query Decomposition)</b>
将一个复杂的问题拆分成多个更简单的小问题,分别进行检索,最后综合所有结果,提供更全面的回答。
示例:
✅ 提升点:确保覆盖问题的所有方面,避免遗漏关键信息。
用户原问题:“比较机器学习和深度学习的优缺点及应用场景。”
拆解为:
“什么是机器学习?”
“什么是深度学习?”
“机器学习有哪些优缺点?”
“深度学习有哪些优缺点?”
“它们各自适用于哪些场景?”
实现查询转换技术
<b>查询重写(Query Rewriting)</b>
<b>回退提问(Step - back Prompting)</b>
目标是生成更宽泛、更高层次的问题,用于检索与用户问题相关的背景信息。
<b>子查询拆解(Sub - query Decomposition)</b>
七、重排序(Reranking)
重排序是在初步检索结果的基础上进行的第二轮筛选与优化步骤,目的是确保最终用于生成回答的内容是最相关、最准确的部分。
在传统的语义搜索中,我们通常使用向量相似度(如余弦相似度)来找到最相关的文本块。但这种 “初步检索” 并不总是完美的,有时会返回一些看似相关但实际上不匹配的内容。
重排序的作用就是:
✅ 在初步检索结果中进一步筛选;
✅ 使用更精确的相关性评分模型对内容重新打分;
✅ 按照实际相关性重新排序;
✅ 只保留最相关的文档用于后续的回答生成。
<b>重排序的核心流程</b>
<b>初步检索(Initial Retrieval)</b>
使用基础的语义相似度搜索(如向量匹配)快速获取一批候选文本块;
这一步速度快,但准确性有限。
<b>文档评分(Document Scoring)</b>
对每个检索到的文档进行更深入的相关性评估;
可以使用专门的重排序模型(如 BERT reranker、ColBERT、Cross - Encoder 等),根据用户查询和文档内容之间的语义关系打分;
相比简单的向量匹配,这种方式能更好地理解 “句子层面” 的相关性。
<b>重新排序(Reordering)</b>
根据评分结果对所有候选文档进行重新排序;
最相关的排在最前面,最不相关的被靠后或剔除。
<b>内容选择(Selection)</b>
只选取排名靠前的几个文档作为上下文提供给语言模型;
避免引入噪音信息,提高回答的准确性和可靠性。
<b>举个例子:</b>
假设用户问:“深度学习有哪些主要应用?”
初步检索可能返回以下三个段落:
“深度学习广泛应用于图像识别和自然语言处理。”
“机器学习可以分为监督学习和无监督学习两种方式。”
“卷积神经网络是一种常用于图像分类的深度学习模型。”
通过重排序,我们可以判断:
第 1 条高度相关 ✅
第 2 条不太相关 ❌
第 3 条部分相关 ✅
于是我们只保留第 1 条和第 3 条作为上下文,用来生成最终答案。
<b>基于大模型的重排序</b>
<b>基于关键词的重排序</b>
<b>带有重排序的完整 RAG 流程</b>
到目前为止,我们已经实现了 RAG 流程中的核心模块,包括:
<b>文档处理(Document Processing)</b>
<b>问答生成(Question Answering)</b>
<b>结果重排序(Reranking)</b>
现在,我们将这些模块整合在一起,构建一个完整的 RAG 系统流程。
八、用于增强 RAG 的相关段落提取
(Relevant Segment Extraction,RSE)
不同于传统的做法 —— 仅仅检索出多个孤立的文本块, 我们的目标是:识别并重建连续的文本片段,从而为语言模型提供更完整、更有逻辑性的上下文信息。
核心理念:
在文档中,与用户问题相关的文本块往往集中出现在同一区域或连续段落中。 如果我们能够识别这些相关文本块之间的联系,并将它们按顺序组织成一个连贯的整体段落,就能显著提升语言模型对上下文的理解能力。
为什么使用 RSE?
传统 RAG 的问题:
检索结果由多个不相连的文本块组成;
块之间可能缺少过渡和背景信息;
导致语言模型理解困难,甚至出现断章取义的情况。
而 RSE 的优势在于: ✅ 将相关文本块组合成连续段落; ✅ 保留原文结构和语义连贯性; ✅ 提供更自然、完整的上下文给语言模型; ✅ 提高最终回答的准确性和流畅度。
RSE 的实现步骤:
<b>初步检索</b> :使用语义搜索从向量库中找出与用户问题最相关的若干文本块。
<b>位置排序</b> :如果原始文档中的文本块有编号或位置信息(如页码、段落顺序),我们可以根据这些信息对检索结果进行重新排序。
<b>聚类分析</b> :分析哪些文本块在原文中彼此靠近且语义相近,将它们归为一组,形成 “相关段落簇”。
<b>段落重建</b> :将属于同一个簇的文本块拼接在一起,形成一个完整的上下文段落。必要时还可以加入相邻的前后内容,以增强上下文连贯性。
<b>输入语言模型</b> :将重建后的连续段落作为上下文,提供给大语言模型(LLM)生成最终回答。
使用 RSE 处理文档
现在,我们来实现相关段落提取(Relevant Segment Extraction, RSE)的核心功能。
九、上下文压缩技术:提升 RAG 系统效率
我们将对检索到的文本块进行过滤与压缩,只保留其中最相关的内容,从而:
✅ 减少噪声信息;
✅ 提高语言模型回答的准确性和相关性;
✅ 更高效地利用有限的上下文窗口(context window)。
<b>问题背景</b>
在使用 RAG 系统进行文档检索时,我们通常会得到一些包含混合内容的文本块:
有些句子与用户的问题相关;
有些句子则完全无关或只是背景介绍。
例如:
“人工智能是计算机科学的一个分支。它旨在让机器模拟人类智能行为。许多 AI 系统依赖于大数据进行训练。深度学习是一种特殊的机器学习方法。”
如果用户的问题是:“什么是人工智能?” 那么只有第一句是最相关的,其余内容虽然正确,但和当前问题无关。
<b>上下文压缩的目标</b>
我们要做的是:
✅ 移除不相关的句子或段落;
✅ 仅保留与用户查询高度相关的信息;
✅ 最大化上下文窗口中的 “有用信息密度”。
这样可以让语言模型更专注于关键内容,避免被无关信息干扰,从而提高最终回答的质量。
<b>实现思路</b>
我们将从零开始实现一个简单的上下文压缩流程,主要包括以下步骤:
<b>逐句分析相关性</b>
将每个文本块拆分为句子,并使用语义模型(如 BERT、Sentence - BERT 等)计算每句话与用户查询之间的相关性得分。
示例:
<b>设定阈值或选择 Top - K 句子</b>
我们可以选择两种策略之一来筛选句子:
保留得分高于某个阈值的句子;
或者保留得分最高的前 K 个句子。
<b>重建压缩后的上下文</b>
将筛选后的句子按原始顺序重新组合成一个新的、更紧凑的上下文段落。
<b>示例演示</b>
原始文本块:
“人工智能是计算机科学的一个分支。它旨在让机器模拟人类智能行为。许多 AI 系统依赖于大数据进行训练。深度学习是一种特殊的机器学习方法。”
用户问题:
“什么是人工智能?”
经过压缩后保留的内容:
“人工智能是计算机科学的一个分支。它旨在让机器模拟人类智能行为。”
<b>实现上下文压缩</b>
这是方法的核心部分。我们将使用一个大语言模型来过滤和压缩检索到的内容,从而保留与用户问题最相关的信息。
完整 pipeline
十、RAG 中的反馈机制(Feedback Loop)
在本节中,我将实现一个带有反馈机制的 RAG 系统,它能够随着时间推移不断自我优化。 通过收集并整合用户的反馈信息,它可以:
✅ 学习哪些回答是有效的,哪些需要改进;
✅ 持续提升检索结果的相关性和回答质量;
✅ 在每一次交互中变得 “更聪明”。
<b>传统 RAG 系统的局限性</b>
传统的 RAG 系统是静态的:
它们仅基于向量相似度来检索信息,不会从用户反馈中学习。
这意味着:
如果返回了不准确或不相关的答案,系统不会自动修正;
即使同样的问题被多次提出,系统也不会 “记住” 之前更好的回答。
<b>带有反馈机制的 RAG 的优势</b>
我们构建的是一个动态、自适应的 RAG 系统,它具备以下能力:
✅ 记忆功能:记住哪些文档曾提供过有用的信息,哪些没有;
✅ 动态调整评分:根据历史反馈更新文档的相关性得分;
✅ 知识积累:将成功的问答对加入知识库,供未来查询使用;
✅ 持续进化:每次与用户的互动都是一次学习机会,系统会越用越准、越用越好。
<b>反馈机制的核心流程</b>
用户提问
用户输入一个问题,并得到一个由 RAG 系统生成的回答。
获取用户反馈
用户可以通过评分、点赞 / 踩、或者直接评论等方式提供反馈;
示例:
“这个回答很有帮助 ✅”
“这个回答不够详细 ❌”
“请补充更多细节 📝”
记录反馈数据
将用户问题、原始回答、反馈内容等信息存储下来,形成反馈日志。
分析与学习
使用模型分析哪些文档和段落产生了高质量的回答;
调整这些文档在未来的检索权重;
将高质量问答对加入知识库,用于增强未来的语义理解。
优化下一次回答
下次遇到类似问题时,系统能更快、更准确地找到最佳答案。
<b>示例演示</b>
用户第一次提问:
“什么是机器学习?”
系统回答:
“机器学习是一种人工智能技术,让计算机通过数据学习规律。”
用户反馈:
“不错,但可以更详细。”
第二次有人问类似问题:
“机器学习的基本原理是什么?”
系统结合之前的反馈,返回更详细的回答:
“机器学习是一种人工智能技术,它通过训练数据让计算机自动学习模式和规律。常见的方法包括监督学习、无监督学习和强化学习。”
<b>构建简单向量数据库</b>
<b>反馈系统功能模块</b>
现在,我们将实现核心的反馈系统组件。
带有反馈意识的文档处理
利用反馈微调索引
完整工作流程:从初始设置到反馈收集
以上内容是 RAG 技巧与底层代码剖析的全部内容,后续内容将分为五个部分来详细介绍 RAG 的各个方面,包括 RAG 的构建与部署、提高 RAG 检索质量、RAG 生成与优化、RAG 的评估与改进以及相关实际案例分享。 这将为你带来更全面、深入的 RAG 学习体验,帮助你更好地掌握和应用这一技术。