函数调用还是 JSON Schema 严格模式:两种结构化输出的选型对照

接入与开发

对照函数调用与 JSON Schema 严格模式在约束强度、兼容性和编排上的差别,给出「什么时候必须用哪一个」的判断条件与工程排查顺序。

你已经能让模型返回 JSON 了,但一到上线就出问题:字段偶尔多一个、嵌套层数不对、多步工具调用时参数和文本混在一起。这篇不重复原理,只讲落地:两种结构化输出方式各自在什么条件下必须用、什么条件下会坑你,以及超时、重试、降级、日志和评测该怎么排。

你已经能让模型吐出 JSON 了,但一上线就出问题:字段偶尔多一个、嵌套层数对不上、多步工具调用时参数和说明文字混在一起。这篇不重复原理,只讲落地:函数调用和 JSON Schema 严格模式各自在什么条件下必须用、什么条件下会反咬你一口,以及超时、重试、降级、日志和评测该怎么排。

先给一个粗糙但好用的直觉:函数调用解决「模型该动手做什么」,严格模式解决「模型交回来的东西长得对不对」。你要的是结构正确还是取值正确,决定了主选哪一个、另一个当兜底。下面逐步拆开。

先把两条路分清:它们到底约束了什么

函数调用本质是让模型挑一个函数并填参数,schema 是提示而不是硬约束,模型理论上可以填错类型、少字段、多塞一个自造字段。严格模式是把 schema 编译进解码过程,输出在结构层面必须合法,凑不出合法结果时才报错或被截断。

对比项函数调用JSON Schema 严格模式
约束对象选哪个工具、传什么参数返回体的字段与类型
字段可否缺失可能缺,尤其参数多时未标记必需的字段可不给,标了必给就必须给
类型漂移常见,数字变字符串基本锁死,除非 schema 本身写宽
多余字段可能自造通常被禁止
适合的活多步工具编排单轮抽取、固定表单

判断口径先立住:确认你要的是结构正确还是取值正确。前者能靠 schema 兜住,后者只能靠业务校验加重试,schema 再严也拦不住模型填一个合法但不合理的 ID。另外别默认所有模型的严格模式都真严格,有相当一部分是软严格,靠后处理和重试兜底,这点必须自己拿固定样例实测。

兼容性:先看你的调用链能不能吃下它

严格模式对 schema 子集有限制,复杂嵌套、联合类型、正则、默认值这些常常不被支持。写 schema 之前先按最小可用子集设计,把校验逻辑放到应用层,而不是塞进 schema 里赌它能编译。

  • 先用最小可用子集写 schema,能扁平就扁平,别一上来玩五层嵌套
  • 接入前拿一份固定样例跑对比:同一份 schema 走不同协议入口,看是否被降级成普通 JSON 提示
  • 多模型混用时按模型分别记录:支不支持严格模式、失败时返回什么、错误长什么样
  • 下游只做文本展示、不解析字段的场景,严格模式收益很低,别为它牺牲模型选择面
  • schema 每次改动都重跑一遍固定样例,行为可能跟你上次测的不一样

多步工具编排与失败处理:这才是真正分叉的地方

需要模型连续调多个工具、每步结果影响下一步时,优先函数调用,它天然带「选谁、传什么、拿回什么」的往返结构。严格模式更适合一次输入一次输出、结构稳定的场景,不需要模型自己决定调什么。

  1. 分类失败把失败拆成三类:结构不合法、取值越界、工具执行失败。三类处理方式完全不同,混在一起排就永远排不清。
  2. 分别处理结构不合法可自动重试;取值越界必须过业务校验并让人看到;工具执行失败要把错误信息回灌给模型,让它调整参数,而不是原样重试。
  3. 设上限和退避重试要有次数上限和退避间隔,连续失败就降级到宽松解析加人工兜底,别让它无限循环烧额度。
  4. 留痕每次重试都记下请求标识和失败类型,否则线上出问题时你只能看到一堆失败请求,分不清是哪一类。

这一节的分野很干脆:模型需要自己决定下一步做什么,用函数调用;模型只需要稳定地产出结构化结果,用严格模式。两者混着上,先想清楚谁是主干谁是兜底。

上线前的落地清单与排查顺序

上生产之前,先把三张表写下来:schema 字段最小集、每个字段的合法取值范围、失败时的动作。这三张表没有,后面的日志和重试都是瞎撞。日志至少记请求标识、命中方式、是否重试、重试次数、最终是否通过校验、耗时,缺一项线上就少一条线索。排查顺序固定下来:先看原始返回是不是合法 JSON,再看字段是否齐,再看取值是否符合业务,最后才怀疑模型和提示词。多数人一上来就改提示词,往往在修一个根本不在提示词层的问题。

排查顺序看什么常见结论
一原始返回是否合法 JSON不合法先查严格模式是否被降级
二字段是否齐全缺字段查 schema 必需标记
三取值是否符合业务合法但不合理,属校验缺失
四模型与提示词前面都过了才轮到改这里

评测别只看一个通过率,把首次通过率和加两次重试后的通过率分开记,两者的成本差很大。要不要换模型、值不值得开严格模式,模型口径和价格口径到 /models 和 /official-api 核对当时的数字再决定。

这两种方式都不适用的时候

有些场景强行上结构化输出是负收益。识别出来比选型更重要,因为一旦上错,改回来的代价比重写提示词大得多。

  • 输出是大段自由文本再从中抽取:先让它自由写,再用规则或二次抽取,强行套 schema 会损害内容质量
  • schema 一天改几次:先把字段稳定下来再上严格模式,否则你在维护一份永远追不上的约束
  • 对延迟极敏感的在线链路:严格模式加校验加重试会拉长尾延迟,先用 /tools/speed-test 量出基线再决定
  • 需要跨厂商可移植:函数调用的事实标准更通用,严格模式各家实现差异更大,可移植性差
  • schema 本身还在讨论阶段:别拿严格模式当需求澄清工具,它只会放大没想清楚的部分
结构正确能靠 schema 兜住,取值正确只能靠校验和重试兜住,别把两件事混成一个开关。
下一步拿你现有的一个接口,按排查顺序跑一遍,把首次通过率和加两次重试后的通过率分开记;再对照中转与协议入口的差异,看你的严格模式有没有被悄悄降级。

常见问题

严格模式能保证模型一定按我的 schema 输出吗?

不能保证「一定」,只是把结构错误的概率压得很低。它约束的是格式合法性,不约束取值是否符合业务含义。字段值对不对、枚举有没有语义错,仍然要在你这边做校验,并且把校验失败当成正常分支来设计,而不是异常。

多步工具调用一定要用函数调用吗?

如果每一步由模型自己决定调哪个工具、传什么参数,用函数调用最省事。反过来,如果你的流程是固定的几步、每步该调什么已经写在代码里,那模型只负责填参数,严格模式或普通 JSON 输出都够用,还更便宜。

两者能不能一起用?

可以,常见做法是函数调用负责编排,工具返回后再用严格模式抽字段。但要注意两套约束叠加会放大兼容性问题,尤其走中转时。建议先在同一模型上分别跑通,再组合,并保留关掉严格模式的开关。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。