让模型老实吐 JSON:结构化输出的几个坑
真正的坑很少在"格式"上,而在字段——模型给你的 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 约束——剩下的,靠你别偷懒把两步并成一步。
这篇对你有用吗?