在大模型应用里,让模型“稳定输出 JSON”是一个非常常见、也非常容易被低估的问题。很多人一开始会在提示词里写一句“请以 JSON 格式返回”,但上线后很快会遇到各种小事故:模型多说了一句解释、字段名拼错、数组变成对象、布尔值变成字符串、输出里混入 Markdown 代码块,或者在复杂任务中漏掉必填字段。

稳定输出 JSON 的关键,不是单靠一句提示词,而是把“输出格式”当成一个完整的工程约束来设计:从提示词、Schema、模型接口、解析校验、失败重试到业务降级,都要形成闭环。

一、先明确 JSON 的边界

让模型输出 JSON 前,首先要想清楚:你到底需要模型输出什么。

很多不稳定来自需求本身不清楚。例如:

请分析这篇文章,并返回结构化结果。

这句话对人来说好像能理解,但对模型来说边界很模糊。结构化结果里应该有哪些字段?字段类型是什么?可以为空吗?枚举值有哪些?数组最多多少项?如果文章没有相关信息该怎么办?

更好的方式是先定义清楚数据结构:

{
  "title": "string",
  "summary": "string",
  "keywords": ["string"],
  "sentiment": "positive | neutral | negative",
  "confidence": 0.0
}

当输出边界清楚后,模型才有机会稳定;当边界不清楚时,后面的解析和重试都只是在补锅。

二、使用明确的 Schema,而不是自然语言描述

自然语言提示可以帮助模型理解任务,但 JSON 结构最好用 Schema 或样例表达。Schema 比普通描述更精确,也更容易在程序里复用。

例如,可以在提示词中写:

你必须只返回一个合法 JSON 对象,不要返回 Markdown,不要返回解释文字。

JSON 结构如下:
{
  "is_relevant": boolean,
  "category": "product" | "operation" | "finance" | "other",
  "reason": string,
  "tags": string[]
}

字段要求:
- is_relevant: 判断内容是否与业务相关。
- category: 只能从给定枚举中选择一个。
- reason: 用一句话说明判断依据。
- tags: 返回 0 到 5 个关键词。

这里有几个要点:

  1. 说明“只返回 JSON”。
  2. 给出完整结构。
  3. 标明字段类型。
  4. 标明枚举范围。
  5. 标明数组长度或数量限制。
  6. 说明空值处理方式。

越是机器要消费的内容,越不能只靠“你应该懂”的默契。

三、优先使用模型接口的结构化输出能力

如果你使用的模型 API 支持结构化输出、JSON Schema、函数调用或工具调用,优先使用这些能力,而不是只靠提示词。

相比普通文本生成,结构化输出能力通常可以在解码阶段约束模型,让它更接近“按格式生成”,而不是“生成一段看起来像 JSON 的文本”。

常见方式包括:

  • JSON mode:要求模型输出合法 JSON。
  • JSON Schema:要求模型输出符合指定 Schema 的 JSON。
  • Function calling / Tool calling:让模型填充函数参数。
  • Constrained decoding:在生成过程中限制可选 token。

在生产环境中,推荐优先级通常是:

结构化输出 / Tool calling > JSON mode > 强提示词 + 后处理

提示词仍然有用,但它应该是辅助说明,而不是唯一防线。

四、提示词里不要制造格式冲突

很多 JSON 输出不稳定,是提示词自己造成的。

例如:

请用 JSON 返回结果,并解释你的思考过程。

这句话同时要求模型输出 JSON,又要求解释思考过程。模型可能会在 JSON 外面补充说明,也可能把解释塞进某个字段里,最终导致解析失败。

再比如:

请返回如下格式:
```json
{
  "answer": "..."
}
如果你不希望模型输出 Markdown 代码块,就不要在最终格式里强调代码块。样例可以用,但必须明确:

```text
下面只是结构示例。最终输出不要包含 ```json 或 ```。

稳定输出 JSON 的提示词应该保持单一目标:只输出机器可解析的 JSON。

五、给模型一个“无法判断”的合法出口

很多格式错误,本质上是模型不知道该如何表达异常情况。

比如你要求:

{
  "company_name": "string",
  "founding_year": "number"
}

但输入文本里根本没有成立年份。模型可能会猜一个年份,也可能输出 "unknown",导致类型错误。

更稳的设计是提前定义空值策略:

{
  "company_name": "string | null",
  "founding_year": "number | null"
}

并在提示词里说明:

如果原文没有明确信息,对应字段返回 null,不要猜测。

这类“合法出口”非常重要。没有出口,模型就容易编造;有了出口,模型更容易遵守结构。

六、减少自由文本字段的不可控性

JSON 结构里最容易失控的是长文本字段,例如 reason、analysis、description。这些字段可能包含换行、引号、列表、冒号,甚至嵌套 JSON,增加解析和下游处理难度。

可以通过限制文本字段来提高稳定性:

reason 字段必须是一句话,不超过 50 个中文字符。

或者把长文本拆成更结构化的字段:

{
  "conclusion": "string",
  "evidence": ["string"],
  "risk_level": "low | medium | high"
}

不要让一个字段承担过多含义。字段越短、类型越明确、枚举越多,输出越稳定。

七、解析前先清洗,但不要过度依赖清洗

实际工程中,即使提示词写得很好,模型仍可能偶尔输出:

```json
{"name": "Alice"}
或者:

```text
这是结果:
{"name": "Alice"}

因此可以做一些轻量清洗:

  • 去掉 Markdown 代码块标记。
  • 截取第一个 { 到最后一个 } 之间的内容。
  • 去掉首尾空白字符。

但要注意,清洗只能兜底,不能成为主方案。如果大量请求都依赖复杂正则才能解析成功,说明前面的格式约束不够强。

推荐做法是:

  1. 首先要求模型接口返回结构化内容。
  2. 然后用 JSON parser 解析。
  3. 再用 Schema 校验。
  4. 失败时触发修复或重试。

八、必须做 Schema 校验

JSON 能解析,不代表结果可用。

例如下面是合法 JSON:

{
  "is_relevant": "yes",
  "tags": "finance"
}

但如果业务要求 is_relevant 是布尔值、tags 是数组,这个结果就是错误的。

所以生产环境中应该至少校验:

  • 是否是合法 JSON。
  • 是否包含必填字段。
  • 字段类型是否正确。
  • 枚举值是否合法。
  • 数组长度是否超限。
  • 数字范围是否合理。
  • 是否出现未知字段。

在 Python 中可以使用 Pydantic,在 JavaScript/TypeScript 中可以使用 Zod、Ajv 等工具。Schema 校验是把模型输出从“看起来对”变成“程序确认对”的关键步骤。

九、失败后让模型修复,而不是直接放弃

当 JSON 解析或 Schema 校验失败时,可以把错误信息反馈给模型,让它只修复格式。

例如:

上一次输出不是合法 JSON,错误原因:
tags 字段应为 string[],但实际是 string。

请基于原始任务重新输出,必须只返回合法 JSON,不要解释。

这种修复提示通常比从头重试更有效,因为错误边界更明确。

不过要控制重试次数,例如最多 1 到 2 次。超过次数后应进入降级逻辑,例如返回空结果、人工审核、记录异常队列,而不是无限重试。

十、把稳定性做成链路,而不是一句提示词

一个比较稳的 JSON 输出链路可以这样设计:

输入数据
  -> 构造明确任务提示
  -> 使用结构化输出或 JSON Schema
  -> 模型生成
  -> JSON 解析
  -> Schema 校验
  -> 失败修复 / 重试
  -> 业务消费
  -> 日志与监控

其中每一步都很重要:

  • 提示词负责让模型理解任务。
  • Schema 负责定义机器契约。
  • 解析器负责确认语法正确。
  • 校验器负责确认结构可用。
  • 重试机制负责处理偶发失败。
  • 日志监控负责发现长期问题。

稳定性不是模型单方面给你的,而是系统设计共同塑造出来的。

十一、一个可直接复用的提示词模板

下面是一个通用模板,可以根据业务调整:

你是一个信息抽取助手。请根据用户输入抽取结构化信息。

要求:
1. 只返回一个合法 JSON 对象。
2. 不要返回 Markdown 代码块。
3. 不要返回解释、前缀或后缀文本。
4. 如果信息不存在,字段值返回 null,不要猜测。
5. 所有字段名必须与 Schema 完全一致。

JSON Schema:
{
  "name": "string | null",
  "organization": "string | null",
  "event_time": "string | null",
  "topics": "string[]",
  "risk_level": "low | medium | high | unknown",
  "confidence": "number between 0 and 1"
}

用户输入:
{{input}}

如果 API 支持真正的 JSON Schema,应该把 Schema 放到接口参数里,而不是只写在提示词里。提示词模板适合用来补充语义要求。

十二、常见坑位清单

上线前可以用下面这份清单检查:

  • 是否要求模型只输出 JSON。
  • 是否禁止 Markdown 代码块。
  • 是否定义了完整字段结构。
  • 是否明确字段类型和枚举范围。
  • 是否定义了缺失信息的返回方式。
  • 是否避免让模型输出思考过程。
  • 是否对长文本字段做了长度限制。
  • 是否使用 JSON parser,而不是手写脆弱正则。
  • 是否做了 Schema 校验。
  • 是否有失败重试和降级策略。
  • 是否记录了解析失败的原始输出。
  • 是否用真实样本做过压力测试。

结语

让大模型稳定输出 JSON,本质上是把自然语言生成纳入工程约束。提示词是起点,但不是终点。真正可靠的方案,需要结构化输出能力、清晰 Schema、严格校验、可控重试和持续监控一起工作。

一句话总结:

不要相信“模型应该会按格式输出”,要让系统确认“输出确实符合格式”。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐