LLM 结构化输出全解:从 Prompt 约束到 Schema 硬保证,三层实现怎么选

LLM 结构化输出全解:从 Prompt 约束到 Schema 硬保证,三层实现怎么选

LLM 系列又一篇。Function Calling 篇讲过「模型输出结构化请求」的协议细节------但那个循环里有个前提没展开:模型输出本来就是不可靠的散文,怎么让它变成代码能直接用的数据? 这篇讲透结构化输出。

一、为什么结构化输出是刚需

Agent 的每次 LLM 调用,下游接的都是代码:分类结果要 if-else、抽取结果要入库、工具参数要执行。而模型的本能是写散文------「分类结果:这可能是一个 Bug,因为......」------下游代码直接崩。

工程化的 LLM 应用,90% 的输出都应该是结构化的。 结构化输出的可靠度分三层,从软到硬,失败率天差地别:

层 手段 合法 JSON 率 字段符合 Schema
层 1 Prompt 约束 ~95% ~85%
层 2 JSON mode ~100% ~90%
层 3 Schema 约束生成 ~100% ~100%

(数字是量级示意,具体看模型------但顺序是稳定的:约束越靠底层,保证越强。)

二、层 1:Prompt 约束(软约束)

在 System Prompt 里定义格式 + few-shot 示范------零 API 依赖,所有模型都支持:

python 复制代码
messages = [
    {"role": "system", "content":
     "分析用户反馈,只返回 JSON,不要任何其他文字。\n"
     '格式:{"category": "Bug|需求|咨询", "severity": 1-5, "summary": "一句话"}\n'
     '示例输入「登录按钮点不动」→ 输出 {"category": "Bug", "severity": 3, "summary": "登录按钮失效"}'},
    {"role": "user", "content": feedback},
]

问题:模型偶尔会「加戏」------前后包 ```json 代码块、加一句解释、字段名擅自改。解析失败率几个百分点,量大会很难受。

什么时候用:原型、低频调用、下游有人工兜底。生产高频场景别停在这一层。

三、层 2:JSON mode(API 层保证合法 JSON)

主流 API 都有 JSON 模式:保证返回合法 JSON(不会包代码块、不会夹散文),但不保证字段符合你的 Schema:

python 复制代码
resp = client.chat.completions.create(
    model=MODEL,
    messages=messages,
    response_format={"type": "json_object"},   # 合法 JSON 硬保证
)
data = json.loads(resp.choices[0].message.content)  # 不会解析失败

JSON mode 的实现原理是约束采样:解码时只允许产生合法 JSON 的 token------这是解码层的硬保证,不是 prompt 恳求。

残留问题 :category 可能返回一个 Schema 里没有的值(「问题反馈」?),severity 可能是字符串不是数字。合法 JSON ≠ 符合 Schema。

四、层 3:Schema 约束生成(硬保证)

把完整 Schema 交给 API,解码层直接禁止生成 Schema 之外的 token:

python 复制代码
resp = client.chat.completions.create(
    model=MODEL,
    messages=messages,
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "feedback_analysis",
            "strict": True,      # 硬保证:字段、类型、枚举全锁定
            "schema": {
                "type": "object",
                "properties": {
                    "category": {"type": "string", "enum": ["Bug", "需求", "咨询"]},
                    "severity": {"type": "integer", "minimum": 1, "maximum": 5},
                    "summary": {"type": "string"},
                },
                "required": ["category", "severity", "summary"],
                "additionalProperties": False,
            },
        },
    },
)
data = resp.choices[0].message.content   # 100% 符合 Schema,直接用

strict 模式下枚举就是真枚举、类型就是真类型 ------「category 返回了 Schema 外的值」这种失败模式直接消失。Function Calling 篇的工具参数(input_schema)就是同一套机制:工具参数是最重要的结构化输出场景。

注意代价:strict Schema 会限制模型自由度,Schema 写太死(枚举太少、字段太碎)会挤歪模型的判断空间------Schema 设计本身是提示词工程的一部分。

五、兜底回路:Pydantic 校验 + 失败重试

即使层 3,也要留一道校验兜底(语义校验是 Schema 管不了的:severity=3 但 summary 说「很严重」,语法对语义错):

python 复制代码
from pydantic import BaseModel, field_validator

class Feedback(BaseModel):
    category: str
    severity: int
    summary: str

    @field_validator("category")
    @classmethod
    def check_category(cls, v):
        if v not in {"Bug", "需求", "咨询"}:
            raise ValueError(f"非法类别: {v}")
        return v

def extract(feedback: str, max_retries: int = 2) -> Feedback:
    last_err = None
    for _ in range(max_retries + 1):
        resp = call_llm(feedback)               # 任意层的结构化输出
        try:
            return Feedback.model_validate_json(resp)
        except Exception as e:
            last_err = e                        # 把校验错误回喂模型,让它自我修正
    raise RuntimeError(f"结构化输出失败: {last_err}")

校验失败把错误信息回喂重试------和 Function Calling 篇的「错误当 tool_result 回填」同一个原理:模型拿到错误描述会自我修正,两轮重试后失败率趋近于零。

六、三层怎么选

场景 用哪层 理由
Agent 工具调用 FC 的 Schema(层 3) 参数直接执行,必须硬保证
抽取入库、分类打标 层 3 或 层 2 + Pydantic 量大,人工兜不起
原型验证、低频任务 层 1 零依赖,先跑通
模型不支持 JSON mode 层 1 + Pydantic 重试 软约束 + 兜底回路

一句话:能上硬保证就上硬保证,上了硬保证也要留 Pydantic 兜底------语义错误是任何解码层约束都拦不住的。

七、踩坑提醒

  1. 别在 prompt 里写「请返回 JSON」就完事:软约束的失败率是按百分比算的,生产量级下每天都会崩几次------至少上 JSON mode
  2. Schema 别设计太碎:二十个嵌套字段会把模型挤得顾此失彼------扁平、少字段、枚举适度
  3. 温度对结构化输出仍然有效:分类任务低温更稳,别因为「反正有 Schema 兜底」就放任高温
  4. 校验错误信息要具体:回喂「校验失败」模型修不好,回喂「category 收到「问题反馈」,只允许 Bug/需求/咨询」才能自我修正

总结

层 手段 一句话
层 1 Prompt 约束 零依赖软保证,原型用
层 2 JSON mode 合法 JSON 硬保证,字段仍可能歪
层 3 Schema 约束生成 字段类型枚举全锁定,生产首选
兜底 Pydantic + 错误回喂重试 语义错误最后防线,两轮重试归零

结合系列:Function Calling 的工具参数、Jev 的 Choice/Score、评测篇的判分------全是结构化输出的应用场景。Agent 工程化的一半工作,就是把模型的散文变成代码能信的数据。觉得有用点个关注。

相关推荐
独立开发者阿乐2 小时前
结构化数据 JSON-LD 实战:让 AI 读懂你的网页
人工智能·json·结构化数据·geo优化·json-ld·ai收录·faqpage
过客123452 小时前
从"发现"到"处置":一个无人值守 AI 闭环的完整拆解(85 天真实数据)
后端·agent·ai编程
sarasuki2 小时前
Agent 的记忆有几种?你的名字是?
人工智能·设计模式·agent
代码山河2 小时前
Java学习路线图:2026年最新版,从入门到架构师
java·学习·架构·教程·面向对象·项目
guslegend2 小时前
AI 编程范式转换与 Memory 工程:从无状态模型到 AGENTS.md 声明式配置
人工智能·大模型·agent·ai编程·opencode
怕浪猫2 小时前
Agent 怎么做规划?这道面试题淘汰了 80% 的候选人
前端·面试·agent
AI技术新视界3 小时前
微软确认 GPT‑6 使用 Looped Transformers
人工智能·llm
网络毒刘3 小时前
Cursor 排障剧本:卡住、乱改、漏测三类故障的复现提问与上下文裁剪法
agent·cursor·上下文·排障·工具实践
?? Daisy3 小时前
ZCode 添加自定义模型:自定义供应商的字段与易错点(2026-09)
人工智能·ai·ai编程