RAG技术与调优实践
摘要
本文系统整理 RAG 项目中的知识库处理、高效召回、Rerank、双向改写、Small-to-Big、GraphRAG 和 Agentic RAG。文章以实际问答场景为线索,结合流程、代码逻辑、结果和方法对比,说明不同技术在工程开发中的适用条件和调优边界。
RAG技术与调优实践
写在前面
RAG 的基础流程是索引、检索、生成。这个流程容易搭出来,但很难稳定运行。生产问题通常不是模型不会回答,而是证据没有找到、证据过期、证据互相冲突,或者检索结果看起来相关但无法支撑最终答案。
因此,RAG 调优不能只调 top_k、Embedding 模型或提示词。更合理的工程视角是把系统拆成四层:知识库处理、高效召回、GraphRAG、Agentic RAG。知识库处理解决“有没有正确知识”,高效召回解决“能不能找对证据”,GraphRAG 解决“跨文档和多跳关系”,Agentic RAG 解决“复杂任务的过程控制”。

这篇文章按照个人工程笔记的方式整理。先给出 RAG 调优的整体框架,再分别讨论知识库处理、召回优化、GraphRAG 和 Agentic RAG,最后总结不同方法的选择顺序。
Native RAG 的问题
一个基础 RAG 系统一般包含三个阶段。
阶段 | 主要工作 | 常见问题 |
|---|---|---|
Indexing | 文档解析、切片、Embedding、建索引 | 切片粒度不合理,知识过期,内容冲突 |
Retrieval | 根据用户问题召回相关片段 | 问法不一致,关键词缺失,召回噪声大 |
Generation | 拼接上下文并生成答案 | 引用不稳定,证据不足,模型补全幻觉 |
如果知识库本身缺失或者错误,后面的召回和生成都无法补救。如果召回结果不包含答案,模型只能猜。如果召回片段过多且噪声大,模型会被无关信息干扰。因此,RAG 调优应从知识库开始,再做召回,再考虑图谱和 Agent。
一、知识库处理
1.1 知识库问题生成与检索优化
基础做法是直接对知识切片建索引。问题在于,用户查询和原文表达可能不一致。例如原文写“创极速光轮位于明日世界主题园区”,用户问“如果我想体验最刺激的过山车,应该去哪个区域”。原文和问题之间存在语义跳转,单纯 BM25 或向量检索都可能漏召回。
这里使用一个景区问答场景说明问题。知识库内容围绕上海迪士尼,包括开园时间、地理位置、主题园区、游玩项目、门票和交通等信息。用户问题并不一定直接问“创极速光轮在哪里”,而可能问“想体验刺激项目应该去哪”。因此,检索目标不是简单匹配原文,而是让系统能覆盖用户的真实问法。
具体做法是对每个知识切片生成一组可能的问题,再分别建立“原文索引”和“问题索引”。查询时既可以匹配原文,也可以匹配这些预生成问题。这个方法本质上是 Doc2Query:把文档提前翻译成用户可能会问的问题。
核心逻辑如下。这里的代码不是完整工程代码,而是表达一个关键思想:原文和生成问题要分别建索引,但最终要通过 question_to_chunk 映射回原始知识切片。
在这个景区问答案例中,系统为“开园时间、主题园区、面积、项目”等内容生成多样化问题。测试查询数量为 3 个,原文 BM25 检索准确率为 66.7%,问题索引 BM25 检索准确率为 100.0%。其中,“如果我想体验最刺激的过山车,应该去哪个区域?”原文检索失败,问题索引检索成功。

这个方法适合内容稳定、用户问法多样的知识库,例如产品说明、政策制度、景区助手、内部知识库。它的风险是问题生成过度发散,导致无关查询被误召回。工程上建议把生成问题作为召回增强,不直接作为生成答案的依据。最终回答仍应回到原始知识切片。
1.2 对话知识沉淀
线上 RAG 系统会产生大量对话。对话中包含用户真实关心的问题,也包含临时需求和个性化信息。直接把对话全文写入知识库会带来噪声,因此需要先结构化抽取,再过滤、合并、审核。
仍以景区问答系统为例,用户在多轮对话中可能询问门票价格、营业时间、停车、地铁路线、餐饮限制和游玩建议。这些对话不是都值得入库。比如“我周六下午去合适吗”是一次性需求,“周末和节假日人流较多,建议提前购票并确认营业时间”则可以沉淀为通用知识。
因此,对话知识沉淀要先做结构化抽取。抽取字段可以包含知识类型、内容、置信度、来源、关键词、分类、对话摘要和用户意图。
处理流程可以拆成三步:先抽取,再过滤,再合并。下面的伪代码表达的是数据流,不是为了展示某个函数实现。
在这个对话沉淀场景中,3 段对话先抽取出 22 个知识点,过滤掉临时“需求”和“问题”后剩余 17 个,最终合并为 3 类知识:事实、流程、注意事项。这个结果说明,对话沉淀的关键不是把对话存进去,而是把对话压缩成稳定、可复用、可追踪的知识。
实际应用中要注意两点。第一,价格、时间、政策、医疗、金融等高风险信息不能直接从对话进入正式知识库,必须有来源校验。第二,对话沉淀适合补充“用户关心但原文没有显式写清楚”的内容,不适合替代正式文档。
1.3 知识库健康度检查
知识库健康度检查主要看三个维度:完整性、时效性、一致性。
完整性回答“用户常问的问题是否有知识覆盖”。时效性回答“价格、活动、时间、政策是否过期”。一致性回答“不同切片之间是否有冲突”。这三类问题比召回参数更基础,因为它们直接决定答案可信度。
检查逻辑可以写成固定流程。
在景区问答系统中,知识库健康度检查可以用一组固定测试问题触发。例如用户会问“有什么特别活动”“停车费是多少”“门票多少钱”“营业时间是什么”。这些问题可以暴露三类风险:知识没有覆盖、知识已经过期、不同切片说法冲突。
本次健康度检查给出整体评分 0.60,健康等级为“良好”,但仍发现 2 个缺失知识点、2 个过期知识点和 2 个冲突点。缺失知识包括“有什么特别活动”和“停车费是多少”;过期知识涉及价格和活动信息;冲突包括价格差异和时间不一致。


这个模块适合做成定期任务。每次知识库更新后,先跑健康度检查,再跑检索评估。否则上线后发现“答案不对”,很难判断是知识缺失、召回失败,还是生成阶段幻觉。
1.4 知识库版本管理与性能比较
知识库不是静态文件,而是系统的一部分。更新知识库也可能引入回归问题,因此需要版本管理。
一个可执行的版本管理模块应至少包含四个功能。
功能 | 作用 |
|---|---|
版本创建 | 保存版本名、描述、切片数量、平均长度、分类分布 |
哈希标识 | 使用 MD5 或内容哈希记录版本唯一性 |
版本比较 | 找出新增、删除、修改的知识切片 |
性能评估 | 在固定测试集上比较准确率、响应时间和回归通过率 |
核心逻辑如下。
版本对比仍沿用景区问答场景。v1.0 是基础知识库,只包含位置、票价和营业时间 3 个切片。v2.0 是增强知识库,新增交通方式和特色项目,并补充票价、园区面积、主题园区和营业时间确认等信息。
评估结果显示,准确率从 60.0% 提升到 100.0%,平均响应时间从 115.8ms 增加到 120.2ms,回归测试通过率为 100.0%。

这个案例说明,版本调优不能只看准确率。准确率提升 40.0% 的同时,响应时间增加约 4.4ms,这是可接受的。如果准确率提升很小但延迟显著增加,就不一定值得上线。
二、高效召回
下面用一个制度问答场景展开高效召回。知识库内容来自银行客户经理管理考核办法,用户问题包括“客户经理被投诉一次扣多少分”“客户经理每年评聘申报时间是什么”。这类文档有明显特点:条款多、表述正式、关键词和数字很重要,因此不能只依赖语义相似度。
基础系统使用 DashScope Embedding 将制度文档切片向量化,使用 FAISS 建索引,再用 DeepSeek 模型根据召回片段回答问题。下面的代码只保留最小链路,表达“切片 -> 向量索引 -> LLM 回答”的基础结构。
基础系统可以回答明确问题。例如“客户经理被投诉了,投诉一次扣多少分”,系统能够召回制度条文,并回答“每投诉一次扣 2 分”。但基础向量检索在复杂问法、专业术语、短查询和多文档问题上仍然容易失效。
2.1 只增加 Top-K 不等于提升质量
最简单的方式是增加召回数量。
增加 k 可以提高召回覆盖率,但也会引入更多噪声。对于短文档或知识库规模较小的场景,这个方法可能有效;对于制度、合同、论文、手册等长文档,单纯增加 k 往往会让生成阶段更混乱。工程上应把增加 k 当作粗召回手段,而不是完整调优方案。
2.2 MultiQuery:用查询改写提升召回覆盖
MultiQuery 的思路是把用户问题改写成多个语义相近但表达不同的查询,从不同角度检索,再合并结果。它适合处理短查询、省略查询和口语化查询。
在制度问答场景中,原始问题是“客户经理被投诉了,投诉一次扣多少分”。这个问题虽然口语化,但文档原文可能写成“服务质量考核”“客户投诉”“每投诉一次扣 2 分”。为了提高命中率,系统生成了以下查询变体:
最终找到 4 个相关文档,并定位到制度中“有客户投诉的,每投诉一次扣 2 分”。这个例子说明,MultiQuery 的价值在于扩大表达覆盖,而不是改变问题本身。
实际使用时要控制两个风险。第一,改写不能偏离原问题,例如把“投诉扣分”扩展成“客户满意度评价体系”就会引入噪声。第二,多查询结果必须去重,否则相同片段会在上下文中重复出现,占用窗口。
2.3 索引扩展:离散索引、连续索引与混合索引
索引扩展解决的是“单一路径不稳定”的问题。在制度、技术文档和企业知识库中,单纯向量检索经常漏掉条款号、编号、专有名词;单纯关键词检索又不理解同义表达。因此索引扩展可以分为三类。
类型 | 方法 | 适用场景 |
|---|---|---|
离散索引扩展 | 关键词抽取、实体识别、BM25 | 专有名词、编号、制度条文、精确匹配 |
连续索引扩展 | 多种 Embedding 模型多路召回 | 语义丰富、表达差异大、跨语言或多领域 |
混合索引召回 | BM25 + Vector + 融合排序 | 大多数工程场景的默认选择 |
BM25 是 TF-IDF 的改进版本,通过词频饱和和文档长度归一化计算相关性。它不理解语义,但对关键词、编号、人名、机构名、条款号非常敏感。向量检索理解语义,但可能漏掉精确关键词。两者互补。

2.4 Hybrid Search:BM25 + Vector
混合检索的核心流程是:用户查询进入系统,同时执行 BM25 和向量检索;将两种分数归一化到 [0, 1];再按权重融合;最后按融合分数排序返回 Top-K。
alpha 决定检索倾向。
alpha | 检索倾向 | 适用场景 |
|---|---|---|
0.0 | 纯 BM25 | 专业术语、编号、精确查找 |
0.3 | 偏关键词 | 技术文档、法规条文、制度检索 |
0.5 | 平衡 | 通用默认值 |
0.7 | 偏语义 | 口语化问答、模糊查询 |
1.0 | 纯向量 | 同义表达丰富、语义匹配为主 |
在制度、合同、技术规范中,我倾向从 alpha = 0.3 或 0.5 开始。因为这些文档中的编号、术语和实体往往是关键证据。纯向量检索有时会找出语义相近但条款不对应的内容。
2.5 Rerank:粗召回之后再精排
Hybrid Search 解决召回覆盖问题,但 Top-K 中仍可能有噪声。Rerank 的作用是对候选片段重新排序,提高最终上下文的相关性。
Rerank 模型通常采用 Cross-Encoder 结构,把 (Query, Document) 成对输入模型,直接输出相关性分数。相比向量检索,它的交互更充分,排序更准,但计算成本更高。
模型 | 部署方式 | 优势 | 适用场景 |
|---|---|---|---|
BGE-Rerank | 本地部署 | 开源、中文任务友好、数据不出本地 | 私有知识库、垂直领域 |
Cohere Rerank | API 调用 | 接入简单、多语言效果稳定 | 快速集成、云端应用 |
BGE-Rerank 的核心用法如下。
完整流程一般是:先用 MultiQuery + Hybrid Search 粗召回 10 到 20 个候选;去重;再用 Rerank 精排;最终保留 3 到 5 个片段。
参数建议如下。
参数 | 含义 | 建议 |
|---|---|---|
| 粗召回候选数量 | 10-20,越多越全,但越慢 |
| 最终返回数量 | 3-5,太多会引入噪声 |
| Rerank 输入长度 | 512 起步,长文档需分段 |
| 混合检索权重 | 0.5 起步,再按评估集调 |
Rerank 最常见的问题是成本和截断。候选数量太多会明显增加延迟;文档太长会被截断,导致真正相关的证据没有进入模型视野。因此,Rerank 前应先做好切片、去重和候选控制。
2.6 双向改写:Query2Doc 与 Doc2Query
双向改写用于缓解短文本向量化效果差的问题。
Query2Doc 是把用户查询扩展成一段“假想文档”。例如用户问“如何提高深度学习模型的训练效率”,系统可以扩展为包含 AdamW、混合精度训练、分布式训练、数据预处理、学习率调度等内容的短文档。这个扩展文本比原始短查询更适合做语义检索。
Doc2Query 是为文档生成可能的用户问题。前面知识库问题生成就是 Doc2Query 的应用。它适合文档表达正式、用户表达口语化的场景。
两者的区别如下。
方法 | 改写对象 | 解决问题 | 风险 |
|---|---|---|---|
Query2Doc | 用户查询 | 查询太短、缺少上下文 | LLM 补充内容可能偏离意图 |
Doc2Query | 文档切片 | 用户问法和文档表达不一致 | 生成问题过多会扩大噪声 |
实际选择上,如果用户问题普遍很短,优先 Query2Doc;如果文档稳定且用户问法多样,优先 Doc2Query;如果是高价值知识库,可以同时使用,但必须有评估集验证。
2.7 Small-to-Big
Small-to-Big 的核心思想是“小块检索,大块生成”。系统先用摘要、关键句、段落等小粒度内容建立索引。检索命中小块后,再通过 parent_id 找到对应的大块上下文,提供给生成模型。
这个方法适合长文档、多文档和论文问答。直接用整篇文档做向量检索太粗,用过小切片生成答案又缺上下文。Small-to-Big 用小块提高定位能力,用大块保证答案连贯性。
Small-to-Big 的关键是建立稳定映射关系。小块不能脱离父块,父块也不能太大。父块过大会重新引入上下文噪声;父块过小则失去补充上下文的意义。工程上可以从“句子或段落作为 small chunk,章节或相邻段落窗口作为 big chunk”开始。
2.8 高效召回方法选择
不同方法解决的问题不同,不应混用为一个大而全流程。
问题 | 优先方法 |
|---|---|
用户问题太短 | Query2Doc、MultiQuery |
用户问法和文档表达差异大 | Doc2Query、问题索引 |
专有名词、编号、条款号重要 | BM25、关键词索引、实体索引 |
同义表达多 | 向量检索、多 Embedding 召回 |
Top-K 噪声大 | Rerank、阈值过滤 |
长文档定位难 | Small-to-Big |
多跳关系或跨文档归纳 | GraphRAG、Agentic RAG |
我的工程判断是,通用 RAG 的默认组合可以是:Hybrid Search + Rerank + 固定评估集。只有当这条链路稳定后,再引入 Doc2Query、Small-to-Big、GraphRAG 或 Agent。
三、GraphRAG
普通 RAG 依赖文本片段相似度。它适合回答局部事实问题,但不擅长回答“整体主题是什么”“不同实体之间有什么关系”“多个文档如何共同支持一个结论”这类问题。
GraphRAG 的核心思路是把非结构化文本转换为结构化图谱。索引阶段先把原始文档切成 TextUnits,再用 LLM 抽取实体、关系和主张,然后进行实体合并、社区发现和社区摘要。查询阶段根据问题类型选择全局搜索或局部搜索。

GraphRAG 的基本流程可以概括如下。

3.1 GraphRAG 索引流程
GraphRAG 索引阶段较重,主要包括六步。
步骤 | 内容 |
|---|---|
文档切片 | 将输入文档转换为 TextUnits |
图谱抽取 | 从 TextUnits 中抽取实体、关系、主张 |
图谱增强 | 合并同名实体,进行社区检测和图嵌入 |
社区总结 | 为每个社区生成摘要 |
文档处理 | 建立文档表、文本块表、实体表、关系表 |
可视化 | 使用 UMAP 等方法生成图谱可视化 |
如果把 GraphRAG 用在《三国演义》或人物关系类文本中,任务就不是简单检索某一句话,而是回答“和曹操相关的人物有哪些”“关羽战胜过哪些武将”这类关系型问题。普通 RAG 可能召回几个片段,但很难稳定组织实体和关系。GraphRAG 的优势在这里更明显。
使用 Microsoft GraphRAG 时,命令行流程如下。
初始化后会生成 .env、settings.yaml 和 prompts。其中 .env 配置模型 API Key,settings.yaml 配置 LLM、Embedding、输入目录、输出目录、缓存目录、向量存储和查询设置。将待检索文档放入 input 后,执行索引。
索引完成后,可以分别执行 global 和 local 查询。
3.2 Global Search 与 Local Search
GraphRAG 的查询模式分为 Global Search 和 Local Search。
维度 | Global Search | Local Search |
|---|---|---|
适用问题 | 全局主题、跨文档归纳、整体趋势 | 具体实体、关系、局部事实 |
数据来源 | 社区报告为主 | 实体、关系、原文 TextUnits、社区报告 |
查询方式 | Map-Reduce 汇总多个社区报告 | 从相关实体扩展邻居和原文证据 |
成本 | 高,需要多次 LLM 调用 | 较高,但通常低于 Global |
风险 | 容易生成宏观但空泛的答案 | 依赖实体抽取和关系质量 |
Global Search 适合回答“数据集的主要主题是什么”这类问题。它会对社区报告进行 Map,然后 Reduce 成最终上下文。Local Search 适合回答“某个实体有什么关系”这类问题。它从查询中识别相关实体,再找实体邻居、关系、原始文本和社区摘要。
GraphRAG 的价值在于结构化理解,而不是替代普通 RAG。对于 FAQ 和单文档事实问答,GraphRAG 的成本通常不划算。对于跨文档归纳、人物关系、事件脉络、研究主题演化等问题,GraphRAG 才有明显优势。
3.3 GraphRAG 参数调优
GraphRAG 的效果很依赖查询参数。下面几个参数会直接影响实体召回、关系扩展、原文证据和社区摘要在上下文中的比例。
参数 | 作用 | 调优方向 |
|---|---|---|
| Local Search 检索相关实体数 | 实体召回不足时增大 |
| 引入上下文的关系数量 | 关系链断裂时增大 |
| 上下文窗口中文本块占比 | 需要更多原文证据时增大 |
| 社区报告占比 | 需要背景摘要时增大 |
| Global Search 上下文上限 | 根据模型窗口调整 |
| Global Search 并发数 | 在成本和速度之间权衡 |
这些参数本质上控制“图结构”和“原文证据”的比例。Local Search 中,如果答案缺少事实依据,应增加 TextUnits 占比;如果答案缺少背景理解,可以增加社区报告占比;如果实体关系召回不足,应提高实体和关系的 Top-K。
四、Agentic RAG
Agentic RAG 是把 RAG 放进 Agent 工作流中。普通 RAG 是一次检索和一次生成;Agentic RAG 可以根据任务动态决定是否检索、检索哪个工具、是否并行阅读多个文档、是否继续追问、是否验证证据。
在工程上,Agentic RAG 可以拆成三个层次。
层次 | 能力 | 适用问题 |
|---|---|---|
Level 1 | 基础 RAG,一次检索后回答 | 单文档事实问答 |
Level 2 | 并行阅读,多路检索后汇总 | 多文档比较、批量阅读 |
Level 3 | 多跳推理,动态规划检索步骤 | 复杂关系、分步推理、证据链问题 |
Agentic RAG 的关键不是让模型自由发挥,而是给它明确的工具边界和验证机制。系统需要规定什么时候检索、最多检索几次、答案必须引用哪些证据、证据不足时如何返回。否则 Agent 会把 RAG 的不确定性放大。
一个合理的 Agentic RAG 控制流程如下。
这个流程比普通 RAG 成本更高,但适合复杂问题。普通事实问答不需要 Agent,多跳问题才值得引入。
五、工程落地顺序
RAG 项目调优建议按以下顺序推进。
顺序 | 工作 | 目标 |
|---|---|---|
1 | 构建基础 RAG | 跑通解析、切片、索引、检索、生成 |
2 | 建立测试集 | 固定高频问题、边界问题、失败样例 |
3 | 知识库健康检查 | 处理缺失、过期、冲突知识 |
4 | 版本管理与回归 | 每次知识更新都可评估 |
5 | Hybrid Search | 同时覆盖关键词和语义召回 |
6 | MultiQuery / Query2Doc / Doc2Query | 处理问法差异和短查询问题 |
7 | Rerank | 降低 Top-K 噪声,提高证据质量 |
8 | Small-to-Big | 处理长文档定位和上下文不足 |
9 | GraphRAG | 处理跨文档、全局归纳和实体关系 |
10 | Agentic RAG | 处理多步任务和复杂推理 |
这个顺序的重点是先建立可评估的基础链路。没有测试集时,任何调优都无法判断是否有效。没有版本管理时,知识库越改越难维护。没有健康度检查时,很多召回和生成问题会被误判为模型能力问题。
小结
RAG 调优的核心不是堆组件,而是建立证据链路。知识库处理保证知识可靠,Hybrid Search 保证召回覆盖,Rerank 保证证据排序,双向改写解决表达不一致,Small-to-Big 解决长文档上下文,GraphRAG 解决结构化关系,Agentic RAG 解决复杂任务控制。
后续做 RAG 工程时,我会优先遵循三个原则。
第一,任何调优都必须有测试集和回归结果。第二,任何答案都应能追溯到原始证据,而不是生成问题或中间摘要。第三,复杂方法必须对应明确问题;如果基础 RAG + Hybrid Search + Rerank 已经足够,就不必引入 GraphRAG 或 Agent。
