返回学习路线/阶段 02 · 让模型稳定地完成任务
LESSON 05 / 18

结构化输出与校验

让模型输出 JSON,并在进入业务逻辑前检查格式与语义。

30 分钟 · 含动手练习入门JSON Schema校验

学完这一课,你将能够

  • 设计一个最小输出契约
  • 校验字段类型、范围与额外字段
  • 处理非法输出而不是默默修补

学习目标

  • 为模型输出设计一个最小契约。
  • 区分 JSON 解析、类型校验和业务校验。
  • 在输出不合法时提供明确的错误分支。

前置要求:理解 Python 字典、JSON 和异常。 本节所有代码使用标准库,完全离线运行。

结构化不等于正确

下面两个输出都可能是合法 JSON:

{ "category": "billing", "summary": "重复扣费", "needs_clarification": false }
{ "category": "unknown", "summary": 42, "needs_clarification": "no" }

只有第一个符合上一课的字段契约。 即使格式和类型都符合,摘要仍然可能捏造事实。 因此要把检查分成三层:语法、结构、语义。

先写最小 Schema

JSON Schema 可以被服务端校验器使用;部分模型服务也支持以它约束输出。 具体支持的关键字和严格模式限制要查所选服务当前文档。

{
  "type": "object",
  "properties": {
    "category": { "type": "string", "enum": ["billing", "technical", "other"] },
    "summary": { "type": "string", "minLength": 1, "maxLength": 40 },
    "needs_clarification": { "type": "boolean" }
  },
  "required": ["category", "summary", "needs_clarification"],
  "additionalProperties": false
}

字段越多,需要满足的条件越多。 如果下游只需要类别和摘要,不必让模型输出十几个无用字段。 新增字段时要考虑旧数据如何迁移,并记录契约版本。

不依赖第三方包的完整校验器

以下代码手工校验本例契约,不是通用 JSON Schema 实现。 保存为 validate_output.py 并运行。

import json

def validate(raw):
    try:
        data = json.loads(raw)
    except json.JSONDecodeError:
        return {"ok": False, "error": "invalid_json"}

    required = {"category", "summary", "needs_clarification"}
    if not isinstance(data, dict) or set(data) != required:
        return {"ok": False, "error": "invalid_fields"}
    if data["category"] not in ("billing", "technical", "other"):
        return {"ok": False, "error": "invalid_category"}
    if not isinstance(data["summary"], str):
        return {"ok": False, "error": "invalid_summary_type"}
    if not 1 <= len(data["summary"].strip()) <= 40:
        return {"ok": False, "error": "invalid_summary_length"}
    if type(data["needs_clarification"]) is not bool:
        return {"ok": False, "error": "invalid_boolean"}
    return {"ok": True, "value": data}

samples = [
    '{"category":"billing","summary":"重复扣费","needs_clarification":false}',
    '{"category":"billing","summary":42,"needs_clarification":false}',
    '{"category":"other","summary":"待确认","needs_clarification":"yes"}',
    '这段话不是 JSON',
]
for sample in samples:
    print(validate(sample))

预期第一条通过,后三条分别报告类型、布尔字段和 JSON 语法错误。 把这些错误码保存进日志,比把原始内容全部展示给最终用户更容易统计。

不要悄悄把错误转换成成功

把字符串 "false" 转成布尔值,很可能得到你不期望的结果。 直接删除未知字段,也可能掩盖契约已经变化的问题。 根据场景明确选择拒绝、有限次纠正请求或交给人工检查。

如果使用纠正请求,只发送需要修正的字段与校验错误,并设置最多一次或两次。 纠正后仍要重新校验,不能假设第二次一定正确。 预算耗尽时返回“无法生成符合格式的结果”,而不是无限循环。

语义检查留在哪里

摘要是否忠实于原文,不能仅靠 JSON Schema 判断。 可以用固定规则检查关键数字、使用人工标注的评估集比较,或增加独立审核步骤。 涉及工具执行时,权限、账户归属和余额等条件必须由可信业务系统验证。 模型给出一个合法的账户 ID,不代表它有权访问该账户。

练习与验收

增加五个输入:空摘要、额外字段、缺失字段、JSON 数组、超过 40 字的摘要。 为每个输入写出预期错误码,再运行程序核对。

  • 有效对象可以进入后续业务逻辑。
  • 非法对象不会被默默修补后执行。
  • 错误结果保持统一结构。
  • 能说出一种“格式正确但事实错误”的例子。

延伸阅读

下一课会讨论模型应该看到多少资料,以及如何选择相关上下文。

让这一课,真正成为你的收获

完成练习后标记完成,也可以随时回来复习。

笔记与进度保存在当前浏览器,无需登录

AgentStudy · Learn by building.以理解为起点,以作品为答案