Grok JSON Schema 输出约束

2026-11-04 17:06 更新

从散文里抠字段这件事,通常从一个正则开始,最后以一屏正则结束。「帮我从合同里抽出金额、期限、违约金」——第一次用正则还能跑,换成十种合同模板就崩了。不是正则写得不好,而是结构没有交给模型。

这一节把「让返回可解析」做成两件事:用 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 校验库。

七、动手试

  1. 给你的业务写一个 schema,列出哪些字段必须进 required、哪些可以有默认值,并说明理由。
  2. 把 validate() 扩展到 anyOf:当字段值匹配任一分支即通过,写一条「两个分支都不匹配」的用例。
  3. 找一个你正在用正则解析的输出,把它改成 response_format + schema,比较两者的维护成本。
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号