流式输出与结构化结果:两种不同的可靠性
理解流式事件和完整业务结果的区别,用本地 Python 示例验证结果字段,而不是把半段 JSON 当答案。
区分增量显示与最终结果校验,为拒绝、中断和字段错误设计分支。
流式输出解决“用户什么时候看到内容”,结构化输出解决“程序如何理解结果”。它们可以同时使用,但不能互相替代。
界面已经出现几个字,不代表任务已经成功。一段 JSON 的前半部分,更不能直接成为数据库更新或工具执行的依据。
先区分显示状态和业务状态
显示状态可以是等待、正在接收、已结束或中断。业务状态则可能是有结果、没有证据、明确拒绝或需要进一步执行工具。
HTTP 流中的事件边界也不等于网络数据块边界。一个事件可能被拆成多个数据块,一个数据块也可能包含多个事件。接入时使用接口支持的 SDK 或正确的 SSE 解析器。
流式结束还要看最终事件和状态。不能因为连接关闭,就默认拿到了完整答案。
JSON 结构正确,只解决一层问题
结构化输出可以让支持它的接口按照给定 schema 生成结果,但 schema 支持范围、拒绝与不完整状态仍需要查看具体文档。
结构正确不保证内容真实,也不表示请求者有权限执行结果。例如 { "amount": 100 } 是有效 JSON,却没有说明这笔操作是否被授权。
把 schema 校验、业务校验与权限检查分开。前者看字段和类型,后两者看值是否合理,以及当前用户能否进行相应操作。
本地练习最终结果校验
下面用 Python 校验一个简单契约。它不请求模型,也不解析 SSE,专门展示最终结果进入业务逻辑前的一道检查。
def validate_result(value):
if not isinstance(value, dict):
raise ValueError("object required")
if set(value) != {"status", "items"}:
raise ValueError("unexpected fields")
if value["status"] not in {"ready", "no_evidence"}:
raise ValueError("invalid status")
if not isinstance(value["items"], list):
raise ValueError("items must be a list")
if not all(isinstance(item, str) for item in value["items"]):
raise ValueError("items must contain strings")
if value["status"] == "no_evidence" and value["items"]:
raise ValueError("no_evidence must have empty items")
return value
result = validate_result({"status": "ready", "items": ["Check sources"]})
assert result["items"] == ["Check sources"]
try:
validate_result({"status": "no_evidence", "items": ["Made up"]})
except ValueError:
print("Rejected inconsistent result")
else:
raise AssertionError("invalid result accepted")
这个检查仍不能判断每条内容是否有证据。你可以继续增加来源 ID 校验,但事实核验需要相应资料与评测用例。
给中断留下明确行为
| 场景 | 界面表现 | 程序行为 |
|---|---|---|
| 收到增量内容 | 持续展示,标明生成中 | 只缓存,不提交业务结果 |
| 收到完整最终结果 | 展示完成 | 校验字段、语义和权限 |
| 连接中断 | 告知未完成 | 保留明确状态,允许安全重试 |
| 模型拒绝或输出不完整 | 解释当前无法完成 | 进入独立分支,不强行解析 |
如果任务包含外部写操作,重试整个请求可能导致重复执行。不要把“重新生成文本”和“重新执行操作”做成同一个无条件按钮。
下一步
给你的界面补上等待、接收、完成和中断四种状态。再准备三份本地数据:有效结果、字段错误、业务矛盾。
让这些输入走同一个校验入口,确认它们分别进入正确分支。完成这一步,再接入真实流式接口。
资料来源与延伸阅读
资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。