为何RAG的回答看起来完整,而Azure DevOps的搜索却显示并非如此
填补基于图的 Azure DevOps RAG 系统中的完整性缺陷:确定性标签检查、图遍历、上下文限制以及只读查询工具。
一种将 Azure DevOps 的历史数据加载到图数据库中的系统,能够以易于理解的文字和可点击的引用形式回答有关项目、负责人以及已交付工作的各种问题。有一段时间,这种方式似乎已经足够——直到答案需要与一个简单的基准进行对比:原始的 Azure DevOps 查询结果。精心整理后的回复与详尽列表之间的差距,正是“完整”检索功能悄然失效的地方。要弥补这一差距,就需要确定性的步骤、量化的测试,以及一些会引发新问题的修复方案。
暴露出这一差距的问题
最初的提问得到了来源清晰、内容简洁且几乎无需额外加工的答案。更宽泛的提示语——“该机构正在开展哪些人工智能相关的工作?”——得到的结果也看似不错:几段文字,少量引用,没有明显问题。但通过az boards query这种缺乏排序智能的暴力搜索方式,却得到了数十条该精炼答案中从未提及的结果。那些关于评估编程助手、比较模型成本或调试特定工具的内容并未出现,因为它们与提问的用词差异较大,在语义搜索中也从未排在前列。
这种缺陷很容易被忽视:排名靠前的答案并不等同于完整的答案,而系统也不会给出任何提示来告知用户所得到的究竟是哪种答案。
仅有偶尔效果好的提示语
最初的思路是要求模型更加谨慎——在处理宏观问题时,先查看类别标签再作回答,而非仅依赖搜索结果。这种方法偶尔能起到作用。相同的表述在不同运行中会产生不同的行为:有时模型会检查标签,有时则跳过。问题本身并未改变,只有当前轮次是否遵循了该指令而已。
对语言模型的指令只是一种引导,而非保证。当完整性至关重要时,不能让模型自行决定何时需要做到全面。
使路径具有确定性
接下来的改进不再依赖询问。无论模型是否认为有必要,代码都会在每个广泛的问题中检查标签。仅这一举措就将原本约有一半被发现的真实数据点减少到16个中的7个——虽然有所改善,但依然不完整。后续的改进则来自测试中发现的具体缺陷,而非推测:
其中一步是筛选出在标签搜索中出现两三次的重复名称和短语,然后直接对这些名称进行搜索——这样一来,只要包含该名称的小型数据群已被识别,即便没有人为明确查询,该产品名称也会显现出来。
这一步骤关注的是图表中的关联关系,而不仅仅是文字内容,因为同级的工作项可以拥有相同的父节点,但标题文字却可能完全不同。一个名为“示例测试仓库”并包含典型编码任务的项,其自身文字中可能不会提及任何与AI相关的内容,只能通过层级结构建立关联。
针对仓库的步骤,由于仓库并不携带与工作项相同的类别标签,因此会在基于标签的路径查询中不可见。
针对人员的步骤,则是在出现诸如“两位协作者多久共同工作一次”这类问题时,系统会给出明显错误的答案。
每一步都填补了某方面的差距。最终,那个包含16个项的最复杂案例也被完全解决了——不是部分解决,而是彻底解决。
获取更多上下文反而让答案更差
与直觉相反,当检索效果提升到足以使模型可用的内容从几百条增加到一千多条时,回答反而变得更短且更不完整。模型并未崩溃,只是在拥有更多真实信息的情况下仍选择输出较少的内容。一旦超过某个容量阈值,回答质量就不会随上下文量的增加而提升,反而会下降。在同一测试中,检索指标有所改善,但最终回答的指标却出现了恶化。解决之道并非“增加更多内容”,而是找到上限并保持在这一范围内。
一种导致简单问题信息过载的透明度改进措施
当模型选择概括掉实际内容而非直接提及时,新的答案部分会列出那些被找到但未命名的项目,因此不会有任何内容悄无声息地消失。在处理较为具体的问题时,初始版本会在回复中列出上千个关联度不高的项目,因为列表生成器无法区分精确匹配的内容与普通的标签噪声。而宽泛的类别则会将所有稍有关联的内容都纳入其中,并认为它们同样值得呈现。
有效的解决方案不仅仅是设置数量上限。更重要的是要区分两种“被找到但未命名”的内容:一类是应始终显示的少量精确匹配项,另一类则是需要设定明确上限并注明还有更多内容的大量标签模糊的噪声内容。这种区分比单纯设置上限更为重要。
为模型提供工具——同时设定约束
有一个关键问题决定了后续的工作方向:既然人类和系统看到的数据相同,为何人类能够给出系统无法得出的答案?当固定工具不适用时,人类可以编写新的查询,并对可疑的答案进行二次核查。而该模型不具备这两种能力。它只能使用一种工具来编写只读数据库查询——该查询由数据库强制设置为只读,有时间限制,且结果数量也有限。
还需要再进行两轮测试。当被问及两位特定人物合作的频率时,该模型首先假设他们因共同被提及而拥有相同的姓氏,但得到空结果后,它没有质疑这一空白结果,而是从无关的评论中猜测。于是规则被调整为:首先将每个名字对应到其确切的记录,再将空结果视为查询有误的证据,而非合作次数为零的证据。
在下一次尝试中,系统成功处理了这两个人的相关数据,执行了正确的查询,得到了二十五个共享项目——但依然没有给出具体数字,因为每一个事实性陈述都需要引用编号,而计算得出的数值则没有对应的引用编号。按照引用规则的字面要求,这样的正确答案会被舍弃。因此需要明确的例外规定:计算出的数值可以直接说明,无需附带来源编号。
这两次失败并非能力不足,而是严格遵循了那些未能涵盖该情况的指令。这一区别的重要性远超表面看上去的那样。
测试为何依然保持公正
真实标准并非简单的氛围检测。对于最复杂的类别,首先会从全面查询中列出十六个已知项目,然后在每次检索结果变化后对其进行评分:有多少出现在最终答案中,有多少被引用,又有多少被悄悄忽略。正是这个评分系统让“十六个中的七个”以及后来的“十六个全中”有了实际意义。如果没有外部的全项目列表,“看起来完整”仅仅只是表面现象而已。
对处理流程进行排序而不使模型不堪重负
确定性步骤仍需遵循一定的顺序。首先通过标签扩展来扩大候选集,接着通过名称挖掘进一步细化候选集;图遍历则用于添加结构上的相邻项;仓库和人员相关步骤则用于填补模态方面的缺失。只有在这套体系构建完成后,才会通过大小上限来筛选出适合放入提示词的内容。如果颠倒这个顺序——在体系构建之前就要求模型做到全面覆盖——就会再次面临引导问题。确保完整性是流程设计的工作,而非在系统提示词中使用更优的形容词所能解决的。
引用与计算得出的事实
“每项主张均需引用来源”的规则旨在防止模型在引用现有成果时进行创新。而当模型对工具输出的结果进行运算或整合时,这一规则反而可能带来问题。通过在指令中将“引用的主张”与“计算出的汇总结果”区分开来,既恢复了25次合作记录的统计,又未削弱对叙述性主张引用要求的约束。使用工具的RAG系统需要明确应用这两项规则。
当前进展
原始数据库查询按设计就是穷尽式的:不会遗漏任何匹配行。但它无法解释、分类或阐述含义——它只返回列表而非答案。该系统现在会在已发现并测试的复杂案例中依据查询的完整性进行匹配,同时仍会用可验证的来源以文字形式解释结果。这些结果是经过衡量得出的,而非凭空假设的。
这仍然不是一个已解决的问题。上述所有解决方案的出现,都是因为在用全面的标准来验证答案时发现了特定的错误。那种方法只能找出漏洞,却无法证明不存在其他漏洞。在进行了上述修改之后,又出现了一种新的问题类型:当引用的资料中并未提及某个人时,却仍有用于支持关于该人的主张的引用——在撰写本文时已发现这一问题,但尚未得到解决。这种模式仍在持续:看似已经完备的内容被再次核查后,总会有新的问题出现。
客观的总结并非“RAG的完整性问题已解决”,而是所有能够被发现并经过测试的具体缺陷都已得到弥补,并且有相应的前后对比数据。无法保证不会再出现新的缺陷,这类系统也不应给人这样的错觉。“该模型通常能答对”并非可靠性的依据——恰恰是“通常”这个词在关键问题上会出错。