2026 年维护 OpenAI Agent 的团队,最容易踩的坑不是「模型会不会说话」,而是旧代码仍把 json_object 当成结构化输出,或在 Chat Completions 上沿用非严格 Function Calling。新项目官方推荐从 gpt-5.6 起步;Function Calling 与 Structured Outputs 底层都是约束解码,但入口、默认 strict 行为和 JSON Schema 子集并不相同。
核对日期为 2026 年 8 月 18 日,字段与行为以 OpenAI Function Calling 指南、Structured Outputs 指南 为准。本文不虚构延迟、价格或成功率;公开文档没写死的地方会标明「需用现网回放确认」。
如果你正在把 OpenAI 兼容后端切到其它模型,结构化输出和工具循环必须单独验收,不能只换 Base URL。可对照站内的 OpenAI API 迁移 Kimi K3 验收清单。
先看结论:三件事同时变了
很多仓库还停在 2024 年的心智模型:提示词里写「请返回 JSON」、再正则抠代码块。2026 年生产链路里,这条路会被 Schema 违约、缺字段、enum 幻觉 直接打穿解析器。
真正要改的是三层,而不是再换一个更大的模型名:
- 交货合同
- 最终给用户或下游服务的对象,用 Structured Outputs(Responses 里是
text.format,Chat Completions 里是response_format.json_schema)。 - 执行合同
- 模型要调用你的工具时,参数必须贴合工具自己的 JSON Schema;这就是 Function Calling,底层与 Structured Outputs 同一套约束解码。
- 兼容合同
- 旧的 JSON Mode(
json_object)只保证「看起来像 JSON」,不保证字段、类型、enum 与 Schema 一致。官方已把它定位成 Structured Outputs 的前身,新项目不要再用它当主路径。
另外还有一条容易漏掉的产品线变化:新代码应走 Responses API。Chat Completions 仍可用,但 strict 的默认策略不同——Responses 会尽量把 Schema 规范化成严格模式,失败才回退;Chat Completions 默认仍是非严格、尽力而为。
模型与 API 主路:gpt-5.6 + Responses
Structured Outputs 从 GPT-4o 一代开始可用;官方对新项目的建议是直接用 gpt-5.6。更老的 gpt-4-turbo 及更早快照,文档仍指向 JSON Mode,而不是完整的 json_schema 严格输出。
选型时先分清两套入口
| 你要的结果 | 该用的入口 | 2026 年注意点 |
|---|---|---|
| 给用户/下游一个固定对象 | Responses:text.format;或 Chat Completions:response_format: json_schema |
打开 strict: true;SDK 可用 Pydantic / Zod + parse() |
| 让模型调用你的函数、查库、改状态 | tools 里的 function tool |
工具参数 Schema 同样走 strict;并行调用、多工具循环要自己写执行器 |
| 工具面太大,不想一次塞进上下文 | tool_search 延迟加载 |
仅 gpt-5.4 及更新模型支持;工具定义会计入输入 token |
| 参数不是 JSON,而是自由文本或特定文法 | custom tools + 可选 CFG | 适合 DSL、查询语言;不要硬套 function JSON Schema |
SDK 侧更值得养成的习惯:不要手写一份容易漏 additionalProperties 的 Schema,而是用官方 helper 从类型生成。Python 走 client.responses.parse(..., text_format=YourModel);JavaScript 走 zodTextFormat。手写 Schema 时,strict: true 不满足约束会直接 请求被拒,而不是「模型随便输出再让你重试」。
和 Gemini 路线对比时,不要把「兼容 OpenAI SDK」理解成 Schema 行为也一样。Google 的能力升级路径见 Gemini 3.5 Pro 的 10 项能力升级;跨厂商拷贝同一份 JSON Schema 时,嵌套对象的 additionalProperties 规则经常是第一个炸点。
JSON Mode、Structured Outputs、Function Calling
生产事故里最常见的混淆是:日志里确实是 JSON,于是以为 Structured Outputs 已经生效。下表按官方语义拆开:
| 能力 | 保证合法 JSON | 保证贴合 Schema | 典型启用方式 | 适用模型 |
|---|---|---|---|---|
| JSON Mode | 是 | 否 | text.format.type = json_object |
含部分 GPT-5 兼容档;老快照常用 |
| Structured Outputs | 是 | 是(受支持的 Schema 子集) | json_schema + strict: true |
gpt-4o-2024-08-06 / gpt-4o-mini 及之后,新项目用 gpt-5.6 |
| Function Calling + strict | 工具参数是合法 JSON | 工具参数贴合 parameters Schema | tools 里 strict: true |
支持 tools 的模型;推荐始终开 strict |
什么时候不该用 Function Calling
如果模型不需要碰你的系统(不查库存、不改工单、不跑脚本),只是要把回答拆成卡片、步骤、评分,那就用 Structured Outputs。反过来,只要输出是「请帮我执行这个副作用」,就必须走 tools,而不是把函数参数伪装成最终回答 Schema。
拒答不再是「坏 JSON」
安全拒答时,模型不会硬塞进你的 Schema。Responses / Chat Completions 会给出独立的 refusal 字段。解析层要把拒答当成一等公民:先看 refusal,再 output_parsed,不要把空对象当成功。
Strict JSON Schema 的硬规则
开启 strict 之后,OpenAI 接受的是 JSON Schema 的子集,不是任意 Draft 2020-12 文档。请求级最常撞墙的三条:
properties里出现的每个字段,都必须出现在required数组。- 每个
object(含嵌套)都必须additionalProperties: false。 - 根对象不能是
anyOf;可选语义用「必填 + 允许 null」表达,例如["string", "null"]。
也就是说:把字段从 required 里拿掉,假装它是可选 这条旧技巧在 strict 下会直接 400。正确写法是字段仍 required,类型写成可空联合,应用层把 null 当成「未提供」。
下面这段是生产里常见的「抽取工单」对象,注意嵌套 object 也写了 additionalProperties:
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
class Ticket(BaseModel):
title: str
priority: str
assignee: str | None
tags: list[str]
response = client.responses.parse(
model="gpt-5.6",
input=[
{"role": "system", "content": "从用户描述中抽取工单字段。"},
{"role": "user", "content": "登录页 500,指派给 Noah,优先级高,标签 auth 与 api。"},
],
text_format=Ticket,
)
ticket = response.output_parsed
print(ticket.title, ticket.priority, ticket.assignee)
调试时若请求被拒,优先看错误信息里缺的是哪条约束,而不是先降模型。Playground 生成的 Schema 默认已开 strict,把它原样拷进仓库通常比「从旧 json_object 提示词改造」更快。
跨厂商时再核对一次:同一份「每个 object 都 false」的 Schema,在部分兼容网关或其它模型上可能变成 HTTP 400。这时应做按提供商变换,而不是维护三份业务 Schema。
Function Calling 2026:strict、tool_search、custom tools
官方现在把 Function Calling 与 tool calling 当作同一件事:用 JSON Schema 描述可调用函数,再用应用侧执行器跑副作用。2026 年文档里新增或被强调的几条,会直接改你的 Agent 循环。
strict 的默认值不要靠猜
- 建议始终显式
strict: true。 - Responses:省略 strict 时,服务端会尝试规范化 Schema;规范化失败则回退非严格,响应里的 tool 会显示
strict: false。 - Chat Completions:省略时默认非严格。
- 微调模型若一轮调用多个函数,文档写明该轮可能关闭 strict。
工具定义会计入上下文并按输入 token 计费。描述写太长、一次挂 40 个工具,会同时打高账单和降低选工具准确率。工具很多时,用 tool_search 把低频工具延迟加载——仅 gpt-5.4 及更新模型支持,循环里还可能先出现 tool_search_call / tool_search_output,再进入真正的 function_call。
custom tools:别把 DSL 塞进 JSON 对象
function tools 适合结构化参数;custom tools 适合自由文本输入输出,并可附加上下文无关文法(CFG)约束。SQL 片段、内部查询语言、需要词法终端互斥的格式,用 CFG 比「string 字段里再写一堆 prompt」稳。若 CFG 报 unexpected tokens,先查终结符是否重叠,而不是先怪模型。
tools = [{
"type": "function",
"name": "get_order",
"description": "按订单号查询状态。仅在用户给出明确订单号时调用。",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"locale": {"type": ["string", "null"]},
},
"required": ["order_id", "locale"],
"additionalProperties": False,
},
}]
执行循环没有变:看到 finish_reason / item type 为工具调用 → 跑本地函数 → 把结果以 tool 角色回传 → 再请求。变的是参数不再需要你用 json.loads 碰运气。仍必须保存完整 assistant 消息(含 tool_calls),否则第二轮会丢调用 ID。
并行工具调用时,顺序以调用 ID 为准
一次响应可能带多个 tool call。回传时按 call_id 对齐,不要按数组下标假设顺序。灰度日志至少记录:工具名、参数哈希、耗时、是否 strict、是否发生 schema 回退。
旧项目迁移清单
把「能跑」和「可交货」拆开验收。建议保留旧后端开关,先在独立环境回放真实流量。
- 模型:新链路指定
gpt-5.6(或账号已开通的同等档),不要让网关静默映射到旧快照。 - 输出:把
json_object换成json_schema+strict: true,或 Responses 的text.format。 - 工具:每个 function 补齐
required与嵌套additionalProperties: false;可选字段改可空联合。 - 解析:接入
parse()与refusal分支;流式场景确认增量 JSON 与最终 parsed 对象一致。 - 工具面:超过十几项就评估
tool_search;先缩短 description,再考虑延迟加载。 - 对照:用同一组固定任务对比旧 JSON Mode 与新 Schema 路径的重试率、缺字段率、人工返工。
通过标准不是「返回 200」,而是:解析器零正则兜底、工具参数类型稳定、拒答可观测、回滚开关演练过。需要长时间挂着 SDK、回放脚本和浏览器会话时,本地笔记本休眠会打断对照实验——这正是后面云端 Mac mini 的切入点。
FAQ
JSON Mode 和 Structured Outputs 可以混用吗?
不要在同一条链路上混。JSON Mode 只保证合法 JSON;Structured Outputs 才保证 Schema。混用会让监控分不清「解析失败」是模型问题还是合同问题。新代码只用 json_schema / text.format。
新项目还要写 Chat Completions 吗?
能用 Responses 就用 Responses。官方示例、parse helper 和 strict 规范化都优先走这条。存量 Chat Completions 可以继续,但必须显式设置 strict,并接受默认非严格这一差异。
为什么我的 Schema 一开 strict 就 400?
最常见是漏了 required、嵌套 object 没写 additionalProperties:false、根类型用了 anyOf,或把可选做成「不出现在 required」。把错误信息里的约束补全后再发;不要靠关闭 strict 掩盖 Schema 错误,除非你明确要非严格回退。
Function Calling 一定要 strict 吗?
官方建议始终开启。不开启时参数是尽力而为,你的执行器还得防缺字段和类型漂移。Responses 省略 strict 还可能被服务端改写,日志里要记下最终 strict 值。
tool_search 什么时候值得上?
工具定义已经明显挤占上下文、或大部分工具在单次任务里根本用不到时。需要 gpt-5.4 及以上。上线前要回放「先搜索工具再调用」的两段轨迹,旧执行器如果只认 function_call 会直接中断。
Schema 保证了,还要校验业务值吗?
要。约束解码不验证外键、权限和幂等。enum 合法不等于枚举值对你的库存有意义。把 Schema 校验和业务校验分成两层日志,故障时才分得清。
gpt-5.6 和更早的 GPT-5.x 在结构化输出上差在哪?
文档把 gpt-5.6 标为新项目默认。能力是否在你的账号、区域、批处理与微调路径上完全对齐,必须以当前模型列表和一次最小 parse 请求为准,不要用博客里的别名去猜网关映射。
和 Claude / Grok 的 JSON Schema 能共用一份吗?
合同方言接近 Draft 2020-12,但子集不同。OpenAI strict 要求每个 object 都 additionalProperties:false;有的提供商会拒绝嵌套层的该字段。保持一份业务 Schema,按提供商做变换层。
延伸阅读
在云端 Mac mini 上,Schema 验收可以 24/7 挂着跑
Function Calling 和 Structured Outputs 的回归,本质是长时间在线的对照实验:两套 SDK、固定回放集、工具沙箱、流式前端,还要避免笔记本合盖休眠。Apple Silicon 统一内存适合同时跑本地代理与浏览器调试;macOS 上 Homebrew、Docker、SSH 开箱即用;M4 Mac mini 待机功耗大约 4W,适合把验收环境挂过夜。
如果你需要一台不抢家庭带宽、能 SSH 常驻的 Mac 来跑 Agent 回放,Nuvcloud 云端 Mac mini M4 是目前把「开发机」和「对照实验机」拆开的低摩擦选项——立即了解套餐方案,让 strict Schema 的灰度不必绑在自己的笔记本上。