跳到主要内容

数据截至 (上游 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):

  1. Prompt: 把「想要的 UI + A2UI schema(含目录)+ 合法 JSON 例子」拼进给 LLM 的 prompt。
  2. Generate: 送给 LLM,拿到生成的 JSON。
  3. 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 包一层

注意: 旧版指南的 InferenceStrategyA2uiStreamParserPayloadFixer 三个接口在新蓝图中已移除——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_componentsinclude_schemainclude_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_chunkprogressive_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.yamlconformance/agent/streaming_parser.yamlconformance/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.mdA2uiGeneratorA2uiRequestProcessorInferenceFormat
流式解析设计同上Parserparse_chunkunwrap
校验设计同上A2uiValidator(a2ui_core)、DirectJsonParser.compile
conformance 套件conformance/(根目录,按 agent/core/extensions 分域)parser.yamlvalidator.yamlstreaming_parser.yaml
客户端能力生成(实现侧)renderers/web_core/src/v0_9/processing/message-processor.tsgetClientCapabilitiesgetClientDataModel
三步循环规范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」的错位。