🐎 第8章 器以载道:Agent SDK 智能体开发套件
📄 一句话概括

结构化输出解决一个老问题:模型说的话,程序读不懂。给一份 schema,它就得照着填,填不对就重来。 实测四份输出只过一份:缺字段、类型不对、枚举值超出范围。 而错误信息的一半价值在措辞——它要能指导下一轮。

📝 本章和前面几章的关系

上一节讲怎么拦,这一节讲怎么收:让它的输出变成程序能直接用的形状。 这一节真跑了一个最小校验器:四样关键字,四份输出,两份过不了。

一、schema 里最常用的四样

type:这是什么类型。对象、数组、字符串、数字。 required:哪些字段必须有。缺一个就整份不合格。 properties:每个字段长什么样。可以层层嵌套。

enum:只能是这几个值里挑一个。比自由文本好校验得多。 四样就能覆盖大部分场景。剩下的(正则、范围、嵌套引用)等真需要了再加。

二、真跑:四份输出逐个校验

演示里四份:一份字段齐全、一份缺 file、一份 issues 给了字符串、一份 severity 写了「严重」。 结果只过一份。另外三份的原因分别是:缺字段、类型不对、枚举值不在范围内。 这三种恰好是结构化输出最常出的三类错——不是「它不会写」,是「它按自己的理解写了」。

所以校验器不是可选件,它是「把自然语言压成字段」这件事的必要成本。

三、错误信息要能指导重试

「校验失败」这四个字没用。「$.file 缺字段」才有用——模型下一轮就知道补哪个。 所以校验器的价值一半在判断,一半在措辞。 这一点和 8.6 节那个参数校验是同一件事:错误信息是给模型看的指令,不是给人看的日志。

一个实用的检验办法:把错误信息给你同事看,他能不能直接改对?能,就够清楚。

四、重试要有上限

校验不过就重试,但必须设次数上限。否则会转成一个死循环:它每次都犯同一个错。 两次还不过,就该换做法:把 schema 简化、把任务拆小、或者干脆改成自由文本加人工看。 这条和 4.8 节那个「打回循环」是同一个坑,只是换了个位置。

更隐蔽的一种:它每次都「通过」了校验,但填的内容是空的——校验器管形状,不管内容。

五、什么时候别用结构化输出

给人看的结论、需要解释的判断,硬套 schema 会把它压成几个字段,信息反而少了。 判据:这份输出会被程序读吗?会,就上 schema;只是给人看,就别管形状。 还有一类要小心:字段太多。二十个字段的 schema,它填错的概率比十个字段高得多。

所以 schema 要小,宁可分两次调用,也别一次要二十个字段。

六、动手 Demo:四份输出按 schema 校验,两份过不了

结构化输出解决一个老问题:模型说的话,程序读不懂。 这里真跑一个最小校验器:type、required、properties、enum 四样关键字,四份输出逐个查。

SCHEMA = {"type": u"object", "required": [u"file", u"issues"],
          "properties": {"file": {"type": u"string"},
                         "issues": {"type": u"array"},
                         "severity": {"type": u"string",
                                       "enum": [u"high", u"mid", u"low"]}}}

实跑输出:

--------------------------------------------------------------------
四份输出按 schema 校验,两份过不了
--------------------------------------------------------------------

  结构化输出解决一个老问题:模型说的话,程序读不懂。
  给一份 schema,它就得照着填;填不对就重来。

  ① schema 里最常用的四样
  --------------------------------------------------------------------
| 关键字 | 管什么 | 例子 |
|---|---|---|
| type | 这是什么类型 | 对象、数组、字符串、数字 |
| required | 哪些字段必须有 | 缺一个就整份不合格 |
| properties | 每个字段长什么样 | 可以层层嵌套 |
| enum | 只能是这几个值里挑一个 | 比自由文本好校验得多 |

  四样就能覆盖大部分场景。剩下的(正则、范围、嵌套引用)等真需要了再加。

  ② 真跑:四份输出逐个校验
  --------------------------------------------------------------------
| 这一份 | 它长什么样 | 结果 | 第一个问题 |
|---|---|:---:|---|
| 第 1 份 | 四个字段齐全 | 通过 | 格式全对 |
| 第 2 份 | 缺 file 字段 | 不通过 | $.file 缺字段 |
| 第 3 份 | issues 给了字符串 | 不通过 | $.issues 应该是数组 |
| 第 4 份 | severity 写了「严重」 | 不通过 | $.severity 只能是 high、mid、low |

  四份里 1 份通过。三份不通过的原因各不相同:缺字段、类型不对、枚举值不在范围内。

  ③ 错误信息要能指导重试
  --------------------------------------------------------------------
  「校验失败」这四个字没用。「$.file 缺字段」才有用——模型下一轮就知道补哪个。
  所以校验器的价值一半在判断,一半在措辞。
  这一点和 8.6 节那个参数校验是同一件事:错误信息是给模型看的指令。

  ④ 重试要有上限
  --------------------------------------------------------------------
  校验不过就重试,但必须设次数上限。否则会转成一个死循环:它每次都犯同一个错。
  两次还不过,就该换做法:把 schema 简化、或者把任务拆小。
  这条和 4.8 节那个「打回循环」是同一个坑,只是换了个位置。

  ⑤ 什么时候别用结构化输出
  --------------------------------------------------------------------
  给人看的结论、需要解释的判断,硬套 schema 会把它压成几个字段,信息反而少了。
  判据:这份输出会被程序读吗?会,就上 schema;只是给人看,就别管形状。
  ! 这里的校验器是最小实现,真实库支持的关键字多得多,也更严格。

①四样关键字够用:type(什么类型)、required(哪些必须有)、properties(每个字段长什么样)、 enum(只能是这几个值之一)。 ②四份只过一份,三种错分别是:缺字段、类型不对、枚举值超出范围。 这三类恰好是结构化输出最常出的错——不是「不会写」,是「按自己的理解写了」。 ③错误信息要能指导重试:「$.file 缺字段」比「校验失败」有用得多。 校验器的价值一半在判断,一半在措辞。 ④重试要有上限:两次还不过就该换做法(简化 schema、拆小任务、或者改回自由文本)。 更隐蔽的是「通过了但内容为空」——校验器管形状,不管内容。

30 秒实验

  1. 把 required 里的 file 删掉,重跑一遍。通过几份? 这说明「必填」这一项怎么定,直接决定校验的松紧。
  2. 给 issues 加一条「最多十项」的限制,重跑一遍。手写的校验器能查吗?要查得改哪儿?
  3. 把 schema 从四个字段扩到十个(随便加),再想一下:它填错的概率会怎么变?

常见误区

  1. 错误信息只写「校验失败」。模型不知道该改哪儿,重试还是错。
  2. 重试不设上限。它每次都犯同一个错,转成死循环。
  3. 以为通过了校验就对了。校验器管形状,不管内容是不是空的。
  4. schema 一次要二十个字段。填错的概率比十个字段高得多,宁可分两次。
  5. 给人看的结论也硬套 schema。信息被压成几个字段,反而少了。
自测三问(点开对答案) 1. schema 里最常用的四样关键字是什么? —— type、required、properties、enum。四样能覆盖大部分场景。 2. 为什么错误信息的措辞很重要? —— 因为它是给模型看的指令。「$.file 缺字段」能让它下一轮改对,「校验失败」只能让它瞎猜。 3. 重试为什么要设上限? —— 不设上限会转成死循环:它每次都犯同一个错。两次不过就该换做法。

输出能读了,下一节把这些拼起来:做一个能被别人调用的服务。

相关笔记