深色模式
07 · 结构化输出与验证
让 Agent 的输出能被程序消费
📚 系列导航:这是《Agent 开发进阶》的第 7 篇(共 24 篇)。上一篇你把上下文整理成了四层结构,这一篇管住另一端——让模型的输出能被下游程序放心地吃进去。下一篇:08 · 规划与任务分解。
你的 agent 在聊天窗口里表现很好,答案又准又客气。可一接自动化流程就露馅:下游代码要的是 result["price"],模型回的是"好的,价格大约是 99 元哦"。demo 和产品的分界线之一就在这:demo 的输出给人看,产品的输出给程序吃,而程序不吃"大约"和"哦"。这一篇只干一件事:给 agent 的输出上契约——校验、重试、降级,三层防御一次配齐。
看完这一篇,你会拿到:
- 一个可复用的"校验-重试-降级"防御函数(十行伪代码)
- 一条重试纪律:带着错误信息重试,而不是原样重发
- 一份结构化输出能力选型顺序(按档位归纳,2026 上半年,以官方为准)
- 一次 100 次调用零解析失败的实测验收
01 自由文本撑不起自动化
单次调用格式正确率 99.7%,听起来很高?换个算法:千分之三的格式漂移,日调用一万次,就是每天 30 个解析事故。
好比流水线上的机械臂收包裹:它只认贴在固定位置的条形码,包裹再精美、码贴歪了整条线就停。下游程序就是那只机械臂——字段名、类型、取值范围,一个都不能含糊。落地做法:凡是输出会被代码消费的地方,先写 schema 再写 prompt。schema 就是你和模型签的合同。
💡 一句话总结:给人看的输出可以自由发挥,给程序吃的输出必须先签合同。
02 能力选型:有原生用原生
各家模型的结构化输出能力按四个档位归纳(2026 上半年,以官方文档为准):原生 schema 约束(生成端保证符合 schema,最稳)>工具调用参数(天然结构化,可借作输出通道)>JSON 模式(合法 JSON 但不保证字段)>纯 prompt 约束(必须配校验)。有原生用原生,没有就退一档加外部校验兜底。但不管哪档,03 节的防御不能省——原生保证格式合法,不保证内容合理。
💡 一句话总结:能力选最高档,防御按最低档配。
03 三层防御:校验、重试、降级
python
def call_with_guard(prompt, schema, max_retry=2):
for _ in range(max_retry + 1):
raw = llm(prompt) # 伪代码,接口以官方文档为准
ok, errors = validate(raw, schema)
if ok:
return parse(raw)
prompt += f"\n上次输出未通过校验:{errors}\n只返回修正后的 JSON"
return fallback(raw) # 降级:默认值或转人工队列预期效果:要么拿到合法对象,要么明确走降级——脏数据永远进不了下游。最容易做错的是重试:必须带上错误信息——原样重发,模型多半在同一处再摔。降级也要设计:给默认值、进人工队列还是明确报错,看业务容错成本。
💡 一句话总结:校验拦住脏数据,重试带着错误单,降级兜住最后一层。
04 反模式:用正则从散文里抠 JSON
模型输出一段客套话夹着 JSON,你写正则抠中间——短期能跑,所以格外害人。我干过:撑了两周,模型小版本更新后客套话位置变了,解析全崩,半夜报警。病根是契约签在了消费端而不是生成端:你在猜模型的输出习惯,而习惯没有保证。实在只能 prompt 约束,也要"只输出 JSON"加校验兜底,而不是接受散文再考古。
💡 一句话总结:契约要签在生成端——靠正则考古的系统,活不过一次模型更新。
05 动手:给 04 篇的循环加三层防御
任务:① 给第 04 篇手写循环的最终答案定 schema(dict 声明字段和类型即可);② 套上 03 节的防御函数;③ 准备 100 次调用,含至少 10 条刁钻输入(超长、带表情、诱导闲聊);④ 记录三个数字:首次通过数、重试救回数、降级数。
验收标准:100 次调用零解析失败——"零失败"包括走降级的(降级是设计内行为,不算失败)。
两种结果都正常:全绿,把防御函数固化成所有项目的标配;若重试也救不回某几条,看降级日志——多半是 schema 嵌套太深或字段语义含糊,拍平结构、加字段说明再测。
06 小结
你现在应该能:为输出定义 schema 契约、写出带错误反馈的校验重试、给失败兜好降级路径。
至此地基四件齐了:循环、工具、上下文、输出。下一篇 08 · 规划与任务分解,治"agent 一头扎进任务就迷路"的病。
📎 实战案例
一个日调用一万次的抽取服务,格式正确率 99.7%,团队觉得够高——千分之三的漂移就是每天 30 个解析事故,下游表每周都要人工修。加上三层防御(schema 校验、带错误反馈的重试、降级)后,连续 100 次调用零解析失败,人工修数班次取消。
踩坑
- 我用正则从散文里抠 JSON 撑了两周,模型小版本更新后全崩——契约要在生成端约束,不是消费端考古。
- 我曾无限重试同一 prompt,5 次全摔在同一个字段上——重试带上错误反馈后,基本 2 次内就过。
- 我信过"模型肯定会按格式来":千分之三漂移 × 日一万次 = 每天 30 个事故——概率面前,"基本没问题"就是天天有问题。