用 JSON Schema 拿到稳定的结构化输出:让大模型返回可直接入库的数据
本站为独立第三方技术服务平台,提供多模型 API 聚合接入服务,与 Anthropic、OpenAI、Google 等模型提供商无任何关联、授权或合作关系。
TL;DR
- 让模型"输出 JSON"最脆弱的做法是在 prompt 里求它,返回里常混着
```json包裹、解释性文字,json.loads直接崩。 - 更稳的做法是用
response_format传一份 JSON Schema,把字段、类型、必填项都约束死,模型按 schema 填空。 - Code0 兼容 OpenAI SDK,这套写法对 Claude、GPT、Gemini 等模型都是同一份代码,切模型只改
model字段。 - 下面给一个可直接运行的完整示例:从一段自由文本里抽出结构化的"发票信息"。
为什么 prompt 里求 JSON 不靠谱
先看反面教材。很多人一开始这么写:
messages=[{"role": "user", "content": "把下面这段话解析成 JSON:张三,13800000000,北京"}]
模型返回可能是:
- 前面加一句"好的,解析结果如下:"
- 用
```json ... ```代码块包起来 - 多轮调用字段顺序、有无某个键都不稳定
于是你的解析代码就得写一堆正则去剥壳、容错,还是三天两头挂。问题的根源是——你没有给模型一个明确的输出契约。
正确姿势:把契约交给 JSON Schema
response_format 支持 json_schema 模式,你把期望的数据结构用 JSON Schema 描述清楚,模型会严格按这个结构返回,返回体本身就是一个干净可解析的 JSON 对象。
前置准备
- 注册 Code0 账号:https://console.code0.ai/console/dashboard
- 获取 API Key(控制台 → API Keys → 创建)
- 安装 SDK:
pip install openai
步骤拆解
步骤 1:定义你的数据结构
假设我们要从一段客服消息里抽出下单信息,目标结构是:客户名、电话、商品列表、总金额。先把它写成 JSON Schema:
invoice_schema = {
"type": "object",
"properties": {
"customer_name": {"type": "string", "description": "客户姓名"},
"phone": {"type": "string", "description": "联系电话"},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"quantity": {"type": "integer"},
"unit_price": {"type": "number"},
},
"required": ["name", "quantity", "unit_price"],
},
},
"total": {"type": "number", "description": "订单总金额"},
},
"required": ["customer_name", "items", "total"],
"additionalProperties": False,
}
required 决定哪些字段必须出现,additionalProperties: False 禁止模型自由发挥加字段——这两项是稳定性的关键。
步骤 2:填写接入参数并发起请求
- Base URL:
https://hk.code0.ai/v1 - API Key: 粘贴你在控制台获取的 Key
- Model:
claude-opus-4-8或gpt-5.4(按需选择)
from openai import OpenAI
client = OpenAI(base_url="https://hk.code0.ai/v1", api_key="sk-你的Key")
resp = client.chat.completions.create(
model="claude-opus-4-8",
messages=[
{"role": "system", "content": "你是订单信息抽取助手,只输出符合 schema 的结果。"},
{"role": "user", "content": "客户李雷 13900001111,要 2 个保温杯单价 59,1 个笔记本单价 30,一共 148 元。"},
],
response_format={
"type": "json_schema",
"json_schema": {"name": "invoice", "schema": invoice_schema, "strict": True},
},
)
步骤 3:直接解析,无需剥壳
因为返回已经是规整 JSON,直接 json.loads 就能拿到 dict:
import json
data = json.loads(resp.choices[0].message.content)
print(data["customer_name"]) # 李雷
print(len(data["items"])) # 2
print(data["total"]) # 148.0
没有 ```json 外壳,没有多余解释,字段结构和你定义的 schema 一致——可以直接入库或往下游传。
完整代码示例(可复制运行)
import json
from openai import OpenAI
client = OpenAI(base_url="https://hk.code0.ai/v1", api_key="sk-你的Key")
invoice_schema = {
"type": "object",
"properties": {
"customer_name": {"type": "string"},
"phone": {"type": "string"},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"quantity": {"type": "integer"},
"unit_price": {"type": "number"},
},
"required": ["name", "quantity", "unit_price"],
},
},
"total": {"type": "number"},
},
"required": ["customer_name", "items", "total"],
"additionalProperties": False,
}
resp = client.chat.completions.create(
model="claude-opus-4-8", # 换 gpt-5.4 / gemini-3-pro 同一套代码
messages=[
{"role": "system", "content": "你是订单信息抽取助手,只输出符合 schema 的结果。"},
{"role": "user", "content": "客户李雷 13900001111,要 2 个保温杯单价 59,1 个笔记本单价 30,一共 148 元。"},
],
response_format={
"type": "json_schema",
"json_schema": {"name": "invoice", "schema": invoice_schema, "strict": True},
},
)
data = json.loads(resp.choices[0].message.content)
print(json.dumps(data, ensure_ascii=False, indent=2))
几个实战建议
- schema 越具体越稳:给字段加
description、约束好type,模型的猜测空间越小,结果越可预期。 - 枚举值用
enum:分类任务里把可选值写进enum,能避免模型返回同义词导致的对不上号。 - 多模型横向验证:同一份 schema,把
model换成不同厂商的模型跑一遍,挑抽取质量最好的那个——这正是一 Key 通模的用处,不用为每家改代码。 - 必要时再套一层校验:拿到结果后用
pydantic或jsonschema再验一次,生产环境更稳妥。
常见问题
- Q: 某个模型不支持 json_schema 怎么办? → 退一步用
response_format={"type": "json_object"},或改用 Tool Use 定义函数入参来约束结构。 - Q: 返回里字段缺失? → 检查 schema 的
required是否写全,并在 system prompt 里强调"字段必须齐全"。 - Q: 想换模型对比效果? → 只改
model字段,其余代码不动。
小结
结构化输出的稳定性,本质是"你有没有给模型一份明确的契约"。用 JSON Schema 把契约写死,比在 prompt 里反复叮嘱可靠得多。在 Code0 上,这套写法对多种模型通用,切模型只是改一个字段——先去控制台拿个 Key,把上面的示例跑起来就有直观感受。具体可用模型与计费口径以 console.code0.ai 控制台为准。



