[控场AI]
· 6 分钟阅读· 3,115 字

JSON Schema 校验通过,Agent 输出却是空的?

JSON Schema 校验通过,Agent 输出却是空的?

JSON Schema 只验证数据形状,无法识别重复列表、占位符等“合法但空洞”的 Agent 输出,需用七个可测量信号在后果发生前拦截虚假绿色。

文章指出,Agent 输出通过 JSON Schema 校验却内容空洞是常见故障:五个完全相同的发现、TODO 占位符、空数组、截断文本等都能合法通过。Schema 只检查数据形状,不判断语义,导致“合法”被误当作“好”。为此作者提出七个可机械测量的信号——相同列表项、占位符值、空白容器、截断标记、低多样性文本、Schema 默认值未改动、枚举卡在首值——每个都对应可展示的证据。信号汇总为 CONTENT_PRESENT、THIN_CONTENT、VALID_BUT_EMPTY 三种裁决,强调裁决需基于证据而非感觉,并应在付款、合并等后果发生前运行,同时承认通过检查不等于内容有意义。

当 Agent 被要求找出合同中的五个漏洞时,它返回了五个发现。JSON 完全合法:五个对象、所有必填字段齐全、严重等级来自允许的枚举、没有多余键。jsonschema.validate 返回 None——没有异常,没有错误路径。流水线把这个步骤标记为绿色通过。

但仔细看那五个发现:它们完全相同。

Agent 什么也没做,却用完全合法的格式报告了成功。这不是罕见的故障模式,而是最常见的一种,因为它是唯一一种在数据的“形状”中不留痕迹的失败。

rss source: Why JSON Schema validates empty agent output

形式不等于内容

Schema 校验只回答一个问题:这个数据的形状是否符合我们的约定?类型、必填字段、枚举、格式、嵌套——每一项检查都关于信封,没有一项关于里面的信。

这不是 JSON Schema 的缺陷。Schema 从未被问过“这个有意义吗”。我们验证了工件“是什么”——一个数据结构——然后悄悄把“合法”当成了“好”。这是两个不同的断言,而它们之间的鸿沟正是 Agent 生存的地方。

JSON Schema 本身是一套用于描述和校验 JSON 文档结构的词汇表,由 IETF 在多个 RFC 中标准化(核心规范为 RFC 8259 的配套草案,现行为 2020-12 版本)。它支持 type、properties、required、enum、pattern、minimum 等关键字,能够精确约束数据的类型、必填性、取值范围和嵌套关系。但 Schema 的设计目标始终是“互操作性”——让不同系统对同一份数据的形状达成一致,而非判断数据在业务上是否成立。例如 {"status": "ok"} 和 {"status": "ok", "detail": ""} 可能都符合 Schema,但后者在语义上可能是残缺的。理解这一点是读懂本文的前提:Schema 是信封规格,不是信件内容审查。

“空”的真实模样

缺失输出很容易处理:没有东西可验证,响应失败,错误很响亮。有趣的情况都是“有存在而无信息”——工件完整成型,却什么也没说:

  • 五个列表项完全相同——“五个发现”其实是一个发现复制了五份;
  • "message": "TODO"——字段存在是因为 Schema 要求,而不是因为有人填了它;
  • "recommendations": []——允许空数组的 Schema 在空数组上报告成功;
  • 一个必填字段被小心地留在 Schema 的默认值上;
  • 一个摘要句子说到一半就结束了,因为模型触发了 token 上限却从未提及;
  • 一段 40 个词的段落,只有 9 个词是独特的。

以上每一项都能通过校验。如果审查者是另一个扫一眼“看起来对不对”的 LLM,大多数也能通过审查。

七个可测量的信号

关键动作是拒绝不可证伪的检查。“这个响应感觉没意义”不是测试——你没法把它展示给别人并争论结果。但“这个必填字段完全由单词 example 组成”是关于字节的事实。

所以我们在可测量的地方测量空——七个信号,每一个都可检查、可质疑:

  1. 相同列表项(uniform_list):如果列表中超过一半的项相同(≥90% 视为严重),这个列表就是列表的占位符。单个重复可能是诚实的;大多数重复是工作的缺失。
  2. 占位符值(stub_value):TODO、TBD、placeholder、example、test、lorem ipsum、n/a——包括中文对应词。整个字段都是占位符时严重性高,占位词出现在真实文本中时严重性低。
  3. 空白和空容器(blank_string、empty_list、empty_object):纯空白字符串和 []/{} 能满足 Schema,但满足不了任何其他东西。
  4. 截断标记(truncation):输出以 ...、[truncated]、continued 结尾——模型被切断了却没说明。
  5. 低多样性重复(repetitive_text):对于 12 个词以上的文本,如果独特词少于约三分之一,那就是填充物。
  6. Schema 默认值未改动(schema_default):一个必填字段恰好持有 Schema 自己的 default,意味着没人填它。
  7. 枚举字段卡在第一个值(enum_first_value):一个分类字段总是第一个枚举选项,就是伪装成决策的猜测。

这些都不检查语义。它们检查缺席的机械指纹——而因为每个指纹都是一个测量,每个都能展示给审查者:这是字段,这是值,这是它触发的原因。

上述信号中,uniform_list 和 repetitive_text 都依赖“重复度”这一统计量,但二者检测的粒度不同:前者针对结构化数组的元素级重复,后者针对自然语言文本的词级多样性。词级多样性通常用独特词比例(unique token ratio)衡量,即独特词数除以总词数。对于短文本,该比例天然偏高;对于 12 个词以上的文本,若比例低于约三分之一,则说明大量词汇在重复堆砌,常见于模型陷入循环或复读机模式。类似地,schema_default 信号之所以有效,是因为 Schema 的 default 关键字本意是“建议值”而非“必填值”,当一个必填字段恰好等于默认值时,极大概率是生成端未真正填充,而是让序列化库或模板自动补全。

裁决,而非感觉

信号汇总为三种裁决:

  • CONTENT_PRESENT:以上信号均未触发。
  • THIN_CONTENT:有一些中等信号,值得一看。
  • VALID_BUT_EMPTY:形式没问题,内容可疑。

第三个名字是刻意的。它意味着“形式没问题,内容可疑”——而不是“内容缺失”。怀疑需要人,裁决需要证据。工具展示两者,且不假装任何一方。

还有一个诚实的限制,写在 README 里因为很重要:没有信号不证明有意义。它只意味着粗略检查没有触发。一个响应可能以我们没想到要测量的七种方式含糊、循环、无用。通过是对检查的陈述,不是对内容的证书。

在流水线中的位置

这个检查该放在 Agent 输出不再是“回复”而开始成为“结果”的边界——在它被付款、转发、合并或作为已完成工作展示给客户之前。

那里有两个属性很重要。第一,它必须在后果之前运行——在退款处理后才发现五个相同的发现是事故报告,不是防护。第二,裁决必须是证据形状的:如果你要拒绝 Agent 的输出,你欠它一个具体的、可争议的理由——uniform_list: 5 of 5 items are identical——而不是一种感觉。

你无法重试你无法检测的东西。一个空的工件通过校验就是一个虚假的绿色,而虚假的绿色是唯一能幸存于所有仪表盘的故障模式。

如果你想看它运行:attest 在四层检查工件——形式和空(本文)、提示注入形状、来源签名和权限——在任何下游信任它之前。代码是开源的,演示只是一个表单,而空层是四十多个测试,正好对应上面的信号。

#JSON Schema#Agent 输出校验#VALID_BUT_EMPTY#空内容检测#LLM 输出质量#流水线防护#uniform_list
分享:

相关推荐