当每个 few-shot 示例里所有字段都成功提取出了非 null 值,模型就学到了一条隐含规则:每个字段必须总有一个值。处理一篇 warranty_expiry 确实不存在的文档时,模型会编造一个看似合理的日期。
修复方法:至少包含一个字段缺失、正确输出为 null 的示例。
Null 示例技巧
一个展示 "warranty_expiry": null 并注明”源文档中不存在”的示例,把幻觉率从 31% 降到了 4%。一个有针对性的示例,87% 的降幅。
幻觉率与字段可用性成反比:
- 总是存在的字段(name、title):96-99% 准确率
- 有时存在的字段(order ID):87%
- 很少存在的字段(warranty、police report):62%
这个差距完全来自模型从未见过 null 案例的示例。示例训练模型总是产出一个值——它需要看到 null 是一个合法答案。
文字指令压不过这个
“只提取明确陈述的信息”和”绝不编造值”——这些强硬的文字指令失败了,而 few-shot 示例成功了。模型跟随演示的模式而不是描述的规则。把指令写得更激烈(“关键:在任何情况下都绝对不可以…”)并不会改变其效果。
“做出你的最佳估计”这条指令在主动鼓励幻觉。用展示缺失数据返回 null 的示例替换它。
多样化文档覆盖
对于一个处理实证论文、理论论文和综述文章的系统——把 3 个同类型示例替换为:
- 实证论文(所有字段都有值)
- 理论论文(若干字段为 null——没有实验数据)
- 部分数据文档(某些字段为 null 并附注释)
这在同样的 token 预算内覆盖了所有三种幻觉模式。模型看到不同文档类型有不同的字段可用性,数据不存在时 null 是合适的。
source_status 模式
最高级的应用是给每个提取值加一个 source_status 元数据字段:
"extracted"— 在源文档中找到了值"not_found_in_source"— 文档中不存在该字段"ambiguous"— 数据存在但不明确
这让下游系统能做出知情决策,而不是对每个提取值一视同仁地信任。
这些方法不行
LLM 置信度分数检测不了幻觉。模型对编造的值也报告高置信度,因为编造的内容本身是合理的。基于置信度的过滤不可靠。
提取后验证能抓到一些错误,但抓不到合理的编造。一个编造的日期”2025-06-15”用在 warranty 字段上,通过了格式验证但完全是瞎编的。
二次验证模型增加基础设施成本并引入新的故障模式。根因——缺少 null 示例——修起来更便宜也更有效。
一句话总结: 至少包含一个字段确实缺失、输出为 null 的 few-shot 示例——这一个技巧就能把幻觉率从 31% 降到 4%,因为模型需要看到”没有数据”是合法答案。