欢迎光临
我们一直在努力

知识库里的文档互相矛盾,AI 到底该信谁?我们做了 20 次自建测试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

知识库里的文档互相矛盾,AI 到底该信谁?我们做了 20 次自建测试

文章目录

  • 知识库里的文档互相矛盾,AI 到底该信谁?我们做了 20 次自建测试
    • 问题背景:知识冲突比「没搜到」更危险
    • 一个"看懂了材料"却仍然答错的例子
    • 找到"相关材料",不等于找到"当前依据"
    • 把判断规则落到系统里:一条最小可用链路
      • 文档至少要带哪些元数据
      • 哪些规则适合直接写进代码
    • 回答之前,先走四步判断
      • 1. 准入:这份材料能不能当依据
      • 2. 适用:它是否回答了当前这个人的当前问题
      • 3. 冲突:剩下的依据是否仍然互相矛盾
      • 4. 输出:回答、追问、拒答或升级
    • 20 次自建测试说明了什么
    • 企业现在可以检查的 8 件事
      • 最小回归测试不要只测"答对的问题"
    • 哪些决定必须留给人
    • 参考资料

摘要:企业知识库同时检索到新旧制度、草案和专项规则时,不能只靠相似度或发布日期选答案。本文用 20 次虚构自建测试,拆解「准入 → 适用 → 冲突 → 输出」四步判断,并给出可直接落地的元数据设计、确定性过滤代码与回归测试清单。


问题背景:知识冲突比「没搜到」更危险

企业知识库最危险的错误,未必是"什么都没搜到",而是同时搜到了几份都很像的材料:旧制度、新制度、未审批草案、群聊通知和某个项目的专项规则。它们各自都能回答问题,却可能给出不同答案。

AI 不该凭文档"更新"、措辞更肯定或者相似度更高就选一份。更稳妥的处理顺序是:先排除没有资格成为依据的材料,再判断日期和对象是否适用;信息不够就追问;仍有同级有效依据冲突,就展示冲突并交给责任人裁定。

企业知识库同时命中多份候选材料

为了看看这个顺序能否减少错误,我们构造了 8 份完全虚构的制度文档、10 个问题,用同一个模型各跑了两组,共 20 次调用。结果里出现了一个很值得拆开的错误:模型把日期读对了,结论却判错了。

实验中的公司、项目、制度、日期和金额均为虚构,不代表任何公司制度,也不涉及真实事故。

一个"看懂了材料"却仍然答错的例子

问题很简单:2026 年 6 月 10 日,常规项目的住宿报销上限是多少?

候选资料有三份:

文档状态有效期或内容
D01 正式批准 2025-01-01 至 2026-07-01,不含结束日;上限 300 元
D02 正式批准 2026-07-01 至 2027-01-01,不含结束日;上限 500 元
D04 未批准群聊 声称统一按 800 元报销

正确答案应当是 300 元。出差日期落在 D01 的有效期内,D02 尚未生效,群聊也不是正式依据。

第一组把三份候选资料全部交给模型。模型回答"依据不足",解释却说 D01 有效至 2026 年 6 月 30 日。它复述对了日期,仍错误地排除了 D01。这不是检索没找到资料,也不是编造了一个新金额,而是多份材料并存时发生的适用性判断错误。

第二组先根据审批状态和有效期过滤,只把 D01 交给模型,模型回答 300 元。一次结果无法证明过滤必然提高准确率,但它说明:能在生成前确定的规则,不必全部留给语言模型临场判断。

找到"相关材料",不等于找到"当前依据"

RAG 会先从外部知识中找出与问题相关的内容,再让模型基于这些内容回答。相关性检索擅长回答"这段文字和问题像不像",却不能单独回答:

  • 这份材料是否经过审批?
  • 它在提问所指的日期是否生效?
  • 它适用于总部、某个地区,还是某个项目?
  • 它是否明确替代了旧版?
  • 两份同级制度冲突时,谁有权作最终解释?

"最新上传"也不是稳妥的替代规则。2027 年的新制度可以在 2026 年提前发布,但查询 2026 年 10 月的标准时,它仍未生效。某个项目的专项规则可能比通用制度更早发布,却对该项目优先。腾讯云的时效性检索文档也明确区分了"知识有效时间"和文件上传、最后修改时间。

企业知识库需要同时管理两类信号:语义相关性负责找到可能有用的材料,业务适用性负责判断材料是否有资格进入本次回答。向量检索、关键词检索和重排主要解决前一类;审批状态、有效期、适用范围、权限和替代关系属于后一类。

把判断规则落到系统里:一条最小可用链路

下面这条链路把"模型自由判断"拆成可以测试的系统步骤。权限过滤应在检索阶段生效,避免无权访问的内容进入模型上下文;日期、范围和审批状态也尽量在生成前处理。

#mermaid-svg-NA8j9rQukFfLGPo3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NA8j9rQukFfLGPo3 .error-icon{fill:#552222;}#mermaid-svg-NA8j9rQukFfLGPo3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NA8j9rQukFfLGPo3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NA8j9rQukFfLGPo3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NA8j9rQukFfLGPo3 .marker.cross{stroke:#333333;}#mermaid-svg-NA8j9rQukFfLGPo3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NA8j9rQukFfLGPo3 p{margin:0;}#mermaid-svg-NA8j9rQukFfLGPo3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster-label text{fill:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster-label span{color:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster-label span p{background-color:transparent;}#mermaid-svg-NA8j9rQukFfLGPo3 .label text,#mermaid-svg-NA8j9rQukFfLGPo3 span{fill:#333;color:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 .node rect,#mermaid-svg-NA8j9rQukFfLGPo3 .node circle,#mermaid-svg-NA8j9rQukFfLGPo3 .node ellipse,#mermaid-svg-NA8j9rQukFfLGPo3 .node polygon,#mermaid-svg-NA8j9rQukFfLGPo3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NA8j9rQukFfLGPo3 .rough-node .label text,#mermaid-svg-NA8j9rQukFfLGPo3 .node .label text,#mermaid-svg-NA8j9rQukFfLGPo3 .image-shape .label,#mermaid-svg-NA8j9rQukFfLGPo3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-NA8j9rQukFfLGPo3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NA8j9rQukFfLGPo3 .rough-node .label,#mermaid-svg-NA8j9rQukFfLGPo3 .node .label,#mermaid-svg-NA8j9rQukFfLGPo3 .image-shape .label,#mermaid-svg-NA8j9rQukFfLGPo3 .icon-shape .label{text-align:center;}#mermaid-svg-NA8j9rQukFfLGPo3 .node.clickable{cursor:pointer;}#mermaid-svg-NA8j9rQukFfLGPo3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NA8j9rQukFfLGPo3 .arrowheadPath{fill:#333333;}#mermaid-svg-NA8j9rQukFfLGPo3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NA8j9rQukFfLGPo3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NA8j9rQukFfLGPo3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NA8j9rQukFfLGPo3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NA8j9rQukFfLGPo3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NA8j9rQukFfLGPo3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster text{fill:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 .cluster span{color:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NA8j9rQukFfLGPo3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NA8j9rQukFfLGPo3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-NA8j9rQukFfLGPo3 .icon-shape,#mermaid-svg-NA8j9rQukFfLGPo3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NA8j9rQukFfLGPo3 .icon-shape p,#mermaid-svg-NA8j9rQukFfLGPo3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NA8j9rQukFfLGPo3 .icon-shape .label rect,#mermaid-svg-NA8j9rQukFfLGPo3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NA8j9rQukFfLGPo3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NA8j9rQukFfLGPo3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NA8j9rQukFfLGPo3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

否

是

否

是

否

是

用户问题

提取身份、日期、项目等业务条件

关键条件是否齐全

clarify:追问缺失条件

带权限与元数据条件检索候选文档

按审批状态、有效期、适用范围过滤

是否还有适用依据

insufficient:限定范围说明依据不足

应用明确的例外与替代规则

同一事项是否仍有不同结论

answer:结论 + 适用条件 + 版本引用

conflict:展示差异并转责任人

记录请求、候选、排除原因与结果

这张图有两个容易忽略的点。第一,clarify 应发生在系统缺少关键条件时,而不是让模型猜一个日期或项目。第二,conflict 是正常产品状态,不是异常兜底;它必须进入接口定义、前端展示和评测指标。

过滤发生在什么时候,也会影响结果。Azure AI Search 的官方文档区分了向量检索的 preFilter、postFilter 和 strictPostFilter:前置过滤先缩小候选范围;后置过滤是在向量 Top-K 之后再排除不符合条件的文档。后两种模式在条件选择性很高或 k 很小时可能出现假阴性——库里明明有符合条件的文档,最终结果却没有返回。因此,权限条件必须可靠执行;有效期、项目等业务过滤还要用真实评测集检查召回率、延迟和吞吐,不能只看最终回答是否顺眼。

文档至少要带哪些元数据

下面是一份最小示例。字段名可以调整,但语义要稳定;valid_to 采用不包含结束日的半开区间,能避免相邻版本在切换日重叠。

{
"document_id": "POLICY-TRAVEL-2026-H2",
"version": "2.1",
"source_type": "formal_policy",
"approval_status": "approved",
"authority": "finance",
"valid_from": "2026-07-01",
"valid_to": "2027-01-01",
"organization_scope": ["all"],
"project_scope": "general",
"region_scope": ["CN"],
"supersedes": ["POLICY-TRAVEL-2025"],
"acl": ["employee"],
"content_hash": "sha256:…",
"owner": "finance-policy-team"
}

published_at 可以保留用于审计,却不应自动代替 valid_from。content_hash 用来判断内容是否真的变化,supersedes 表达明确替代关系,owner 则给冲突升级提供责任人入口。

哪些规则适合直接写进代码

对于结构明确的制度问答,可以先用确定性代码缩小证据集,再把保留下来的正文交给模型解释。下面的 Python 示例对应本次实验里的住宿标准场景:

from datetime import date

APPROVED_TYPES = {"formal_policy", "exception_policy"}

def decide_policy(docs, trip_date: date | None, project: str | None):
if trip_date is None:
return {"status": "clarify", "missing": ["trip_date"]}
if project is None:
return {"status": "clarify", "missing": ["project"]}

applicable = [
d for d in docs
if d["approval_status"] == "approved"
and d["source_type"] in APPROVED_TYPES
and date.fromisoformat(d["valid_from"]) <= trip_date
< date.fromisoformat(d["valid_to"])
and (d["project_scope"] == "general"
or d["project_scope"] == project)
]

if not applicable:
return {"status": "insufficient", "citations": []}

# 明确适用的项目例外优先于通用制度。
exceptions = [d for d in applicable if d["project_scope"] == project]
basis = exceptions or [d for d in applicable
if d["project_scope"] == "general"]

# 只处理文档明确声明的替代关系,不用"发布时间更晚"猜测。
superseded = {old_id for d in basis for old_id in d["supersedes"]}
basis = [d for d in basis if d["document_id"] not in superseded]

if not basis: # 元数据异常或替代链配置错误
return {"status": "insufficient", "citations": []}

amounts = {d["amount"] for d in basis}
citations = [d["document_id"] for d in basis]
if len(amounts) != 1:
return {"status": "conflict", "amount": None,
"citations": citations}

return {"status": "answer", "amount": amounts.pop(),
"citations": citations}

这段代码没有解决所有知识冲突。它假设入库时已经校验必填字段和日期格式,只负责结构化规则能明确判断的部分。自由文本中"同一事项"如何识别、数值与条件如何抽取、两段话是否真的矛盾,仍需要信息抽取、冲突检测和人工复核。但即使后面使用模型,也应该输出结构化结果和引用,而不是直接生成一段无法审计的自然语言。

回答之前,先走四步判断

在这里插入图片描述

1. 准入:这份材料能不能当依据

至少区分正式制度、专项规则、草案、会议纪要、群聊记录和个人笔记。草案和聊天记录可以解释背景,但不应自动与正式制度获得同等权重。

对于"未审批草案不得作为最终报销标准"这类确定规则,可以直接在检索或生成前执行,不必让模型给每份材料临时估算可信度。

2. 适用:它是否回答了当前这个人的当前问题

有效期最好用开始时间和结束时间表达,明确边界是否包含。适用范围也要结构化保存,例如组织、地区、项目、产品版本和用户角色。

如果用户没提供必要条件,系统不应猜。实验中有两个问题分别缺少出差日期和项目名,两组都选择追问。对用户来说,"请补充日期"比给出一个条件不完整的数字更有价值。

3. 冲突:剩下的依据是否仍然互相矛盾

过滤不是万能裁判。我们故意设计了两份南桥项目专项制度:审批部门相同、有效期相同、适用范围相同,一份写 600 元,另一份写 650 元,而且都没有声明替代对方。

两组实验都返回冲突,并引用了两份文档。系统可以发现当前证据无法支持唯一答案,但不能凭发布日期晚一天就替组织决定哪份制度有效。腾讯云的产品文档对冲突问答也采用显式提示和人工处理,而不是让模型静默挑选。

4. 输出:回答、追问、拒答或升级

最终输出不应只有"回答成功/失败"两种状态。至少需要四类:

状态适用情况面向用户的动作
回答 存在唯一适用的正式依据 给出结论、适用条件和引用
追问 日期、项目、地区或角色缺失 说明缺少哪项条件
依据不足 当前提供的资料中没有适用依据 限定范围,不把"没检索到"说成"事实不存在"
冲突升级 同级有效依据给出不同答案 展示差异、来源和责任人入口

这四类状态比一句"请以实际为准"更有用。用户能知道系统为什么不能直接回答,也知道下一步该补什么信息、找谁处理。

20 次自建测试说明了什么

实验使用 8 份虚构文档和 10 个预设案例,覆盖新旧通用制度、项目例外、未批准群聊、未来草案、未来已批准制度,以及两份同级冲突制度。每个问题跑两个版本:

  • A 组把预设候选文档全部交给模型;
  • B 组先按批准状态、文档类型、有效期和项目范围做确定性过滤,再交给同一模型。

两组使用相同系统规则、问题和模型参数。预期答案在调用前冻结,模型看不到答案。实际使用阿里云百炼 qwen-plus,温度设为 0;每个案例、每组只运行一次。

在这 10 个预设案例中,A 组的状态与金额匹配 9 个,B 组匹配 10 个。B 组输入的文档总数从 26 份次降到 14 份次,输入 token 也更少。唯一的答案差异,就是前文的日期判断错误。

这些结果不能写成"准确率提升 10%"。样本是人工设计的,没有独立留出集,也没有重复运行;正文还明确提示了审批状态和冲突,难度有限。实验没有测试向量召回、分块、重排、权限控制或真实生产环境。

它能支持的结论更窄:在生成前排除明确不适用的材料,可以减少模型需要同时判断的条件;同级有效依据发生冲突时,过滤仍无法替代业务裁决。

企业现在可以检查的 8 件事

如果你的知识库已经上线,可以先抽查下面八项:

  • 每份正式知识是否有稳定的文档 ID,而不是只靠文件名。
  • 是否记录来源类型、审批状态和责任部门。
  • 是否记录内容有效期,而不只是上传时间和修改时间。
  • 是否记录适用组织、地区、项目、产品版本和用户角色。
  • 新版发布后,旧版是归档、失效,还是仍用于历史日期查询。
  • 检索过滤是否发生在证据进入模型之前,并记录了排除原因。
  • 回答能否追溯到具体版本,而不是只显示一个文档标题。
  • 评测集是否包含旧版与新版、草案与正式版、专项与通用规则、条件缺失、无答案和同级冲突。
  • 这些字段可以保存在数据库、搜索索引、文档管理系统或其他可查询的知识资产层中。技术选型不是这篇文章的中心,关键是规则必须可表达、可执行、可追溯。只有把"谁发布、何时有效、对谁有效、是否替代"变成系统能读取的数据,模型才不必从文件语气里猜组织规则。

    最小回归测试不要只测"答对的问题"

    上线前至少固定下面几类测试。每次修改切分、索引、过滤规则、提示词或模型,都重复运行并保存候选文档、排除原因、最终上下文和输出。

    测试类型输入设计预期行为
    版本边界 旧版结束日与新版开始日同一天 只命中正确版本,不出现一天重叠或空档
    未来制度 已发布但尚未生效的正式文件 历史/当前问题不使用,未来日期可使用
    草案干扰 草案内容比正式版更新、措辞更明确 不作为最终依据
    专项规则 通用制度与匹配项目的例外并存 按明确规则选择专项依据
    条件缺失 不提供日期、项目或角色 返回 clarify,指出缺失字段
    同级冲突 两份同级、同范围、同有效期文件结论不同 返回 conflict 并列出全部冲突来源
    无答案 只有过期、无权或未批准材料 返回 insufficient,不得补写答案

    只统计"最终文字看起来对不对"还不够。至少分别记录:正确依据是否进入候选集、错误依据是否被过滤、状态是否正确、关键字段是否正确、引用是否覆盖必要来源,以及调用失败和格式错误。这样才能区分是检索错、规则错,还是生成错。

    哪些决定必须留给人

    系统可以自动排除过期材料、未审批草案和范围不匹配的规则,也可以发现两份答案冲突。但"哪份同级制度最终有效"“某部门是否有发布权限”“特殊情况能否例外”,属于组织权责问题。

    更可靠的企业知识库,应当在有唯一依据时准确回答,在条件缺失时把问题问完整,在证据不足时限定结论,在权威依据互相冲突时让冲突可见。

    知识库接入 AI 以后,资料治理并没有消失。它只是从文件夹里的隐性问题,变成了每一次回答都会暴露的产品问题。

    参考资料

    • 腾讯云智能体开发平台:时效性检索
    • 腾讯云智能体开发平台:问答
    • Google Research:(D)RAGged Into a Conflict
    • Microsoft Learn:Filters for keyword search in Azure AI Search
    • Microsoft Learn:Vector Query Filters
    • Pinecone Docs:Filter by metadata
    赞(0)
    未经允许不得转载:171主机测评 » 知识库里的文档互相矛盾,AI 到底该信谁?我们做了 20 次自建测试
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址