人机交互转录 UI:Routine 与 Studio 的统一架构

Routine 迭代「Iterations Full View」与 Home/Studio 中栏的「人机交互」对话已统一为同一套回合制转录渲染器TranscriptItemsView + TranscriptPolicy)。本文记录语义重映射、业界对照、机制 vs 策略正交分解与关键设计决策。


#1. 背景与动机

两处「人机交互」UI 长期双轨割裂

  • Routine Iterations Full View 已有一套成熟的回合制转录 UI(TranscriptView):按发言方左右分栏、per-agent 角色徽章、可展开的按类型渲染工具卡(shell / read / edit-diff / write / search / sub_agent)、≥3 连续工具折叠、thinking 折叠、在途 WorkingIndicator
  • Home/Studio 中栏 则是另一套流式聊天渲染(ChatStreamMessageBubble / AssistantReplyBubble),观感与 Routine 不一致,且无 per-agent 显化。

割裂带来三类成本:① 用户在两处切换时认知摩擦;② 设计语言双份维护;③ 工具调用渲染深度不一(Studio 浅、Routine 深)。目标:让 Studio 中栏复用 Routine 的同一套转录渲染器,仅做语义重映射,使「设计语言单一事实源」。

#2. 业界对照研究

回合制(turn-based)人机交互 UI 是当前主流编码/会话 Agent 产品的公约数。三家范本的对齐要点:

产品回合组织工具调用思考片段在途态
Conductor[1]data-turn-index 分组、左右分栏可展开卡 + diff/terminal 分节、≥3 折叠可折叠 thinkingLottie running / typing / waiting
ChatGPT[2]单列时间线、Reasoning 折叠工具卡折叠 + 参数/结果分节Reasoning 默认折叠进度条 + 气泡内三点
Claude.ai单列、思考可展开工具时间线 + 紧凑摘要Thinking 展开式流式光标 + 状态文案

提炼共性(本系统采纳):① 按发言方左右分栏区分人/机;② 工具调用可展开折叠,按类型给差异化明细(diff / 命令 / 内容);③ 思考片段默认折叠降噪;④ 在途态脉冲指示(Working… / Planning…)。详见 Conversation Foundation 的「行业基线」节与 AG-UI 协议调研 的事件模型分析。

#3. 语义重映射(核心差异)

同一渲染器,两处「人/机」语义不同——这是唯一需要策略化的差异:

维度Routine(Iterations Full View)Studio(Home 中栏)
人(居右)一核五翼 6 Agent(审 Plan / 答问 / 门控 / 评估)真实用户输入
机(居左)Claude Code(assistant / tool / cc_request)一核五翼 Agents + Claude Code(均带 per-agent 徽章)
编排者Negentropy Engine(gate / evaluation / result,居右)无独立编排者
触发模型ADK 事件流 + Routine 编排AG-UI 事件流 + 用户直连

直觉记忆:Routine 里「人」是 Agent 团队、「机」是 Claude Code;Studio 里「人」是用户、「机」是 Agent 团队 + Claude Code。

#4. 架构方案(机制 vs 策略正交分解)

单一渲染器 TranscriptItemsView(纯展示,policy 化)+ TranscriptPolicy 契约(承载左右对齐 / 徽章 / 在途文案差异)+ 两个适配器(各自把域数据归一化为 TranscriptItem[] IR)。

TranscriptPolicy 契约(components/transcript/policy.ts):

hljs ts
interface TranscriptPolicy {
  align(item: TranscriptItem): "left" | "right";        // 物理对齐
  turnGroup?(item: TranscriptItem): string;             // 换方间距的发言人分组(缺省回落 align)
  roleHeaderFor(item, prev): AgentRole | null;          // 机侧 per-agent 徽章(null=不渲染)
  workingLabel?(last): string;                          // 在途 WorkingIndicator 文案
}
  • ROUTINE_POLICYhuman_reply/task_dispatch/engine 居右、其余居左;turnGroup 三分(cc / human / engine);roleHeaderFornull(徽章内嵌在 Engine/Human/TaskDispatch 块中)。
  • STUDIO_POLICY:仅 user 居右、其余全居左;roleHeaderFor 在机侧 role 相邻变化时渲染徽章(分组降噪);不设 turnGroup(回落 align)。

薄壳 TranscriptView(Routine)与 StudioTranscript(Studio)各自归一化后委派给同一渲染器,外部签名稳定、行为各表

#5. TranscriptItem 视图模型

12 个 kind 的判别联合(components/transcript/types.ts):

Kind用途RoutineStudio对齐
task_dispatch开场任务下发(iteration.prompt 合成)
user真实用户文本
assistant文本(可带 thinking / citations / streaming / role)
tool单次工具调用(可展开 / running / progress / role)
tool_summary≥3 连续工具折叠(透传 role)
cc_request机→人请求(ExitPlanMode / AskUserQuestion)
human_reply人→机应答(approve / refine / answer / deny)
engineEngine 编排产出(gate / evaluation / result)
systemRoutine 系统事件(携带 DTO,可展开 payload)
system_noteStudio 纯文本系统/状态/摘要行
truncated动作数超限截断哨兵

可选字段(全 additive):role: AgentRole(机侧 + human_reply)、nodeId: string(Studio 选中/搜索)、citations: Citation[](assistant,Studio 引用尾注)、progress: ToolProgressSnapshot(tool,Studio 流式进度)、streaming / thinking(assistant)。所有可选字段对 Routine 归一化输出恒空 → Routine 渲染逐像素等价。

#6. Agent 身份映射

跨域单一事实源 features/agent-identity/AGENT_ROLE_META(标签 / 图标 / 徽章配色):

Role中文Lucide 图标徽章配色(dark-safe)
engineNegentropyCpuslate
claude_codeClaude CodeBotviolet
perception慧眼Eyecyan
action妙手Handorange
internalization本心BrainCircuitemerald
contemplation元神Sparklesindigo
influence喉舌Megaphonerose

agentRoleFromAuthor(author) 用大小写不敏感子串匹配(兼容 EN + 中文学名 + 别名前后缀)把 ADK message.author 派生为 AgentRole;未知回退 engine。后端 agent 名源自 apps/negentropy/src/negentropy/agents/agent.py:根 NegentropyEngine + 五翼 PerceptionFaculty / InternalizationFaculty / ContemplationFaculty / ActionFaculty / InfluenceFaculty

#7. 关键决策与权衡

  1. turnGroup 三分(Routine)enginehuman_reply/task_dispatch 都居右,但语义不同(编排者 vs 人侧 Agent)。若仅按 align 二态判换方间距,连续的 human→engine 会被当同方(8px),丢失编排转折的视觉呼吸;按 speaker 三分(cc / human / engine)才正确触发 16px 换方间距。Studio 无此三分语义,回落 align
  2. collapseToolRunstool_summary 透传 role:≥3 工具折叠后,Studio 的徽章分组逻辑(roleHeaderFor)需以折叠行首工具的 role 参与「上一条机侧 role」比较,避免折叠行后又重复刷同 role 徽章。Routine 的 tool 无 role,透传 undefined 无副作用。
  3. citations 仅挂首个 text 段:避免同回合格式化的引用尾注块重复出现。聚合走 extractCitationsFromToolCallsutils/citation-parser.ts),匹配 search_knowledge_base / search_knowledge_graph_with_papersformatted_citation
  4. Studio itemWrapperdata-testid="message-bubble" + data-message-role:旧 E2E「双气泡守卫」(防流式 dedup 双气泡)依赖该选择器;新 UI 把 text 段拆为独立气泡,但单文本回复仍恰好 1 气泡——选择器兼容让守卫零改动保留。
  5. 类型化工具明细derive-tool-detail.ts 按工具名分派到 shell / read / edit / write / search / fetch / sub_agent / plan / generic 九类,tool-detail-sections.tsx 各自渲染(shell 显 $ cmd + output,edit 显 diff,read/write 显等宽内容等)。这是 Routine 范式的深度复用,Studio 直接受益。
  6. 机制 vs 策略边界:渲染原语(block 组件、collapseToolRunsstatus-sharedmessage-shared)与域无关,落 components/transcript/;事件耦合的归一化(normalize-transcript 消费 RoutineIterationEventDTO)留 app/interface/routine/;跨包对 Routine 的 DTO 仅 import type(运行时零反向依赖)。

#8. 边缘 Case 与回归守卫

Case机制位置
流式 dedup复用 buildChatDisplayBlocks(6 层去重 + 时钟漂移排序 + reasoning/agent-transfer);nodesFingerprint 含子内容长度保证流式更新StudioTranscript / chat-display.ts
≥3 连续工具折叠collapseToolRuns 折叠非 running 工具;任何非 tool 项打断 flush;running 工具排除collapse-tool-runs.ts
引用聚合replyCitations 从 assistant-reply 的 tool-group 段提取(按 rawName 匹配原始工具名),仅挂首段 textbuild-studio-transcript.ts
工具进度旁路progressMap[tool.id](state_delta SSE)透传到 tool.progress,渲染细进度/阶段行build-studio-transcript.ts / ExpandableToolCallRow
节点选中 / 搜索itemWrapperdata-node-id + 点击 ring + 搜索高亮 ring + scrollIntoViewStudioTranscript
在途指示器pending && !lastIsStreamingAssistant 时尾随 WorkingIndicator;label 由 policy.workingLabel 解析(cc_request+pending → Planning…,否则 Working…)TranscriptItemsView
CC 交互错误红显AssistantTextCC_ERROR_RE 检测「returned an error」等模式,红框 + ⚠AssistantText.tsx
thinking 折叠默认收起为「思考…」单行 + Brain 图标,点击展开AssistantText
空 assistant 丢弃{raw} 无 text 的 assistant 事件归一化时丢弃,防空气泡normalize-transcript.ts
持久化 + live 合并mergeEventsseq 去重(持久化优先)+ 升序重排IterationAuditDrawer.tsx

#9. 关键文件索引

路径角色
components/transcript/TranscriptItemsView.tsxSSOT 策略化渲染器
components/transcript/policy.tsTranscriptPolicy + ROUTINE_POLICY + STUDIO_POLICY
components/transcript/types.tsTranscriptItem 视图模型(12 kind)
components/transcript/collapse-tool-runs.ts≥3 工具折叠
components/transcript/{AssistantText,ExpandableToolCallRow,ToolSummaryRow,CcRequestBlock,HumanReplyBlock,EngineMessageBlock,TaskDispatchBubble,UserBubble,SystemNoteRow,WorkingIndicator}.tsxblock 原语
components/transcript/{message-shared,status-shared,derive-tool-detail,tool-detail-sections}.ts(x)共享明细/徽章/状态
features/agent-identity/AgentRole / AGENT_ROLE_META / agentRoleFromAuthor(跨域 SSOT)
app/interface/routine/_components/transcript/{TranscriptView,normalize-transcript}.ts(x)Routine 适配器(薄壳 + 事件归一化)
app/interface/routine/_components/{IterationAuditDrawer,IterationEventTimeline}.tsxRoutine Full View 容器
components/ui/StudioTranscript.tsxStudio 外壳(滚动 / FAB / 空态 / 选中 / 搜索)
components/ui/studio-transcript/build-studio-transcript.tsStudio 适配器(block → TranscriptItem)
app/home-body.tsx接入点(<StudioTranscript> 替换 <ChatStream>

#10. 演进路径

沿用 040 ADR 的 Phase 路线:Phase 2 后端落地 agent_role 字段后,归一化层切 ev.agent_role ?? deriveHumanRole(action),前端零扩散;Studio 侧 agentRoleFromAuthor 同样可平滑切到后端归因字段(当 ADK 事件流显式携带 agent 元数据时)。短期可演进项:① Routine/Studio 的 thinking 与 Reasoning Panel 统一(0002 RFC);② 工具明细的 Mermaid / 大输出虚拟化。

#11. 风险评估

编号风险影响概率对策
R1Routine 渲染回归(迁移共享模块)44 项转录契约测试零改动全绿;ROUTINE_POLICY.roleHeaderFor 恒 null 保逐像素等价
R2流式 key 抖动致重挂载item key ${kind}-${seq}-${id}id 源自稳定 nodeId;fingerprint memo 含子内容长度
R3per-agent 归因失真(author 字段不规范)子串匹配 + 中文学名兜底 + 未知回退 engine;Phase 2 切后端字段后消除
R4Studio 选择器破坏旧 E2EitemWrapperdata-testid="message-bubble" + data-message-role,双气泡守卫零改动保留
R5跨包对 Routine DTO 的运行时耦合import type 擦除;共享包无运行时反向依赖

#参考

[1] Conductor Team, "Conductor — Run multiple coding agents in parallel," conductor.com, 2025. [Online]. Available: https://conductor.com [2] OpenAI, "Introducing ChatGPT & Canvas: agentic tool use," openai.com, 2025. [Online]. Available: https://openai.com