同一个 Prompt,为什么这次跑对了上次跑错了:LLM 推理的非确定性与工程对策

在生产环境里,最折磨人的 bug 往往不是报错,而是"时灵时不灵":同一个 prompt,连发五次,两次答对,三次答错。这背后不是玄学,而是 LLM 推理里几个可以量化的机制。

专属插画
同一个 Prompt,为什么这次跑对了上次跑错了:LLM 推理的非确定性与工程对策

同一个 Prompt,为什么这次跑对了上次跑错了:LLM 推理的非确定性与工程对策

在生产环境里,最折磨人的 bug 往往不是报错,而是"时灵时不灵":同一个 prompt,连发五次,两次答对,三次答错。这背后不是玄学,而是 LLM 推理里几个可以量化的机制。

温度、Top-p:随机性从哪里来

采样参数直接决定输出的随机程度。temperature=0 只是把 argmax 变成确定的选择路径,但只要存在浮点舍入、batch 尺寸变化或不同硬件后端,底层 logit 可能有微小差异,输出仍可能漂移。top_p 截断会让"第二选项"有机会上位,当第一名和第二名的概率很接近时,换一次采样结果就翻转了。工程上的第一个对策:把 temperature 和 top_p 写进配置并纳入版本管理,而不是散落在调用代码里。复现问题之前,先确认"这两次请求的参数真的相同"。

再往下一层看,softmax 前的 logit 差值才决定翻转概率。假设第一名 51%、第二名 49%,无论温度怎么调,两次采样之间出现翻转都是常见事件;而第一名 95%、第二名 5% 时,结果基本稳定。也就是说,“同一个 prompt 有时对有时错”往往说明任务处在模型能力边界上,prompt 本身需要更强的约束(示例、格式、判据),而不是单纯调采样参数。调参数治标,改任务定义才治本。

批处理和硬件引入的扰动

推理引擎里,continuous batching 会让同一请求和不同请求拼进同一个 batch,GPU 上的浮点累加顺序随之改变。结果就是:同一个模型、同一台机器、同一个 prompt,不同时间点的输出可能不同。这类差异通常是轻微的措辞变化,但一旦下游用正则或字符串匹配解析输出,轻微措辞变化就会变成解析失败。对策是两层:解析层不要依赖"恰好是这个词",用结构化输出(JSON schema、函数调用)加校验重试;如果业务要求严格复现,就要固定 batch 行为(比如独占 batch)或接受"输出会变"这个前提去做容错设计。

缓存命中路径不一致

KV cache 复用、前缀缓存、投机解码,这些加速机制会让"同一段输入走不同的计算路径"。大部分实现能保证数值近似一致,但近似不等于相等。做 A/B 对比或离线评测时,一个常见坑是:评测跑在无缓存的冷启动路径上,线上跑在热缓存路径上,两边结果分布不一样,结论就偏了。最小可行做法:评测环境和线上环境用同一套推理配置,并在报告里注明缓存状态。

重试不是万能药

"错了就重试三次"是成本最低也最危险的对策。它把偶发错误变成成本波动:错误率 5% 时,重试三次能把单请求成功率提到 98.75%,但平均成本接近 1.15 倍,且每次重试都要重新付整条 prompt 的 token 费。更稳的做法是给重试加"记忆":第二次重试时把第一次的失败输出附进 prompt("上一次回答错在 X,请修正"),这类 self-correction 在有明确判据的任务上有效,在开放式任务上收益有限。判据越明确(可运行测试、schema 校验、关键词核对),重试策略越值得投入。

一个可执行的检查清单

1. 固定并版本化 temperature、top_p、seed(如后端支持);

2. 解析层用结构化输出 + 校验,不靠字符串匹配;

3. 评测与线上同配置,报告注明缓存状态;

4. 重试带失败上下文,并设上限(建议 2 次);

5. 对"时灵时不灵"的请求保留完整请求/响应日志,先复现再优化。

非确定性消除不了,但可以把它关进笼子里:让它在措辞层面抖,而不是在正确性层面抖。

留言区

欢迎分享你的想法!

发表留言

0/500

加载留言中…