打开 stream=True 后逐块打印,第一块 delta 里只有 role=assistant,content 是空字符串,代码直接拼接导致输出开头多出一个空行,前端以为是模型没回答。

原因不止一个,得分开看。首个增量只用于声明角色,本来就没有正文;部分兼容接口的 delta 字段名与本家不同;结束块带的是 finish_reason,content 同样为空;把 SSE 注释行也当成数据解析,得到空对象。

拼接时用 get 取字段并跳过空值

text += (chunk.choices[0].delta.get('content') or '')

再往下打印前三块确认结构再写解析逻辑

print(chunk.choices[0].delta)

单独处理结束块,不要把它当正文

if chunk.choices[0].finish_reason: break

过滤掉非 data 行与心跳注释

if not line.startswith('data:'): continue

隔了一天再看,现象没变,说明不是偶发。

恢复的判定其实很直接。输出文本开头没有空行,关闭流后拼出的内容与一次性返回的结果基本一致,前端渲染也不再先空一段。

补一句。有些网关会在流里插入心跳注释行,解析时要先判断是否以 data: 开头并跳过 keep-alive,否则 json 解析会随机失败。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189