生产环境中的结构化输出:让 LLM 返回可靠的 JSON
在生产环境中的某个阶段,每个由 LLM 驱动的应用都需要停止将模型输出视为散文,而开始将其视为数据。当你尝试从语言模型中可靠地提取 JSON 对象——并将其输入到下游的数据库、API 调用或 UI 中时——你会发现这有多少种出错的方式。模型会将 JSON 包裹在 Markdown 栅栏中。它会生成一个有效的对象,但遗漏了必填字段。它在多次调用中格式化日期的格式不一致。它会幻觉出枚举值。这些失败中的任何一个都会静默地破坏下游状态。
结构化输出已经从一个事后才考虑的问题演变成生产级 LLM 系统的一等公民。这篇文章介绍了强制执行结构化输出的三种主要机制、各自失效的场景,以及如何在约束下设计高质模式(Schema)。
强制执行结构的三种方法(及其失败模式)
1. 仅提示词的 JSON 模式 (Prompt-Only JSON Mode)
最简单的方法:要求模型以 JSON 格式响应,可以是自由格式,也可以在提示词中提供模式示例。大多数供应商都提供“JSON 模式”,它保证了语法上有效的 JSON,但并不强制执行任何特定的模式——模型只是不会生成格式错误的输出。
这种方法在失效之前一直有效。JSON 模式消除了解析错误,但它不能保证:
- 必填字段确实存在
- 类型正确(例如
"score": "high"而不是"score": 8) - 枚举值被限制在你允许的集合内
- 嵌套结构符合你预期的形状
当你确实预先不知道输出形状时(如探索性提取、开放式总结),JSON 模式是正确的工具。对于其他一切,你需要更强大的工具。
2. 受限解码(原生结构化输出)
现代 LLM 供应商——OpenAI、Anthropic、Google——现在提供模式强制的结构化输出,其原理是在 Token 级别修改模型的生成过程。在生成每个 Token 之前,会违反模式的 Token 将从概率分布中被遮盖掉。模型在物理上无法生成不符合你的 Pydantic 或 Zod 模式的输出。
OpenAI 在 2024 年中期推出的“严格模式”(Strict Mode)就采用了这种方法。其承诺是:100% 的模式合规性,而不仅仅是“通常正确”。对于数据提取流水线和智能体(Agent)工具调用,这是一个具有重要意义的保证。
但受限解码有一个容易被忽视的隐形代价:在严格约束下质量会下降。
其机制是模型逐个生成 Token,而不预先知道接下来会生成什么。当模式约束消除了所有高概率 Token 时,模型必须从排名较低的替代方案中进行选择。输出在结构上保持有效,但在语义上读起来像拙劣的翻译——技术上正确但略显生硬。
一个具体的例子:如果你约束模型在分类字段中必须输出两个特定字符串值之一,而正确答案不在其中,模型会选择最接近的选项,而不是发出不确定的信号。系统不会报错。输出符合模式,但在语义上是错误的。
这意味着受限解码并不能消除你的流水线中对人类可读推理的需求——它只是改变了失败的形式,从程序崩溃变成了静默的错误分类。
3. 带有重试循环的验证
第三种方法将 LLM 视为不可信源,并在生成后根据你的模式验证其输出,在失败时自动重试,并将错误消息包含在上下文中。像 instructor (Python) 这样的库实现的正是这一点:封装你的 LLM 调用,验证响应,如果验证失败,则在后续消息中发回错误,以便模型进行自我纠正。
这对于受限解码无法表达的语义约束非常有效:“开始日期必须在结束日期之前”,“当设置了另一个字段时, 该字段是必填的”,“置信度分数必须解释推理过程”。你将这些定义为 Pydantic 验证器,重试循环会处理它们。
权衡点在于延迟和成本。每次重试都是另一次 LLM 调用。对于大多数应用,一次重试就能捕获绝大多数失败。重要的设计问题是当重试次数耗尽时该怎么办——停止运行、回退到默认值,还是标记为人工审核。
模式设计:隐藏的杠杆
即使有完美的强制手段,糟糕的模式设计也会降低输出质量。以下是一些能带来显著差异的原则:
先推理,后输出(Reason first, commit second)。 LLM 自左向右生成内容。如果你的模式将 classification 字段作为第一个属性,模型必须在处理完可用证据之前就确定一个值。将自由文本推理字段放在模式的前面,这样模型的思维链就会先于受限的选择。
让可选字段真正可选。 如果输入中可能不存在某项信息,将字段标记为必填会迫使模型幻觉出一个值。在数据可能缺失的地方使用 null 或可选类型。缺失的字段是诚实的;幻觉出的值是具有破坏性的。
保持模式扁平且聚焦。 深层嵌套的模式(4 层以上)和宽模式(50 个以上字段)都会降低质量。模型在生成时必须维持更多的状态,且约束压力会通过嵌套复合。如果你需要提取大量字段,请将其拆分为多个聚焦的提取调用。
使用描述性的字段名称。 模型将字段名称视为隐含的提示词上下文。名为 category 的字段可能意味着任何东西 。名为 content_moderation_category 并带有描述的字段会缩小模型的理解范围。枚举值也受益于语义明确的名称——避免使用缩写和内部代码。
避免使用不受支持的 JSON Schema 特性。 供应商的实现并不支持完整的 JSON Schema 规范。例如,OpenAI 的严格模式要求所有对象属性都列在 required 中,且不支持 additionalProperties: true。根据实际的供应商行为来测试你的模式,而不是根据规范。
你仍然需要的验证层
即使有约束解码(constrained decoding)的保证,你仍然应该进行验证。原因如下:
首先,供应商的 API 会发生变化。今天能保证 100% 合规的功能,在负载过高、模型更新或输出异常长的情况下可能会出现边缘情况。深度防御意味着当这种保证减弱时,你的应用程序不会悄无声息地崩溃。
其次,语义正确性与结构正确性是正交的。一个格式良好的 JSON 对象,即使字段值看起来很合理,在语义上可能仍然是错误的——例如,本应是未来的日期变成了过去,评分超出了有效的业务范围,或者同时设置了相互矛盾的标志位。约束解码对这些失败是感知不到的。
第三,验证为你提供了可观测性。当验证失败时,你清楚地知道哪个字段失败了以及原因。如果没有验证,你只能等待下游错误显现——通常是在流水线的后端,那时追溯根因会变得非常困难。
一个实用的生产模式:
- 使用供应商原生的结构化输出(约束解码)作为架构合规性的第一道防线
- 使用 Pydantic/Zod 验证器进行语义约束和跨字段验证
- 为第一次尝试失败的情况添加带有错误上下文的重试循环
- 记录所有验证失败及其输入上下文,以便进行监控和提示词优化
选择正确的方法
这并非非黑即白的选择。大多数生产系统会结合使用这三种机制:
| 场景 | 推荐方法 |
|---|---|
| 数据提取流水线 | 约束解码 + 验证 |
| Agent 工具调用 | 约束解码(Schema 必须与工具签名完全匹配) |
| 带有置信度评分的分类 | 约束解码 + 优先输出推理字段 |
| 复杂的跨字段验证 | 验证 + 重试循环 |
| 探索性或开放式提取 | JSON 模式 + 后处理 |
在生产中唯一要避免的方法是仅仅依赖提示词(prompting)。“始终以有效的 JSON 格式回答”在演示中效果很好,但在分布偏移(distribution shift)下效果很差——例如模型从未见过的新输入模式、触发不同行为的长输入,或者改变输出格式默认值的微小模型更新。
在生产中监控结构化输出
结构化输出的失败具有误导性,因为它们通常不会以错误的形式出现。模型返回 200 状态码和有效的 JSON,验证通过,然后一个微妙的错误值传播到了你的系统中 。最重要的失败往往在没有刻意埋点的情况下很难检测到。
值得追踪的指标:
- 每个 Schema 字段的验证失败率(哪些字段最常出错)
- 重试率(多大比例的调用需要第二次尝试)
- Schema 覆盖率(可选字段中已填充与为空的比例——突然的变化预示着输入漂移)
- 约束与非约束 Token 分布(如果你控制推理栈,则此项可用)
尤其是 Schema 覆盖率,是一个被低估的信号。如果一个通常有值的字段开始频繁返回 null,说明上游发生了变化——要么是输入内容不再包含该信息,要么是模型开始以不同的方式进行回避。无论哪种情况,都值得在演变成数据质量事故之前进行调查。
核心结论
结构化输出现在已成为生产级 LLM 应用的基本门槛,生态系统已经足够成熟,没有理由再依赖正则表达式解析模型响应。原生约束解码消除了整类错误。但它也引入了自己的失败模式——隐性质量退化——这需要仔细的 Schema 设计和验证层来捕捉。
做得出色的工程师会将结构化输出视为 LLM 与系统其余部分之间的契约。精确定义契约,在多个层面执行,监控漂移,并为第一次尝试失败的情况构建重试逻辑。目标不仅仅是有效的 JSON——而是你可以信任的 JSON。
- https://www.aidancooper.co.uk/constrained-decoding/
- https://python.useinstructor.com/
- https://arxiv.org/html/2501.10868v1
- https://pydantic.dev/articles/llm-validation
- https://pydantic.dev/articles/llm-intro
- https://mbrenndoerfer.com/writing/constrained-decoding-structured-llm-output
- https://dev.to/pockit_tools/llm-structured-output-in-2026-stop-parsing-json-with-regex-and-do-it-right-34pk
- https://techsy.io/blog/llm-structured-outputs-guide
- https://www.cognitivetoday.com/2025/10/structured-output-ai-reliability/
