RAG 明明检索到了正确文档,为什么 AI 还是答错?从召回、重排到证据校验的工程实践
前言
在大模型应用开发中,RAG(Retrieval-Augmented Generation,检索增强生成)已经成为构建企业知识库、智能客服、文档问答等系统的重要技术方案。
它的基本思路并不复杂:先从外部知识库中检索相关信息,再将这些信息提供给大模型,让模型根据检索结果生成回答。
很多开发者刚接触 RAG 时,会认为只要把文档存入向量数据库,再接入大模型,就能解决知识库问答的准确性问题。
但实际开发后会发现一个现象:
有时候,正确答案明明已经出现在检索结果里,大模型却仍然回答错误。
例如,企业知识库中明确规定:
“普通员工每年享有 10 天年假。”
用户提问:“普通员工一年有多少天年假?”
系统成功检索到了这条规定,但最终模型可能回答:
“根据一般企业惯例,员工通常享有 5~15 天年假。”
问题显然不在于知识库缺少答案,而在于从检索到生成的整个过程中,系统没有有效地约束模型使用证据。
这说明:RAG 的核心挑战不只是能否检索到信息,而是如何保证正确的信息真正参与最终回答。
本文将从 RAG 的完整链路出发,分析其中容易出现的问题,并介绍几种实用的工程优化方法。
一、RAG 的基本工作原理
一个典型的 RAG 系统通常包含两个主要阶段。
1. 知识库构建阶段
首先,将 PDF、Markdown、网页或其他业务文档读取出来。
随后对文档进行清洗、分块,并利用 Embedding 模型将文本转换为向量,最终存入向量数据库。
基本流程如下:
原始文档 → 文本清洗 → 文档切分 → 向量化 → 向量数据库
其中,向量化的目的是让系统能够根据语义相似度查找相关内容,而不只是依赖关键词匹配。
2. 用户问答阶段
当用户提出问题时,系统会进行以下操作:
用户问题 → 问题向量化 → 知识检索 → 上下文组织 → 大模型生成回答
例如:
用户提问:
“公司的试用期员工是否可以申请远程办公?”
系统检索相关制度后,将条款提供给模型,再由模型组织最终答案。
这种方式的优势在于,大模型不必提前记住全部企业知识,也能使用相对较新的外部文档回答问题。
但每个环节都可能影响最终结果。
二、第一个问题:语义相似,不代表答案相关
向量检索的基本逻辑是将用户问题和文档内容映射到向量空间,再比较它们的相似程度。
然而,语义相似度并不等于问题的答案相关性。
例如,用户询问:
“公司员工申请远程办公需要提前几天?”
知识库中有两份文档:
文档 A:
“远程办公申请需要提前 3 个工作日提交。”
文档 B:
“公司支持弹性办公与远程协作,员工可以根据项目情况灵活选择办公方式。”
文档 B 与“远程办公”的语义关系很强,但真正回答“提前几天”的是文档 A。
如果检索系统只依赖向量相似度排名,可能将描述性文档排在具体制度条款之前。
解决方案:混合检索
一种常用方法是结合关键词检索与向量检索。
向量检索擅长发现语义相关内容。
关键词检索则能够更准确地匹配具体术语、产品编号、制度名称和时间表达。
例如:
-
向量检索负责查找与“远程办公申请”语义相关的内容。
-
BM25 关键词检索负责强调“提前”“工作日”等关键表达。
-
两路结果经过融合后,再选择更适合回答问题的文档。
常用的结果融合方法之一是 RRF(Reciprocal Rank Fusion,倒数排名融合)。
其公式为:
[
RRF(d)=\sum_{r\in R}\frac{1}{k+\operatorname{rank}_r(d)}
]
其中:
-
(d) 表示某个候选文档。
-
(R) 表示参与融合的检索结果列表。
-
(k) 是用于控制排名影响程度的参数。
-
(\operatorname{rank}_r(d)) 表示文档在某路检索中的排名。
RRF 的优势是可以直接融合不同检索器的排序,而不要求各个检索器的原始分数具有相同尺度。
三、第二个问题:文档切分破坏了完整语义
RAG 系统通常不会将整份大型文档一次性提供给模型,而是将其拆分为较小的文本块,即 Chunk。
例如,一份员工管理制度可能包含几十页内容。
为了方便向量化与检索,系统会将文档切分成多个片段。
但是,不合理的切分方式可能破坏原有语义。
假设原文是:
“员工申请远程办公,需要满足以下条件:第一,入职时间超过三个月;第二,工作岗位符合远程办公要求;第三,须提前三个工作日提交申请。”
如果按照固定字符长度强制切分,可能得到:
片段 A:
“员工申请远程办公,需要满足以下条件:第一,入职时间超过三个月。”
片段 B:
“第二,工作岗位符合远程办公要求;第三,须提前三个工作日提交申请。”
此时,如果只检索到片段 B,模型可能不知道这段条件属于什么制度。
解决方案:语义切分与上下文补全
可以从三个方面优化。
第一,优先根据文档结构切分。
例如根据标题、段落、列表和章节进行切分,而不是完全依赖字符数量。
第二,为相邻文本块设置适度重叠。
这样能够降低关键句子被切断的概率。
但重叠过多会增加存储和检索成本,还可能让模型反复看到相同内容。
第三,保留文本块的元数据。
例如:
{
"document": "员工远程办公管理制度",
"section": "第三章:申请条件",
"version": "2026-01",
"content": "申请须提前三个工作日提交。"
}
这样即使检索到的是一个较小片段,系统仍然知道它来自哪份文档、哪个章节以及哪个版本。
对于制度、合同和技术文档,元数据尤其重要。
四、第三个问题:召回成功,但正确证据没有排在前面
即使检索系统成功找到了正确文档,也不意味着最终回答一定正确。
假设向量检索返回 20 个候选片段。
其中,第 12 个片段包含准确答案。
但系统只把前 5 个片段提供给大模型。
那么正确答案虽然已经被检索出来,却没有进入模型最终使用的上下文。
这类问题在实际 RAG 应用中很常见。
解决方案:增加重排模型
Rerank(重排)的目的,是在初步检索之后,对候选文档与用户问题的相关性进行更精细的评估。
常见流程是:
用户问题 → 初步召回 20 个片段 → Rerank 重排 → 选择最相关的 5 个片段 → 模型生成回答。
与单纯依赖向量距离不同,重排模型通常能够综合分析问题与候选文本之间的具体关系。
例如:
用户问题:
“远程办公需要提前几天申请?”
重排模型应优先选择:
“远程办公申请须提前三个工作日提交。”
而不是:
“公司支持员工使用在线协作工具开展远程办公。”
当然,重排也不是万能的。
如果初步检索根本没有找到正确答案,后续重排无法凭空恢复缺失的信息。
因此,应该同时关注召回质量和重排质量。
五、第四个问题:模型仍然倾向于使用自身知识回答
这是本文最值得关注的问题。
即使正确证据已经被提供给模型,模型仍然可能使用预训练过程中学到的常识来组织答案。
例如:
检索内容:
“本公司普通员工每年享有 10 天年假。”
模型回答:
“员工年假一般根据工龄确定,通常为 5 至 15 天。”
从语言表达上看,答案很自然。
但在当前问题中,它并没有忠实使用检索证据。
解决方案:证据约束提示词
可以为模型提供明确的回答规则:
你是企业知识库问答助手。
请仅根据提供的参考资料回答问题。
要求:
1. 优先使用资料中明确给出的事实。
2. 不得使用外部常识覆盖资料中的具体规定。
3. 如果资料不足以回答问题,请明确说明信息不足。
4. 回答时标明所依据的文档编号。
5. 不要编造不存在的制度或条款。
随后将参考资料与用户问题分开提供:
参考资料:
[文档1]
标题:员工休假管理制度
内容:本公司普通员工每年享有 10 天年假。
用户问题:
普通员工每年有多少天年假?
期望得到的回答是:
“根据《员工休假管理制度》,普通员工每年享有 10 天年假。[文档1]”
但是需要注意:
提示词只能降低不遵守证据的概率,不能从机制上绝对保证模型不会产生错误。
因此,对于高可靠性应用,还需要额外的验证措施。
六、第五个问题:有引用,不代表引用真正支持答案
很多知识库系统会在回答后面添加文档引用。
例如:
“普通员工每年享有 15 天年假。[文档1]”
但如果文档1实际上写的是“10 天”,这个引用并不能证明答案正确。
因此,不能只检查回答有没有引用编号。
更进一步,应检查三个问题:
-
引用的文档是否真实存在?
-
引用内容是否与回答属于同一个事实?
-
引用是否足以支持回答中的具体结论?
这就涉及答案的证据支持度校验。
例如,可以将模型生成的回答拆解为若干事实陈述,再判断各个陈述能否被引用片段支持。
对于无法找到可靠证据的内容,可以要求模型重新生成,或者在结果中标记为“未经证实”。
不过,使用第二个大模型执行证据校验,也可能产生误判。
因此,更可靠的方案通常是结合规则校验、引用检查以及必要时的人工复核,而不是将另一个模型的判断直接视为绝对事实。
七、实战:用 Python 实现一个简单的混合检索排序
下面用一个不依赖外部向量数据库的示例,演示如何融合两路检索结果。
假设某个系统已经分别完成了关键词检索和向量检索。
关键词检索结果:
keyword_results = ["doc_a", "doc_c", "doc_b"]
向量检索结果:
vector_results = ["doc_b", "doc_a", "doc_d"]
我们可以使用 RRF 将两路结果融合:
def rrf_fusion(result_lists, k=60):
scores = {}
for results in result_lists:
for rank, doc_id in enumerate(results, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
return sorted(
scores.items(),
key=lambda item: (-item[1], item[0])
)
keyword_results = ["doc_a", "doc_c", "doc_b"]
vector_results = ["doc_b", "doc_a", "doc_d"]
ranking = rrf_fusion([
keyword_results,
vector_results
])
for doc_id, score in ranking:
print(doc_id, round(score, 6))
这个示例展示的核心思想是:
某份文档如果在多路检索结果中都获得较高排名,那么融合后的排名通常也会更高。
当然,这只是一个最小演示。
实际生产系统还需要处理文档去重、权限过滤、元数据、候选数量、排序模型和召回质量评估等问题。
八、如何评估 RAG 的真正效果?
很多开发者测试 RAG 系统时,只会随意问几个问题,再判断回答是否合理。
这种方式适合初步演示,却很难支撑长期优化。
如果需要持续改进系统,就应该建立一套评估数据。
例如准备 100 个具有标准答案的问题,覆盖:
-
明确事实查询。
-
多文档信息整合。
-
需要精确数字的问题。
-
存在相似但不同制度的问题。
-
知识库中没有答案的问题。
然后分别评估检索和生成两个阶段。
| 指标 | 主要衡量内容 |
|---|---|
| Recall@K | 正确证据是否出现在前 K 个检索结果中 |
| MRR | 第一个正确结果排得是否靠前 |
| nDCG@K | 检索结果的整体排序质量 |
| Faithfulness | 回答是否得到参考资料支持 |
| Answer Correctness | 回答是否符合标准答案 |
| Abstention Accuracy | 无法回答时能否正确拒绝猜测 |
这些指标各有侧重点。
例如,Recall@K 很高,说明系统能够找到正确资料,但不代表最终回答一定准确。
Faithfulness 很高,说明回答较好地遵循了参考资料,但如果资料本身过期,最终答案仍可能不符合当前业务要求。
因此,不能依赖单个指标判断整个 RAG 系统的质量。
九、RAG 优化时最容易走进的三个误区
误区一:检索结果越多越好
增加召回数量有时能够提高找到正确证据的概率。
但如果将大量无关内容直接交给模型,也可能造成信息干扰,并增加推理成本。
更合理的方式是扩大候选召回范围,再利用重排与筛选机制控制最终上下文规模。
误区二:换更强的大模型就能解决所有问题
更强的模型可能具有更好的阅读理解和证据整合能力。
但如果检索到了错误版本的文档,或者必要证据从未进入上下文,那么升级模型也不能从根本上消除问题。
应先定位问题出在检索、排序、上下文组织还是回答生成阶段。
误区三:只要有向量数据库,就是完整的 RAG 系统
向量数据库只是检索系统的一部分。
一个可长期维护的 RAG 应用,还需要文档更新机制、权限管理、检索评估、来源追踪和异常处理。
否则系统虽然能够运行,却很难保证准确性和可维护性。
十、总结
RAG 的价值在于让大模型能够利用外部知识回答问题。
但真实的工程实践表明:
检索到正确文档,只是生成正确答案的必要条件之一,并不是充分条件。
从文档切分、候选召回、混合检索、结果重排,到提示词约束、证据校验和效果评估,每个环节都可能影响最终结果。
如果希望构建更加可靠的企业知识库或智能问答系统,我认为应重点遵循四个原则:
第一,让正确证据更容易被检索到。
第二,让最相关的证据优先进入模型上下文。
第三,让模型尽量根据证据回答,而不是自由发挥。
第四,让最终答案能够被验证,而不只是看起来合理。
未来,RAG 系统的竞争力可能不再只是“能够连接多少份文档”,而是能否稳定地从复杂知识中找到可靠依据,并把这些依据转化成准确、可追溯的回答。
好的 RAG 系统,不只是让 AI 有资料可看,而是让 AI 真正学会根据资料回答。
- 点赞
- 收藏
- 关注作者
评论(0)