Grok JSON Schema 输出约束
从散文里抠字段这件事,通常从一个正则开始,最后以一屏正则结束。「帮我从合同里抽出金额、期限、违约金」——第一次用正则还能跑,换成十种合同模板就崩了。不是正则写得不好,而是结构没有交给模型。
这一节把「让返回可解析」做成两件事:用 schema 约束输出,用校验器守住边界。
一、问题不是「能不能解析」,而是「结构由谁保证」
结构化输出有两条路,官方文档写得很清楚:
response_format:把type设为"json_schema",schema 放在response_format.json_schema里。也接受"json_object"(任意合法 JSON)与"text"(默认,自由文本)。- 工具调用:定义工具时,模型生成的参数总是严格符合工具的输入 JSON Schema——官方明确
strict隐式为true。
两条路都能拿到结构,但适用场景不同:要模型「直接回答一个结构化结果」,用 response_format;要模型「先决定调用哪个工具」,用工具 schema。
二、最小可运行示例:一个够用的 schema 校验器
下面的脚本不联网、不需要真实密钥。它只校验四类最常出问题的约束:必填、多余字段、枚举、长度。真正的项目可以用 Pydantic 或 Zod,但先看清这四类,才知道该约束什么。
"""极简 schema 校验:required / additionalProperties / enum / maxLength。"""
def validate(obj: dict, schema: dict) -> list[str]:
errors: list[str] = []
props = schema.get("properties", {})
for key in schema.get("required", []):
if key not in obj:
errors.append(f"缺少必填字段 {key}")
if schema.get("additionalProperties") is False:
for key in obj:
if key not in props:
errors.append(f"出现未定义字段 {key}")
for key, spec in props.items():
if key not in obj:
continue
value = obj[key]
if "enum" in spec and value not in spec["enum"]:
errors.append(f"{key} 不在枚举内:{value}")
if spec.get("type") == "string" and "maxLength" in spec \
and len(value) > spec["maxLength"]:
errors.append(f"{key} 超过 maxLength={spec['maxLength']}")
return errors
SCHEMA = {
"type": "object",
"properties": {
"title": {"type": "string", "maxLength": 20},
"level": {"type": "string", "enum": ["low", "high"]},
},
"required": ["title"],
"additionalProperties": False,
}
def offline_check() -> None:
cases = [
("合法", {"title": "缓存", "level": "low"}),
("缺必填", {"level": "high"}),
("多余字段", {"title": "缓存", "extra": 1}),
("枚举越界", {"title": "缓存", "level": "mid"}),
]
for name, obj in cases:
errs = validate(obj, SCHEMA)
print(f"{name}:" + ("通过" if not errs else ";".join(errs)))
if __name__ == "__main__":
offline_check()
真正上线时,把 SCHEMA 换成你的业务结构,并把 validate() 放在「模型返回之后、入库之前」——它不是替代官方保证,而是防止 schema 之外的东西漏进来,比如多出来的字段。
三、三类 schema 特性:保证、尽力、拒绝
官方把 JSON Schema 支持分成三档,这个划分是本节最该记住的部分:
| 档位 | 代表关键字 | 实际含义 |
|---|---|---|
| 结构保证 | required、enum、const、type、additionalProperties |
在支持范围内,输出保证符合 |
| 尽力而为 | not、if/then/else、多子 schema allOf、未列出的 format |
接受但不结构强制,需自行校验 |
| 直接拒绝 | enum 零变体、属性 schema 为 true/false、maxContains、items 为数组 |
返回 400 |
另外三条容易踩的细节:
additionalProperties默认是false,要用必须显式设true。这和很多人的直觉相反。- 约束有上限:
minLength/maxLength保证到 2048,minItems/maxItems到 256,minProperties/maxProperties到 64;超出上限仍接受,但一致性靠模型行为,不再保证。 pattern只支持 ECMAScript 子集:不支持反向引用、\p{}、\b、前后行断言与内联修饰符;.会匹配换行,^/$隐式(整串匹配)。
四、预期输出与异常分支
运行校验器,预期输出:
合法:通过
缺必填:缺少必填字段 title
多余字段:出现未定义字段 extra
枚举越界:level 不在枚举内:mid
换成真实返回时,若出现异常,按下面顺序定位:
| 现象 | 优先怀疑 | 第一步动作 |
|---|---|---|
| 接口直接 400 | schema 命中拒绝清单 | 核对是否零变体 enum 或数组型 items |
| 字段时有时无 | 该字段不在 required 里 |
把关键字段加入 required |
| 多出未定义字段 | additionalProperties 被设为 true |
确认是否真的需要放开 |
| 长度/条数偶尔越界 | 超出保证上限 | 收紧 schema 或下游补校验 |
最后一行尤其重要:maxLength 超过 2048 之后,官方不再结构保证。此时把校验放在应用层,比反复调 prompt 更可靠。
五、判据卡
| 判断项 | 该这么做 | 不该这么做 |
|---|---|---|
| 要结构化答案 | 用 response_format + json_schema |
用正则从散文里抠 |
| 要模型选工具 | 用工具 schema(strict 隐式 true) |
用 response_format 模拟工具选择 |
| 关键字段 | 写进 required |
只写在 properties 里 |
| 多余字段 | 显式设 additionalProperties |
依赖默认值放行 |
| 超限约束 | 下游补校验 | 假定模型一定遵守 |
这张卡的重点是分清「谁保证结构」。五条里有三条在回答同一个问题:这是结构保证,还是只是「模型通常会照做」。
六、来源与核验
- 官方 Structured Outputs:
https://docs.x.ai/developers/model-capabilities/text/structured-outputs(核验日期 2026-11-04)。response_format三种类型、工具 schema 的strict隐式 true、支持类型、三档特性、约束上限与pattern支持范围均以该页为准。 - 官方 Function Calling:
https://docs.x.ai/developers/tools/function-calling(核验日期 2026-11-04)。工具parameters根必须是 object 或其 union 的规则来自该页。 - 本节校验器为演示用极简实现,不覆盖
$ref、anyOf等复杂结构;生产建议用成熟的 schema 校验库。
七、动手试
- 给你的业务写一个 schema,列出哪些字段必须进
required、哪些可以有默认值,并说明理由。 - 把
validate()扩展到anyOf:当字段值匹配任一分支即通过,写一条「两个分支都不匹配」的用例。 - 找一个你正在用正则解析的输出,把它改成
response_format+ schema,比较两者的维护成本。

TRAE-AI编程


更多建议: