GLM 5.2 结构化输出:JSON 模式、Schema 校验与可靠数据提取
Jul 27, 2026

GLM 5.2 结构化输出:JSON 模式、Schema 校验与可靠数据提取

掌握 GLM 5.2 结构化输出:开启 JSON 模式、在系统提示词中定义 schema、用 Pydantic 校验响应,并用 Python 构建可靠的数据提取管道。

如果你曾经上线过一个基于 LLM 的功能,结果却因为模型返回了大段散文而不是 JSON 而崩掉,那你对结构化输出这个痛点一定深有体会。GLM 5.2(模型名:glm-4-plus,由 Zhipu AI 于 2025 年 5 月发布)提供了 JSON 模式,保证每次响应都是合法 JSON;再加上 1M token 的上下文窗口,你可以从那些足以让小型模型崩溃的文档中提取结构。这篇教程会带你过一遍每一层:开启 JSON 模式、在系统提示词中定义 schema、用 Pydantic 校验,以及构建一个能优雅处理边界情况的重试管道。

如果你想在写代码之前先交互式地测试 API,glm5.app 让你无需任何配置就能在浏览器里体验 GLM 5.2 的能力。

为什么非结构化 LLM 输出是生产环境的大问题

一次典型的 LLM 调用可能返回:

Sure! Here is the product information you asked for:

**Name:** Widget Pro  
**Price:** $29.99  
**In Stock:** Yes

这段文本给人看没问题,但对一个期望收到 {"name": "Widget Pro", "price": 29.99, "in_stock": true} 的下游系统来说,简直是灾难。你没法可靠地解析 markdown 标题,没法把自由格式的散文直接塞进数据库插入语句,也没法在缺少中间解析层的情况下把它喂给数据管道的下一步——而那层解析迟早会撞上模型某次发挥创意搞出来的边界情况。

这正是 LLM 集成的核心痛点:模型被训练成跟人交流,而下游系统需要的是精确、机器可读的结构。能不能可靠地解决这个问题——而不是「大多数时候」解决——正是原型和正式产品之间的分水岭。

GLM 5.2 用两种可以组合使用的机制来应对:

  1. JSON 模式 —— 一个运行时开关,强制模型返回合法 JSON
  2. 系统提示词中的 schema 定义 —— 精确描述你期望哪些字段

两者配合使用,每次调用都能拿到一致、可解析的输出。

配置客户端

GLM 5.2 的 API 兼容 OpenAI。你直接用标准的 openai Python 包,把 base URL 指向 Zhipu 即可。认证和端点的完整介绍见 GLM 5.2 API 集成指南

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_ZHIPU_API_KEY",
    base_url="https://open.bigmodel.cn/api/paas/v4/"
)

到 Zhipu AI 开放平台 open.bigmodel.cn 获取你的 API key。任意近期版本的 openai 包都可以直接使用,无需改动——不需要专门的 GLM SDK。

开启 JSON 模式

JSON 模式就是一个参数:response_format={"type": "json_object"}。设置后,模型保证每次响应都是合法 JSON——没有 markdown 代码围栏,没有散文式前言,没有结尾的附加评论。

response = client.chat.completions.create(
    model="glm-4-plus",
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": "Extract product information as JSON. Return only valid JSON."
        },
        {
            "role": "user",
            "content": "Widget Pro is priced at $29.99 and is currently in stock."
        }
    ]
)

import json
data = json.loads(response.choices[0].message.content)
print(data)
# {"name": "Widget Pro", "price": 29.99, "in_stock": true}

有一点很重要:JSON 模式保证的是语法上合法的 JSON,但并不能保证 JSON 里包含你需要的字段。除非你明确告诉模型要什么,否则它自己决定包含哪些 key。这正是 schema 定义发挥作用的地方。

在系统提示词中定义输出 Schema

GLM 5.2 目前还不支持 OpenAI 的 response_format.json_schema 参数(也就是直接在 API 调用里传入 JSON Schema 对象)。改为在系统提示词中定义 schema。这种方式工作可靠,而且除了结构之外,你还能完全控制每个字段的描述。

SYSTEM_PROMPT = """
You are a data extraction assistant. Extract the following fields from the user's input and return them as a JSON object with exactly these keys:

{
  "product_name": "string — the full product name",
  "price_usd": "number — price in USD, numeric only (no currency symbols)",
  "in_stock": "boolean — true if available, false if not",
  "category": "string — product category if mentioned, otherwise null",
  "description": "string — brief product description if present, otherwise null"
}

Return only the JSON object. Do not include explanation, markdown, or any text outside the JSON.
"""

response = client.chat.completions.create(
    model="glm-4-plus",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_input}
    ]
)

这里的关键实践:

  • 列出每个字段及其类型和简短描述
  • 说明字段缺失时返回什么(用 null 而不是省略)
  • 在系统提示词末尾加上明确的指令:只返回 JSON

用 Pydantic 校验

JSON 模式保证你拿到合法 JSON。系统提示词里的 schema 让你大多数时候拿到正确的 key。Pydantic 校验则兜住剩下的边界情况——缺少必填字段、类型错误、数值超出可接受范围——并给你类型化的 Python 对象,而不是原始的字典。

from pydantic import BaseModel, Field
from typing import Optional
import json

class ProductExtraction(BaseModel):
    product_name: str
    price_usd: float = Field(ge=0)
    in_stock: bool
    category: Optional[str] = None
    description: Optional[str] = None

def extract_product(text: str) -> ProductExtraction:
    response = client.chat.completions.create(
        model="glm-4-plus",
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": text}
        ]
    )
    raw = response.choices[0].message.content
    data = json.loads(raw)
    return ProductExtraction(**data)  # raises ValidationError on schema mismatch

product = extract_product("Widget Pro — $29.99, electronics, in stock")
print(product.product_name)   # Widget Pro
print(product.price_usd)      # 29.99
print(product.in_stock)       # True

如果 ProductExtraction(**data) 抛出 ValidationError,你能确切知道是哪个字段错了、为什么错,调试起来一目了然。

构建重试管道

即使有了 JSON 模式和 schema 提示词,模型偶尔还是会返回结构不符合预期的 JSON——多了一个字段、某个你需要的可选字段缺失、价格被返回成字符串而不是数字。重试循环能在不崩掉整个管道的情况下处理这些情况。

import json
from pydantic import ValidationError
import time

def extract_with_retry(text: str, max_attempts: int = 3) -> ProductExtraction:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": text}
    ]

    for attempt in range(max_attempts):
        try:
            response = client.chat.completions.create(
                model="glm-4-plus",
                response_format={"type": "json_object"},
                messages=messages
            )
            raw = response.choices[0].message.content
            data = json.loads(raw)
            return ProductExtraction(**data)

        except json.JSONDecodeError as e:
            # JSON mode should prevent this, but handle defensively
            messages.append({"role": "assistant", "content": raw})
            messages.append({
                "role": "user",
                "content": f"Your response was not valid JSON: {e}. Please return only a JSON object."
            })

        except ValidationError as e:
            # Model returned JSON but with wrong structure
            messages.append({"role": "assistant", "content": raw})
            messages.append({
                "role": "user",
                "content": f"The JSON structure was incorrect: {e}. Please match the schema exactly."
            })

        if attempt < max_attempts - 1:
            time.sleep(1)

    raise RuntimeError(f"Failed to extract structured data after {max_attempts} attempts")

这个模式——把出错的响应追加进对话,再追加一条修正提示——之所以有效,是因为模型拥有完整上下文,能看到自己返回了什么、为什么错了。重试一次之后修正率就很高。

长文档提取:1M 上下文的优势

大多数模型的上下文窗口只有 128K token 甚至更小。一旦文档超过这个限制,你就得分块:拆分文档、对每一块做提取、再合并去重。分块会在信息跨块的边界处引入错误,多次 API 调用带来额外延迟,还让管道复杂度显著上升。

GLM 5.2 的 1,048,576 token 上下文窗口(1M token,大约 75 万词)让绝大多数真实文档都不再需要分块。财务报表、法律合同、研究论文、完整代码库、客服工单存档——这些都能放进单次 API 调用。你写一个提取提示词,调一次接口,拿回一个结构化结果。

按每百万输入 token $1.40 计算,处理一份 500 页的文档,输入 token 成本大约 $0.50。对处理成千上万份文档的管道来说,分块出错(漏提取、重复记录、边界错误)的成本,通常比使用更大上下文带来的按 token 成本更高。

实际实现与上面的例子完全一样——把整份文档作为用户消息内容传入即可。访问 1M 上下文不需要任何特殊参数。

批量提取管道

要处理大量文档,可以把提取函数和 Python 的 concurrent.futures 结合,并行跑多个调用:

from concurrent.futures import ThreadPoolExecutor, as_completed

def batch_extract(documents: list[str], max_workers: int = 5) -> list[dict]:
    results = []
    failed = []

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        future_to_doc = {
            executor.submit(extract_with_retry, doc): i
            for i, doc in enumerate(documents)
        }

        for future in as_completed(future_to_doc):
            idx = future_to_doc[future]
            try:
                result = future.result()
                results.append({"index": idx, "data": result.model_dump()})
            except RuntimeError as e:
                failed.append({"index": idx, "error": str(e)})

    results.sort(key=lambda x: x["index"])
    return results, failed

documents = [
    "Widget Pro — $29.99, electronics, in stock",
    "Budget Desk — $149.00, furniture, limited availability",
    "Pro Camera Lens — $899, photography, backordered"
]

results, errors = batch_extract(documents)
for r in results:
    print(r["data"])

max_workers 保持在适中水平(5–10),避免触发速率限制。Zhipu API 有每分钟 token 限制;如果撞上了,就给 extract_with_retry 加上指数退避。

常见模式与使用场景

结构化输出在各类提取任务中都很有用:

文档解析:单次遍历从合同或发票中提取实体、日期、金额和当事人——配合 GLM 5.2 的 1M 上下文处理长文档尤其有价值。

带元数据的分类:与其让模型返回一个标签字符串,不如让它返回 {"category": "billing", "confidence": "high", "reason": "mentions invoice number and payment terms"}。一次调用同时拿到分类结果和可审计的理由。

表单预填:在写入数据库之前,解析非结构化的客户提交内容,映射到结构化的表单字段。

API 响应规范化:聚合多个 schema 不一致的数据源时,用 GLM 5.2 把每个响应规范成你的标准结构。

日志事件提取:用批量模式从原始日志行中规模化提取结构化事件(时间戳、严重级别、服务、消息、错误码)。

生产环境中,原始模型输出和校验后的 Pydantic 对象都要保存。原始输出能帮你调试校验失败,也能审计模型实际说了什么。

准备好对着线上模型跑这些例子了吗?glm5.app 让你直接访问 GLM 5.2,不用先写任何基础设施代码,就能测试你的提示词和 schema 定义。

总结

GLM 5.2 的结构化输出分三层工作:

  1. response_format={"type": "json_object"} —— 保证合法 JSON 语法
  2. 系统提示词中的 schema 定义 —— 告诉模型返回哪些字段
  3. Pydantic 校验 —— 兜住结构不匹配,并给你类型化的 Python 对象

生产可靠性再加上重试循环;文档大到其他模型处理不了时,就靠 1M 上下文窗口。这套组合让 GLM 5.2 成为需要大规模一致、机器可读输出的数据提取管道的务实之选。

参考来源

Start Using GLM 5 Today

Try GLM 5 free — reasoning, coding, agents, and image generation in one platform.