AI 给出的归因结论怎么校验,防止编造数据和因果?
简化版
核心做法是让每条结论都挂到真实取过的数据上,并由代码而不是模型来核对。先让模型按固定格式输出结论,每条结论必须写明证据来自第几步取数、哪一行(定位字段)、哪个字段、值是多少;代码拿着这些信息回到落库的取数结果里反查:查不到就整条剔除,查到了就用库里的真值覆盖模型写的值。之后再检查因果链是否完整(「原因」必须指向一条真实存在的结论)、时序是否成立(原因不能晚于它解释的现象),不满足的**降级为「现象」**而不是删除。最终报告只写通过校验的结论,被剔除的单独列出并写明原因。
详细版
取数阶段:每一步的取数结果都落库(步骤序号、工具、入参、逐行数据)
成文阶段:模型输出结构化结论
{
"id": 2,
"content": "上映第二周排片占比明显下滑",
"role": "原因",
"explains": 3,
"timeAnchor": "2025-07-19",
"evidence": [{"step": 2, "locator": "2025-07-19", "field": "排片占比", "value": "18.5%"}]
}
校验阶段(代码,顺序固定):
C1 证据挂载:按 step + locator 反查取数结果 → 挂不上:整条剔除;挂上:用真值覆盖
C3 链条完整:「原因」的 explains 必须指向一条存在且未被剔除的结论,否则降级为「现象」
C2 时序成立:「原因」要有时间锚点且不晚于被解释的结论,否则降级为「现象」
报告阶段:只写通过的结论;降级的作为现象保留;剔除的单独列出并写明原因
| 处理 | 触发条件 | 结果 |
|---|---|---|
| 剔除 | 任何一条证据挂不上 | 不进报告,单独列出原因 |
| 降级 | 因果链不完整或时序不成立 | 作为「现象」进报告,不再当原因 |
| 通过 | 三项都满足 | 作为原因或现象进报告 |
完整版教学
一、归因里的幻觉长什么样
让模型「分析票房下滑的原因」,它可能输出一段流畅的结论:「由于口碑下滑,评分从 8.2 降到 7.1,导致第二周票房下降 40%」。这句话里可能藏着三种问题:
编造数字:评分其实是从 8.0 降到 7.6,「8.2」「7.1」「40%」都是模型写的
编造因果:口碑下滑和票房下降同时发生,不代表一个导致另一个
颠倒时序:口碑在第 8 天才开始下滑,票房在第 3 天就开始下降了,前者不可能是后者的原因
这些问题都不会报错,而且读起来很合理。所以归因结论不能只靠 Prompt 里的「请不要编造」,必须有代码层面的校验。离线评测时如何衡量幻觉与事实性,见「如何评估大模型的幻觉与事实性?常见指标有哪些局限?」。
二、前提:每一步取数都要落库
校验的前提是「能回查」。归因之前的每一步取数,都要把结果存下来:
步骤记录:第几步、用的什么工具、入参是什么、成功还是失败
取数结果:这一步返回的逐行数据
没有落库,模型说「第 2 步显示排片占比是 18.5%」,代码无从核对。落库还有第二个作用:页面上可以展开每一步,看到这一步查了什么、查到了多少行,整个归因过程可以回放。
三、C1 证据挂载:挂不上就整条剔除
证据格式要设计成可以反查的:
step 第几步的取数结果
locator 定位字段的值,用来在那一步的结果里找到唯一的一行(比如日期、影片名)
field 这一行里的哪个字段
value 模型认为的值
代码拿 step 找到那一步的结果,用 locator 找到那一行,取出 field 的真实值:
- 找不到(步号不存在、定位不到行、字段不存在):这条证据挂不上,整条结论剔除。一条结论里只要有一个数字是编的,这条结论就不可信。
- 找到了:用库里的真值覆盖模型写的值。即使模型把 18.5% 写成了 18%,最终展示的也是库里的 18.5%。
几个实现细节:
数值比对按数值比,不按字符串比:库里 12.00 和模型写的 12 是同一个数
没有可唯一定位字段的结果,一律当找不到:让它找不到,比随便挑一行更安全
记忆钩子:页面上出现的每一个数字,都来自查库而不是来自模型。模型写的值只是「指路」,真值由代码去取。
四、C3 链条完整:原因必须指向真实存在的结论
归因结论之间有指向关系:「原因」解释某一条「现象」。代码检查这个指向:
没有指向
指向自己
指向一个不存在的序号
指向一条已经在 C1 被剔除的结论
→ 以上任何一种,这条「原因」降级为「现象」
降级而不是删除,是因为这条结论的证据是真的,只是它能不能充当原因没有得到支持。作为「现象」它依然有信息量。
五、C2 时序成立:原因不能晚于结果
因果关系的必要条件之一是时间先后。代码比较「原因」与它解释的那条结论的时间锚点:
原因没有时间锚点 → 降级为现象
原因晚于它解释的那条结论 → 降级为现象
例如:口碑在上映第 8 天才开始下滑,而票房在第 3 天就开始下降,「口碑下滑导致票房下降」时序上不成立。时序只是必要条件,不是充分条件——时序成立不代表就是因果,但时序不成立一定不是因果。
六、三项校验的顺序为什么是 C1 → C3 → C2
C1 先做:证据挂不上的结论直接剔除,后面的校验不需要再看它
C3 再做:链条没接上的结论,连「它解释的是哪一条」都不确定
C2 最后:只有确定了「解释哪一条」,才谈得上比较两条的时间先后
顺序反过来,会出现给一条根本没接上的结论判时序、或者让一条指向已剔除结论的原因通过时序检查的情况。
七、剔除和降级都要可见
报告里只写通过校验的结论,但被处理掉的结论不能悄悄消失:
| 校验结果 | 报告里怎么呈现 | 用户能看到什么 |
|---|---|---|
| 通过 | 按原因或现象正常写入 | 结论、证据(库里真值)、来源步骤 |
| 降级 | 标注为「现象」写入 | 证据仍在,但不再作为原因 |
| 剔除 | 不进正文,单独列一段 | 被剔除的原因,比如第几条证据在第几步找不到 |
| 全部剔除 | 不出报告,任务失败 | 每条结论被剔除的原因 |
- 被剔除的单独列一段,写明为什么剔除(第几条证据在第几步找不到);
- 被降级的照常出现,标注为「现象」;
- 所有结论都被剔除时,这次归因没有依据,按失败处理,而不是输出一份空洞的报告。
这样用户和开发者都能看到模型原本答成了什么样、哪些地方被代码拦下了,这本身也是评估模型质量的数据。
八、常见误区与追问
- 误区:在 Prompt 里要求「不要编造数据」就够了。 模型无法保证遵守,编造的数字不报错也看不出来,必须由代码回查。
- 误区:证据对不上就改成库里的值,结论保留。 挂不上(找不到那一行)和值不一样是两回事:找不到说明这条证据是编的,整条剔除;找到了才用真值覆盖。
- 误区:时序不成立的结论直接删掉。 它的证据是真的,只是不能当原因,应降级为现象保留。
- 误区:时序成立就说明是因果。 时序是必要条件不是充分条件,只能用来排除错误归因。
- 误区:数值比对直接比字符串。 12.00 和 12 按字符串永远对不上,应按数值比较。
- 追问:为什么 C3 要在 C2 前面? 链条没接上就不知道解释的是哪一条,没法比较时间先后。
- 追问:全部结论都被剔除了怎么办? 这次归因没有依据,按失败处理,并保留剔除原因供排查。
九、加强记忆
归因结论防编造,靠代码回查而不是 Prompt 约束。前提是每一步取数落库。模型按结构化格式写结论,证据写明步号、定位字段、字段名和值。代码按顺序做三项校验:C1 按步号和定位字段反查,挂不上整条剔除,挂上用库里真值覆盖;C3 检查原因是否指向真实存在且未被剔除的结论;C2 检查原因有时间锚点且不晚于被解释的结论。C3、C2 不通过降级为现象而不删除。顺序是 C1 → C3 → C2。报告只写通过的结论,剔除的单独列出原因,全部剔除按失败处理。