A2UI · Agent-to-UI 协议校正与 Chat 页落地方案
#AGUI / A2UI 协议校正与 Chat 页落地方案
#1. 文档定位
本文档是本项目关于 AGUI / A2UI 的单一事实源,目标有三件事:
- 校正协议事实,避免把官方规范与本项目约定混写。
- 说明本项目如何用 AGUI 事件流构建 A2UI 读模型。
- 明确 Chat 页的聊天优先落地方式、边界与验证口径。
截至 2026-03-08,本项目采用:
AGUI作为传输层与事件事实源。A2UI作为前端读模型与生成式 UI 组织方式。Chat主区采用聊天优先呈现,而不是树形结构优先。
#2. 协议事实
#2.1 AGUI 的职责
AGUI 负责定义 agent 交互中的事件、消息、工具调用、状态与流式生命周期,是本项目的事实输入层[1][2]。
在本项目中,AGUI 的职责是:
- 提供事件流输入。
- 作为实时 SSE 与历史回放的共同语义基底。
- 保证文本消息、工具调用、状态变化等信息可被统一归并。
AGUI 不是页面布局协议。页面如何把这些事件组织成聊天流、树或组件,是前端读模型的职责。
#2.2 A2UI 的职责
A2UI 关注的是生成式 UI 的结构化表达:容器、数据绑定、组件组合与基于状态的渲染约定[3][4]。
在本项目中,A2UI 不作为第二条传输协议接入;它的工作定义是:
- 使用 AGUI 事件作为输入事实源。
- 在前端构建
Message Ledger与ConversationTree projection两类读模型。 - 将消息、工具、状态、活动等内容映射到聊天主轨和可递归渲染的 UI 模块。
因此,本项目的准确描述是:
AGUI transportA2UI-inspired read modelchat-first presentation
而不是“AGUI 与 A2UI 双传输协议并存”。
#2.3 官方字段与项目自定义字段
官方字段:
RUN_STARTEDTEXT_MESSAGE_*TOOL_CALL_*STATE_*MESSAGES_SNAPSHOTSTEP_*RAWCUSTOM
项目自定义事件:
ne.a2ui.linkne.a2ui.reasoning
这些 ne.* 事件仅代表本项目的父子关系回填与额外语义补充,不属于 AGUI 或 A2UI 官方标准。
#3. 本项目读模型约定
#3.1 总体结构
对应代码锚点:
- 传输入口:route.ts
- ADK 到 AGUI 归一化:agui-normalization.ts
- Session Application Service:useSessionService.ts
- Session List Service:useSessionListService.ts
- Session Projection / Message Ledger:message-ledger.ts
- Session Projection Hook:useSessionProjection.ts
- 事件到树构建:conversation-tree.ts
- Chat 主区渲染:ChatStream.tsx
- 递归节点渲染:ConversationNodeRenderer.tsx
#3.2 Session Projection 约定
本项目当前把 Message Ledger 前移为 session 级显式状态,而不是在页面渲染期临时从 rawEvents 全量重建。
rawEvents:保留 AGUI 事件事实源。messageLedger:保留聊天消息事实与角色纠偏结果。snapshot:保留状态快照事实。conversationTree、timeline、state 面板都属于从 session projection 派生出的 surface projection。
这样做的目标是把“协议输入”“消息事实”“页面结构”三层职责拆开,避免聊天主区、历史回放与技术面板各自维护不同事实源。
当前实现进一步把这套 projection orchestration 从页面组件中抽离到 features/session,页面只消费 session feature 暴露的 projection 与 action,不再直接持有 reducer 和投影拼装逻辑。
当前的推荐边界为:
useSessionService:负责 session detail 拉取、hydration 调度、竞态保护与 projection 更新协调。useSessionListService:负责 session list 拉取、创建、归档、改名与标题刷新调度。useSessionProjection:负责 confirmed projection、optimistic overlay 与 render projection 派生。- 页面组件:只负责 UI 容器、输入交互和面板编排,不再直接持有 session hydration 定时器、list fetch 逻辑或请求版本控制。
遗留兼容入口:
useSessionManager仅为兼容旧调用面暂时保留,已不代表当前 session feature 的推荐架构边界。- 新增代码已通过全量 ESLint 门禁中的
no-restricted-imports明确禁止导入useSessionManager,避免 legacy 入口重新漂移回主路径。 - 新增能力、回归测试与文档描述一律以
useSessionListService + useSessionService + useSessionProjection为准。
#3.3 Canonical Role 约定
本项目内部统一使用 canonical role:
userassistantsystemdevelopertool
兼容入口:
agent仅作为兼容输入值保留。- 一旦进入系统,
agent必须单向收敛为assistant。
角色优先级:
- 显式
message.role/ 事件role messagesSnapshot中的消息角色- 工具消息特征
- 兼容兜底为
assistant
author 只保留为展示元数据,不作为自由 role 来源直接参与聊天主语义判定。
#3.3 ConversationNode 节点模型
当前读模型节点类型:
turntexttool-calltool-resultactivityreasoningstate-deltastate-snapshotsteprawcustomeventerror
父子关系规则:
- 同一
runId生成一个turn根节点。 TEXT_MESSAGE_*聚合为text。TOOL_CALL_*优先挂到当前 assistant 文本节点下。TOOL_CALL_RESULT挂到对应tool-call下。STEP_*生成step,并补一个reasoning摘要节点。STATE_*、ACTIVITY_*默认挂到turn或对应答复上下文。ne.a2ui.link仅做父子关系修正,不直接进入主聊天区。
#3.4 历史回放与实时流一致性
实时流与历史回放必须走同一套归并语义,避免刷新前后出现 split-brain:
- 文本流靠
TEXT_MESSAGE_START / CONTENT / END收敛。 - 乐观用户消息与历史确认消息通过统一 identity key 去重。
messagesSnapshot用于修正历史角色缺失或补足消息列表。- 当实时 assistant 流与历史回放使用不同
messageId但语义上属于同一答复时,canonical message 必须在Message Ledger层收敛为一条消息事实;ConversationTree只能复用该事实,不得再次生成第二条主消息。 - 命中同一 canonical message 的 hydration / snapshot 只能升级既有节点内容与完成态,不能把 streaming 中间态和最终态并排展示。
- 树构建是读模型重建,不是事实源本身。
#4. Chat 页落地方式
中栏的回合制转录 UI(左右分栏 + per-agent 徽章 + 类型化工具卡)设计见 人机交互转录 UI ADR,实操见 实操手册。
#4.1 为什么是聊天优先
Chat 页的主任务是“阅读并参与对话”,不是“调试节点树”。如果把 turn、状态、结构性事件直接放在主车道,会破坏用户对“我说了什么、智能体回了什么”的基本心智。
因此主区遵循:
- 优先展示稳定的 user / assistant 消息流。
- 技术节点作为答复附件或折叠块。
- 调试节点进入右侧观测区或
debug-only链路。
#4.2 主区信息分层
主区分三层:
-
聊天主语义
user文本assistant/developer文本system提示
-
技术附属语义
tool-calltool-resultactivitystate-*stepreasoningerror
-
调试语义
raw- 纯结构
custom - 空 payload 技术节点
可见性约定:
chat:进入主区collapsed:进入主区但默认摘要化debug-only:不进入主区
#4.3 当前 Chat 页强约束
- 用户消息必须渲染为 user bubble,不能被历史回放降级为 assistant bubble。
- assistant 的流式片段与最终快照必须收敛为一个答复块。
turn容器只作为轻量轮次边界,不得喧宾夺主。- 工具、状态、活动不能伪装成一条新的聊天答复。
- 历史刷新后,主区消息顺序、角色和数量必须与实时态保持一致。
#4.4 UI 结构示意
#5. 已知问题与修正策略
#5.1 已知问题
本轮修正前,Chat 页存在以下风险:
- 用户历史消息可能因为 role 丢失,被按 assistant 方式渲染。
agent/assistant混用,导致类型与渲染逻辑双轨。author被宽泛当作 role 来源,污染聊天主语义。- A2UI 树在主区中过度显性,削弱了聊天流体验。
#5.2 修正策略
本轮采用以下原则:
- role 统一收敛为 canonical role。
messagesSnapshot优先用于历史角色纠偏。turn卡片视觉弱化,消息气泡回到阅读中心。- 自定义
ne.*事件只用于结构修正,不直接进入聊天主语义。
#6. 验证口径
必须覆盖以下场景:
- 乐观用户消息发送后立即显示为 user bubble。
- 历史回拉确认同一条用户消息后,不出现重复 bubble。
- 历史 payload 中
message.role = "user"时,主区稳定显示为 user bubble。 - 历史事件角色异常但
messagesSnapshot正确时,主区仍以 snapshot 纠正。 - assistant 实时流与最终快照
messageId不同,仍只显示一个最终答复。 - 工具、活动、状态节点仍可见,但不会打断主聊天主线。
#7. 后续演进建议
- 引入正式的节点渲染注册表,替代当前轻量分支渲染。
- 将技术附件与聊天主语义拆成明确的 presentation layer 接口。
- 在历史 hydration 链路显式消费
messagesSnapshot,减少 UI 层补救负担。 - 为真实会话样本增加快照回放回归集,持续监控角色和去重一致性。
#8. 参考文献
[1] AG-UI Docs, "Events," AG-UI Documentation, 2026. [Online]. Available: https://docs.ag-ui.com/concepts/events
[2] AG-UI Docs, "Messages," AG-UI Documentation, 2026. [Online]. Available: https://docs.ag-ui.com/concepts/messages
[3] AG-UI Docs, "Generative UI Specifications," AG-UI Documentation, 2026. [Online]. Available: https://docs.ag-ui.com/concepts/generative-ui-specs
[4] A2UI, "A2UI Concepts Overview," A2UI Documentation, 2026. [Online]. Available: https://a2ui.org/concepts/overview/