让输出可以被检查:字段、契约与校验

说明复杂任务为什么应当要求固定字段的输出,数据契约包含哪些内容,以及为什么格式合法仍然需要逐条校验。

  • 结构化输出
  • 数据契约
  • 校验
  • 资料整理

Reading Guide

先问一个问题

什么时候应当要求结构化输出,以及结构化之后还需要做什么?

适合读者
需要用 AI 产出可复用数据而非一次性文字的读者
阅读基础
task-definition-and-acceptance

Key Points

先记住这些

  1. 散文式输出只能整体接受或整体重来,结构化输出可以逐条判定、逐条返工。
  2. 凡是结果要被再次使用的任务,都应当先定字段再动手。
  3. 数据契约不只是字段名,还包括取值范围、必填与否、缺失如何表示、不确定如何标注。
  4. 受约束的生成能保证格式合法,但保证不了内容为真,两者是不同层次的问题。
  5. 缺失值必须有专门表示,否则模型会用貌似合理的内容把空位填满。
  6. 校验应当分三层:格式、字段规则、与原文的一致性;前两层可自动,第三层须抽样人工完成。

Terms

几个必要概念

数据契约

对一批数据的字段、类型、取值范围与缺失表示所作的书面约定。

它使不同批次、不同时间、不同模型产出的结果可以合并,也使错误可以定位到具体字段。
模式校验

用形式化的模式描述文件自动检查数据是否符合约定结构。

它能在几秒钟内挡下所有结构性错误,把人的精力留给内容判断。
受约束生成

在生成过程中限制候选片段,使输出必然符合给定语法或模式。

它解决了输出格式不合法的问题,但不解决字段填得对不对的问题。

为什么要放弃整段文字

让模型写一段关于某书版本源流的说明,读起来往往很好,但很难用。你无法回答其中哪几句有依据、哪几句是推断,也无法把它与另外二十本书的说明并列比较。改成固定字段之后,同样的内容变成若干条记录,每条都有书名、版本、年份、依据、可信度、备注。这时候每一条都能单独判定真伪,也能单独返工。

判断标准很简单:这份输出是给人读一遍就算完,还是要被再次使用。前者可以是散文,后者必须有结构。本站的多数工作属于后者,因为书目、年表、异文表都要长期累积、反复比对。

契约要写到什么程度

  • 字段名与含义:每个字段一句话说明,避免同一字段在不同批次里被理解成不同东西。
  • 类型与取值范围:年份是四位整数还是可含「约」字,朝代纪年与公元纪年分列还是合一。
  • 必填与选填:哪些字段缺失即视为该条不合格。
  • 缺失的表示:明确规定用空值表示未知,并禁止用推测填补。
  • 不确定的表示:单设一个字段记录存疑理由,而不是把疑问写进正文字段。
  • 来源字段:每条至少记录一个可回溯的出处标识。
  • 版本号:契约本身要有版本,改字段时旧数据才知道如何迁移。
S1

把这份约定写成一个形式化的模式文件,就可以自动检查数据是否合规。这类模式描述有成熟的公开规范可循,不必自创。产品层面也有相应支持,可以在生成时就要求输出符合给定模式,从而消除括号不配对、字段名拼错一类问题。S1S2S3

格式合法不等于内容正确

受约束的生成方法可以在解码时限制候选,使输出必然满足给定语法。这是一项确凿的技术能力,但它作用于形式而非内容:一个完全合法的记录,年份可以是错的,来源可以是虚构的,归并可以是错的。把「通过了模式校验」当成「这批数据可用」,是把两个层次混为一谈。S3

因此校验应当分三层。第一层是格式,用模式文件自动完成。第二层是字段规则,用简单脚本完成,例如年份必须落在著者生卒年之间、必填的来源字段不得为空、同一编号不得出现两次。第三层是与原文的一致性,无法自动完成,只能抽样人工核对。三层之中,最容易被跳过的是第二层,而它的性价比最高。

字段设计的几条经验

字段设计没有通行标准,但有几条经验反复被验证。第一条是把「事实」与「判断」分开成两个字段。书目条目里的出版年份是事实,「疑为伪托」是判断;两者混在一栏,日后就无法区分哪些内容需要重新审视。分开之后,事实字段可以由程序核对,判断字段可以按人名与日期署名,成果的可信层次一目了然。

第二条是为每一个判断字段配一个依据字段。只写结论不写依据的判断,半年之后连自己都无从复核。依据不必长,一句话或一个位置标识即可,但必须能顺着它回到原文。第三条是保留一个原文摘录字段。凡是不好归类、体例特殊、语义含混的内容,宁可原样留下,也不要硬塞进某个既有字段。抹平的信息不可恢复,冗余的信息随时可以再整理。

  • 事实与判断分列,判断字段附依据与署名。
  • 保留原文摘录字段,宁可冗余不可抹平。
  • 缺失、不适用、待考三种情形要有各自不同的表示,不可共用一个空值。
  • 枚举型字段的取值范围写死,新增取值须先改契约再改数据。
  • 标识字段一律用不带含义的编号,不要用书名或人名当主键。
  • 时间字段区分原始纪年与换算后的公元纪年,两者并存不互相覆盖。
  • 字段数量宁少勿多,宁可日后拆分,不要一开始就设十个含义相近的细分栏。
S1

契约的演进同样需要规矩。字段一旦被使用就不宜改名,需要新含义时应当新增字段并把旧字段标为废弃,让两批数据可以共存一段时间。契约文件本身要有版本号,每条数据记录它是按哪一版产生的。这些做法看起来繁琐,但只要项目跨过一年,它们就会开始省时间;而在跨过一年之前,任何未经版本管理的字段改动都会在日后变成一批来历不明的数据。S1

字段设计不必一次做对,但要在做错时能够察觉。一个简单的检验是看待考条目的分布:若某个字段的缺失率长期居高不下,通常说明它要么定义太细、要么材料本身根本不提供这项信息,应当合并或删去。反之,若原文摘录字段被大量使用,则说明现有字段没能覆盖材料的实际形态,该考虑新增。让数据自己反过来指出契约的问题,比凭空设想更省力。

Conclusion

结语

结构化的意义不在于好看,而在于把「这份东西对不对」拆成许多个可以分别回答的小问题。能被分别回答,才谈得上校验;能被校验,才谈得上累积。

仍可追问

  • 受约束生成是否会在某些任务上损害内容质量,公开研究结论不一,本文未作断言。
  • 针对古籍异文、注疏关系的字段设计尚无通行标准,本站的契约仍属自拟。

参考资料

  1. S1

    JSON Schema 组织.JSON Schema Specification.2026

    查看来源
  2. S2

    Tim Bray (编).RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format.2017

    DOI / ISBN:10.17487/RFC8259查看来源
  3. S3

    Brandon T. Willard, Rémi Louf.Efficient Guided Generation for Large Language Models.2023

    DOI / ISBN:arXiv:2307.09702查看来源