前言:

做多文档 RAG 之后,发现一个比“命中率”更危险的问题:

跨文档污染。

这个污染,就是:

  • 问 A 文档的问题,却引用了 B 的内容

  • 两个版本的参数混在一起

  • 用户明确指定 B,系统却回答 A

这不是模型能力问题,是工程边界问题。

这篇记录怎么把污染“测出来”,然后怎么把它“控住”。

一、多文档 RAG 最大风险是什么?

很多时候做 RAG,只关注:

  • top_k 多少

  • 命中率高不高

  • embedding 模型强不强

但在多文档场景下,更危险的是:

文档边界失控。

当多个文档内容高度相似时:

  • 版本 A:top_k = 3

  • 版本 B:top_k = 5

如果混答,就是严重逻辑错误。

二、第一步:让污染“可观测”

污染如果不可观测,就不可控制。


① 标准化 doc_id

所有 chunk_id 统一格式:

A-p1-c00
B-p2-c03

doc_id 直接嵌入 chunk_id。

这样可以在日志中直接看到引用来源。


② 在回归测试里增加 scope 断言

新增字段:

expected_scope = A / B

断言规则:

  • 若 expected_scope = A

  • used_chunk_ids 必须全部以 A- 开头

  • 否则记为 scope_leak

污染统计直接可量化:

Scope 污染次数: X

这一点关键。

没有污染统计,就谈不上控制。


③ 决策日志可视化

增加日志:

[DECISION] SCOPE dominant_doc=B filtered=3->1

让每一次过滤过程可追踪。

三、第一轮问题:scope 依赖 top1

初版规则:

  • 取检索 top1 的 doc_id

  • 作为 dominant_doc

  • 过滤其他文档

结果出现问题:

问题:

B 版本 top_k 是多少?

如果 top1 恰好是 A 的 chunk:

  • scope 锁定 A

  • 回答错误

  • 触发 scope_leak

这不是 bug。

是规则缺陷。

四、第一次修改:forced_doc(显式约束优先)

解决思路:

显式约束优先级 > 检索排序

规则:

若 question 包含:

  • “A 版本”

  • “B 版本”

则:

  • 强制 doc_scope = A/B

  • 不再依赖 top1

修复后:

  • 指定文档问题稳定

  • scope_leak 降为 0

五、第二个问题:跨文档对比类问题

例如:

  • 请分别给出 A 和 B 的 top_k

  • 哪个更推荐?

当前系统设计是:

单文档回答器

scope 只允许保留一个 dominant_doc。

如果强行回答:

  • 只答其中一个

  • 或混答

都属于风险行为。

第二次修改:Compare Gate

新增规则:

若 question 同时满足:

  • 出现 A 和 B(或 A版本 + B版本)

  • 且包含对比关键词:

    • 分别

    • 对比

    • 区别

    • 推荐

    • 哪个更好

直接:

REFUSE
reason = cross_doc_compare_not_supported

原则:

明确边界,比错误回答更重要。

六、v0.5 稳定化:规则鲁棒性增强

修完逻辑后,又暴露两个细节问题。


① 空格规范化问题

问题:

"B 版本" → lower() 后是 "b 版本"

但代码匹配的是:

"b版本"

空格导致 forced_doc 失效。

修复:

q_normalized = q.lower().replace(" ", "")

再匹配关键词。


② "A 和 B" 模式未覆盖

原规则只检测:

  • A版本 + B版本

未覆盖:

  • “A 和 B”

修复:

新增 has_a_and_b_pattern:

  • 检测 “A 和 B” / “B 和 A”

  • 结合对比关键词触发拒答

七、最终结构:Scope + Gate 双层控制

现在的多文档控制结构是:

1️⃣ Retrieval —— 找候选
2️⃣ Scope —— 控制文档边界
3️⃣ Gate —— 控制问题类型

三层各司其职。

不是靠模型聪明解决。

是靠规则边界控制。


八、v0.5 回归结果

指标 结果
总体准确率 100% (10/10)
ANSWER 100%
REFUSE 100%
Scope 污染 0

污染被测出来,也被控住。


九、 本篇总结

多文档 RAG 的核心不是:

  • embedding 多强

  • 检索多准

而是:

  • 文档边界是否稳定

  • 显式约束是否优先

  • 对比类问题是否有边界

  • 是否有污染断言

最终确人一件事:

多文档 RAG 本质是边界控制问题,而不是检索问题。


🔗 源码(v0.5 稳定版本):
https://github.com/test202005/project2_mvp/tree/v0.5-rule-stable

更多推荐