溯因推理是推理模型经常要处理的一类任务:给出若干观察结果,反推最可能的解释。比如系统日志中出现连接超时和磁盘写入变慢,模型需要判断是网络故障、磁盘老化还是其他原因。问题在于,很多推理模型在这个阶段会快速锁定一个表面合理的假设,然后围绕它寻找支持证据,忽略了同样能解释当前现象的备选原因。这种现象在故障诊断、医疗辅助决策、产品归因分析里会直接造成误判。

要解决这个问题,不能只告诉模型“多考虑几种可能”。因为模型在生成时已经受到解码策略、训练数据分布和注意力机制的共同影响,早期出现的词语会强烈引导后续内容。真正有效的做法是把候选假说显式地保留下来,再用概率方法逐步筛选。下面分别从问题根源、多假说并行、贝叶斯更新以及工程落地四个角度展开。
为什么溯因推理里模型容易只认一个原因
溯因推理和演绎、归纳不同。演绎是从一般规则推到具体结论,归纳是从多个样本总结规律,而溯因是从结果反推最可能的原因。它天然带有不确定性,因为同一个结果可能由多个原因造成。模型在训练时见过的文本里,人类表达因果时往往只写最显著的原因,很少把备选原因完整列出。这会让模型学习到一种倾向:出现一组症状,就匹配一个最常见的诊断;出现一组日志,就匹配一个最典型的故障。
除了训练数据偏置,解码阶段也会加剧这个问题。默认的贪婪解码每一步都选概率最高的词,一旦模型在第一句输出“最可能的原因是 CPU 资源不足”,后续生成就会围绕这个判断继续补证据。即使模型内部对网络延迟、磁盘 I/O 饱和也有一定概率估计,这些可能性也不会被展开。束搜索虽然保留多个序列,但它的目标仍然偏向整体概率最高的回答,未必能覆盖语义不同的假说。
还有一个隐蔽因素是自注意力机制。模型在生成过程中会强烈关注已经写出的内容,前面形成的因果叙事会抑制新的解释方向。比如模型先写了“连接超时说明网络有问题”,后面就很难再切换到“连接超时也可能由服务端线程池耗尽引起”。这种早期锁定在需要反复比较备选原因的复杂溯因任务里尤其危险。
prompt = """ 观察结果: 1. 服务响应时间从 200ms 增加到 1200ms 2. CPU 使用率从 30% 升高到 85% 3. 数据库连接池偶尔出现等待 请判断最可能的原因。 """ # 默认贪婪解码只会生成单一答案 output = model.generate(prompt, do_sample=False) # 实际输出往往类似于:最可能的原因是 CPU 资源不足
上面的例子并不是说 CPU 资源不足一定错,而是模型没有给出其他同样合理的解释。单条输出无法体现模型对“数据库连接池配置不当”“流量突发”“慢查询导致连接堆积”等可能原因的保留。要让模型在溯因推理中更可靠,首先要从生成机制上改变单一路径的输出方式。
多假说并行:先保留候选集合,再谈筛选
多假说并行的核心思想是阻止模型过早收敛到一个解释。实现方式主要有三种。第一种是采样多样性,通过提高 temperature 并多次采样,让模型生成不同开头的候选回答。第二种是提示词显式约束,要求模型先列出三个到五个可能原因,再分别分析支持证据和反对证据。第三种是结构化解码,在推理过程中维护一个假说列表,每生成一个假说就把它加入上下文,避免后面的内容重复或覆盖前面的假说。
采样多样性的做法实现成本最低。它不改变模型结构,只需要在生成时打开随机采样,并把 temperature 调到 0.8 到 1.0 左右,重复生成多条结果。这样可以拿到多个语义不同的解释。下面是一段简化的 Python 伪代码,展示如何调用模型得到候选假说集合。
candidates = []
for _ in range(6):
out = model.generate(
prompt,
do_sample=True,
temperature=0.9,
max_new_tokens=160
)
candidates.append(out.strip())
# 去重并初步过滤过短或重复度过高的输出
unique_candidates = list(dict.fromkeys(candidates))
print("候选假说数量:", len(unique_candidates))
采样得到的候选只是第一步。这些候选可能有的表达相似,有的包含多个原因混合,有的证据明显不足。因此还需要一个打分或更新环节。不能简单地让模型自己选一个“最合理”的,因为模型在选择时仍然可能偏向高频答案。更好的做法是把候选集合送入后续的贝叶斯更新流程,用观察证据对每个假说的可信度进行量化修正。
提示词约束方法也很常见,但稳定性不够好。比如在提示词里写“请先列出至少三个可能原因,并为每个原因给出支持和反对证据”,模型在小模型上可能仍然先写一个主答案,再补充几个相关性不高的备选。采用多假说并行时,最好把候选生成和候选评估拆成两个阶段。生成阶段只要求广覆盖,评估阶段再用逻辑和概率手段筛选,这样能减少同一路径下的互相干扰。
贝叶斯更新如何修正每个假说的可信度
有了候选假说集合后,问题变为:在观察到新证据时,如何调整对每个假说的信心。这正是贝叶斯更新适合的场景。贝叶斯公式写出来并不复杂:后验概率等于似然乘以先验概率再除以证据概率。对每个候选假说 H,观察到证据 E 后的后验概率 P(H|E) 可以表示为 P(E|H) * P(H) / P(E)。这里 P(H) 是先验,表示在未看到当前证据前该假说的可信度;P(E|H) 是似然,表示如果 H 成立,看到当前证据的概率;P(E) 是所有假说下看到证据的总概率。
在推理模型里落地时,不需要手推复杂积分。可以用一组候选假说构成离散概率空间,对每个假说维护一个概率值,然后随着证据逐步更新。先验可以根据领域知识初始化,也可以全部设为均匀分布。似然则需要模型对每个假说打分,问题转化成“给定这个原因,当前观察结果出现的可能性有多大”。下面是一段 Python 示例,展示多假说下的离散贝叶斯更新。
def discrete_update(priors, likelihood_given_evidence):
total = 0.0
posteriors = {}
for hypothesis, prior in priors.items():
likelihood = likelihood_given_evidence.get(hypothesis, 0.0)
posteriors[hypothesis] = prior * likelihood
total += prior * likelihood
if total == 0.0:
return priors
for hypothesis in posteriors:
posteriors[hypothesis] /= total
return posteriors
priors = {
"CPU资源不足": 0.25,
"数据库连接池配置不当": 0.25,
"突发流量导致排队": 0.25,
"慢查询堆积": 0.25,
}
# 观察到连接池等待时间增加时,不同原因解释该证据的能力
likelihoods = {
"CPU资源不足": 0.4,
"数据库连接池配置不当": 0.9,
"突发流量导致排队": 0.7,
"慢查询堆积": 0.6,
}
posteriors = discrete_update(priors, likelihoods)
print(posteriors)
这段代码先把四个候选原因都设成相同的先验,然后根据一个证据更新。更新后,“数据库连接池配置不当”的概率会上升。后续再观察到 CPU 使用率升高等证据时,可以继续调用同一函数,把上一次的后验当成新的先验。多轮更新后,假说概率会逐步向真正能解释多项观察的原因集中。需要特别注意的是,这种离散更新对先验和似然的数值很敏感,如果某个假说似然取得过小,例如 0.001,它可能被过早淘汰。实际使用时可以给似然加一个下限,比如 0.05,避免单一证据过度惩罚。
贝叶斯更新不只是排序。它还能暴露证据与假说之间的冲突。如果所有假说的后验概率都很低,说明现有候选集合都不能很好解释观察结果,这时应当触发新一轮假说生成。反过来,如果两个假说更新后概率非常接近,就不应该强行选一个,而是输出并列结论,并给出建议采集哪些额外证据。这样做比让模型直接输出“最可能原因是某一种”要稳妥得多。
结合生成与更新时需要注意的工程问题
这套方案在实际系统里会带来额外的计算开销。多假说并行意味着同一条输入要生成多次,贝叶斯更新可能需要额外调用模型来估计每个假说的似然。如果每次请求都这样做,延迟和成本会明显上升。一个可行做法是根据任务风险分层处理:低风险问题仍走单路输出,高风险问题才启用多假说和贝叶斯更新。对于日志诊断、医疗辅助、金融风控等领域,多消耗一些 token 换取更低的误判率通常是值得的。
假说去重和语义归并也是容易忽略的环节。采样得到的多个候选可能用不同措辞表达同一个原因,比如“连接池太小”和“数据库连接池配置不足”本质相同。直接把它们当作两个假说会让先验和计算产生偏差。可以先做嵌入聚类,或者让模型对候选进行归一化表达。去重后,先验需要重新分配,最好保留原始出现频次作为先验参考,而不只是简单均匀分配。
评估这套方法时,不能只看模型有没有提到正确原因。更合理的指标是召回率和排序质量。召回率衡量正确原因是否出现在候选集合中;排序质量衡量经过贝叶斯更新后正确原因是否排在前面。很多推理模型能够在小参数下把正确原因排进前五,但很难稳定排第一。通过多假说并行和贝叶斯更新,排序可以更符合证据强度,而不是只依赖语言表面流畅度。
最后还要处理模型输出概率与真实置信度不一致的问题。模型给出的似然分数往往偏离真实概率,需要做温度缩放或校准。可以在标注数据上拟合一个简单映射,把模型的 0.8 分映射到真实意义上的 0.6。否则贝叶斯更新会被过度自信的似然值带偏,导致某个假说概率迅速接近 1,又退回到单一结论。先验估计、似然校准、候选去重这三件事做好,多假说并行与贝叶斯更新才能在实际溯因任务中稳定发挥作用。