当RAG系统未返回任何结果时,如何排查空检索问题。
逐步调试、最终无结果返回的RAG系统:数据摄取、分数阈值、过滤器、查询重写、混合搜索、重新排序及备用方案。
检索增强生成技术为语言模型提供外部知识,使其不必仅依赖训练数据来回答问题,因此最终答案的质量取决于检索结果的质量。当搜索没有返回任何有用信息时,模型几乎没有任何依据,可能只能给出通用回复、直接拒绝,或编造细节。下文将探讨为何会出现空结果或近乎空的结果,以及如何找出问题所在。
检索可能在多个环节出错
空结果并非单一类型的故障。候选内容可能在数据导入、嵌入、向量搜索、元数据过滤、阈值设定或排序等任意环节消失。如果能够分别检查每个阶段,而不再将整个RAG系统视为一个黑箱,调试工作就会更加高效。
1. 从索引和嵌入开始
在接触检索器之前,先确认知识库中确实包含你认为存在的内容。正常的处理流程应该是从源文档逐步转换为解析后的文本,再生成片段、嵌入向量以及存储向量。如果生成了2,000个片段,但只有200个向量被存储下来,那么再怎么调整检索器也毫无用处。
- 手动打开少量已存储的片段,它们应包含可读的文本、稳定的标识符、嵌入向量以及过滤规则所依赖的各类元数据。
- 文档和用户查询需使用相同的嵌入模型及维度。若更换查询模型而不重建索引,相似度得分将失去意义。
- 还要对解析器进行评估。PDF、扫描件、表格和HTML页面可能会被转换为空白或损坏的片段,而系统仍会显示处理成功。
2. 查看阈值、得分含义及过滤规则
过高的分数阈值是导致无结果出现的常见原因。嵌入分数并非置信度百分比,其有效范围会因模型、距离度量方式、数据以及向量数据库的不同而有所差异。在调试时,无需设置任何阈值,直接获取排名前10或20的候选项,并查看它们的原始分数:
results = vector_store.search(query_embedding, top_k=10)
for r in results:
print(r.score, r.metadata.get('source'))
接着要确认分数是按哪个方向计算的。有些 API 返回余弦相似度,数值越高越好;而另一些则返回余弦距离或欧几里得距离,数值越低越好。如果使用了错误的比较方式,就会无形中淘汰掉最优秀的候选项,只保留较弱的那些。通常,在确认数据库的评分规则并查看代表性查询的实际分数分布之前,不要设定任何阈值条件。
过滤器可能会移除优质结果
对于多租户系统而言,元数据过滤非常重要,它涉及权限、产品范围、日期、语言和文档类型等方面,同时也是导致结果集为空的最常见原因之一。常见的问题包括将字符串与整数进行比较、大小写不一致、缺少元数据的旧记录、错误的租户 ID,以及单个来看有效但组合起来却不可行的过滤器。
3. 审核查询重写结果与分块质量
重写查询有助于提升检索效果,但也可能删掉让查询能够被找到的关键术语。想象有人询问如何重置X500,而重写工具却将其转化为“为X系列恢复默认配置”。如果手册名为《重置X500》,用户自己使用的表述很可能更匹配该名称。请同时记录原始查询和重写后的查询,并对每一种进行测试。对于精确的标识符、错误代码、产品名称及API参数,即便查询的其他部分已标准化,也应保留其原始形式。
分块处理也会以类似方式出错。诸如“重置X500路由器”这样的标题绝不能与其下方的说明分开。应依据文档结构进行分块,确保标题、其段落、列表项以及周围的上下文保持完整。合适的块大小取决于具体任务量,不过一个合理的起始范围是500到1,000个标记,并保留一定程度的重叠,之后再通过实际查询进行评估。
衡量标准:选取一小组带标签的查询,记录预期的查询结果是否出现在前k个结果中。这种以召回率为重点的测试,比凭直觉选择块大小或阈值更为有效。
4. 结合词汇搜索与向量搜索
当用户与文档对同一概念的表述存在差异时,向量搜索能发挥优势。而当精确的词元内容至关重要时,BM25这类词汇检索方法则更为适用。由于实际查询很少完全属于某一种类型,生产系统通常会同时使用这两种方法。对于错误代码(例如ERR-XK-1042)、SKU、发票号、产品名称、类别名称、配置键以及API参数名称等内容,BM25往往更具优势。向量搜索可以找到概念上相近的匹配结果,而BM25则能确保不会遗漏精确的词汇证据。典型的设计流程是先获取大量候选结果,再通过更强大的重排算法提升精确度。
5. 先广泛检索,再重排
第一阶段的检索器与重排器承担不同的任务。检索器应通过收集所有可能回答问题的内容来最大化召回率;而重排器则需通过将查询与候选内容进行比对,将最有力的证据置于顶部,从而最大化精确度。
常见的处理方式是从向量搜索和词汇搜索中获取20到50个候选内容,对其进行合并去重,然后再将排名降序筛选出最佳的5到10个片段。交叉编码器效率较高,专为相关性评分设计;而基于大语言模型的重排器虽然能遵循更复杂的指令,但通常速度较慢且成本更高。
避免过早淘汰候选内容。如果过早设置严格的阈值,那些本可提供帮助的片段可能永远无法被更强大的相关性模型识别。通常,先进行粗略筛选后再进行重排,比完全依赖向量分数来确定排名效果更好。
融合与去重
当向量搜索结果与BM25搜索结果结合时,某些内容片段可能会同时出现在两个列表中,且它们的得分标准并不一致。此时可采用互反排名融合、标准化加权得分或简单排名合并等融合方法,并在重新排序之前利用稳定的片段或文档ID来去除重复项。这样的设计思路是让第一阶段专注于提升召回率,而由重新排序模块和上下文选择逻辑来保证精确度,从而便于对整个流程进行调优和评估。
6. 扩大查询范围,并设定备用方案
用户与文档的词汇表很少重合。输入“我无法登录”的人可能需要查找标题为“身份验证失败”、“登录故障排除”或“账户访问”之类的页面。扩展功能会生成若干备选搜索词,逐一执行后对结果进行去重并重新排序。需对其加以控制:不要改动已命名的实体和精确标识符,避免产生大量几乎相同的查询,同时限制重试次数,防止一个棘手问题引发昂贵的循环处理。
仅放宽那些确实可以放宽的部分。安全性、租户及权限过滤机制绝非可选,必须始终启用。诸如可选的文档类别、严格的日期范围或辅助标签等,若应用程序规则允许,则可适当放宽。一个简单的操作流程如下:
docs = retrieve(query)
if not useful(docs): docs = retrieve(expand(query), hybrid=True)
if not useful(docs): return ask_for_clarification_or_no_answer()
无结果与检索错误
这两种情况需要不同的解决方案。如果没有任何结果返回,需检查数据摄入、过滤器、权限设置、阈值、分数解读或索引访问问题。如果返回了结果但与主题不符,则需审视分块处理、嵌入质量、词汇缺失、词汇覆盖不足或排序机制。
7. 更具弹性的生产架构
成熟的RAG系统不会对所有查询都采用相同的检索路径。它会对请求进行分类,并选择与之匹配的检索策略:针对精确标识符的查询使用词汇搜索,针对宽泛的概念性问题使用向量搜索,而对于模糊或混合类型的查询则同时使用这两种方法。
之后通过聚合步骤,可以将多个来源合并、去除重复内容,应用必要的元数据及权限规则,最终生成一份统一且一致的候选列表提交给重排序器。不应仅依赖单一向量得分来判断可靠性,更可靠的指标包括重排序器的得分、最高分结果之间的差异、参与贡献的来源数量、回答所需实体是否确实出现在上下文中,以及多个片段是否一致。
可靠性决定下一步操作
当可靠性足够高时,排名靠前的证据会被传递给大语言模型;若可靠性较低,系统可以扩展或重新表述查询,再次尝试,或向用户提出澄清问题。这比一味给出答案更好,因为这样就能明确告知模型当前的证据不足以支撑有根据的回复。
避免在重试流程中涉及安全问题
任何备用设计都必须将相关性规则与授权规则分开处理。扩展可能会放宽表述要求,可选的主题过滤器有时也可以被省略,但租户边界、ACL、行级权限以及隐私政策在每次重试时都保持不变。重试流程绝不能成为绕过访问控制的方法。
8. 使检索过程可观测
如果日志中只有最终结果,调试RAG就只能靠猜测。检索层应当输出足够的数据,以便重现针对任何查询所发生的一切。至少需要记录:原始查询、经过重写或扩展后的版本、过滤条件、嵌入模型的维度、所使用的检索器、top-k排序结果、返回的原始候选项数量及其得分、选中的片段、重新排序后的得分、延迟时间以及所使用的备用方案。
log({
'query': query, 'rewritten': rewritten_query, 'filters': filters,
'candidate_count': len(raw_results), 'scores': scores,
'selected_chunk_ids': selected_ids, 'fallback': fallback_stage
})
合理的调试顺序
当出现检索结果为空的情况时,应从成本最低、最确定的检查方法开始,逐步转向那些更具主观性的排序调整方式。这样就能避免团队在问题实际上出在索引缺失或过滤器无效时仍去调整提示词或嵌入模型。
- 首先检查数据导入过程:统计文档数量、解析后的文本量、分块数量、存储的向量以及已知应该存在的少量记录。
- 接着进行原始检索:不使用任何可选的过滤器或阈值,直接发送原始查询,然后查看排名靠前的结果及其得分。
- 逐个重新启用过滤器,从而确定导致结果集为空的具体过滤器。
- 验证得分机制:是得分越高越好,还是越低越好?是否有任何标准化处理?
- 保留完全相同的标识符,比较原始查询与修改后的查询,判断重新编写查询是否有助于提升或降低召回率。
- 使用带标签的查询集评估混合检索与重排机制,检查重排后的前k项和前n项中是否包含预期的来源内容。
- 通过强制生成空结果或质量较低的结果来测试备用机制,确认系统会给出明确说明或拒绝响应,而不会产生错误信息。
需要衡量的指标
仅凭答案质量无法充分反映检索效果。需跟踪召回率@k(预期来源是否出现在候选集中)、重排后的精确度、空结果率、备用机制触发率、响应延迟,以及需要进一步扩展或说明的查询比例。还应按查询类型(如语义类问题与精确标识符类问题)、租户、语言及文档集合对这些指标进行分类,因为总体平均值可能会掩盖某些存在问题的部分。
先不要归咎于大语言模型
当RAG没有返回任何结果时,不要急于指责模型本身。问题通常出在搜索流程的早期阶段:数据摄取不完整、嵌入向量不兼容、阈值设置过严、元数据过滤条件有误、对得分的解读错误、查询重写方式过于激进,或是所采用的检索策略不适合该查询。可靠的系统会结合合理的索引机制与混合检索方式、大规模候选项生成、重新排序、受控的查询扩展、明确的备用机制以及强大的监控功能。一个有用的思维模型是:RAG首先是一个搜索与排序问题,其次才是生成问题。当检索层能够稳定地提供相关且经过授权的证据,并且清楚何时无法做到这一点时,模型的准确性就会提高,结果也更容易解释,同时幻觉现象也会减少。
相关阅读
- Dartboard RAG:当Top-K三次返回相同内容片段时该怎么做 ——普通的Top-K检索可能会返回几乎相同的片段。Dartboard RAG同时考虑内容的相关性与多样性;本文将介绍该算法、相关代码以及适用场景。
- 生产环境中的高级RAG:架构、工具与权衡 ——全面介绍生产环境中的RAG系统:数据接入、解析、分块、嵌入、向量存储、混合检索、重排序、评估与安全措施,以及各环节的权衡因素。