指出RAG评估方法的结构缺陷:金标答案由分块方案生成,评估只能检测检索失败,无法发现分块本身的缺陷。提出解耦方案。
TL;DR——大多数 RAG 评估框架把“正确检索”定义为:检索结果与生成标准答案的文本块相匹配。但这个标准文本块,恰恰是由你本应测试的同一套分块方案切分出来的。这会让分块失败变得不可见:评估只能检测错误检索,却无法发现“正确检索到了一个残缺文本块”的情况。解决办法是让标准答案的文本跨度与文本块边界解耦,并直接对分块器进行压力测试——在 embedding 或 re-ranker 介入之前就完成这项工作。
每次对 RAG 系统进行事后分析,最终几乎都会归咎于同样三个嫌疑对象:embedding 模型、re-ranker 或 prompt。几乎没人责怪分块器。这并不是因为分块没有问题,而是因为大多数评估方案在结构上根本无法捕捉分块失败——从设计之初,它们就让分块变得不可见。
下面这个循环,几乎没人意识到自己正身处其中。你先拿一份文档进行分块,再让人类或模型编写一个问题,并确保答案存在于其中某个文本块里,以此构建标准评估集。接着,你通过检查 retriever 是否找回了同一个文本块来评估检索效果。Context precision、context recall、hit rate at k——所有这些指标衡量的都是:你是否找回了标准评估集认定应该返回的那个文本块。但标准评估集是在分块之后定义的,并且把文本块边界当作事实判断的基本单位。你测试的是 retriever 是否与你的分块器意见一致,而不是分块器是否保留了 retriever 查找信息时真正需要的内容。
团队通常把分块当作基础管道:选一个大小,设一个重叠范围,然后上线。但文本块边界本质上是在对语义独立性作出判断。每次切分文本时,你其实都在断言:“回答与这段内容有关的问题所需的一切信息,都包含在这 N 个 token 之内。”而这个断言通常是错的。定义会与被定义的术语分离;第四段里的限制条件会与第一段中它所限定的主张失去联系;表格的表头行可能与数据行落入不同的文本块。
这些问题都不会出现在标准评估中,因为评估所使用的标准答案,本来就是从一个按照定义已经包含答案的文本块中提取出来的。你从未生成过答案横跨两个文本块的标准问题——因为构建标准评估集的工作流,天然会偏向选择那些看起来已经“完整”的文本块。这个评估集其实是一个带有幸存者偏差的样本,只包含了分块方案成功的案例。
这正是容易让资深工程师误判的地方:它看起来像是检索质量问题,因此也会被当作检索问题处理。如果 context precision 很低,大家的第一反应通常是更换 embedding 模型、增加 re-ranker,或者调整检索的 k 值。有时这些方法确实有帮助——re-ranker 可以把勉强相关的文本块排到更前面,更好的 embedding 模型也能找到关键词搜索可能遗漏的语义相关文本块。但无论哪一种方法,都无法把已经被文本块边界切断的信息重新补回来。
如果回答“这项政策有哪些例外”既需要文本块 12 中的政策说明,也需要文本块 14 中的例外列表,那么仅仅让 re-ranker 把文本块 14 排到更高位置,并不能解决问题。你需要同时检索出这两个文本块,并在拼接时保留足够的上下文,让模型理解它们之间存在关联。Re-ranking 优化的是排序,对完整性无能为力。而完整性恰恰是分块会保留或破坏的属性,它发生在整个技术栈中所有其他组件之前。
如果想知道问题是否出在分块器上,就必须有意识地打破这种循环依赖。这意味着你需要构建一个小型、独立的评估集:在分块之前,直接根据源文档编写标准答案,而且负责标注的人完全不知道文本之后会如何切分。然后运行分块器、生成 embedding、执行检索,并检查一个比“我们是否找回了正确文本块”更具体的问题:检索出的所有文本块合在一起,是否包含了回答问题所需的每一项事实?每项事实是否都能被独立检索到,而不依赖它们在原文中恰好彼此相邻?
这会暴露一种标准 RAG 指标甚至没有为其命名的失败类型:答案的局部检索。Retriever 完成了自己的工作——它确实找到了相关内容——但找到的只是一个片段。随后 generator 要么凭空编造缺失部分,要么更糟:只依据一半事实,自信满满地给出答案,却完全不承认还有信息缺失。如果这个片段恰好与标准文本块存在重叠,那么以文本块为粒度计算的 context recall 指标会欣然将其判定为命中。这些指标从一开始就不是为了判断片段是否足以回答问题而设计的。
比任何聚合检索分数都更有用的诊断方式,是进行边界压力测试:使用真实的生产环境分块方案,处理那些具有已知内部依赖关系的文档——包含交叉引用的编号列表、表头与数据相隔较远的表格、在数页之后才使用已定义术语的合同,以及函数定义远离调用位置的代码。通过机械化方式统计:一对相互依赖的内容被拆分到不同文本块中,且没有共同检索锚点的情况究竟有多频繁。这个数字通常会高得令人不安,而且它不会与你现有的评估分数呈现清晰的相关性,因为现有评估分数从未衡量过这种问题。
得到这个数字之后,可选的修复方案其实范围很窄,而且早已广为人知:使用尊重文档层级结构、而非固定 token 窗口的语义分块或结构感知分块;进行上下文化文本块增强,让每个文本块都携带其父级章节的摘要;或者采用检索文本块邻域、而非孤立文本块的检索策略。这些方法都不新奇。大多数技术栈缺少的并不是技术手段,而是能够告诉你究竟需要哪种技术的衡量方式。
一个令人不安的结论是:基于你自己的分块方案构建的 RAG 评估集,其有效期取决于分块器多久发生一次变化。每当你调整文本块大小、重叠范围或切分逻辑时,曾经与旧文本块“对齐”的标准评估集,可能就无法再反映新文本块的失败模式。更糟糕的是,它仍然会给出看似健康的分数,因为它从一开始就没有真正独立于分块器。与评估对象纠缠在一起的评估体系不会平稳退化,而会悄无声息地失效:即使底层系统已经改变形态,指标依然一路亮绿灯。
让标准答案与文本块边界解耦,前期成本确实更高——你需要进行源文档级标注,而不是文本块级标注,构建速度也会更慢。但这是获得有效信号的唯一方法,只有这样才能真正区分“retriever 能力不足”和“分块器已经丢掉了 retriever 所需的信息”。这是两类不同的 bug,需要不同的修复方式。而现在,大多数 RAG 团队都在调试错误的问题,因为他们的评估根本无法区分二者。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。