
流式接口偶尔丢失中文字符,json.loads 随机报错,工具参数只收到半个对象。先检查是否把一次网络 read 当成了一个完整事件。TCP 字节块、UTF-8 字符、SSE 事件和工具参数分别有自己的边界。
本文针对常见 Chat Completions 风格的 SSE 数据结构,限制 n=1。使用合成响应验证解析过程,不直接执行工具,也不覆盖所有供应商的事件格式。
一、四层缓冲分别处理
| 层 | 边界 | 处理方式 |
|---|---|---|
| 字节 | 网络分块可以切在任意位置 | UTF-8 增量解码器保留未完成字节 |
| SSE 行与事件 | 换行及空行 | 缓冲未完成行,空行时分发 data |
| 事件 JSON | 一个完整 data 载荷 | 识别 [DONE] 后再解析其他 JSON 载荷 |
| 工具参数 | 多次 delta 拼接后的字符串 | 按工具 index 累积,完整结束后解析及校验 |
SSE 允许注释行和多条 data 行,行结束符可为 LF、CRLF 或 CR。同一事件的 data 行以换行连接。配套解析器保留跨分块的 CRLF,并对 EOF 时未结束的事件报错。它是面向一次请求的受限解析器,未实现 EventSource 的自动重连和 Last-Event-ID。
参考资料 · WHATWG SSE 事件流解析规则
二、增量解码后,等事件完整再解析
import codecs
decoder = codecs.getincrementaldecoder("utf-8")("strict")
# 每次获取到字节块,送入同一个解码器
text = decoder.decode(chunk, final=False)
# 响应结束时刷新,未完成 UTF-8 会报错
tail = decoder.decode(b"", final=True)
上面的 text 仍可能只包含半行。将它送入行缓冲区,等空行完成一个事件,再处理 data。空白心跳和冒号注释不能被当成 JSON。HTTP 非成功响应、错误内容类型和流内 error 事件应进入错误路径,不要吞掉异常后继续展示成功。
三、按 index 拼接每个工具的参数
delta.tool_calls 中的 index 用来关联增量片段,id 和 function.name 可能只在开始时出现。arguments 是字符串片段,可在任意字符位置分开。下面两个片段分别不是完整 JSON,拼接以后才得到北京。
first = '{"city":"北'
second = '京"}'
import json
arguments = json.loads(first + second)
assert arguments == {"city": "北京"}
配套 ChatAccumulator 用 index 分开保存工具 id、名称和参数。在 [DONE] 到达且 finish_reason 为 tool_calls 后,才解析工具参数。若因 length 截断或连接中断而结束,则拒绝作为完整调用交付。解析成功后,业务层还需检查工具白名单、参数 schema 和权限。
参考资料 · Chat Completions 流式字段与工具调用
四、把解析器接到字节流
from stream_parser import SSEParser, ChatAccumulator
parser = SSEParser()
chat = ChatAccumulator()
for chunk in response.iter_content(chunk_size=4096):
for event in parser.feed(chunk):
chat.accept(event)
for event in parser.feed(b"", final=True):
chat.accept(event)
tool_calls = chat.finish()
print(chat.text, tool_calls)
response 由调用方创建,并在进入此循环前检查 HTTP 状态和 text/event-stream 类型。配套解析器不负责网络请求。真实服务还要设置读超时、响应总时限和总字节预算。示例对单行、事件、文本、单个工具参数和工具数量设置了上限,调用方也要限制整个请求的累计资源。
五、用故意切碎的响应验证
运行 python stream_parser.py,测试会按 1、2、7 字节以及整块四种方式分割同一响应。这样会切开中文 UTF-8、CRLF 和 JSON。程序还覆盖心跳、多个事件合并到一块、分段工具参数以及不完整 EOF。正常输出应包含 PASS。
仅收到 finish_reason、仅看到 TCP EOF 或仅拿到可解析的 JSON,都不足以代表应用层调用完整。还要按所用接口验证正常结束标记和终止原因。供应商格式变化时,保存脱敏响应夹具再适配。
验证范围:上述合成分块测试已执行。没有对各供应商全部流式接口进行互通测试。工具执行边界可继续阅读MCP 工具调用权限与审批。
配套文件与同系列文章
下载本文 Python 示例 ZIP。技术文档核对日期为 2026 年 11 月 10 日。
汤不热吧