结构化输出 JSON Mode:强制大模型返回合法 JSON
什么是 JSON Mode
在与大语言模型交互时,我们常需要模型返回结构化的数据,例如用于后续程序处理、填充数据库或生成配置文件。但默认情况下,模型可能返回带解释性文字的 JSON,或者直接在文本中夹杂 JSON 片段,甚至生成不合法的 JSON(缺少引号、尾随逗号等),导致解析失败。
JSON Mode 是一种强制模型输出合法 JSON 的功能。一旦启用,模型只会输出一个完整的 JSON 对象或数组,不会附加任何解释文本,且会严格遵循 JSON 语法,极大降低解析出错的可能。你可以把它理解为向模型下达了一条死命令:“只输出纯 JSON,别废话。”
什么时候需要 JSON Mode
- 系统集成:你的后端服务期望从模型获得一个可直接
JSON.parse()的数据结构。 - 数据提取:从非结构化文本中提取实体、情感、摘要等,并以结构化格式返回。
- 工具调用:需要模型生成符合特定 Schema 的参数,以便调用外部 API。
- 自动化流程:多个步骤协同工作,每个步骤的输出必须能被下一个程序可靠读取。
总之,任何需要“机器可读”输出的场景,都推荐使用 JSON Mode。
开启 JSON Mode 的方法
OpenAI 平台
OpenAI 在 GPT-3.5 Turbo 和 GPT-4 等模型中提供了原生 JSON Mode。使用时需要在 API 请求中设置 response_format 参数:
{
"model": "gpt-4-turbo",
"messages": [
{
"role": "system",
"content": "你是一个数据提取助手,请返回包含姓名和年龄的 JSON。"
},
{
"role": "user",
"content": "张三今年28岁。"
}
],
"response_format": { "type": "json_object" }
}
关键点:
- 必须在
system或user消息中明确提到“JSON”这个词,否则 OpenAI API 可能会拒绝(这是安全机制,防止模型在非预期场景下输出 JSON)。 - 模型输出将是一个纯 JSON 对象,例如:
{ "name": "张三", "age": 28 } - 不会再有
好的,这是提取的结果:这类引导语。
其他大模型平台与框架
许多平台也支持类似功能,只是命名和实现稍有不同:
- Anthropic Claude:通过提示词强制,例如在 Human 消息中写“你必须只返回 JSON,不要包含任何其他文本。”,并在代码层面检查输出是否以
{开头。 - Google Gemini:提供
generationConfig.responseMimeType设为application/json。 - 开源模型与 llama.cpp:可以通过 Grammar 约束输出,限制生成 token 只能符合 JSON 语法。
- LangChain:使用
JsonOutputFunctionsParser或StructuredOutputParser来解析并强制输出格式。 - VLLM:支持
guided_json参数,可传入 JSON Schema 严格限制输出。
无论哪种工具,核心思想都是约束生成空间,让模型只能选择符合 JSON 语法的 token 序列。
使用 JSON Schema 精确定义输出结构
普通的 JSON Mode 只保证输出是合法 JSON,但不保证字段名、类型符合你的预期。为了精确控制,可以使用 JSON Schema(或 Function Calling)来定义输出结构。
OpenAI 的 Structured Outputs
OpenAI 在新的模型中推出了更强大的 Structured Outputs,允许你直接传入 JSON Schema,模型会严格按照 Schema 输出,甚至不会遗漏必填字段。
示例请求:
{
"model": "gpt-4o-2024-08-06",
"messages": [...],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_schema",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number" },
"city": { "type": "string" }
},
"required": ["name", "age", "city"],
"additionalProperties": false
}
}
}
}
输出将严格匹配:
{
"name": "张三",
"age": 28,
"city": "北京"
}
Function Calling 绕道实现
在没有原生 Structured Outputs 的模型上,你可以使用 Function Calling 来实现类似效果。定义函数的参数 Schema,然后让模型调用该函数,实际不执行函数,只取参数 JSON。这种方式同样能保证字段和类型的准确性。
实践演示:构建一个电影信息提取器
假设我们要从一段影评中提取电影名、评分和情绪标签,返回结构化 JSON。
步骤一:编写提示词
即使开启 JSON Mode,System Prompt 依然很重要,它定义了任务和输出格式:
你是一个专业的影评分析助手。根据用户提供的影评,提取以下信息并以 JSON 格式返回:
{
"movie": "电影名称",
"rating": 评分(数字,1-10),
"sentiment": "positive/negative/neutral"
}
只返回 JSON,不要任何额外文字。
步骤二:调用 API(Python 示例)
import openai
client = openai.OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "你是一个影评分析助手……只返回JSON。"},
{"role": "user", "content": "《流浪地球》太震撼了,特效拉满,我给9分!"}
],
response_format={"type": "json_object"}
)
print(response.choices[0].message.content)
步骤三:输出结果
{
"movie": "流浪地球",
"rating": 9,
"sentiment": "positive"
}
如果使用 JSON Schema 进一步约束,可以确保 rating 是数字类型,且必须在 1 到 10 之间。
常见问题与注意事项
模型有时仍会输出额外文字怎么办?
- 检查提示词是否明确要求“只返回 JSON”,并在 System 或 User 消息中出现了“JSON”字样(针对 OpenAI)。
- 减小
temperature,使用 0 或较小的值,让输出更确定。 - 在代码层面添加保护:检查输出第一个字符,如果不是
{或[则尝试提取 JSON 片段,或重新请求。 - 升级至支持严格 JSON 模式的模型或平台。
JSON Mode 会影响模型推理能力吗?
有些研究表明,强制输出格式可能会让模型在“思考”上表现得略微受限,因为它必须尽早进入 JSON 序列。但在多数结构化提取任务中,影响可忽略。如果你的任务需要大量推理后再输出结构,可以考虑两阶段:先自由输出分析,再让另一个模型(或同一模型在第二回合)转化为 JSON。
如何处理很长的 JSON 输出?
模型有最大输出 token 限制。如果 JSON 过大,可能被截断,导致非法 JSON。解决方案:
- 分块生成,让模型输出部分内容,然后拼接。
- 缩小预期结构,只保留必要字段。
- 使用支持更长上下文的模型。
流式输出对 JSON Mode 的影响
启用流式(streaming)时,每个 chunk 只是整体 JSON 的一部分,直接 parse 会失败。你需要收集所有 chunks 拼接后再解析。许多 SDK 提供了自动拼接的方法。
最佳实践总结
- 明确指令:在提示词中清晰描述你需要的 JSON 结构,并强调“只输出 JSON”。
- 合理使用 Schema:如果对字段要求严格,尽量使用 JSON Schema 或 Function Calling。
- 设置低温度:
temperature=0可降低输出的随机性,提高结构一致性。 - 错误处理:始终用
try...catch包裹 JSON 解析,失败时采取重试或 fallback 策略。 - 测试真实场景:用各种边界输入测试你的提示词和解析器,确保鲁棒性。
JSON Mode 是大模型走向生产环境的重要基石。掌握它,你就能可靠地将自然语言能力嵌入到结构化工作流中,实现真正的自动化。