当需要构建展示 agent-management 工作流执行进度或历史运行详情的前端页面/组件时,使用本参考。保留其内容、状态流转和信息层级;视觉设计则按目标产品自身风格适配。
硬性要求:业务方必须完整呈现「实时状态、思考过程、工具调用、待办清单、执行日志、最终结果」六类内容,交互逻辑(折叠策略、multi-visit 渲染、AskFor 处理)与本系统一致。可直接复用的 JS/Vue 实现见 js-implementation.md;接口契约见 external-workflow-api.md。
如果用户提供了已部署的参考页面,可将其作为可选的内容与交互参考,而不是必须照搬的样式来源。
采用面向执行的视图。对于管理/历史类页面,左右两栏的运行记录布局是较优的默认选择,但嵌入式或面向特定任务的集成不强制使用。
运行记录 或目标应用中的等价文案。集成到目标应用时,遵循其既有约定,例如:
除非用户明确要求近似视觉,否则不要照搬原运行记录页样式。
展示运行历史时,按工作流分组有助于用户查找历史执行:
#a3f9b2c1常用交互:
对于纯实时执行视图,完整的历史列表是可选的;展示当前工作流/运行身份、实时状态与节点推进即可。
运行详情头部:
运行 #<runId>随后按 visit.sortOrder 顺序渲染 Visit 卡片(不是节点卡片,见「multi-visit 渲染」)。
页脚:
下载工作空间(运行成功结束时 workflow_complete 事件携带 workingDirFiles 文件列表,可与下载按钮归到一组展示)。workspace.downloadUrl 或 /api/v1/workflows/{workflowName}/runs/{runId}/workspace。{workflowName} 是 kebab-case 唯一标识(推荐),纯数字 id 作为兼容兜底也合法。路由主键是 visitUuid,不是 nodeId:
卡片头部信息:
visit.iterSeq > 1 时展示「第 N 次访问」徽章visit.fromNodeId 存在且跳转不在画布边上(visit.decisionJson === '{"offCanvas":true}')时,展示「来自 {fromNodeName}」徽章补充建议:AGENTIC 模式下可额外渲染「AI 决策链」(消费 planner_decision 事件的 decision.nextNodeId + decision.reason),让用户理解为什么跳到这个节点。
节点运行时按以下顺序渲染面板;面板顺序固定为「实时状态 → 思考过程 → 待办清单 → 工具调用 → 执行日志」,最终输出在面板区之后单独渲染。
node_stream kind=status 的 content(覆盖式,永远只保留最新一条)。node_stream kind=thinking 的 content 依次追加。node_stream kind=todo_update 的 content(JSON 字符串,解析后为 TodoWrite 工具输出)整体覆盖。content 与 status(pending/in_progress/completed),渲染为 ✓/○/进行中图标 + 文本。tool_call(开始)、tool_progress(细粒度进度)、tool_result(结束)三类 node_stream 事件。tool_call 与 tool_result 事件不含 callId,按「同 Visit 内到达顺序」配对:tool_call 入队,tool_result 关闭最早未关闭的同名调用(Hermes 工具串行执行,顺序配对安全)。tool_call / tool_result 的 content 已被服务端截断到 500 字符,仅作预览;完整内容从节点 logs(TOOL_CALL.detail / TOOL_RESULT.detail)获取。node_stream kind=step 追加;工具调用、状态变更等也可同步追加一行摘要日志。node_status 终态事件的 output(或回放场景 visit.outputJson)。node_stream kind=clarify_request 的 content(JSON 字符串:{"clarify_id", "question", "choices"})。POST /api/v1/runs/{runId}/resume(v1.2,路径不带 workflowName 段);同一 clarifyId 重复提交返回 409,按幂等约定视为成功(详见 external-workflow-api.md 的「AskFor(用户确认)」)。节点 output 是输出信封(envelope)结构:
{ "status": 200, "message": "...", "data": { "业务字段": "..." } }
status 是数字:200 成功 / 400 失败。
解包逻辑(伪代码):
function isEnvelope(o) {
return o && typeof o === 'object' && !Array.isArray(o)
&& ('status' in o) && ('data' in o) // 必须同时含 status 与 data
}
function unwrapOutput(o) {
if (!isEnvelope(o)) return o // 非 envelope:原样展示
return o.data ?? {} // 业务字段在 data 内
}
envelope.status === 400(或节点状态 FAILED)时输出区红化,优先展示 envelope.message 的失败原因。workflow_complete.output 与查询结果的 outputs)同样带 envelope 壳(成功时 status: 200、message: "工作流执行成功"),用同样的 unwrapOutput 取 data 渲染。data 为空对象且无 message)时不渲染输出区,避免空占位框。node_status):卡片自动折叠为头部一行(状态 + 名称 + 耗时),聚焦后续节点。visit_recorded / 查询结果接口加载):全部卡片默认折叠,与运行结束后的视觉一致。展示历史运行(非实时 SSE)时,按以下优先级取数:
Last-Event-ID 从头开始,服务端缓存 25 小时)或内部 Visit 接口;每个 visit_recorded 事件的 visit.logsJson 可还原六面板:
type=THINKING 的 messageTOOL_CALL / TOOL_RESULT 序列TODO_UPDATE 的 detailnodes[] + 每节点 logsUrl 拉取 {type, message, detail, timestamp} 日志数组,同样按 type 分流到六面板。使用一致的节点标签。下方颜色与图标仅为示例,不是必须的样式:
| nodeType | 标签 | 颜色 | 图标建议 |
|---|---|---|---|
userInput |
用户输入 |
#10B981 |
聊天/输入 |
llm |
大模型 |
#8B5CF6 |
闪光 |
skill |
技能 |
#06B6D4 |
烧瓶 |
agent |
智能体 |
#F59E0B |
火箭 |
smartAction |
智能操作 |
#EC4899 |
魔杖 |
condition |
条件 |
#F97316 |
分支 |
output |
输出 |
#EF4444 |
出口/输出 |
文案:
running 或 RUNNING:执行中success 或 SUCCESS:成功failed 或 FAILED:失败skipped 或 SKIPPED:跳过未知若使用 CSS 类,将状态归一化为小写,例如 status-success。
可选的状态色意图(当目标设计体系使用状态色时):
思考过程与最终输出必须按 markdown 渲染(本系统智能体输出普遍含 markdown),最低要求:
/lib/common 子集控制包体),代码块横向滚动不换行,配滚动条样式。marked ^12、marked-highlight ^2.2、highlight.js ^11.12、dompurify ^3.2、katex ^0.16。事件处理总表(6 种事件类型;没有 run_started 事件):
| 事件 | 处理 |
|---|---|
node_status(RUNNING) |
按 visitUuid 创建卡片(若不存在),状态置执行中 |
node_status(终态) |
更新卡片状态、绑定 output/error、触发自动折叠 |
node_stream(8 种 kind) |
按 kind 分流到对应面板(见上文六面板) |
visit_recorded |
补充卡片元数据(nodeLabel/nodeType/iterSeq/fromNodeId/sortOrder),必要时按 sortOrder 重排卡片 |
planner_decision |
可选:渲染 AI 决策链 |
workflow_complete |
运行成功终态;存储最终输出;展示工作空间文件列表与下载入口;停止重连 |
workflow_error |
运行失败终态;展示错误;停止重连 |
断线重连要求(服务端机制见 external-workflow-api.md):
Last-Event-ID。GET /runs/{runId}(30s/次);SSE 恢复后立即停止。visitUuid 幂等合并,不得产生重复卡片。Last-Event-ID 缺省),服务端补发全部缓存事件,实现完整回放。终态事件到达后,可再查询持久化结果端点,对齐持久化输出、日志数量与工作空间元数据。
实现以下辅助函数或等价物:
formatTime(t):兼容 ISO 字符串与 Spring/Java 的数组式日期数组。duration(run):基于 started/completed 计算耗时;按秒、分秒或时分适配展示。parseJson(str):成功返回解析后的对象,否则返回 null。extractThinking(logsJson):解析日志数组,拼接 THINKING.message。parseTodo(content):JSON.parse todo_update 的 content,容错返回条目数组。parseClarify(content):JSON.parse clarify_request 的 content,容错返回 {clarify_id, question, choices}。isEnvelope(o) / unwrapOutput(o):见「envelope 解包规则」。对实时 SSE 视图,即便日志是以增量方式到达而非来自持久化 JSON,也应保持同样的派生数据形态。
data)。