大模型最麻烦的时刻,不是它说不知道。

而是它给出一个语气笃定、结构完整、甚至带着出处的答案,等你真的去查,才发现内容根本不存在。

这种现象通常被叫作幻觉。

模型不是在主观撒谎。它的核心任务是根据上下文预测接下来最可能出现的内容。当可靠信息不足时,它仍然可能生成语言上非常合理的答案。读起来顺,不等于事实正确。

所以减少幻觉,不能只靠一句「请不要胡编」。更有效的思路是让模型有依据可用,在依据不足时允许拒答,并让最终结论能够被检查。

普通使用时,先缩小回答空间

问题越开放,模型自由发挥的空间越大。

直接问「介绍一下这个项目」,模型可能自行补充作者、时间线和功能。把要求改成「只根据下面提供的 README,总结项目功能,材料没有提到的信息请标为未知」,回答边界会清楚很多。

一个实用的提示可以包含三件事。

1
2
3
只根据我提供的资料回答。
资料没有覆盖的内容,请明确说不知道,不要补充猜测。
每个关键结论后标出对应的资料片段或编号。

它不能保证模型绝不出错,但能把「自由生成」变成「有边界的整理」。

还可以要求模型把事实、推断和建议分开。事实必须来自材料,推断要说明推理过程,建议则明确标注为建议。三类内容混在一起时,读者很容易把模型的推测当成已经发生的事实。

要求来源,但不要盲信来源

让模型给出处是个好习惯,不过这里还有一个坑。

模型可能连引用也一起编出来。一个看起来很像论文标题的字符串,一组格式规整的作者与年份,都不能自动成为可信证据。

真正有用的是可访问、可定位、能支持当前结论的来源。看到链接后,还要确认页面是否存在、原文是否真的表达了同一件事,以及信息是否已经过时。

医疗、法律、金融和生产环境操作等高风险问题,更不能只看答案写得像不像。至少应该回到官方文档、法规原文或权威机构材料核对。

语言模型很擅长生成「像答案的文本」,验证环节负责判断它是不是答案。

RAG 能降低幻觉,但不会自动消灭幻觉

在应用系统里,常见方案是 RAG,也就是检索增强生成。

用户提出问题后,系统先从知识库中检索相关内容,再把这些内容与问题一起交给模型。模型不必只靠训练阶段记住的知识回答,而是可以参考当前资料。

这对私有知识和频繁更新的信息很有帮助。产品文档更新后,只要重新建立索引,就能让后续回答使用新版本内容,不需要为了每一次文档变化重新训练模型。

可问题并没有到这里结束。

RAG 自己也会产生新的失败路径。

检索器可能没有找到真正相关的文档,也可能召回一堆表面相似但回答不了问题的内容。文档找对了,模型还可能脱离材料继续推断。材料之间互相冲突时,模型也可能悄悄选择其中一份,却不告诉用户冲突存在。

于是系统治理需要覆盖整条链路,而不只是 Prompt。

第一道防线是检索质量

如果交给模型的上下文本身就是错的,后面写再严格的指令也很难救回来。

知识库需要选择合适的切分方式。块太小,完整语义会被切断。块太大,又容易混入很多无关信息。关键词检索适合精确术语,向量检索擅长语义相似,两者可以组合召回,再通过 RRF 一类方法融合结果。

召回之后,还可以使用 Reranker 对查询和候选文档做更细的相关性判断。检索指标也应该单独评估,例如观察正确资料是否进入 TopK,而不是只看模型生成的文字是否顺畅。

这一步不够显眼,却决定了模型手里到底拿到了什么。

第二道防线是低置信度拒答

很多系统最大的错误,是无论检索结果多差,都要求模型必须回答。

如果最高相关性低于阈值,或者多个结果都无法覆盖问题,系统可以直接说明资料不足。拒答不是能力下降,而是系统知道自己的证据边界。

阈值也不能拍脑袋设置。太高会让系统频繁拒绝本来能回答的问题,太低又拦不住无关材料。可以准备一组包含可回答和不可回答问题的验证集,观察不同阈值下的误答率与拒答率,再选择能够接受的平衡点。

允许模型说不知道,往往比教它把每句话写得更肯定重要。

第三道防线是生成约束

检索结果合格后,Prompt 仍然需要给出清楚边界。

可以要求模型只使用提供的上下文,资料不足时返回固定状态,并让每条结论携带来源编号。结构化输出也能让程序更容易检查结果。

1
2
3
4
5
{
"answer": "根据资料生成的回答",
"citations": ["doc-3#chunk-8"],
"supported": true
}

这里的 supported 不该完全听模型自我判断。系统仍然需要检查引用 ID 是否存在、引用片段是否进入过上下文,并在必要时验证结论与证据是否一致。

Prompt 是约束,不是安全边界。真正稳定的系统要把能用程序检查的部分留给程序。

第四道防线是引用核查

答案生成后,可以逐条检查关键声明。

最基础的检查,是确认每个引用都对应真实 chunk。再往前一步,可以把单条结论与引用片段交给一个核查模型,判断证据是否支持结论。如果证据只覆盖一半,就删除未被支持的部分,或者把答案降级成不确定表述。

核查模型也可能犯错,所以它更适合当第二道筛网,而不是绝对裁判。对特别重要的操作,还可以要求人工确认后再执行。

一个成本更低的策略,是只核查日期、数字、名称、因果关系和操作指令等高风险声明。没必要给每一句自然语言都增加一次模型调用。

如果把这些方法收束成工程实践,我会优先看四个方向。

第一是 Prompt 强约束。明确告诉模型只能根据提供的资料回答,资料里没有就说不知道,不要为了完整性补充猜测。

第二是检索质量门控。Rerank 分数或相关性分数低于阈值时,系统直接拒答,避免模型在低质量上下文上硬撑。

第三是生成后引用核查。每个关键声明都要能在 chunk 里找到来源依据,找不到就删除、降级或标为不确定。

第四是结构化输出强制溯源。比如让模型输出 JSON,每条结论附上来源编号,方便程序继续检查引用是否存在、证据是否进入过上下文。

如果只选成本最低、见效最快的组合,我会先做 Prompt 强约束和检索质量门控。前者减少模型自由发挥,后者减少系统把错误材料交给模型的概率。生产系统上线前,这两层至少应该有。

评估时不要只看答案好不好读

生成质量很容易被语言流畅度迷惑。

一个更实用的测试集,应该同时包含知识库中有答案的问题、只有部分答案的问题、完全没有答案的问题,以及资料彼此冲突的问题。

然后分别观察几件事。

  • 正确资料有没有被召回
  • 没有证据时系统会不会拒答
  • 答案中的声明能否在引用里找到
  • 引用是否真的支持对应声明
  • 更新知识库后,旧答案是否及时失效

只有把检索、生成和引用拆开测,才知道错误到底发生在哪里。否则看到一个错误答案,很难判断是检索没找到、排序排错了,还是模型拿到正确资料后仍然编了内容。

没有一种办法能把幻觉降到零

提示词约束有用,RAG 有用,Reranker 和引用核查也有用。但它们都不是「安装以后永不出错」的开关。

更可靠的系统,往往不是让模型显得无所不知,而是认真处理不知道的时刻。

有资料就引用资料,证据不足就降低语气,超出边界就明确拒答,高风险结论再交给人确认。

模型负责生成,系统负责约束,使用者负责判断。

把这三层责任分清楚,幻觉才会从一个模糊的模型缺陷,变成可以逐步治理的工程问题。