打开 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 解析会随机失败。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。