[控场AI]
· 5 分钟阅读· 2,710 字

AI SDK 新增 interrupted 中断状态:流式输出处理升级

AI SDK 新增 interrupted 中断状态:流式输出处理升级

AI SDK xAI 适配包 5.0.28 新增 interrupted 完成状态,统一十余家 provider 的流式中断语义。

AI SDK 的 @ai-sdk/xai 发布 5.0.28 补丁,核心变更是新增 `interrupted` finish reason,用于标识上游模型流在缺少 provider 完成信号时干净结束的状态:保留已产出的部分输出,且不单独抛出错误。该变更虽挂在 xAI 包下,实际覆盖 OpenAI、Anthropic、Google、Bedrock、Cohere、DeepSeek、Groq、Mistral、Moonshot、Perplexity 等十余家 provider,属于 provider 抽象层的统一语义调整。官方特别给出兼容性警示,要求应用在 switch、映射表和校验中显式处理 interrupted;同时真正的 provider、解析与传输错误现在会正确报 error,不再混入 other,可能导致错误率统计上升。文章建议检查所有 finish reason 消费点、区分可续写与需重试场景,并同步升级相关依赖包。

rss source: @ai-sdk/xai@5.0.28

AI SDK 的 xAI 适配包发布了 5.0.28 补丁版本,表面看只是一次常规依赖更新,但其中的 patch change 触及了所有流式调用方都必须正视的问题:当上游模型流在没有给出 provider 完成信号的情况下正常结束时,finish reason 应该报什么。

一个被长期忽略的灰色地带

流式输出是当前大模型应用的主流调用方式,但流"结束"和流"完成"是两件事。完成意味着 provider 明确发出了终止信号,而结束可能只是连接断开、消息截断或上游服务提前关闭。此前这类情况在 AI SDK 中会回退到 other,或者干脆不输出 finish part。

5.0.28 的做法是把这种状态正式建模为 interrupted:保留已产出的部分输出,同时不因为"中断"本身抛出错误。这个区分很关键——中断不等于失败,它可能是一次用户主动取消,也可能是一次需要重试的截断。

覆盖范围远超 xAI

尽管版本号挂在 @ai-sdk/xai 下,这次变更的影响面横跨 OpenAI(Chat、Responses、Completions)、Anthropic、Google(Generate Content 与 Interactions)、Alibaba、Amazon Bedrock Converse、Cohere、DeepSeek广告、Groq、Hugging Face Responses、Mistral Chat、Moonshot AI、Open Responses、Perplexity 以及 xAI Responses。此外还覆盖了 Anthropic 后续消息中的截断,以及 Google 的自动续写场景。

换句话说,这是 provider 抽象层的一次统一语义调整,选择在 xAI 包里发版更多是发布节奏的结果,而非范围限定。

finish reason(完成原因) 是 AI SDK 在每次生成结束后返回的枚举字段,用于告知调用方本次生成为何终止。常见的取值包括 stop(模型自然结束)、length(触发 token 上限)、content-filter(被安全策略拦截)、tool-calls(模型请求调用工具)、error(发生错误)以及 other(无法归类的未知原因)。在流式调用中,该字段通常由最后一个 finish part 携带;若上游从未发送终止信号,调用方就拿不到可靠的 finish part。interrupted 的引入正是为了填补这一枚举空白:它表示流已结束但未收到 provider 的完成信号,属于"正常结束但结果可能不完整"的独立状态,而非错误。

provider 抽象层 指 AI SDK 对多家模型服务商 API 做的统一封装。各家的流式协议、终止标记和错误码格式差异很大,SDK 通过 @ai-sdk/provider 定义统一接口,再由各个适配包(如 @ai-sdk/xai、@ai-sdk/openai)把厂商原始事件翻译成这套统一语义。因此 finish reason 的取值一旦调整,所有适配包和所有下游应用都会同步受影响。

兼容性警示:switch 语句会静默漏判

官方在补丁说明中专门给出了 Compatibility notice,这在 patch 版本里并不常见。核心意思是:过去依赖 finishReason 做分支判断的应用,需要在 switch、映射表和自定义校验中补上 interrupted 分支。

推荐写法是显式判断 finishReason === 'interrupted' 来识别不完整响应。需要强调的一点是:中断本身不触发错误处理路径,如果你的代码用 try/catch 兜底,这类情况不会被捕获,只能靠 finish reason 判断。

error 与 other 的边界被重新划定

变更同时收紧了两类状态的语义:

  • 显式的未知完成原因仍然映射到 other;
  • 真正的 provider 错误、解析错误和传输错误保留 error 路径,此前被归入 other 的部分错误路径现在会正确报 error。

这对日志和监控有直接影响。此前混在 other 里的异常流量会被拆分出来,错误率统计可能因此上升,但这属于修正而非回归。OpenAI 兼容层保持不变,说明官方对存量兼容性做了取舍。

对应用层的三点实践建议

第一,检查所有 finish reason 的消费点。除了主流程,别忘了埋点上报、A/B 实验分组和内容审核这些边缘路径,它们往往用字符串匹配而非枚举。

第二,区分"可续写"与"需重试"。Google 自动续写和 Anthropic 多消息截断属于可续写场景,可以基于已保留的部分输出拼接后续内容;纯传输中断则更适合重试。

第三,依赖版本同步升级。本次同时更新了 @ai-sdk/provider 至 4.0.32、@ai-sdk/provider-utils 至 5.0.66,跨包混用旧版本可能导致 finish part 解析不一致。

可续写(continuation) 与 重试(retry) 的区分依赖对中断上下文的理解。可续写的前提是:上游已产出的内容在语义上是连续且可拼接的,例如 Google Generate Content 因输出长度上限触发的自动续写、Anthropic 后续消息中的截断,这些都保留了完整的消息边界,可以携带已有内容发起下一次请求继续生成。重试则适用于传输层中断,此时上下文可能不完整或重复,直接重放请求更安全。实践中建议为中断事件记录元数据(如已生成 token 数、是否收到过 finish part、中断发生在第几个 chunk),据此判断走续写还是重试路径,避免把半截内容当作完整结果落库。

小结

5.0.28 是一次小版本号、大语义面的变更。它把流式输出中"静默截断"这个长期模糊的状态变成了可编程的一等公民,代价是所有下游应用需要主动适配。对构建生产级 AI 应用的团队来说,这类底层抽象的变化值得纳入回归测试清单,而不是等线上出现半截答案才发现。

相关讨论可参考 GitHub Releases 页面的原始发布说明。

#AI SDK#@ai-sdk/xai#interrupted#finish reason#流式输出#provider 抽象层#兼容性变更
分享:

相关推荐