让模型老实吐 JSON:结构化输出的几个坑

2026-06-20结构化输出·1027 字·约 3 分钟

真正的坑很少在"格式"上,而在字段——模型给你的 JSON 能解析,内容却不是你要的。

先说结论

能用工具调用(tool use / function calling)拿结构化数据,就别用"请用 JSON 回答"去求。 前者是模型被 schema 约束着填空,后者是你赌它这次心情好。

下面几个坑,我几乎每个项目都会再踩一遍。

坑 1:用自然语言求 JSON

"请只返回 JSON,不要解释"——结果开头一句"当然可以,这是你要的:“,中间一个 json 代码块,末尾再补一句"需要我调整吗?”。解释、寒暄、Markdown 围栏,全来了。

解法分两档:

  • 首选:把输出定义成一个工具的 input_schema,让模型走工具调用。它返回的是结构化参数,天生没有外层文字。
  • 退一步:用原生 JSON 模式——Claude 可以在 assistant 回合 prefill 一个 {,逼它从对象开头写起;OpenAI 用 response_format

别再写正则去文本里抠 {...} 了,那是给自己埋雷。

坑 2:字段会"漂"

格式对了,不代表内容稳。同一个 prompt 跑 100 次,你会看到:

  • 可选字段时有时无
  • count 一会儿是 3,一会儿是 "3"
  • 枚举值自由发挥:你要 "high" | "low",它给你个 "medium-high"

这些在 demo 里看不出来,上了量就炸。解法是把约束写进 schema,而不是写进 prompt 的客套话里:必填字段标 required,枚举显式列全,数字声明 type: number。schema 是给机器读的合同,比"请尽量保证格式正确"有用得多。

坑 3:JSON 被截断

输出长度撞到 max_tokens,JSON 断在一半,JSON.parse 直接抛。

  • 留足 token,宁可浪费也别卡边
  • 要返回长列表时,先让模型报一个 total,再分批要
  • 真要边出边渲染,用容忍不完整的增量解析器,别傻等整段拼完

坑 4:既要思考又要纯 JSON

最常见的自伤:让模型"先分析一下,再用 JSON 给结论"。它要么把大段分析塞进某个字段污染数据,要么为了格式干净干脆不想了,结论质量跟着掉。

把思考和结构化拆成两步:第一步让它自由推理,第二步只做"把上面的结论整理成 JSON"。多一次调用的钱,换的是你不用再 debug 脏数据。

一句话

结构化输出的目标不是"长得像 JSON",是"拿到能直接喂给下游、不用二次清洗的数据"。格式靠工具调用兜底,内容靠 schema 约束——剩下的,靠你别偷懒把两步并成一步。

这篇对你有用吗?

如果这篇文章对你有帮助,可以请博主喝杯咖啡 ☕