流式JSON为什么总报错?理解结构化输出就能接稳AI接口

流式JSON为什么总报错?理解结构化输出就能接稳AI接口

💡 原文中文,约2800字,阅读约需7分钟。
📝

内容提要

流式JSON报错源于逐块解析,正确做法是按事件仅收集文本增量,拼接后在流结束再解析校验。文章以Gemini结构化流式输出为例,给出Pydantic模式与代码,强调处理背压、断线重试、跳过未知事件,且校验通过前不得触发业务。

🔎

延伸解读

为什么不能逐块解析JSON

文章指出,流式返回的每个片段可能只是不完整的字符串,例如只有“{"sent”,直接交给JSON解析器必然报错。正确做法是按事件类型筛选出文本增量,持续拼接,等流结束后再统一解析和校验。这提醒开发者,流式传输的片段边界与JSON字段边界并不对应,不能假设每个片段都是完整语义单元。

生产环境必须处理背压与断线

文章强调,真实服务中消费者处理速度可能跟不上数据到达速度,需要限制缓冲区以应对背压。若连接在完成事件前断开,不能把当前拼接的字符串当作有效对象,最安全的策略是丢弃未完成结果并重试。若业务需要断点续传,必须依赖服务端明确支持的交互标识,不能自行猜测缺失字符。

校验通过前不要触发业务

文章明确,只有收到完成事件、JSON解析成功、模式校验通过且业务规则满足后,数据才应进入自动化。界面可以把已到达文本作为“生成中预览”,但不应提前用其中的字段触发工单等操作。这个边界能避免半截字段、后续修正和连接重放造成重复动作,是流式结构化输出落地时的关键安全线。

结构化输出不等于事实正确

文章提醒,JSON语法合格并不代表内容真实或准确,summary字段仍可能曲解原文,因此业务规则和来源核验不能省略。此外,不要把所有step.delta都当作文本,工具参数、图像或思考摘要可能有其他增量类型;也不要假设事件类型永远不变,客户端应对未知事件安全跳过并记录告警,而不是直接崩溃。

Q&A

为什么流式返回的JSON直接逐块解析会报错?

因为流式返回的每个小块可能只是JSON的一部分,例如只有'{"sent',直接交给JSON解析器会因不完整而报错。正确做法是按事件筛出文本增量,持续拼接,等流结束后再解析和校验。

结构化输出和流式模式可以同时使用吗?

可以。以Gemini Interactions API为例,官方示例显示结构化输出可与流式模式同时使用,流中的step.delta事件携带文本片段,所有片段拼起来才形成最终JSON。

如何用Python代码实现流式结构化输出并校验?

使用google-genai和pydantic:定义Pydantic模型(如Feedback),创建流式请求并传入response_format包含schema,循环中只收集event_type为step.delta且delta.type为text的文本增量,拼接后json.loads解析,再用Pydantic模型校验。

流式结构化输出时如何处理背压和断线重试?

背压是消费者处理速度跟不上数据到达速度时的控制机制,需限制缓冲区。若连接在完成事件前断开,应丢弃未完成结果并重试;若需断点续传,必须依赖服务端明确支持的交互标识,不能自行猜测缺失字符。

流式结构化输出有哪些常见误区?

常见误区包括:结构化不等于事实正确;不要逐块解析JSON;不要把所有step.delta都当文本;不要把密钥写进代码或前端;不要假设事件类型永远不变,应安全跳过未知事件并记录告警。

流式结构化输出适合哪些场景?不适合哪些?

适合分类、表单抽取、内容卡片和工作流参数等“边生成边提示、完成后机器消费”的场景。不适合必须逐字段立即执行的高风险交易,也不适合把尚未完成的JSON直接写数据库。若响应很短且只在完成后使用,非流式请求更简单。

🏷️

标签

➡️

继续阅读