本文要点
- 模型返回的 JSON 虽然能解析,但字段名称漂移、类型变化,前端和数据库仍然频繁报错。
- 最快解法:普通 JSON 只解决数据格式,JSON 模式通常只保证可解析,生产环境优先用 Structured Output 配合 JSON Schema;执行工具前仍必须做业务规则和权限校验。
模型返回的 JSON 虽然能解析,但字段名称漂移、类型变化,前端和数据库仍然频繁报错。 最快解法:普通 JSON 只解决数据格式,JSON 模式通常只保证可解析,生产环境优先用 Structured Output 配合 JSON Schema;执行工具前仍必须做业务规则和权限校验。
谁该看这篇?
如果你刚接触 AI 应用,需要先分清普通 JSON、JSON 模式与 Structured Output 不是同一个概念。 如果你负责后端、工作流或 Agent 架构,本文重点帮助你判断:什么时候值得增加结构约束,什么时候反而应该保留自然语言。
先把三个概念放回正确的位置
普通 JSON 是一种数据表示格式,例如对象、数组、字符串、数字、布尔值和空值都可以用 JSON 表达。它本身不规定“必须有哪些字段”,也不规定字段必须是什么类型,因此下面这类结果完全可能通过 JSON 解析:
{
"customer": "李明",
"amount": "一千元",
"status": "已付款"
}
问题在于,amount 有时是字符串,有时是数字;status 也可能变成 paid、已支付或完成付款。解析器只关心语法是否正确,不会替你判断业务字段是否稳定。
JSON Schema 是描述 JSON 数据结构、类型和约束的契约。它本身通常也是一个 JSON 文档,但它描述的是“数据应该长什么样”,不是业务数据本身。官方规范将 type、properties、required、enum 等关键字用于定义结构和允许值,你可以参考 JSON Schema 入门说明。
Structured Output 则是模型生成机制中的一种约束方式:你把 Schema 作为输出契约交给模型,让模型按照指定字段和类型生成结果。它解决的是“模型如何生成符合结构的数据”,而 JSON Schema 解决的是“结构规则如何被定义和验证”。
JSON Schema 是不是一种 JSON 格式?
严格来说,JSON Schema 文档可以用 JSON 编写,所以它看起来是 JSON;但它和被描述的 JSON 数据不是同一份东西。可以把 Schema 理解为接口协议,把普通 JSON 理解为一次具体的接口响应。
结构约束的层级差异
在实际开发中,你通常会遇到四个层次:
| 方案 | 能解决什么 | 主要风险 | 更适合的场景 |
|---|---|---|---|
| 自然语言 | 解释、总结、对话 | 不便于程序直接消费 | 面向用户的回答 |
| 提示词要求 JSON | 引导模型输出 JSON 外形 | 字段漂移、漏字段、类型不稳定 | 原型验证、低风险脚本 |
| JSON 模式 | 通常保证结果是可解析 JSON | 不一定保证字段、枚举和业务含义 | 简单交换、弱约束任务 |
| Structured Output + JSON Schema | 约束字段、类型和部分结构 | 受平台支持范围、Schema 复杂度影响 | 生产抽取、接口响应、工具参数 |
JSON 模式与 Structured Output 的区别,核心不在于“一个输出 JSON、另一个不输出 JSON”,而在于约束粒度。以官方 API 文档为例,旧式 json<em>object 主要对应 JSON 模式;json</em>schema 则用于让结果匹配给定 Schema,而且严格模式只支持 JSON Schema 的一个子集。你可以查看 JSON 响应格式与 Schema 说明。
另一个平台的官方文档也明确区分了两者:Structured Output 用于最终响应格式,Function Calling 或 Tool Calling 用于模型请求外部工具执行动作。该平台同时只支持 JSON Schema 的一部分,而不是所有规范关键字。参考 Structured Output 与工具调用对比。
Structured Output 和 JSON mode 的区别是什么?
如果你只要求“返回一个能被 JSON.parse 读取的对象”,JSON 模式可能够用;如果你要求字段必须存在、类型不能漂移、枚举不能越界,就应该使用 Schema 约束,并在服务端再次验证。前者是语法层保证,后者更接近接口契约层保证,但两者都不等于事实正确。
第一种场景:面向人的回答通常不必强制 Schema
客服答复、产品解释、故障排查建议和长篇摘要,主要消费方是人。此时自然语言可以根据上下文调整详略,模型也能补充说明、承认不确定性,并把多个条件组织成易读段落。
如果你强行规定:
{
"answer": "string",
"reason": "string",
"confidence": "number"
}
看起来更整齐,却可能带来额外负担。你需要维护字段定义、处理空值、设计版本变化,还要考虑 confidence 到底表示模型自评、证据覆盖率,还是业务系统的可信等级。若前端不消费这些字段,Schema 只是增加了接口复杂度。
✅ 适合自然语言的情况:
- 结果直接展示给用户;
- 内容需要根据上下文自由展开;
- 下游没有固定字段或自动执行动作;
- 失败时人工可以快速判断和修正。
❌ 不适合只依赖自然语言的情况:
- 结果要直接写入数据库;
- 结果要驱动表单、卡片或流程节点;
- 结果包含工具参数;
- 结果需要跨服务长期保存。
第二种场景:数据抽取从普通 JSON 升级到 Schema
假设你从发票、合同或工单中抽取客户名、金额、日期和风险等级。提示词可以要求模型输出 JSON,但模型仍可能遗漏字段,或者把日期写成不同格式,把金额写成带货币符号的文本。
更稳妥的 Schema 会明确:
{
"type": "object",
"properties": {
"customer_name": { "type": "string" },
"amount": { "type": "number" },
"currency": { "type": "string", "enum": ["CNY", "USD", "EUR"] },
"risk_level": { "type": "string", "enum": ["low", "medium", "high"] }
},
"required": ["customer_name", "amount", "currency", "risk_level"],
"additionalProperties": false
}
这里真正有价值的不是 JSON 外观,而是下游可以根据固定类型处理结果:数据库把 amount 当数值存储,流程引擎根据 risk_level 分支,前端根据字段是否存在决定显示内容。
| 抽取要求 | 普通 JSON | Schema 约束 | 采购建议 |
|---|---|---|---|
| 只需快速读取 | ✅ | 可选 | 先用提示词或 JSON 模式 |
| 字段名称固定 | ⚠️ | ✅ | 使用 properties 与 required |
| 类型必须稳定 | ❌ | ✅ | 明确 string、number、boolean |
| 值只能来自有限集合 | ❌ | ✅ | 使用 enum |
| 数据要写入生产库 | 风险高 | ✅ | 模型约束后再做服务端校验 |
AI Agent 为什么不能只输出普通 JSON?
不是因为普通 JSON 无法使用,而是因为 Agent 的每一步都可能成为下一步的输入。一个字段从 task_id 变成 taskId,一个布尔值从 false 变成“否”,就可能让状态机、重试逻辑或数据库写入失败。普通 JSON 对低风险交换仍然有价值,但不应承担生产接口契约的全部责任。
第三种场景:Tool Calling 参数需要严格约束和双重检查
Tool Calling 的参数通常比普通回答更敏感,因为它可能触发发邮件、创建订单、修改权限、删除资源或调用内部 API。工具定义中的参数可以用 JSON Schema 描述,例如要求 user_id 为字符串、limit 为整数、action 只能是预设枚举。
官方工具调用文档通常把 Schema 放在工具的输入定义中,并要求应用在收到工具调用后提取工具名、调用 ID 和输入,再由自己的代码执行工具。参考 工具输入 Schema 与调用流程说明。
但格式正确不等于应该执行。下面两道检查不能省:
- 结构校验:字段是否齐全,类型是否正确,枚举值是否允许;
- 业务与权限校验:用户是否有权限,目标资源是否存在,金额是否在限额内,当前状态是否允许执行。
例如,delete<em>file 的参数完全符合 Schema,并不代表这个用户可以删除该文件;transfer</em>money 的金额是合法数字,也不代表账户余额、风控状态和审批条件已经满足。
⚠️ 经验提醒:不要把“模型输出符合 Schema”写成“系统可以直接执行”。Schema 约束的是数据形状,权限系统约束的是谁能做什么,业务服务还要确认资源和状态真实存在。
第四种场景:动态界面要优先考虑版本兼容
当模型输出要驱动表单、卡片或组件时,结构化结果可以让前端根据 component、label、value 和 options 动态渲染页面。这比让前端从自然语言中猜测按钮、字段和选项更可靠。
不过,动态界面会把 Schema 变成前后端共同依赖的协议。你新增必填字段,旧客户端可能无法渲染;你把 options 从字符串数组改成对象数组,已有转换逻辑可能直接失效。
建议至少保留以下兼容策略:
- 在顶层加入
schema_version或等价版本标识; - 新字段优先设为可选,并提供默认值;
- 不要随意重命名旧字段;
- 客户端遇到未知组件时回退为文本;
- 服务端保留旧版本 Schema 的解析能力;
- 对模型拒答、截断和验证失败设计回退响应。
这里不建议把所有内容都设计成高度嵌套的 Schema。动态界面需要扩展性,过度严格的结构会使每次组件迭代都变成接口迁移。
第五种场景:多步骤 Agent 区分中间状态与最终结果
多步骤 Agent 至少存在两类数据:
- 中间事件:工具调用、调用 ID、参数、工具结果、重试状态;
- 最终结果:给用户看的答案、摘要、下一步建议或可展示卡片。
中间事件应尽量机器可读,因为编排器需要根据事件继续运行;最终回答则可以同时提供结构和文本。例如,前端需要卡片数据时返回结构字段,同时保留一个 display_text 作为无法渲染时的回退内容。
但不要把内部推理过程当作必须输出的 JSON 字段。Agent 真正需要传递的是可审计的事件、工具输入、工具结果和状态变化,而不是要求模型暴露一段所谓“完整思考过程”。这样既减少协议耦合,也方便你控制敏感信息和日志内容。
推荐的状态对象可以包含:
{
"run_id": "run_123",
"status": "waiting_for_tool",
"tool_call": {
"id": "call_456",
"name": "search_order",
"arguments": {
"order_id": "A100"
}
},
"display_text": "正在查询订单状态……"
}
其中 run<em>id、status、tool</em>call.id 和 arguments 适合纳入严格结构;display_text 则应允许自然语言变化。这样既能让工作流继续执行,也不会牺牲用户体验。
按消费方选择 JSON、Schema 或自然语言
你可以用下面的决策表快速判断,而不是先选某个平台再倒推架构:
| 你的消费方与风险 | 推荐输出 | 是否需要 Schema | 额外验收 |
|---|---|---|---|
| 用户阅读说明、总结、解释 | 自然语言 | 通常不需要 | 检查事实、语气和敏感内容 |
| 脚本读取少量字段 | 普通 JSON 或 JSON 模式 | 可选 | JSON 解析、空值和超时处理 |
| 批量数据抽取 | Structured Output | 建议需要 | 服务端 Schema 验证、抽样复核 |
| Tool Calling 参数 | 工具 Schema | 必须 | 权限、资源、状态和幂等校验 |
| 动态表单或卡片 | 版本化结构输出 | 需要 | 默认值、未知组件和文本回退 |
| 多步骤 Agent 状态 | 事件 Schema + 最终响应结构 | 中间状态需要 | 调用 ID、重试、超时和审计日志 |
工具参数和最终回答都需要 Schema 吗?
工具参数通常应严格约束,因为它们会进入可执行代码;最终回答则取决于消费方。如果最终回答只是聊天文本,不必强制完整 Schema;如果前端要渲染卡片、数据库要入库,或者另一个 Agent 要继续消费,就应为最终响应定义结构,并保留人类可读的回退文本。
结构化输出对内容准确性没有直接担保。
它主要降低格式错误、字段漂移和类型不一致的风险,不能证明模型抽取出的日期、金额、客户名称或事实判断一定正确。即使结果通过 Schema 验证,你仍需要做来源核对、范围检查、业务规则验证和必要的人工抽样。关于支持范围与实现限制,可参考 Structured Output 官方说明。
落地时按这 6 步验收
第一步,先写清楚消费方:人、前端、数据库、工作流还是工具执行器。消费方不同,结构约束强度就不应相同。
第二步,列出真正需要稳定的字段,只把会被程序读取的内容放进 Schema,不要为了“看起来完整”把所有解释文字都塞进去。
第三步,明确类型、必填项、枚举、默认值和未知字段策略。尤其要区分“字段缺失”“字段为空”和“模型无法判断”这三种状态。
第四步,确认目标模型支持哪些 Schema 特性。不同平台的 Structured Output 支持范围并不完全一致,复杂嵌套、引用、正则或特殊格式不能想当然地跨平台复用。
第五步,在模型返回后执行本地验证。验证失败时记录原始响应、请求 ID 和 Schema 版本,再决定重试、转人工或回退到自然语言。
第六步,给工具执行加业务闸门。即使参数通过 Schema,也要检查登录身份、资源归属、权限、额度、状态和幂等键;高风险动作还应加入人工确认。
如果你需要为批量抽取、工具执行或持续工作流准备稳定的运行环境,先从 kvmboot 帮助中心确认远程开发、连接和运维方式,再安排验收任务;涉及团队协作或节点规划时,也可以通过 联系 kvmboot 获取环境层面的建议。
当前方案和 Mac 方案的取舍
如果你现在主要依赖个人电脑、本地临时脚本或多人共用的云主机,常见缺点是环境版本容易漂移、权限边界不清晰、长时间批处理会被本地休眠或网络中断打断;当 Agent 还需要固定的开发工具链和持续运行时,这些问题会直接影响复现与验收。
自购 Mac 的长期成本和维护责任更高,但设备归属、系统版本和本地工具链更可控;普通云主机弹性较好,却可能在远程桌面体验、权限配置和图形化调试上增加额外工作。对需要临时算力、短期测试环境或跨团队验收的人,租赁 kvmboot 的 Mac 环境更适合先验证 Structured Output、Tool Calling 和工作流稳定性,再决定是否购买设备或建设长期基础设施。你也可以先了解 kvmboot 的服务与团队信息,把环境选择和应用验收分开决策。