AI 为什么需要 JSON Schema?Structured Output、Function Calling 与 JSON Schema 完整解析
2024 年起,产品页把 JSON Schema、Function Calling、Structured Output 写成同一件事。它们不是。Schema 是契约语言,Function Calling 是「请宿主执行」,Structured Output 是「模型必须吐出这个形状」。本文把三个词拆开,再说明为什么概率模型必须接到一份可校验的 Schema 上,而不是一句「请返回 JSON」。
阅读前速览
| 概念 | 一句话 | 落在哪 |
|---|---|---|
| JSON Schema | 字段、类型、必填、枚举的契约,不是模型功能 | git 里的 .json,CI 里的 validator |
| Function Calling | 模型提出调用,宿主执行,常有副作用 | tools[] / arguments |
| Structured Output | 模型最终回复必须符合这份形状,通常无副作用 | json_schema / output_type |
先记住分工:Schema 是口径,Function Calling 是伸手,Structured Output 是交回对象。把三者写成「让模型输出 JSON」,下一跳就会在 parse 散文。
三个词为什么总被混用?
文档、SDK 示例和推文喜欢把它们叠成一句话:给模型一份 Schema,它就会输出合法 JSON。演示里确实经常成功。线上事故通常发生在你以为「已经结构化」之后。
混用会掩盖三层完全不同的工作:
- Function Calling 的
arguments仍常是字符串。 宿主必须再JSON.parse,类型、枚举、多余字段都还没被拦住。 - Structured Output 只管最终回复。 它不替你查订单、不打款、不写数据库。有副作用的动作仍走 Function Calling。
- Schema 如果只写在 prompt 里,CI 测不了。 「请返回 JSON」不能失败;一份进 git 的 Schema 可以。
所以问题不是「AI 会不会 JSON」,而是:谁在什么时刻,用哪一份契约,拦住非法对象。三个词回答的是三个不同的谁。
JSON Schema 是什么(先分清:它不是模型功能)
JSON Schema 是一份描述 JSON 形状的文档:有哪些键、各是什么类型、哪些必填、枚举取哪些值、能不能多出字段。它比「看起来像 JSON」严得多,也比 TypeScript 类型或 protobuf 更常被模型 API 直接引用。
Agent / Structured Output 真正用到的,通常就这几个关键字:
type/properties:对象长什么样。required:缺了哪个键就不算过。enum/pattern/ 数值上下限:把「差不多」收成有限集合。additionalProperties: false:多余键直接失败,挡住 prompt 注入带进来的额外字段。
语法细节见 JSON Schema 教程。这里只要先记住:Schema 先给 validator 和测试看。清晰的 description 能帮模型选对工具,但不能代替 enum 和 required。
{
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
"amount": { "type": "number", "exclusiveMinimum": 0 },
"status": { "type": "string", "enum": ["paid", "refunded", "pending"] }
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
上面这份订单契约可以同时喂给 Function Calling 的 parameters、Structured Output 的 json_schema,以及宿主里的 ajv。改字段只改这一份。
Function Calling 是什么?
Function Calling(以及更泛的 Tool Calling)不是「模型去执行函数」。模型只做一件事:发出函数名和参数。宿主 parse、校验、执行,再把结果塞回对话。查库存、退款、写工单——副作用在这一层。
厂商把工具定义写成 tools[],其中的 parameters 就是一份 Schema 子集:
{
"type": "function",
"function": {
"name": "lookup_order",
"description": "Look up an order by ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^A-[0-9]{4,}$"
}
},
"required": ["order_id"],
"additionalProperties": false
},
"strict": true
}
}
AI Agent 完整指南 把这一跳画进完整循环;MCP Tool Schema vs Function Calling 说明 MCP 的 inputSchema 和厂商 parameters 是同一本字典的两种写法。
看起来已经「结构化」了。缺口在于:arguments 经常仍是字符串;工具返回值往往没有 Schema;strict 只约束模型侧。没有宿主校验,amount 是数字还是字符串,仍然看今天的快照心情。
Structured Output 是什么?
Structured Output(Structured Outputs / JSON mode 的严格形态)约束的是模型自己的最终消息。分类、抽取、摘要对象、Agent 的 handoff——这些不需要外部副作用,但下游必须拿到固定形状。
各家字段名不一样:response_format、text.format.json_schema、output_type。内核相同:把一份 Schema 交给解码器,非法 token 路径直接剪掉。
{
"type": "json_schema",
"json_schema": {
"name": "order",
"strict": true,
"schema": {
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" },
"status": {
"type": "string",
"enum": ["paid", "refunded", "pending"]
}
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
}
}
DevDay 2026 预测 把这一层和 Function Calling、Agent 运行时写成同一根 JSON 管。口径仍是 Schema:管子的直径先定,协议才能接上去。
打开 strict: true 之后,漏字段、枚举写错、多出键,常常在模型侧就被拦住。它替代的是「请用 JSON 回答」那句 prompt,不替代宿主再验一次,更不替代 Function Calling。
三者怎么叠在一起?
把三个词压成「让模型输出 JSON」,职责就丢了。拆开之后:
- JSON Schema 是共用口径:字段词典、必填、枚举。它不调用、不生成,只描述「合法长什么样」。
- Function Calling 是向外伸手。模型请求一次副作用,参数必须过 Schema,结果最好也过 Schema。
- Structured Output 是向回交对象。模型结束这一跳时,回复本身必须是那份形状。
一张对照表,避免下次再把它们写成同义词:
| 层 | 模型在做什么 | 没有 Schema 会怎样 |
|---|---|---|
| Function Calling | 提议调用,等待宿主 | 参数是散文;额外键可能打进账单系统 |
| Structured Output | 直接生成最终对象 | 下游用正则从段落里抠字段 |
| JSON Schema | 什么都不做,只当尺子 | 尺子若只存在于 prompt,CI 量不到 |
多步 Agent 把两层都用上:先 Structured Output 做分诊对象,再 Function Calling 查单、退款。AI Agent 与 JSON Schema 工作流 写的是门禁怎么排;本文只把三块积木先认清。
AI 为什么离不开 JSON Schema?
因为模型和程序不是同一种东西。模型按概率续写 token;程序按确定规则读对象。中间如果只靠「它今天写得像 JSON」,漏字段、数字变字符串、枚举写成 "Paid"、note 多出来——每一种都可能被下一跳当成事实。
2024 年以前,链路短:一次聊天、一次函数、失败了人能看见。2026 年的链路更长:分类 → 查单 → 决定退款或转人工 → 把对象交给另一个 Agent。任何一跳只是「像 JSON」,后面的步骤都会把错误固化。
同一份订单,模型随时可能吐出这种「看起来合法」的对象:
{
"order_id": "A-1042",
"amount": "99.00",
"status": "Paid",
"note": "please hurry"
}
amount 是字符串,status 大小写不对,还多了 note。没有 Schema,下游可能把 99.00 当字符串拼进 SQL,或把 Paid 当成未知状态重试。调试时先在本地 格式化 看清结构,再和上周能过的对象 JSON Diff;不要把带用户数据的响应贴到公共 JSON 站点。
Schema 的价值不在「教模型说话」,而在把非法对象挡在宿主门内。调用前拦一次,回复后拦一次,评测再断言一次。窗口越大、步骤越多,散文错误越容易变成「系统里的事实」。
同一份 Schema,两种投影
不要为 Function Calling 和 Structured Output 各手写一份「差不多」的定义。推荐形状是一份源文件,两套导出:
- 仓库里放
schemas/order.json,带稳定$id和版本。 - 导出 Function Calling:裁成
function.parameters,打开strict。查单工具往往只需要order_id,不要把整份订单对象塞进参数。 - 导出 Structured Output:同一份对象作为
json_schema/output_type,给抽取、分诊、最终回复用。
投影时注意子集:厂商 strict 常常要求每个 property 都在 required 里,并禁用部分 oneOf / $ref。源文件可以更完整,导出器按目标裁剪;CI 对「源 vs 投影」做结构 Diff,避免有人手改其中一侧。
宿主侧校验仍然必要
strict: true 只约束模型解码。换快照、厂商关掉 strict、arguments 二次 parse、工具自己返回了开放对象——都可能绕过模型侧。宿主仍要在三处跑同一份 Schema:
- 执行工具前。 parse → validate → execute。失败就返回结构化错误,不要把原始字符串传给下一跳。
- 收下模型最终对象后。 Structured Output 成功也不等于你的业务规则过了(金额上限、货币、权限)。
- 写入状态 / 评测夹具时。 checkpoint 和 eval 必须是合法 JSON,不能从聊天记录猜进度。
模型侧约束和宿主侧校验是两份工作。关掉 strict 或换一家 API 之后,只留 prompt 里的 Schema,契约就消失了。
2026 年怎么落地(先写文件,再写 prompt)
- 在仓库建
schemas/tools/、schemas/extract/、schemas/state/。每个文件有$id和版本。 - 先写 Schema,再写「请返回 JSON」。prompt 里的句子过不了 CI。
- 有副作用(查库、打款、发信)走 Function Calling;分类、抽取、handoff 走 Structured Output。
- 新对象默认
additionalProperties: false加明确enum。要开放扩展,单独加metadata,不要松开根对象。 - 工具超过十个就先检索再调用。不要把整本目录塞进一次请求。
- 密钥和用户字段只在浏览器里看:格式化、JSON Diff,零上传。
目录可以很薄。要紧的是文件先于 prompt。模型还会换,协议还会加;口径应该先稳定。
常见误区
- 把 Structured Output 当成 Function Calling。 前者不执行代码。要查单、退款,仍需工具层。
- 只在 prompt 写「请返回 JSON」。 没有进 git 的 Schema,就没有可失败的测试。
- 只校验工具入参,不校验模型最终对象。 抽取和分诊的事故,往往发生在「看起来已经是 JSON」的回复上。
- 为每种协议手写一份差不多的 Schema。 第三周就会分叉。导出,不要复制。
- 用宽
type: object逃避设计。 开放对象不是门禁。真要扩展,用版本号或显式metadata。
FAQ
Structured Output 能替代 Function Calling 吗?
不能。Structured Output 约束最终回复的形状;Function Calling 请求宿主执行有副作用的动作。抽取订单字段用前者,查库存、退款用后者。两者应引用同一份 Schema,而不是互相替代。
Function Calling 已经有 parameters,还要不要单独的 Schema 文件?
要。parameters 是投影,不是源。单独文件才能同时喂给 Structured Output、MCP、评测和宿主 ajv。只改 tools[] 里的一份,三周后就会和抽取 Schema 对不上。
没有 Agent,只做一次信息抽取,需要 Schema 吗?
更需要。一次抽取的结果如果直接进数据库或表格,非法对象没有下一跳可以「再理解一次」。Structured Output + 宿主校验是最小完整集。
开了 strict: true,还要宿主校验吗?
要。strict 管模型解码;宿主管业务规则、二次 parse、以及厂商关掉 strict 之后的退化路径。两道门成本很低,少一道就会在换模型时露出来。
被拒的 JSON 怎么在本地查?
先格式化看结构,对照 Schema 找缺的 required 或错的 enum,再和上一版能过的对象 Diff。JSONSort 在浏览器本地做这些事,不上传数据。不要把带密钥的响应贴到公共站点。
结论
AI 需要 JSON Schema,不是因为模型突然读不懂自然语言,而是因为概率输出必须接到确定的门禁上。Function Calling 表达「去做」;Structured Output 表达「交回这个对象」;Schema 是两者共用的口径。
从今天起,先写 schemas/extract_order.json,再决定调用哪一家的 API。模型会换,协议会变,直径应该先稳定。
延伸阅读
- AI Agent 为什么开始需要 JSON Schema 从 Tool Calling 到结构化工作流的门禁
- AI Agent 是什么?Tool Calling 与 JSON 运行时、工具调用与 JSON 契约
- MCP Tool Schema vs Function Calling 工具 JSON 的协议边界
- JSON Schema 是什么 required、enum 与 additionalProperties
- JSONSort 本地工具箱 格式化、校验、Diff
- 全部文章 更多 JSON 与 AI 开发笔记
更新记录: 首次发布