让大模型稳定输出 JSON:结构化输出的三种方法与 Python 校验实践

适合人群:在程序中调用大模型,需要把结果交给后续代码处理,却经常遇到"模型多说了一句话导致 JSON 解析失败"的开发新手。

一、背景

X 上有中文开发者分享:让大模型"吐 JSON",模型要一个 token 一个 token 地生成,既慢又容易出格式问题。在实际开发中,这个问题非常普遍:模型返回的内容前面多了一句"好的,以下是结果",或者用 Markdown 代码块包裹,或者漏了一个引号,程序就直接报错。

本文整理三种由浅入深的方法,让模型输出稳定可用的 JSON,并用 Python 做校验和兜底。

二、为什么模型会输出"坏 JSON"

大模型的本质是逐个预测下一个 token,它并不"知道"自己在写 JSON,只是根据训练经验模仿格式。因此:

  • 提示词不够明确时,它会按聊天习惯加上开场白;
  • 输出很长时,容易在中途出现格式错误;
  • 字段名、类型可能和你期望的不一致。

解决思路有两类:在提示词层面引导 ,以及在解码层面约束。

三、操作步骤

方法一:提示词约束 + 代码兜底(适用于所有模型)

步骤 1:在提示词中给出明确格式

复制代码
请从下面的商品评论中提取信息,只输出 JSON,不要输出任何其他文字。
格式:
{"sentiment": "positive|negative|neutral", "topics": ["字符串"], "score": 1到5的整数}

评论:物流很快,但包装有点破损,客服处理还不错。

要点:写清字段名、类型和取值范围,并明确"只输出 JSON"。

步骤 2:解析时做清理

python 复制代码
import json, re

def parse_json(text):
    text = text.strip()
    m = re.search(r"```(?:json)?\s*(.*?)```", text, re.S)
    if m:
        text = m.group(1)
    start, end = text.find("{"), text.rfind("}")
    return json.loads(text[start:end + 1])

这段代码会去掉 Markdown 代码块,并截取第一个 { 到最后一个 } 之间的内容。

方法二:使用 API 的 JSON 模式

许多模型服务提供了专门的结构化输出参数。以 OpenAI 兼容接口为例,可以设置 response_format:

python 复制代码
from openai import OpenAI

client = OpenAI()  # 密钥从环境变量读取
resp = client.chat.completions.create(
    model="你使用的模型名称",
    messages=[
        {"role": "system", "content": "你只输出 JSON。"},
        {"role": "user", "content": "提取评论信息......"},
    ],
    response_format={"type": "json_object"},
)
print(resp.choices[0].message.content)

JSON 模式能保证输出是合法 JSON,但不保证字段符合你的预期。部分服务还支持通过 JSON Schema 指定精确结构(Structured Outputs),能进一步约束字段和类型。具体参数名称和支持情况,以所用服务的官方文档为准。

本地模型服务也有类似能力,例如 Ollama 的接口支持 format 参数,可以传入 "json" 或一个 JSON Schema。

方法三:用 Pydantic 定义结构并校验

无论使用哪种方法,都建议在代码里做一次结构校验:

python 复制代码
from pydantic import BaseModel, Field, ValidationError
from typing import Literal

class Review(BaseModel):
    sentiment: Literal["positive", "negative", "neutral"]
    topics: list[str]
    score: int = Field(ge=1, le=5)

try:
    data = Review.model_validate(parse_json(model_output))
except (ValidationError, json.JSONDecodeError) as e:
    print("格式不符合要求:", e)

还可以用 Review.model_json_schema() 生成 JSON Schema,直接放进提示词或传给支持 Schema 的接口,做到"一处定义、两处使用"。

步骤补充:失败重试

校验失败时,可以把错误信息反馈给模型,让它修正:

python 复制代码
def ask_with_retry(prompt, max_retry=2):
    msg = prompt
    for _ in range(max_retry + 1):
        out = call_llm(msg)          # 你自己的模型调用函数
        try:
            return Review.model_validate(parse_json(out))
        except Exception as e:
            msg = f"{prompt}\n\n上一次的输出不符合格式,错误:{e}\n请只输出修正后的 JSON。"
    raise RuntimeError("多次重试后仍然失败")

设置重试上限,避免无限调用。

四、方法对比与选择

方法 优点 局限
提示词 + 清理 通用,任何模型可用 无法完全保证格式
JSON 模式 / Schema 格式可靠性高 依赖服务支持
Pydantic 校验 + 重试 保证进入业务代码的数据正确 失败时需要额外调用

实际项目中建议组合使用:能用 Schema 约束就用,再加 Pydantic 校验和有限次重试。

另外,如果任务只是从几个固定选项中选一个(例如情感分类),其实不必生成 JSON,直接比较候选选项的概率会更快、更稳定。

五、常见问题 FAQ

Q1:设置了 JSON 模式还是报错?

有的服务要求在提示词中明确出现"JSON"字样,否则会报错或表现异常。另外检查输出是否因长度限制被截断。

Q2:中文被转成了 \u 编码?

这是 json.dumps 的默认行为,加上 ensure_ascii=False 即可正常显示中文。

Q3:字段偶尔缺失怎么办?

在 Pydantic 模型中区分必填和可选字段,可选字段设置默认值;必填字段缺失时触发重试。

Q4:输出很长的列表时容易出错?

把任务拆小,每次只处理少量数据,再在代码中合并结果。

Q5:小模型适合做结构化输出吗?

小模型遵循复杂格式的能力较弱,建议结构尽量扁平、字段尽量少,并配合解码约束和校验。

六、实用技巧

  1. 字段名用英文:减少模型自由发挥的空间,也方便代码处理;
  2. 枚举值写全 :用 Literal 或在提示词中列出所有允许的取值;
  3. 温度设低:结构化任务通常不需要创造性,较低的温度更稳定;
  4. 记录失败样本:分析失败原因,持续改进提示词;
  5. 不要信任未经校验的数据:模型输出进入数据库前,必须经过校验。

七、总结

让大模型稳定输出 JSON 的关键是"三层保障":提示词写清格式 → 接口层使用 JSON 模式或 Schema 约束 → 代码层用 Pydantic 校验并有限次重试。做好这三步,模型输出就能可靠地接入你的程序。

参考资料

  • OpenAI 官方文档:Structured Outputs 章节
  • Pydantic 官方文档:模型与校验章节
  • Ollama 官方文档:结构化输出说明
  • X 用户 @ataosky 关于大模型输出 JSON 与小模型限定选项的分享
相关推荐
今天的砖头有点烫手啊2 小时前
Java 序列化到底怎么选?从 Serializable 到 Protobuf 的演进与选型
java·json
小白学鸿蒙2 小时前
OpenHarmony 预置应用配置详解:/system/etc/app/ 下的 5 个 JSON
json·openharmony·预置应用
for_ever_love__4 小时前
LangChain 结构化输出:让模型稳定吐出符合 schema 的 JSON
langchain·json
健康活着就好4 小时前
# 手写Promise.all和Promise.race,面试再也不怕了
面试·json
slacker-kian17 小时前
[实践]-用LLM对文本结构化:把多段文本描述变成规范的 JSON 知识库
人工智能·llm·prompt·结构化·pydantic·one-shot 提示
Dxy12393102161 天前
MySQL JSON 生成列索引原理、实战与坑点
mysql·adb·json
l1t1 天前
DeepSeek总结的DuckDB 从 JSON 字符串到类型化列
数据库·sql·json
积流成海1 天前
CSV与JSON转换:RFC 4180 转义规则与 Excel 中文乱码
json·excel·盘子工具站
夜雪一千1 天前
MySQL中查询和操作JSON类型的数据
数据库·mysql·json