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

形式不等于内容
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 组成”是关于字节的事实。
所以我们在可测量的地方测量空——七个信号,每一个都可检查、可质疑:
- 相同列表项(
uniform_list):如果列表中超过一半的项相同(≥90% 视为严重),这个列表就是列表的占位符。单个重复可能是诚实的;大多数重复是工作的缺失。 - 占位符值(
stub_value):TODO、TBD、placeholder、example、test、lorem ipsum、n/a——包括中文对应词。整个字段都是占位符时严重性高,占位词出现在真实文本中时严重性低。 - 空白和空容器(
blank_string、empty_list、empty_object):纯空白字符串和[]/{}能满足 Schema,但满足不了任何其他东西。 - 截断标记(
truncation):输出以...、[truncated]、continued结尾——模型被切断了却没说明。 - 低多样性重复(
repetitive_text):对于 12 个词以上的文本,如果独特词少于约三分之一,那就是填充物。 - Schema 默认值未改动(
schema_default):一个必填字段恰好持有 Schema 自己的default,意味着没人填它。 - 枚举字段卡在第一个值(
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 在四层检查工件——形式和空(本文)、提示注入形状、来源签名和权限——在任何下游信任它之前。代码是开源的,演示只是一个表单,而空层是四十多个测试,正好对应上面的信号。
相关推荐

ClaudeMem:让Claude、ChatGPT、Grok共享记忆的AI Agent记忆层
ClaudeMem为Claude、ChatGPT、GrokBot等多个AI Agent提供共享记忆,解决跨模型协作时的上下文断裂问题。本文解析其本地优先架构、安装接入方式、实际工作流用法,以及创作者对未来单一编排者Bot的设想。

OpenAI Codex CLI实战指南:终端里的开源AI编程代理
OpenAI Codex CLI是一款运行在终端里的开源AI编程代理。本文梳理其2025年发布、2026年桌面版及与ChatGPT合并的演进脉络,并用三个步骤说明安装配置、发起任务与审查迭代的实操路径。

AI入门课程实测:ChatGPT、Gemini与AI智能体实战
一套面向初学者的完整AI课程实测解读:覆盖ChatGPT、Gemini、Notebook LM、Manus与DeepSeq,包含18000条数据集分析、AI智能体搭建网站等实操,讲解大语言模型原理与局限,适合营销、金融、数据、产品与软件岗位。