用户过早行动的流式 Token
一个用户询问你的助手某项配置更改是否可以安全发布。模型流式返回了:“是的,你可以安全地部署。” 300 毫秒后,它继续写道:“——但在 us-east 区域除外,那里的旧连接池仍在排空。”但用户已经读完了前半部分,感受到了绿灯带来的放松,并点击了部署。这句修正说明到达时,人已经走开了。
这里没有人犯错。模型是正确的。用户阅读了屏幕上的内容。渲染器忠实地显示了每个到达的 Token。然而结果却是一次糟糕的部署,因为流式传输将模型的“中间状态”变成了用户视为“最终结果”的东西。
这是逐 Token 流式传输中一种隐蔽的失效模式。我们采用它是为了对抗延迟 —— 为了让助手显得快速且鲜活,而不是冻结在加载图标后面。这奏效了。但在这样做时,我们将模型未完成的思考暴露了出来,就好像它是完整的答案一样,而且我们没有给用户任何区分这两者的方法。半截句子和最终结论在屏幕上看起来一模一样。它们都只是黑色文字。
流式传输暴露了草稿并将其称为答案
非流式响应具有一个非常有用的属性:它是原子性的(atomic)。在模型完成之前,用户什么也看不到,然后会看到全部内容。模型生成的任何限定条件、警告和修正,在第一次接触时都会完整呈现。用户阅读的是一个完整的想法。
流式传输故意打破了这种原子性。它用消息的完整性换取了感官上的速度。每个 Token 在到达时被绘制出来,这意味着用户正在阅读一份仍在编写中的文档。对于大多数内容来说,这没问题 —— 散文会优雅降级,渲染了一半的段落显然就是渲染了一半。
危险在于特定且狭窄的场景:当文本的“含义”在两个 Token 之间发生变化时,问题就出现了。语言经常如此。“你可以部署这个”和“你可以部署这个 —— 除了”并不是同一个观点增加了细节;它们是相反的观点。“迁移是可逆的”和“只有在没有运行回填(backfill)的情况下,迁移才是可逆的”在程度上有本质区别。流式句子的前半部分不仅可能是不完整的,而且可能具有极大的误导性,而模型无法知道用户会在后半部分到达之前就采取行动。
大语言模型采用单次前向传递(forward pass)从左到右生成。它们在浮现“除非”之前就已经确定了“是”。这不是一个待修复的 bug —— 这是自回归生成(autoregressive generation)的工作原理。模型的限定条件确实出现在它的断言“之后”。流式传输提取了这种内部顺序,并将其呈现为一个用户可以随时打断的公开时间线。
