程云来 / 杭州
返回文章·AI 工程
·约 7 分钟

告诉模型输出 JSON,就一定能得到正确的 JSON 吗?

从 A2UI 的一个疑问出发,分清模型训练、提示词、约束解码和应用校验各自负责什么,以及结构化输出能保证到哪一步。


讨论 A2UI 时,我一开始关心的是:模型怎么知道有哪些组件,又怎么生成组件描述?如果 function calling 能让模型输出函数名和参数,界面描述是否也通过类似的方式生成?

当答案落到“把组件规范和示例告诉模型,让它生成 JSON”时,我又有了一个疑问:

把组件规范告诉模型,模型就一定能吐出符合规范的 JSON 吗?这种可靠性也是模型训练带来的吗?

这里需要拆开两个问题:模型有没有能力按规范生成,以及系统有没有机制限制它偏离规范。

提示词中的“必须”,怎样才能成为程序约束?

设想我们希望显示一个人的姓名,给模型这样的要求:

text
使用 Text 组件显示“张三”。
component 必须是 Text,text 必须是字符串。
只输出 JSON,不要解释。

期望结果很简单。下面只是教学用的组件对象,省略了 A2UI 的消息外层和界面创建流程:

json
{"component": "Text", "text": "张三"}

通用模型已有理解指令、组织 JSON 和参考示例的能力,训练为这些行为提供了基础。提示词则补充这次任务的规则;模型不必事先训练过我们的组件库,才有可能使用这个 Text。

但如果只是普通文本生成,模型仍然根据上下文预测后续 token。“必须是字符串”作为一条指令参与生成,并没有自动变成程序里的类型检查。它仍可能漏掉 text,把值写成数字,或者在 JSON 前面加一句解释。Claude 的官方文档也明确列出了单靠提示词时可能出现的语法、必填字段和类型错误。结构化输出说明

因此,训练和提示词能提高遵守规范的成功率;要回答“一定符合吗”,还得看生成阶段是否真的施加了约束。

同一份规则,放进提示词和交给解码器有什么区别?

我们可以用 JSON Schema 表达刚才的规则。Schema 是描述数据形状的规则,下面要求两个字段都存在,且不接受额外字段:

json
{
  "type": "object",
  "properties": {
    "component": {"type": "string", "const": "Text"},
    "text": {"type": "string"}
  },
  "required": ["component", "text"],
  "additionalProperties": false
}

把它作为文字贴进提示词,作用仍是让模型阅读和遵守。把它交给支持严格结构化输出(Structured Outputs)的 API,才可能进入另一条处理路径。

以 Claude 官方描述的机制为例,服务会把 Schema 编译成语法,在采样时限制输出;这种做法称为约束解码(Constrained Decoding)。可以把它理解为:生成每一步时,根据已经生成的内容,排除无法继续满足语法规则的 token。模型仍负责从允许的选项中选择内容,解码器负责缩小选择范围。机制说明

这样,“会按规则写”和“生成时受到规则限制”就有了明确区别。约束解码发生在推理阶段,不能把它的作用全部归功于训练。

不过,具体保证要按供应商的实现判断:API 支持哪些 Schema 特性、有没有已声明的例外、响应是否完整结束,都影响最终结果。拒绝响应或 token 用尽造成的截断仍需单独处理。看到“支持 function calling”,也应继续核对工具参数是否启用了严格约束。限制与异常说明

格式正确以后,还有什么可能错?

即使收到的对象完全符合上面的 Schema,也可能是:

json
{"component": "Text", "text": "李四"}

两个字段都存在,类型正确,组件名称也正确。但用户要求的是“张三”。Schema 能限定 text 是字符串,却不能凭这个类型要求判断姓名是否符合用户需求。 如果把已知姓名也写成 const,当然能进一步限制;保证范围随我们实际写入、且 API 支持的规则而变化。

界面生成中的“正确”至少有四种不同含义:

检查一个失败例子谁来判断
JSON 语法引号或括号没闭合JSON 解析器
Schematext 是数字,或缺少必填字段Schema 校验器;支持的规则也可用于约束解码
组件关系Column.children 指向不存在的组件 ID结合界面状态的应用校验
用户需求用户要“张三”,界面显示“李四”业务数据核对与任务评估

例如,children 的每一项都是字符串,只说明 ID 的类型符合要求;这个 ID 对应的组件是否已存在,还得查询界面状态。Gemini 的官方文档同样提醒:结构化输出之后,应用仍应校验值,并处理符合 Schema 但语义错误的结果。值校验与语义边界

回到 A2UI:协议把责任放在哪里?

A2UI1 定义了用 JSON 消息描述和更新界面的方式。协议给出了数据应当长什么样,模型是否每次都能生成合格消息,则取决于接入方式和校验流程。

这里的版本差异很有启发性:A2UI v0.8.1 偏向结构化输出,v0.9 转向 Prompt First,把规范和示例放进提示词。官方解释,这样可以采用更丰富、较少受结构化输出 Schema 子集限制的表达方式。版本演进说明

这项选择也把生成后校验放到了显眼的位置。v0.9 规范明确给出“提示 → 生成 → 校验”的循环:生成结果通过校验后交给客户端;失败时,把具体错误反馈给模型修正。官方生成流程

在这个场景里,我会先把渲染入口收紧:只接受通过结构和应用规则校验的消息。遇到 children 引用了不存在的 ID,就返回具体字段路径和错误原因,限定修正次数;仍然失败时显示预设界面。通过拒收记录,可以验证错误消息有没有进入渲染器;仅凭一次重试成功,不能证明之后每次都会成功。

重新看最初的问题,我现在会追问:这份规范究竟只是模型读到的一段文字,还是已参与生成时的限制?输出之后,又有哪些错误能被程序检查出来? 回答清楚这两个问题,才能知道系统的可靠性具体来自哪里。

Footnotes

  1. A2UI v0.9 规范定义服务端向客户端发送的界面描述消息,客户端据此构建和更新界面。本文用该版本讨论提示优先与校验机制;官方站点在资料核对日 2026-10-10 已将 v0.9.1 列为当前版本。 ↩