欢迎光临

LLM 流式 JSON 解析失败怎么查?UTF-8 半包、SSE 事件与 tool_calls 参数拼接

开发者在代码编辑器中调试流式 JSON 响应,AI 生成写实配图
AI 生成写实配图,展示本文讨论的工作场景。

流式接口偶尔丢失中文字符,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 日。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » LLM 流式 JSON 解析失败怎么查?UTF-8 半包、SSE 事件与 tool_calls 参数拼接
分享到: 更多 (0)