PS:关于Agent应用渲染的问题,A2UI 协议就是一个不错的落地的实现。感兴趣阅读完这篇文章和Agent应用的渲染问题后,可以去了解一下。
讨论 Agent应用的渲染问题 时,我一开始关心的是:模型怎么知道有哪些组件,又怎么生成组件描述?如果 function calling 能让模型输出函数名和参数,界面描述是否也通过类似的方式生成?
当答案落到"把组件规范和示例告诉模型,让它生成 JSON"时,我又有了一个疑问:
把组件规范告诉模型,模型就一定能吐出符合规范的 JSON 吗?这种可靠性也是模型训练带来的吗?
这里需要拆开两个问题:模型有没有能力按规范生成,以及系统有没有机制限制它偏离规范。
提示词中的"必须",怎样才能成为程序约束?
设想我们希望显示一个人的姓名,给模型这样的要求:
text
使用 Text 组件显示"张三"。
component 必须是 Text,text 必须是字符串。
只输出 JSON,不要解释。
期望大模型输出的结果很简单:
json
{"component": "Text", "text": "张三"}
通用模型已有理解指令、组织 JSON 和参考示例的能力,训练为这些行为提供了基础。提示词则补充这次任务的规则;模型不必事先训练过我们的组件库,就可以理解提示词吐出这个 Text。
但如果只是普通文本生成,模型仍然根据上下文预测后续 token。尽管在提示词中约束了text"必须是字符串",但如果没有后续程序里的类型检查,它仍可能漏掉 text,或者把值写成数字。Claude 的官方文档也明确列出了单靠提示词时可能出现的语法、必填字段和类型错误。结构化输出说明
因此,训练和提示词能提高遵守规范的成功率;要回答"一定符合吗",还得看模型的能力是否支持,也就是模型对输出JSON Schema是否支持。
同一份规则,放进提示词和交给解码器有什么区别?
我们可以用 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",也应继续核对工具参数是否启用了严格约束。限制与异常说明
我列了一些国内支持程度情况:
| 模型 | 是否支持 | 文档 | 备注 |
|---|---|---|---|
| DeepSeek | ✅ | api-docs.deepseek.com/zh-cn/guide... | |
| Qwen | ✅ | platform.qianwenai.com/docs/develo... | Qwen3.7-Plus 系列、Qwen3.7-Flash 系列、Qwen3.7-Max 系列、Qwen3.8-Max 系列、Qwen3.8-Flash 系列 |
| 火山引擎 | ✅ | console.volcengine.com/ark/region:... | |
| 智谱 GLM | ✅ | docs.bigmodel.cn/cn/guide/ca... | |
| 月之暗面 Kimi | ✅ | platform.kimi.com/docs/guide/... | K3、K2.7 Code 支持较完善 |
格式正确以后,还有什么可能错?
即使收到的对象完全符合上面的 Schema,也可能是:
json
{"component": "Text", "text": "李四"}
两个字段都存在,类型正确,组件名称也正确。但用户要求的是"张三"。Schema 能限定 text 是字符串,却不能凭这个类型要求判断姓名是否符合用户需求。 如果把已知姓名也写成 const,当然能进一步限制;保证范围随我们实际写入、且 API 支持的规则而变化。
界面生成中的"正确"至少有四种不同含义:
| 检查 | 一个失败例子 | 谁来判断 |
|---|---|---|
| JSON 语法 | 引号或括号没闭合 | JSON 解析器 |
| Schema | text 是数字,或缺少必填字段 |
Schema 校验器;支持的规则也可用于约束解码 |
| 组件关系 | Column.children 指向不存在的组件 ID |
结合界面状态的应用校验 |
| 用户需求 | 用户要"张三",界面显示"李四" | 业务数据核对与任务评估 |
例如,children 的每一项都是字符串,只说明 ID 的类型符合要求;这个 ID 对应的组件是否已存在,还得查询界面状态。Gemini 的官方文档同样提醒:结构化输出之后,应用仍应校验值,并处理符合 Schema 但语义错误的结果。值校验与语义边界
回到 A2UI:协议把责任放在哪里?
A2UI^1^ 定义了用 JSON 消息描述和更新界面的方式。协议给出了数据应当长什么样,模型是否每次都能生成合格消息,则取决于接入方式和校验流程。
这里的版本差异很有启发性:A2UI v0.8.1 偏向结构化输出,v0.9 转向 Prompt First ,把规范和示例放进提示词。官方解释,这样可以采用更丰富、较少受结构化输出 Schema 子集限制的表达方式。版本演进说明
这项选择也把生成后校验放到了显眼的位置。v0.9 规范明确给出"提示 → 生成 → 校验"的循环:生成结果通过校验后交给客户端;失败时,把具体错误反馈给模型修正。官方生成流程
在这个场景里,我会先把渲染入口收紧:只接受通过结构和应用规则校验的消息。遇到 children 引用了不存在的 ID,就返回具体字段路径和错误原因,限定修正次数;仍然失败时显示预设界面。通过拒收记录,可以验证错误消息有没有进入渲染器;仅凭一次重试成功,不能证明之后每次都会成功。
重新看最初的问题,我现在会追问:这份规范究竟只是模型读到的一段文字,还是已参与生成时的限制?输出之后,又有哪些错误能被程序检查出来? 回答清楚这两个问题,才能知道系统的可靠性具体来自哪里。
Footnotes
- A2UI v0.9 规范定义服务端向客户端发送的界面描述消息,客户端据此构建和更新界面。本文用该版本讨论提示优先与校验机制;官方站点在资料核对日 2026-10-10 已将 v0.9.1 列为当前版本。 ↩