报错是这么冒出来的。客户端解析 SSE 时只取 data 行,网关中途推送 event: error 加一段 JSON 的错误信息,前端把这段错误文本当模型输出显示,用户看到回答里夹着 upstream timeout。
中间走过弯路,把「没有判断 finish_reason 就直接结束」当成主因,改完现象没变。
原因不止一个,得分开看。解析器忽略了 event 字段,只按 data 处理。也可能是错误帧的 data 也是合法 JSON,被当成 delta 追加。
还有一种情况,重连后历史片段与新片段被拼接两次。
动手时先把收到的每一帧原样落盘
curl -N -s https://gateway.internal/v1/chat/completions -H "Authorization: Bearer $TOKEN" -d @payload.json -o sse.log
接着统计日志里出现过哪些 event 名称
python -c "import re;print(set(re.findall(r'event: (\w+)', open('sse.log').read())))"
再往下在解析器里对 error 事件单独抛错
python -c "print('if ev == \"error\": raise UpstreamError(data)')"
只在 choices 里取 delta,其余字段忽略
python -c "import json;d=json.loads('{\"choices\":[{\"delta\":{\"content\":\"a\"}}]}');print(d['choices'][0]['delta'].get('content'))"
改之前我把相关日志按时间截了一段存着,事后对时间线用得上。
别只看报错消失,验证要多做一步。错误帧不再出现在回答文本里,前端能弹出明确的失败提示,正常回答与之前一致。
SSE 不只有 data,event 和 id 都带语义,只认 data 行的解析器早晚会踩到错误帧。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
