AI中转遇到上游返回chunk乱码
AI中转服务在转发大模型流式响应时,最让人头疼的就是上游返回的chunk出现乱码。
它通常表现为中文变成问号或火星文、接口输出的换行位置错乱、以及多个data块粘在一起导致解析失败。
这些问题大多出在编码、换行和分隔符三个环节。
下面按排查顺序,给出可以直接套用的调试方法。
第一步:先复现并抓取原始响应
在动手改配置前,先用curl直接打上游接口,把chunk原样保存下来,避免中转服务二次加工干扰判断。
以下命令只打印响应头:
curl -i -N -H "Authorization: Bearer $YOUR_KEY" https://upstream.example.com/v1/chat/completions \
-d '{"model":"gpt-3.5-turbo","stream":true}' -o /tmp/raw_chunk.txt
保存后重点看两处:响应头里的Content-Type是否包含charset=utf-8,以及chunk内容中是否出现\xef\xbb\xbf(UTF-8的BOM标志)。
如果Content-Type没有明确charset,很多HTTP客户端会默认按ISO-8859-1解码,中文自然就乱了。
第二步:从编码层面把字符集统一成UTF-8
确认原始响应确实正常后,再检查中转服务转发时是否做了错误的编码转换。
常见坑有两个:
- 上游返回UTF-8,中转服务用
gzip压缩后没有正确设置Content-Encoding,客户端解压失败产生乱码。 - 代码里用
decode('utf-8', errors='ignore')吞掉异常,导致字符被截断成半个。
建议在中转服务中强制做一次标准化输出。
以Python为例,在返回响应前统一转码:
# 强制将上游字节流转为UTF-8文本再转发
text = raw_bytes.decode('utf-8', errors='replace')
clean_text = text.replace('\ufeff', '') # 去掉BOM
如果是Java或Go服务,检查响应头是否显式指定Content-Type: application/json; charset=utf-8。
第三步:处理换行符不一致引发的错乱
上游流式响应通常使用\n分隔不同事件,但有些模型服务端会返回\r\n,或者在中转过程中被Nginx等代理吞掉换行。
典型现象是:用调试工具能看到{"choices":[...]}一段一段出现,但用tail看时全挤在一行。
解决办法是通过正则将换行标准化:
import re
normalized = re.sub(r'\r\n|\r', '\n', chunk_text)
另外,
如果Nginx开启了chunked_transfer_encoding,
注意保持proxy_buffering off;,
否则缓冲区可能等到数据积攒后才整体转发,
破坏流式时机的感知。
第四步:分隔符不匹配导致的数据粘包
大模型接口的流式格式多为SSE(Server-Sent Events),每个事件以data:开头,以空行\n\n结束,最后再用data: [DONE]标示完成。
乱码问题如果表现为“一段数据里混杂多个data前导”,通常是分隔符处理失误。
可以写段脚本先拆分再解析:
sed 's/^data: //; /^\s*$/d' /tmp/raw_chunk.txt | head -50
如果希望可靠地重组JSON,用Python读流并逐行判断:
for line in sys.stdin:
line = line.strip()
if not line:
continue
if line.startswith("data:"):
payload = line[5:].strip()
if payload == "[DONE]":
break
# 对payload做json.loads处理
避坑清单:这三个细节最容易忽略
- 不要随意改变上游的Content-Type,尤其是把
text/event-stream改成application/json,会导致客户端错误解析二进制流。 - 中转服务不要开额外的字符编码转换插件,例如某些网关自动按GBK转换,必须关闭。
- 测试时避免用浏览器直接访问流式接口,浏览器解码方式不同,容易误报乱码,建议用
curl或写脚本验证。
验证是否修好
执行完上述调整,重新用curl拉取并检查三点:第一,响应头包含charset=utf-8;
第二,chunk中的中文能正确显示,无BOM;
第三,用Python脚本逐行解析能完整拿到所有data字段并以[DONE]结尾。
命令如下:
curl -N -H "Authorization: Bearer $YOUR_KEY" YOUR_GATEWAY_URL | python3 inspect_sse.py
如果输出正常,说明AI中转的chunk乱码问题已经在编码、换行和分隔符三个层面解决。
以后再遇到类似报错,优先按这个顺序排查,能省下大量翻日志的时间。