让 LLM 输出可靠的结构化数据
你写了个 prompt,让 LLM 把一段用户评论解析成 JSON:情感、评分、关键词。本地跑了二十次,完美。上线。 三天后告警响了。某条响应里,LLM 在 JSON 后面多写了一句"希望这个分析对你有帮助!"。你的 json.loads() 当场抛异常,整条链路挂掉。 这不是小概率事件,是结构性问题。只要你还在用"自由文本里夹一段 JSON"的方式跟 LLM 要数据,这种崩溃就是迟早的——区别只是它发生在测试环境还是生产环境。 这篇讲清楚:为什么自由文本提 JSON 天生不可靠,2026 年有哪几种正经方案、各自的代价是什么,schema 怎么设计才不坑自己,以及最难的那块——流式场景下怎么拿到结构化数据。 为什么"让它输出 JSON"本身就是错的 先理解 LLM 在干什么。它做的是一件事:根据前面所有 token,预测下一个 token 的概率分布,然后采样。它没有“我现在要写一个合法 JSON"这种全局意识。 所以当你在 prompt 里写"请只返回 JSON,不要有多余文字”,你是在用一句话,对抗模型训练数据里成千上万条"先解释再给结果"的对话样本。大多数时候它听话,因为你的指令把概率压过去了。但只要某次采样,在该写 } 的位置,“希望"这个 token 的概率偶然爬到了第一,它就会写下去——而且一旦写下去,后面就会顺着"希望这个分析对你有帮助"这条最自然的路径滑下去。 常见的失败长这样: JSON 外面套了 ```json 代码块,或者前后有一段自然语言 字符串里有没转义的换行、引号 该是数字的字段写成了 "4.5"(带引号),或者写成 4.5分 嵌套对象少了一个括号,尤其是输出很长的时候 枚举字段返回了你没定义的值——你要 positive/negative/neutral,它给你个 mixed 这些都不是模型"笨”,是概率采样的必然结果。你不可能靠把 prompt 写得更恳切来根治它,你只能降低概率,没法归零。要归零,得换思路:不是请求它输出合法结构,而是从机制上让它没法输出不合法的结构。 五种方案,以及它们各自的代价 2026 年,从最弱到最强,实际可用的方案是这五种。关键不是"哪个最好",是搞清楚每个的边界。 flowchart TB A["LLM 要输出结构化数据"] --> B{"模型在哪?"} B -->|"闭源 API"| C{"要不要 100% 保证 schema?"} B -->|"自己部署的开源模型"| D["约束解码xgrammar / llguidance"] C -->|"要"| E["Structured Outputs / strict 工具"] C -->|"差不多就行"| F["JSON Mode(已是 legacy)"] E --> G["拿到必定合法的 JSON"] D --> G F --> H["只保证语法合法,schema 靠运气"] style E fill:#fde7c2,stroke:#e8b23c style D fill:#fde7c2,stroke:#e8b23c 1. 纯 prompt 约束。 就是开头那种"请只返回 JSON"。它的唯一价值是当作其他方案的补充——把字段含义、示例写清楚能提升内容质量。但别拿它当结构保证。如果你现在生产环境还在裸用这个,这篇文章后面的部分就是为你写的。 ...