structured-output-repair · ZH · 2026-10-25

一把 key 上的结构化输出三选一:JSON mode、函数调用与纯提示词,哪个真的守住了形状

本文对比了在 LLM API 聚合平台上实现结构化输出的三种常见机制:提示词约束、JSON 模式与函数调用(工具调用),分析它们在不同模型后端上的行为差异与可靠性,并给出当某模式不被支持时的降级策略。核心观点是:不要假设所有后端对同一机制的支持度一致,应优先使用平台默认支持的结构化输出方式,并以提示词约束作为兜底。

在调用大语言模型时,让输出严格符合预期的 JSON 结构是工程上最常见的需求之一。在聚合平台上,一把 key 会路由到不同厂商的模型,而各家对结构化输出的支持程度并不一致。本文对比三种机制:纯提示词约束、JSON 模式、函数调用(工具调用),并讨论降级策略。

三种机制在做什么

  • 纯提示词约束:在 system 或 user 消息中描述目标结构,要求模型“只输出 JSON”。机制本身不改变解码过程,完全依赖模型遵循指令的能力。
  • JSON 模式:部分后端提供专门参数(如 response_format 设为 JSON 对象)。模型被引导生成合法 JSON,但不保证字段名或嵌套结构符合你的 schema。
  • 函数调用 / 工具调用:把目标结构声明为一个工具的参数 schema,模型返回结构化的参数对象。多数后端会在解码阶段约束或强引导输出符合 schema。

各后端的表现差异

在聚合场景下,同一个请求可能打到 Claude、GPT、DeepSeek、Qwen、GLM、Kimi 等不同后端。它们的差异体现在:

  • 支持度:不是所有后端都实现了 JSON 模式或函数调用参数。把同一份请求原样转发,可能在某些后端上直接报错,或被忽略。
  • 约束强度:即使都叫“函数调用”,有的后端能严格保证参数符合 schema,有的只是倾向性引导,仍需校验。
  • 字段漂移:纯提示词约束下,模型可能增删字段、改嵌套、把数字写成字符串,或在 JSON 前后附加解释文字。
  • 流式行为:开启流式输出时,结构化参数可能分块返回,需要客户端拼接后再解析。

因此,不能假设“在 A 模型上跑通的 JSON 模式,在 B 模型上也一样稳定”。

如何选择:可靠性阶梯

从工程稳健性看,推荐按以下优先级选择:

  1. 优先使用函数调用:如果目标后端支持且平台透传该能力,它通常提供最强的结构约束。把 schema 定义为工具参数,并设置 tool_choice 强制调用。
  2. 其次使用 JSON 模式:在后端支持但函数调用不可用或不稳定时,用 JSON 模式确保语法合法,再配合客户端 schema 校验。
  3. 最后用纯提示词约束:作为通用兜底,所有后端都可用,但必须配合解析与重试。

降级阶梯:当某模式不被支持时

聚合平台的价值在于一把 key 切换模型,但这也意味着请求可能路由到不支持某模式的后端。建议实现一个探测与降级流程:

  • 能力探测:首次对某模型发请求时,先用一个极小的结构化请求测试其是否接受 JSON 模式或工具参数;记录结果,避免每次重试。
  • 逐级降级:函数调用失败(报错或不支持)→ 降为 JSON 模式;JSON 模式仍失败 → 降为纯提示词加严格解析。
  • 客户端校验:无论用哪种机制,收到响应后都用 JSON Schema 或 Pydantic 等做校验。校验失败时,把错误信息附回提示词重试一次,仍失败则抛出明确异常。
  • 容错解析:对纯提示词输出,先剥离 Markdown 代码围栏,再尝试提取第一个 JSON 对象,避免模型附加说明文字导致解析失败。

实践建议

  • 在请求中同时带上 schema 描述和“只输出 JSON”的指令,即使使用了函数调用,也能降低后端忽略参数时的风险。
  • 对关键字段使用枚举和类型约束,减少模型自由发挥空间。
  • 记录每个后端实际生效的机制,在路由层做映射,而不是把同一份请求盲发给所有模型。
  • 对流式场景,先累积完整参数再解析,不要边流边解析不完整的 JSON。

结构化输出的可靠性不取决于你选了哪个“高级模式”,而取决于你是否假设了后端行为一致。把降级路径设计好,比追求单一模式的完美更重要。