如果你把模型流式返回的每一小块都直接交给JSON解析器,大概率会报错,因为一块可能只有{"sent。正确做法是按事件筛出文本增量,持续拼接,等流结束后再解析和校验。学会这条链,你就能同时获得更早的界面反馈和稳定的数据对象,而不是在速度与可靠性之间二选一。
最新事件与真实问题
Google在2026年9月2日发布Gemini 3.8 Flash,并更新Gemini Interactions API文档。官方示例显示,结构化输出可以与流式模式同时使用;流中的step.delta事件会携带文本片段,所有片段拼起来才形成最终JSON。该更新距今天12天,超过七天但仍在30天回看窗口内,本文明确把它当技术关联,不包装成今日新闻。
这个概念解决什么问题
普通流式输出让聊天框尽快出现文字,却很难直接驱动程序。结构化输出则要求结果符合JSON Schema(JSON模式)描述的字段和类型,便于写入数据库、生成卡片或触发下一步。两者结合后,前端可以展示进度,后端在完成时拿到可校验对象。
生活类比是快递分箱:每辆车送来一个纸箱,箱内可能只是同一件家具的一块板。你可以显示"已到三箱",但不能拿第一块板就当成完整书桌。类比的边界在于,网络片段不一定对应人类可读语义,也不保证按字段边界切开。
去掉类比,准确定义是:服务端通过SSE(Server-Sent Events,服务器发送事件)持续发送带类型的事件;客户端只累积属于模型文本输出的增量字符串,在完成事件后把字符串解析为JSON,再按模式与业务规则校验。
最小实践
先安装依赖并在终端设置密钥:
bash
python -m pip install google-genai pydantic
export GEMINI_API_KEY="你的授权密钥"
python demo.py
demo.py内容如下:
python
import json
import os
from typing import Literal
from google import genai
from pydantic import BaseModel
class Feedback(BaseModel):
sentiment: Literal["positive", "neutral", "negative"]
summary: str
if not os.getenv("GEMINI_API_KEY"):
raise RuntimeError("请先设置 GEMINI_API_KEY")
client = genai.Client()
stream = client.interactions.create(
model="gemini-3.8-flash",
input="界面很直观,但首次加载偏慢。请分析这条反馈。",
response_format={
"type": "text",
"mime_type": "application/json",
"schema": Feedback.model_json_schema(),
},
stream=True,
)
chunks = []
for event in stream:
if event.event_type == "step.delta":
if event.delta.type == "text" and event.delta.text:
chunks.append(event.delta.text)
raw = "".join(chunks)
result = Feedback.model_validate(json.loads(raw))
print(result.model_dump())
代码分三段:Feedback声明允许的字段;客户端用环境变量自动读取密钥,并把模式随请求发送;循环只收集文本增量,最后由json.loads解析,再让Pydantic检查类型和枚举。示例未在本次任务中实际运行,因为环境没有Gemini密钥,code_verified因此为false,没有虚构测试结果。
真实服务还要处理背压与中断。背压是消费者处理速度跟不上数据到达速度时的控制机制;文本片段通常很小,但并发连接多时仍要限制缓冲区。若连接在完成事件前断开,不要把当前字符串当作有效对象。最安全的策略是丢弃未完成结果并重试;若业务要断点续传,则必须依赖服务端明确支持的交互标识,不能自行猜测缺失字符。
界面展示也要分层:可以把已到达文本作为"生成中预览",却不应提前把其中的sentiment触发工单。只有收到完成事件、JSON解析成功、模式校验通过且业务规则满足后,数据才进入自动化。这个边界能避免半截字段、后续修正和连接重放造成重复动作。
常见误区
第一,结构化不等于事实正确。JSON语法合格,summary仍可能曲解原文,所以业务规则和来源核验不能省。第二,不要逐块解析JSON;官方明确说片段是可拼接的部分字符串。第三,不要把所有step.delta都当文本,工具参数、图像或思考摘要可能有其他增量类型。第四,不要把密钥写进代码或浏览器前端,生产环境应使用后端代理和密钥管理服务。
第五,不要假设事件类型永远不变。官方文档要求客户端对未知事件安全跳过而不是直接崩溃。应记录未知类型、设置告警,并保持SDK升级测试;这比用一个穷举分支把未来扩展都当异常更稳健。
适用与不适用
适合分类、表单抽取、内容卡片和工作流参数等"边生成边提示、完成后机器消费"的场景。不适合必须逐字段立即执行的高风险交易;也不适合把尚未完成的JSON直接写数据库。若响应很短且只在完成后使用,非流式请求更简单。
我的判断是:结构化流式输出的价值不在"JSON出现得更快",而在统一了人类反馈与机器消费的通道。但生产级接入必须把未知事件安全跳过、处理断线重试,并在最终对象进入业务前做第二次语义校验。
5分钟实践题
把Feedback增加一个1到5的urgency整数,并写校验保证范围合法;再故意把循环改成对每个片段调用json.loads,观察为什么会失败,然后恢复为完成后统一解析。
你的业务更看重首字节速度,还是拿到完整且校验通过的对象?
关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。
本文首发于 java4u.cn,转载请注明出处。