数据截至 (上游 commit f5baf760d23a)
05 · Agent 侧:从目录到合法 UI
本章讲什么: 前面四章都在讲「客户端怎么渲染」。这章看反方向——agent 怎么被引导生成合法的 A2UI。内容主要基于设计文档
blueprints/modules/a2ui_agent.blueprint.md(一份架构/移植指南;原agent_sdks/agent_sdk_guide.md,已迁入 blueprints 体系并按新 API 重写),故凡描述 SDK 行为处,均按「指南所述设计」对待,而非逐行实现核验。
1. Agent SDK 负责什么
协议规范把 A2UI 的使用收敛成一个三步循环(a2ui_protocol.md:1133-1146):
- Prompt: 把「想要的 UI + A2UI schema(含目录)+ 合法 JSON 例子」拼进给 LLM 的 prompt。
- Generate: 送给 LLM,拿到生成的 JSON。
- Validate: 按 schema 校验;合法就发给客户端,不合法就把错误回喂给 LLM 让它自我修正。
Agent SDK 就是把这三步工程化的库,职责是目录管理 + 能力协商 + prompt 工程 + 响应解析 + 载荷校验 + 传输封装(blueprints/modules/a2ui_agent.blueprint.md:11-20)。它的架构分两层(blueprints/modules/a2ui_agent.blueprint.md:24-56):底层是解耦的原语(目录表示 → 目录变换器 → 推理格式「prompt 生成器 + 解析器」→ 校验层),上层是封装好的应用门面(CatalogConfig / A2uiGenerator / A2uiRequestProcessor)统一调度。
2. 核心接口
新蓝图把 SDK 拆成规格化的接口族(blueprints/modules/a2ui_agent.blueprint.md:96-616):
| 接口 | 干什么 |
|---|---|
CatalogConfig + CatalogTransformer 家族 | 注册目录并按规则变换——ComponentPruningTransformer / FunctionPruningTransformer 把组件/函数剪到白名单 |
PromptGenerator / InferenceFormat | 生成系统 prompt 片段;格式策略把「prompt 生成器 + 解析器」配成对(标准实现 DirectJson,另有省 token 的 Express DSL) |
Parser(unwrap / compile / parse_response / parse_chunk) | 从 LLM 输出(含流式)解包哨兵标签、编译成合法 A2UI 消息 |
A2uiValidator + Parser.compile 内修复 | 安全网:先修常见格式错,再做深度语义校验;校验器直接复用 a2ui_core,不再由 agent SDK 包一层 |
注意: 旧版指南的
InferenceStrategy、A2uiStreamParser、PayloadFixer三个接口在新蓝图中已移除——prompt 组装归PromptGenerator,流式解析归Parser.parse_chunk,格式修复并入DirectJsonParser.compile。
3. Prompt 工程:token 省着花
SDK 的主要价值是生成动态、省 token 的系统 prompt(blueprints/modules/a2ui_agent.blueprint.md:159-193):
- 剪枝 schema: agent 只用 Text+Button,就用
ComponentPruningTransformer把目录剪到只剩这两个再进 prompt,省 token(blueprints/modules/a2ui_agent.blueprint.md:126-142)。 - 注入 few-shot 例子:
PromptGenerator构造期接收examples(例子描述 → 期望 A2UI 消息的映射),对 LLM 准确率关键;例子还会在create_processor时对照生效目录校验,用了不支持的组件直接报错。 - 标准信封标签:
DirectJsonPromptGenerator.generate产出的指令要求 LLM 把 A2UI 输出包在<a2ui-json>...</a2ui-json>里,以便确定性解析(blueprints/modules/a2ui_agent.blueprint.md:692-697)。
注意:旧版 generate_system_prompt 签名上那组开关(allowed_components、include_schema、include_examples 等)已随接口移除。新设计把同样的裁剪能力拆到两处:组件/函数白名单由 transformer 在目录上完成,allowed_messages 则作为 DirectJsonFormat / DirectJsonPromptGenerator 的构造参数保留(blueprints/modules/a2ui_agent.blueprint.md:673-690)——同一份目录仍能按场景裁出不同大小的 prompt。
4. 流式解析:哨兵标签解包 + 增量自愈
Parser 基类从 LLM 文本流里抓 A2UI 载荷:先 unwrap 按 <a2ui-json> 哨兵标签把响应切成「对话文本 / 原始 A2UI 块」的有序序列,再 compile 把原始块编译成合法消息;流式走 parse_chunk 增量产出(blueprints/modules/a2ui_agent.blueprint.md:241-331):
LLM 流: "这是你的表单 <a2ui-json> [ {...} ] </a2ui-json> 还需要什么?"
│ │
缓冲并透传对话文本 检测到闭合标签 → 解包抽出原始块 → compile
▼ ▼
产出 TextPart 产出 A2uiPart(发给客户端)
机制(blueprints/modules/a2ui_agent.blueprint.md:258-331):
- 解包:
unwrap按哨兵标签切分,标签前的文本当对话内容透传,严格保持「文本/载荷块」的时间顺序。 - 编译:
compile把抽出的原始 JSON 解析并修复成AgentToRendererMessage(修正常见 LLM 格式错,blueprints/modules/a2ui_agent.blueprint.md:722-723)。 - 多块: 一段流里多个块交替产出 text / a2ui part(
parse_response的实现直观展示了这一交替)。 - 流式自愈(新增):
DirectJsonParser.parse_chunk对progressive_keys里的字符串属性做实时「自动闭合/愈合」,流被截断也能产出可用增量(blueprints/modules/a2ui_agent.blueprint.md:744-754)。
5. 校验:比 JSON Schema 更深
校验由 a2ui_core 包的 A2uiValidator 直接承担(agent SDK 不再包一层),除 schema 校验外还有图结构层面的完整性检查(blueprints/modules/a2ui_agent.blueprint.md:333-341):
| 检查 | 防什么 |
|---|---|
| 组件完整性 | ID 唯一、存在合法 root |
| 拓扑与可达性 | 循环引用(含自引用)、从 root 不可达的孤儿组件 |
| 递归深度限制 | 嵌套/函数调用过深,防客户端栈溢出 |
| 路径语法 | JSON Pointer 绑定路径语法 |
| 版本分支 | 校验器原生按协议版本(v0_8/v0_9/v0_9_1/v1_0)分流 |
「拓扑可达性」这条对邻接表模型尤其重要:因为树是靠 ID 引用隐式拼的,很容易出现 root 指不到的孤儿或自指环,光靠 JSON Schema 查不出来,得专门走图遍历。旧版独立的 PayloadFixer 接口已移除,修 LLM 常见的格式错(尾逗号、漏引号、括号没闭合)并入 DirectJsonParser.compile 的「解析并修复」一步(blueprints/modules/a2ui_agent.blueprint.md:722-726)。
6. 封装传输
校验过的 payload 要上网——新蓝图把「传输封装」列为 SDK 职责之一,工作流第 5 步即「把 A2UI 载荷交付给渲染器」(blueprints/modules/a2ui_agent.blueprint.md:773-801)。在典型 Agent-to-App(A2A)拓扑里包成 DataPart(Python 参考实现 agent_sdks/python/a2ui_agent/src/a2ui/a2a/parts.py):
- MIME 类型: A2UI JSON 标
application/a2ui+json(parts.py:30),告诉前端怎么解读这段流;旧协议版本(0.8/0.9)兼容用application/json+a2ui(parts.py:31)。 create_a2ui_part助手: 自动完成 DataPart 封装(parts.py:33-54)。- 产出策略: 既支持 LLM 说完后的完整对象(
parse_response),也支持流式解析器的增量产出(配合客户端的渐进渲染)。
7. 跨语言一致性靠 conformance 套件
SDK 是多语言生态(Python 为参考,另有 Kotlin 等)。为保证各语言行为一致,仓库维护一套语言无关的 conformance 测试套件(blueprints/modules/a2ui_agent.blueprint.md:804-808)。套件已从 agent_sdks/conformance/ 移到仓库根 conformance/ 并按域组织:集中的 YAML 用例(conformance/agent/parser.yaml、conformance/agent/streaming_parser.yaml、conformance/core/validator.yaml 等)验证不同语言实现「在流式与校验边界上行为相同」。移植新语言 SDK 时,跑这套件就能确认行为对齐。(旧版指南末尾的「五步分阶段移植指南」在新蓝图中已移除,没有对应章节。)
8. 这章与前几章如何对接
[本章: Agent 侧] [01-04 章: Client 侧]
目录 ──► prompt ──► LLM ──► 流式解析 ──► 校验 ──► DataPart ──► MessageProcessor ──► 渲染
│ │ (01 章) (02-04 章)
└── 同一份目录(白名单)── 两头共享 ─────┘
关键洞察:agent 侧和 client 侧共享同一份目录定义——agent 用它生成 prompt 和校验,client 用它渲染和守安全边界。目录是把两端钉在一起的契约。
9. 代码地图
| 主题 | 文件 | 符号 |
|---|---|---|
| Agent SDK 架构 | blueprints/modules/a2ui_agent.blueprint.md | A2uiGenerator、A2uiRequestProcessor、InferenceFormat |
| 流式解析设计 | 同上 | Parser、parse_chunk、unwrap |
| 校验设计 | 同上 | A2uiValidator(a2ui_core)、DirectJsonParser.compile |
| conformance 套件 | conformance/(根目录,按 agent/core/extensions 分域) | parser.yaml、validator.yaml、streaming_parser.yaml |
| 客户端能力生成(实现侧) | renderers/web_core/src/v0_9/processing/message-processor.ts | getClientCapabilities、getClientDataModel |
| 三步循环规范 | specification/v1_0/docs/a2ui_protocol.md | 「prompt-generate-validate loop」节 |
差异点回顾: 客户端能力对象在 v0_9 实现里用的键默认是
'v0.9'(实现已支持'v0.9' | 'v0.9.1'两版本,message-processor.ts:127-139;getClientDataModel同样默认version: 'v0.9',message-processor.ts:238-254),而 v1.0 规范用'v1.0'(a2ui_protocol.md:1266)。再次印证「规范候选 vs 实现 v0.9」的错位。