# Office UI Guide 本文件用于记录 `office UI` 相关的想法、讨论结论、约束和后续修正。 目标: - 作为实现前的持续设计指南 - 作为后续页面开发时的范围边界 - 作为每次对话补充和纠偏的记录落点 约定: - 后续在对话中凡是对 `office UI` 的想法有新增、修正、否定、细化,都继续更新本文件 - 这里记录的是当前共识,不代表已经完成实现 ## 当前结论 `office UI` 不是传统意义上的“任务管理页”,更适合定义为: 一个面向长耗时、多 agent、多阶段任务的“运行态可视化总览页”。 当前开发阶段补充: - 优先目标已切换为“稳定可用、稳定可演示” - 当前阶段不再继续追求更复杂的像素美术细节 - 当前冻结项: - 四区地图结构 - 当前家具资源方案 - 当前角色资源方案 - 当前优先确保: - `/office/[taskId]` 真实可用 - 角色状态有基础差异 - 构建、测试、类型检查稳定 它借鉴 `Star-Office-UI` 的核心概念: - 用办公室空间隐喻表达 agent 状态 - 让主 agent 和 sub-agent 的工作状态可视化 - 让用户快速感知“谁在做什么、是否卡住、任务进展到哪里” 但它不应直接替代现有结构化任务管理界面。 ## 产品定位 建议定位: - `office UI` = 总览层 / 态势层 / 趣味化运行时观察台 不建议定位: - 唯一的任务管理入口 - 唯一的故障诊断入口 - 唯一的任务事实来源 原因: - 办公室隐喻适合表达“状态”和“分工” - 不适合单独承载依赖关系、失败原因、产物细节、真实阻塞点 ## 核心设计思想 ### 1. session、task、office 是三层关系 当前方向: - `session` 用于维护长期对话上下文 - 用户在 session 中发起“让主 agent 干 XXX”即创建一个 `task` - `office` 是这个 `task` 的运行态可视化现场 当前共识: - 一个 session 可以包含多个 task - 一个 task 对应一个临时 office - task 结束后,office 解散,不作为长期容器保留 补充约束: - office 解散指“退出活跃运行态视图” - 不等于删除该 task 的历史事实 - task 完成后,仍应保留最小可回看的摘要、结果和产物入口 预期收益: - 对话上下文和任务执行上下文分离 - office UI 可以只关注“当前这次任务在发生什么” - 避免把整个 session 的历史信息都带进办公室画布 ### 2. 办公室负责概览,不负责全部事实 办公室画布适合承载: - 主 agent / sub-agent 在线状态 - 当前任务归属 - 工作区域映射 - 简化的进度感知 - 团队分工 - 当前办公室成员名单 办公室画布不应单独承载: - 全量日志 - 复杂筛选 - 深层错误排查 - 完整产物浏览 - 精确任务编排 结论: - 画布负责“扫一眼看懂” - 结构化面板负责“点进去看清” ### 3. 趣味性可以有,但不能压过可读性 允许: - 空间化布局 - 角色位置变化 - 轻量动效 - 有辨识度的视觉氛围 不建议: - 过多持续动画 - 复杂装饰导致信息遮挡 - 仅为可爱而牺牲状态辨识度 ## 页面建议结构 建议采用混合布局,而不是纯画布页。 ### A. 办公室画布区 用于展示: - 主 agent - sub-agent - 当前状态分布 - 所属任务或阶段 - 简化状态气泡 可能的空间分区示例: - 接待区:待命 / 排队 - 工位区:执行中 / 处理中 - 会议区:同步 / 协作 - 研究区:检索 / 分析 - 故障区:异常 / 阻塞 - 完成区:已完成 / 待汇总 注意: - 分区必须和真实状态枚举稳定映射 - 不能靠“视觉猜测”表达状态 ### B. 任务看板区 用于展示当前办公室里的任务拆分情况,例如: - 待开始 - 进行中 - 已完成 - 阻塞 要求: - 看板是结构化事实,不是装饰物 - 每个任务卡应至少显示标题、负责人、状态、阶段、更新时间 ### C. 进度区 可以有进度条,但要非常克制。 原则: - 只有在后端有真实阶段数据时才显示百分比进度 - 如果没有可靠进度,只显示“阶段进度”而不是伪百分比 可以接受: - `3 / 7` 子任务完成 - `当前阶段:资料检索` - `已完成:需求分析 -> 分工 -> 编码中` 不建议: - 没有依据的 `78%` - 基于耗时猜测的 ETA ### D. 分工区 用于展示: - 谁负责什么 - 主 agent 当前委派给了哪些 sub-agent - 各 agent 当前所属阶段 ### E. 办公室成员名单 用于展示: - 当前 task 参与成员 - 角色类型 - 当前状态 - 最近更新时间 ## 当前明确的产品判断 ### 支持的判断 - 这个方向有价值 - 比纯日志或纯表格更适合“长耗时任务”的态势感知 - 对多 agent 协作特别有帮助 - 与 task 生命周期绑定是合理的 - 如果做得好,既能提高清晰度,也能提高使用乐趣 ### 反对的判断 - 不应把它做成唯一任务管理页 - 不应让办公室隐喻替代真实任务数据 - 不应为了像素办公室效果而牺牲后台控制台的可操作性 - 不应直接照搬 `Star-Office-UI` 整套产品结构 ## 关键风险 ### 1. 隐喻过强,事实过弱 风险: - 用户知道“谁在忙”,但不知道“为什么慢” 控制方式: - 必须有结构化任务卡、状态、阶段、更新时间、负责人 ### 2. 进度条失真 风险: - 进度看起来很满,但实际任务没有可靠里程碑 控制方式: - 没有真实进度数据就不用百分比 ### 3. 页面职责过载 风险: - 一个页面同时做总览、诊断、管理、配置,最后都做不强 控制方式: - 明确 `office UI` 首先是总览页 - 诊断和深度操作应下钻到详细面板 ### 4. 代码移植成本被低估 风险: - `Star-Office-UI` 是完整产品,不是简单组件 - 直接搬代码的成本和适配复杂度可能高于重写 控制方式: - 优先借鉴概念、布局语言和交互隐喻 - 代码层面只选择性复用可拆分部分 ### 5. 资产许可风险 风险: - 原项目代码和美术资产许可范围不同 控制方式: - 直接复用图像和素材前,必须再次核对许可 - 有商用可能时,应准备自有素材方案 (暂时不商用) ## 实现原则 实现时建议遵守: - 先做信息架构,再做美术包装 - 先做静态结构,再接实时数据 - 先保证状态映射准确,再增加趣味动效 - 先让用户看懂,再让页面有趣 ## 与现有前端的关系 当前前端已经存在: - 聊天工作台 - 执行过程展示 - 定时任务管理 - 智能体管理 因此 `office UI` 更像新增一个: - `运行态总览页` - 或者 `task 级运行态页面` 而不是替换这些已有页面。 ## 适配现有数据结构的信息架构 当前前端已具备的关键数据: - `sessionId` - `sessions` - `processRuns` - `processEvents` - `processArtifacts` - `selectedRunId` - run 的 `parent_run_id` 父子关系 现有数据模型说明: - `session` 适合作为 task 的发起上下文,不适合直接等同办公室 - `ProcessRun` 更适合作为 `task / 子任务` 的基础执行实体 - `ProcessRun` 是最适合作为“任务实体”的基础对象 - `ProcessEvent` 是状态变化和过程流 - `ProcessArtifact` 是阶段产物和结果输出 - `parent_run_id` 可以推导主 agent 与 sub-agent 的委派关系 结论: - `office UI` 第一版应围绕 `task -> run tree -> event/artifact` 构建 - `session` 只用于找到当前 task 的来源上下文 - 不要第一版就引入全新任务模型,除非现有结构明显不够 ## 页面级信息架构 建议采用四层结构。 ### 1. 办公室切换层 位置: - 页面顶部或左侧一级导航区 作用: - 切换不同 task 对应的办公室 - 展示当前办公室的摘要状态 建议展示字段: - 办公室名称 - 对应 task 标题 - 最近活跃时间 - 运行中 agent 数 - 阻塞 / 错误数 数据来源建议: - 当前 session 中可查看的 task 列表 - 当前 task 下筛选后的 `processRuns` ### 2. 办公室画布层 位置: - 页面主视觉中心 作用: - 用空间化方式展示当前 task 内主 agent / sub-agent 的实时分布 画布中的基本对象建议: - 办公室房间 / 分区 - agent 角色卡 - 当前任务气泡 - 简化状态标识 - 高风险提醒标识 数据映射建议: - 一个 `agent run` 对应一个画布中的“办公人员状态实例” - 同一 agent 多个并发 run 时,需要有明确合并策略 第一版建议合并策略: - 只显示每个 agent 最近一个活跃 run - 若存在 `running` / `waiting`,优先显示活跃 run - 已完成 run 不长期占据画布主位 ### 3. 结构化事实层 位置: - 画布右侧栏或下方主面板 作用: - 承载不能只靠视觉隐喻表达的真实任务信息 建议包含 4 个模块: - 任务看板 - 进度区 - 分工区 - 成员名单 ### 4. 下钻详情层 位置: - 抽屉、侧边详情面板或弹层 作用: - 用户点击画布中的 agent、任务卡或异常标识后查看详情 建议展示: - 当前 run 摘要 - 最近事件 - 当前状态 - 产物列表 - 失败原因 - 上下游关系 ## 元素归位规则 本节用于明确什么应该放在办公室画布,什么不应该。 ### 应该放在办公室画布里的元素 适合放入画布的前提: - 用户需要扫一眼快速理解 - 信息可以被抽象成状态、位置、分工、风险 - 不依赖长文本解释 具体包括: - 主 agent - sub-agent - agent 当前状态 - agent 当前负责的任务短标题 - 所在阶段的标签 - 阻塞 / 错误告警标记 - 办公室内的功能分区 - 简化进度感,例如“阶段 2/4”或“3 个子任务进行中” - 当前在线成员总览 这些信息适合视觉化,因为: - 强调态势而不是细节 - 适合空间映射 - 能快速建立“工作现场感” ### 不应该直接放在办公室画布里的元素 以下内容不适合直接塞进画布主体: - 全量事件流 - 全量日志文本 - 复杂任务依赖图 - 多条件筛选器 - 大量操作按钮 - 大段失败堆栈 - 完整 artifact 内容 - 大量表格字段 - 精确排程配置 原因: - 会破坏扫视效率 - 会让画布从“总览层”退化成“信息垃圾堆” - 视觉隐喻会被结构化细节打散 结论: - 画布里只保留高信号、低文本密度的信息 - 深度信息必须进入结构化面板或详情抽屉 ## 现有数据到页面模块的映射建议 ### 办公室 建议实体: - `office = current task` 需要的基础字段: - task 标题 - task 创建时间 - 最近活跃时间 - 所属 `sessionId` 如果 task 当前没有独立名称: - 第一版可以显示为主 run 标题或默认编号 ### 办公人员 建议实体: - `office member = actor` 主要来源: - `processRuns.actor_id` - `processRuns.actor_name` - `processRuns.actor_type` 注意: - 成员名单应按 actor 聚合,而不是按 run 平铺 - 否则一个 agent 多次执行会在名单中重复出现 ### 任务卡 建议实体: - `task card = root run 或独立 run` 第一版推荐: - 一个 office 绑定一个主 task - 以 `root run` 作为该 task 的主执行单元 - 其 `member runs` 视为子任务 / 分工项 依据: - 现有 `parent_run_id` 已足够支撑这种树状关系 - 现有聊天页中的 team group 逻辑已经在这么做 建议字段: - `title` - `status` - `actor_name` - `started_at` - `finished_at` - `summary` - 子任务数量 - 最近更新时间 ### 进度 当前数据模型下,最安全的进度表达方式: - 按子任务完成数计算 - 按状态分布计算 - 按阶段标签计算 当前不安全的进度表达方式: - 伪百分比 - 纯时间推算 ETA 第一版建议的进度来源优先级: 1. `metadata` 中显式阶段信息 2. `member runs` 的完成数量 3. `run status` 分布 ### 分工 建议实体: - `assignment = root run -> member runs` 展示方式: - 主 agent - 子 agent 列表 - 每个子 agent 的当前标题 / 阶段 / 状态 适合来源: - `parent_run_id` - `actor_name` - `title` - `status` ### 详情抽屉 建议实体: - `detail drawer = selected run` 主要来源: - `selectedRunId` - 对应 run 的 `events` - 对应 run 的 `artifacts` 这层应承接: - 任务摘要 - 最近事件时间线 - 产物列表 - 失败信息 ## 第一版页面布局建议 推荐布局: ### 顶部 - task office 切换器 - 当前办公室摘要 - 总体状态统计 ### 中部左侧 - 办公室画布 ### 中部右侧 - 任务看板 - 进度区 - 分工区 ### 底部或右侧抽屉 - 成员名单 - 任务详情 - 事件与产物 这样的好处: - 左边负责“看态势” - 右边负责“看事实” - 抽屉负责“看细节” ## 第一版强约束 为了避免首版失控,建议强约束如下: - 一个 task 对应一个办公室 - office 生命周期跟随 task,task 结束后 office 解散 - 画布只展示 agent 和简化任务态势,不展示全量任务文字 - 任务看板只展示 root run 及其子任务摘要 - 进度不用伪百分比 - 详情只对单个 run 下钻 - 不在首版加入自由拖拽装修系统 - 不在首版复刻像素资产编辑系统 ## 当前建议的数据派生逻辑 建议在前端增加一层 office view model,而不是直接把 store 数据原样灌进 UI。 建议派生对象: - `OfficeView` - `OfficeMemberView` - `OfficeTaskView` - `OfficeAssignmentView` - `OfficeAlertView` 建议派生过程: 1. 先识别当前 task 的 root run 2. 收集该 root run 及其子树中的 runs / events / artifacts 3. 将 `runs` 按 `parent_run_id` 组织成树 4. 按 `actor_id` 聚合成员状态 5. 从状态和 metadata 推导画布分区 6. 将异常和阻塞单独提取为 alert 这样做的好处: - 避免页面组件直接承担复杂归并逻辑 - 便于后续替换视觉方案而不动数据组织 当前 task 的推荐判定规则: - 用户在 session 中发起一次主请求,即创建一个主 task - 主 agent 开始执行,到主 agent 向用户给出最终回复为止,视为该 task 生命周期 - 该期间产生的 root run 及其子 runs 构成一个 office - task 结束后,office 从活跃列表移除,但 task 摘要不应丢失 ## Task 识别规则 这一节用于把“什么算一个 task”说清楚,否则 office UI 很容易做成不稳定状态机。 ### task 定义 建议定义: - task = 用户在 session 中发起的一次主请求所触发的完整执行周期 起点: - 主 agent 接收到该次用户请求 - 对应 root run 被创建 终点: - 主 agent 针对该次请求输出最终用户可见回复 - 对应 root run 进入终态:`done` / `error` / `cancelled` 一个 task 内可以包含: - 主 agent 自身执行 - sub-agent 委派 - MCP 调用 - 中间产物 - 中间进展事件 一个 task 不应跨越: - 多次独立用户请求 - 不同 session - 已结束后再次重新触发的新一轮执行 ### 第一版 task 主键建议 首选: - `taskId = root run_id` 原因: - 当前数据里最稳定的执行主键就是 `run_id` - `parent_run_id` 已能自然形成 task 树 - 前端不需要首版额外等后端提供新 ID ### 第一版 task 归属规则 推荐规则: 1. 找到 `parent_run_id = null` 的主 run 2. 将该 run 及其所有子孙 runs 视为一个 task 3. 该 task 的 office 生命周期跟随 root run 生命周期 边界情况: - 如果一个用户请求触发多个并列 root run,第一版必须选一个明确策略 第一版推荐策略: - 默认一个用户请求只允许一个主 root run - 如果出现多个并列 root run,先按时间拆成多个 task - 不在首版支持“一个 task 多个 root run 合并” ### task 状态映射建议 建议 task 状态从 root run 派生: - `waiting` -> 待启动 - `queued` -> 排队中 - `running` -> 进行中 - `done` -> 已完成 - `error` -> 失败 - `cancelled` -> 已取消 补充派生状态: - `blocked`:仅当存在明确阻塞信号时额外推导,不要乱推 ### task 标题生成建议 优先级: 1. root run.title 2. 用户该轮请求的首句摘要 3. 默认标题,例如 `Task 2026-03-24 14:32` 要求: - 标题应短 - 可用于列表和 office 顶部摘要 - 不依赖长文本解析 ## 路由与入口建议 这一节用于明确 `office UI` 在当前前端中的落点。 ### 页面关系 建议关系: - `session page` 负责对话 - `office page` 负责展示某个 task 的运行态 两者是关联页面,不是同页双模式。 ### 路由建议 第一版推荐: - 列表页:`/office` - 详情页:`/office/[taskId]` 路由职责: - `/office` - 查看活跃 office 列表 - 查看最近结束 task 摘要 - 支持从 session 跳转过来的当前 task 高亮 - `/office/[taskId]` - 查看某个 task 的办公室画布 - 查看对应结构化面板和详情抽屉 ### 入口建议 首选入口: - 聊天页在主 agent 开始执行后,出现“查看任务现场”入口 - 点击后跳转 `/office/[taskId]` 辅助入口: - 顶部导航新增 `Office` - 进入 `/office` 查看当前活跃任务与最近任务 不建议的入口方式: - 强制把用户从聊天页自动跳走 - 每次任务启动都弹窗打断对话 ### 与现有导航的关系 建议: - 顶部导航增加 `Office` - 但不要把 `Office` 放成比 `对话` 更中心的一级任务入口 原因: - 主工作流仍然是对话发起任务 - `office UI` 是运行态观察层,不是主输入层 ## 首版页面线框 本节提供实现时可直接参考的结构。 ### `/office` 列表页 建议结构: - 顶部标题区 - 活跃 office 列表 - 最近结束 task 列表 - 过滤栏:全部 / 进行中 / 失败 / 最近完成 每个卡片建议包含: - task 标题 - 来源 session 标识 - 主 agent 名称 - 当前状态 - 最近活跃时间 - sub-agent 数量 - 错误 / 告警数 - “进入办公室”按钮 页面目标: - 让用户快速找到正在跑的 task - 让用户快速回看最近结束的 task ### `/office/[taskId]` 详情页 建议结构: - 顶部摘要条 - 主画布区 - 右侧事实面板 - 底部或侧边详情抽屉 #### 顶部摘要条 建议包含: - task 标题 - task 状态 - 来源 session 入口 - 创建时间 / 运行时长 - 返回对话按钮 #### 主画布区 建议包含: - 办公室背景分区 - 主 agent 卡 - sub-agent 卡 - 状态气泡 - 简化分工连线或区域归属 #### 右侧事实面板 建议包含 4 个模块: - 任务看板 - 当前进度 - 分工面板 - 成员名单 #### 底部或侧边详情抽屉 点击对象后展示: - run 摘要 - 最近事件 - 产物列表 - 错误信息 ## 首版组件清单 建议先拆成以下组件,而不是把整个页面写成单文件。 ### 页面级组件 - `OfficeListPage` - `OfficeDetailPage` ### 数据组装层 - `buildOfficeView(taskId, storeState)` - `buildOfficeTaskList(sessionId?, storeState)` ### 列表页组件 - `OfficeTaskCard` - `OfficeTaskStatusBadge` - `OfficeTaskFilterBar` ### 详情页组件 - `OfficeHeader` - `OfficeCanvas` - `OfficeZones` - `OfficeAgentCard` - `OfficeTaskBoard` - `OfficeProgressPanel` - `OfficeAssignmentPanel` - `OfficeRosterPanel` - `OfficeDetailDrawer` ### 公共派生组件 - `TaskDuration` - `TaskStageBadge` - `AgentStateDot` - `OfficeAlertPill` ## 首版交互原则 ### 列表页 - 单击卡片进入 office 详情 - 已结束 task 默认折叠到“最近任务”区 - 活跃 task 永远优先展示在上方 ### 详情页 - 点击 agent 卡,打开对应 run 详情 - 点击任务卡,定位到对应分工或详情 - 点击 session 入口,返回聊天页对应会话 ### 任务结束时 - 如果用户正在 office 详情页内,不强制跳走 - 页面显示“任务已完成,办公室已解散” - 保留摘要和结果查看能力 - 从活跃状态切换为历史状态 ## 当前实现建议 如果按现有代码结构推进,推荐顺序: 1. 先新增 `/office` 和 `/office/[taskId]` 路由骨架 2. 先做 `OfficeView` 派生函数 3. 先接真实 `processRuns / processEvents / processArtifacts` 4. 再做静态办公室画布 5. 最后补动效和视觉包装 这样可以避免先做出一套漂亮但没有稳定数据归属的空画布。 ## OfficeView 类型设计 这一节定义首版前端建议使用的 view model,目的是隔离原始 store 数据和页面 UI。 ### 顶层对象 ```ts type OfficeTaskStatus = | 'queued' | 'running' | 'waiting' | 'done' | 'error' | 'cancelled' | 'blocked'; type OfficeZoneId = | 'reception' | 'workspace' | 'collab' | 'research' | 'alert' | 'done'; interface OfficeView { officeId: string; taskId: string; sessionId: string | null; title: string; status: OfficeTaskStatus; createdAt: string; updatedAt: string; finishedAt: string | null; durationMs: number | null; sourceSessionLabel: string; rootRunId: string; rootActorName: string; currentStageLabel: string | null; progress: OfficeProgressView; stats: OfficeStatsView; alerts: OfficeAlertView[]; zones: OfficeZoneView[]; members: OfficeMemberView[]; tasks: OfficeTaskView[]; assignments: OfficeAssignmentView[]; detailRunIds: string[]; } ``` 说明: - `officeId` 和 `taskId` 第一版可以相同,均使用 `rootRunId` - `status` 以 root run 为主,再允许前端推导 `blocked` - `detailRunIds` 用于详情抽屉的可选对象列表 ### 进度对象 ```ts interface OfficeProgressView { mode: 'stage' | 'ratio' | 'status'; label: string; value: number | null; max: number | null; stageLabel: string | null; } ``` 约束: - `value/max` 只在有可靠分子分母时填写 - 没有可靠比值时只显示 `stageLabel` ### 统计对象 ```ts interface OfficeStatsView { totalRuns: number; activeRuns: number; doneRuns: number; errorRuns: number; cancelledRuns: number; memberCount: number; artifactCount: number; } ``` ### 分区对象 ```ts interface OfficeZoneView { id: OfficeZoneId; label: string; memberIds: string[]; taskIds: string[]; tone: 'neutral' | 'info' | 'warn' | 'danger' | 'success'; } ``` ### 成员对象 ```ts interface OfficeMemberView { memberId: string; actorId: string; actorName: string; actorType: 'agent' | 'mcp' | 'system'; status: OfficeTaskStatus; zoneId: OfficeZoneId; currentRunId: string; currentTitle: string; stageLabel: string | null; summary: string | null; startedAt: string | null; updatedAt: string | null; finishedAt: string | null; childRunIds: string[]; artifactCount: number; isPrimary: boolean; } ``` 设计原则: - `member` 是按 actor 聚合,不是简单复制 run - `currentRunId` 指向该 actor 当前最值得展示的 run ### 任务对象 ```ts interface OfficeTaskView { taskId: string; runId: string; parentRunId: string | null; actorId: string; actorName: string; actorType: 'agent' | 'mcp' | 'system'; title: string; status: OfficeTaskStatus; stageLabel: string | null; summary: string | null; startedAt: string; updatedAt: string; finishedAt: string | null; childTaskIds: string[]; artifactCount: number; errorText: string | null; isRoot: boolean; } ``` 说明: - `OfficeTaskView` 面向任务看板 - `taskId` 第一版可直接等于 `runId` ### 分工对象 ```ts interface OfficeAssignmentView { ownerRunId: string; ownerActorName: string; assigneeRunIds: string[]; assigneeActorNames: string[]; label: string; } ``` ### 告警对象 ```ts interface OfficeAlertView { id: string; level: 'info' | 'warn' | 'error'; title: string; description: string | null; runId: string | null; actorId: string | null; createdAt: string; } ``` ## buildOfficeView 派生规则 这一节定义如何从现有 store 数据推导 `OfficeView`。 ### 输入 建议输入: ```ts interface BuildOfficeViewInput { taskId: string; sessions: Session[]; processRuns: ProcessRun[]; processEvents: ProcessEvent[]; processArtifacts: ProcessArtifact[]; } ``` ### 第一步:定位 root run 规则: - `taskId` 第一版默认等于 `rootRunId` - 在 `processRuns` 中找到 `run_id === taskId` 的 run - 若找不到则返回 `null` 失败处理: - 详情页展示 “任务不存在或已失效” ### 第二步:收集 task 子树 规则: - 从 root run 出发 - 递归收集所有 `parent_run_id === 当前 run_id` 的子孙 runs - 形成当前 task 的完整 run tree 派生结果: - `taskRuns` - `taskRunIds` ### 第三步:收集 events / artifacts 规则: - `taskEvents = processEvents.filter(event => taskRunIds.has(event.run_id))` - `taskArtifacts = processArtifacts.filter(artifact => taskRunIds.has(artifact.run_id))` ### 第四步:派生 task 更新时间 优先级: 1. taskEvents 中最新 `created_at` 2. taskArtifacts 中最新 `created_at` 3. root run.finished_at 4. root run.started_at ### 第五步:派生 task 状态 规则: - 默认直接取 root run.status - 只有在存在明确阻塞信号时才提升为 `blocked` 第一版阻塞信号建议: - root run.status 为 `waiting` - 且在一段阈值时间内没有新的 progress / artifact / status 更新 注意: - `blocked` 是前端推导态,不是后端事实态 - 第一版如果证据不足,可以先不启用 `blocked` ### 第六步:派生阶段标签 优先级建议: 1. `rootRun.metadata.stage_label` 2. 最近一个相关 event 的 `metadata.stage_label` 3. 基于状态的回退文案,例如“执行中”“等待中”“已完成” 第一版要求: - 没有可靠阶段字段就显示空,不要硬猜复杂阶段 ### 第七步:派生成员列表 规则: - 按 `actor_id` 分组 taskRuns - 每组选择“当前展示 run” 当前展示 run 选择优先级: 1. `running` 2. `waiting` 3. `queued` 4. 最近更新的非终态 run 5. 最近完成的终态 run 然后派生: - `status` - `zoneId` - `currentTitle` - `artifactCount` - `childRunIds` ### 第八步:派生任务看板 规则: - 将 taskRuns 全量转为 `OfficeTaskView` - 保留树形关系 `parentRunId -> childTaskIds` - root run 置 `isRoot = true` 排序建议: 1. `isRoot` 2. 非终态优先 3. 最近更新时间倒序 ### 第九步:派生分工关系 规则: - 对每个有子 run 的 run 生成一条 `OfficeAssignmentView` - owner = 当前 run - assignee = 直接 children ### 第十步:派生告警 第一版告警来源建议: - `run.status === error` - 最近 `run_status` / `run_finished` 事件中有错误摘要 - 长时间 `waiting` 输出原则: - 少而准 - 不在首页堆满低价值提醒 ### 第十一步:派生分区 第一版分区映射建议: - `queued` -> `reception` - `waiting` -> `collab` - `running` - actor_type 为 `mcp` 时可放 `research` - 其他默认放 `workspace` - `error` -> `alert` - `cancelled` -> `alert` - `done` -> `done` 注意: - 分区映射是 UI 规则,不是业务事实 - 必须保持稳定,不能频繁变动语义 ### 第十二步:派生进度 优先级建议: 1. 若存在明确阶段总数与当前阶段,使用 `mode = ratio` 2. 若仅能得到子任务完成数,使用 `mode = ratio` 3. 若只能得到当前阶段标签,使用 `mode = stage` 4. 否则使用 `mode = status` 第一版推荐公式: - `value = doneRuns` - `max = totalRuns` 注意: - 这只是“子任务完成比例”,不是业务整体真实完成度 - 文案必须明确,例如“已完成子任务 3 / 7” ## 字段来源对照 ### 来自 ProcessRun - `run_id` - `parent_run_id` - `session_id` - `actor_id` - `actor_name` - `actor_type` - `title` - `status` - `started_at` - `finished_at` - `summary` - `metadata` ### 来自 ProcessEvent - `created_at` - `text` - `status` - `kind` - `metadata` 用途: - 更新时间 - 阶段标签 - 告警摘要 - 最近进展 ### 来自 ProcessArtifact - `artifact_id` - `run_id` - `title` - `artifact_type` - `created_at` 用途: - 产物数量 - 最近活跃时间 - 结果完成感 ### 来自 Session - `key` - `created_at` - `updated_at` 用途: - office 返回入口 - source session label ## 当前实现提醒 ### 不要在类型层做的事 - 不要把办公室坐标直接写死进 `OfficeView` - 不要把动画状态混进数据模型 - 不要把完整 event 文本直接塞进成员对象 ### 可以在 UI 层处理的事 - agent 卡的像素样式 - 分区背景样式 - 动画和入场过渡 - hover / selection / drawer open 状态 ## 当前明确不该做的事 第一版不要做: - 在画布上塞完整聊天消息 - 在画布上展示原始 event 列表 - 让一个 agent 因多个 run 复制出多个角色分身且没有解释 - 使用没有依据的任务百分比 - 把任务配置、任务编辑、故障排查都塞到同一屏 ## 本次补充结论 本次新增共识: - `session` 和 `office UI` 是分开的页面 - `session` 负责维护对话 - `task` 是用户在 session 中发起的一次主 agent 执行请求 - `office` 是 task 的临时运行态空间,不是 session 的长期容器 - `office UI` 第一版应直接建立在现有 `processRuns + processEvents + processArtifacts` 之上 - `root run / member runs` 结构适合作为任务看板和分工区的基础 - 办公室画布只表达态势,不承载深度细节 - 深度细节通过结构化面板和详情抽屉承接 - 页面应按“概览层 -> 事实层 -> 详情层”分层,而不是全部平铺 ## 暂定非目标 当前不把以下内容设为首期目标: - 完整项目管理系统 - 甘特图级别依赖编排 - 精确工时统计 - 复杂权限编排 - 复刻 `Star-Office-UI` 的全部玩法和资产系统 ## 后续需要继续明确的问题 - 办公室切换是放在聊天页内,还是独立路由页 - 一个 session 中多个 task 的切换入口应该如何暴露 - 任务看板的数据源来自 process runs、cron、会话消息,还是新的聚合接口 - “分工” 是自动从委派关系推导,还是允许人工编辑 - 进度计算是否需要后端显式上报阶段 - 画布是否允许拖拽布局,还是固定布局 - 是否采用像素风,还是只保留办公室隐喻、不使用像素素材 ## 更新记录 ### 2026-03-24 首次创建,记录当前共识: - 借鉴 `Star-Office-UI` 的办公室可视化概念 - 目标是长耗时、多 agent 任务的运行态总览 - session 和 office UI 分离 - session 用于维护对话 - task 是一次主 agent 执行周期 - 一个 task 对应一个临时 office,task 结束则 office 解散 - 办公室中可包含任务看板、进度、分工、人员名单 - 但办公室页不应替代结构化任务管理和详细诊断视图 同日实现进展: - 已新增 `/office` 列表页骨架 - 已新增 `/office/[taskId]` 详情页骨架 - 已在顶部导航加入 `Office` 入口 - 已实现 `buildOfficeView` 和 `buildOfficeTaskList` - 第一版页面已接入真实的 `processRuns / processEvents / processArtifacts` 数据 - 已补 `OfficeView` / `OfficeTaskList` 的单元测试 - 已在聊天页加入直达当前 task 对应 `/office/[taskId]` 的入口 - 已对 office 详情页的画布分区做第一轮视觉打磨 同日导航结构修正: - 一级导航将 `Office` 和 `定时任务` 合并为 `任务管理` - `任务管理` 默认跳转到 `/office` - `任务管理` 内部再分两个子页: - `Office` - `定时任务` - `Office` 列表页、`Office` 详情页、`定时任务` 页共享同一组二级页签 - 路由保持不变: - `/office` - `/office/[taskId]` - `/cron` 同日视觉方向补充: - `Office` 画布应继续向“像素风游戏感”靠拢 - 目标不是简单套一层像素贴图,而是让页面更像一个可观察的 2D 办公室场景 - 第一优先级是空间感、角色感、状态感 - 第二优先级才是像素装饰、像素字体、像素动效 - 第一版技术选型改为 `Phaser` - `Phaser` 只负责办公室场景画布 - 结构化面板、任务看板、详情抽屉仍保留在 React 页面层 - 已把 `/office/[taskId]` 进一步改成更接近 `Star-Office-UI` 的舞台式布局 - 页面主结构调整为:上方像素办公室舞台 + 下方像素面板 + 右侧结构化任务板 像素风画布的实现原则: - 画布先像一个房间,再像一个后台页面 - 成员先像场景角色,再像数据卡片 - 状态先通过位置、朝向、动作、灯光表达,再补文字标签 - 仍然必须保留结构化信息入口,不能为了游戏感牺牲可读性 第一版建议做的视觉元素: - 固定视角办公室平面布局 - 像素化地板、隔断、会议桌、工位、服务器角、归档区 - 每个 agent 使用统一规则的小型像素角色牌 - 用状态色和状态动作表达: - `running`:轻微闪动、桌面亮起 - `waiting`:低饱和、静止 - `blocked`:红色告警灯或感叹号 - `done`:柔和高亮或归档动作 - 用连接线或小型任务浮标表达主 agent 与 sub-agent 的委派关系 第一版不要做的伪像素化处理: - 整屏强行加像素滤镜 - 用大量像素素材堆满背景 - 为了复古感牺牲文字可读性 - 把所有信息都做成悬浮气泡导致画面很吵 - 让所有成员持续运动,破坏扫描效率 推荐的实现顺序: 1. 先把画布从“分区卡片网格”改成“房间式布局” 2. 再把成员卡改成带角色感的场景单位 3. 再补状态动画、灯光、任务浮标 4. 最后才决定是否引入真正的像素素材或像素字体 ### 2026-03-25 像素风实现路线补充: - 当前版本虽然使用了 `Phaser`,但主要仍是程序化矩形和几何块体,观感更接近“示意图”而不是“像素游戏房间” - 与 `Star-Office-UI` 的核心差距不在是否使用 `Phaser`,而在是否采用了完整的像素游戏生产方式 - 后续不应继续主要依赖 `graphics` / `rectangle` 拼场景,而应转向 `tileset + sprite + atlas + 低分辨率放大` 像素风的 6 个必要层级: 1. 低分辨率逻辑画布,再整数倍放大 2. 统一调色板,而不是自由配色 3. 像素素材驱动,而不是几何图形驱动 4. 固定房间语法和明确空间分区 5. 克制的帧动画和状态动画 6. Phaser 世界层与 React / DOM 信息面板分离 建议工具链: - 场景引擎:`Phaser` - 角色 / 家具 / 图块绘制:`Aseprite` - 平铺地图编辑:`Tiled` - 最终资源形式:`tileset png + sprite sheet png + json atlas` 建议目录结构: - `public/office/tiles/` - `public/office/sprites/` - `public/office/atlas/` - `public/office/maps/` - `components/office/phaser/` 重做顺序: 1. 先确定逻辑分辨率,例如 `400x225` 或 `480x270` 2. 先定义办公室专用调色板,控制主色、阴影色、灯光色、状态色 3. 先做地板、墙体、隔断、桌椅、服务器、归档箱的 tileset 4. 再做 agent 角色的最小动画集: - idle - walk - type - blocked - done 5. 用 `Tiled` 产出固定房间地图,而不是继续在代码里硬编码大量图形 6. 在 `Phaser` 中加载 tilemap、tileset、sprite atlas,按图层拼装办公室 7. 把运行状态映射成“房间位置 + 动画 + 状态浮标”,而不是主要靠文字标签 8. React 页面继续只承载: - 昨日小记 - 任务看板 - 办公人员名单 - 详情抽屉 当前代码层面的明确改造方向: - [components/office/OfficePhaserCanvas.tsx](/home/ivan/xuan/nano_project/app-instance/frontend/components/office/OfficePhaserCanvas.tsx) 不应继续扩展矩形绘制细节 - 下一版应拆成: - 场景资源加载 - tilemap 创建 - 成员 sprite 实例化 - 动画注册 - run -> scene state 映射 - 角色、家具、房间应优先做成资源文件,而不是继续写死在 TSX 内 明确的非目标: - 不靠整页像素滤镜制造“伪像素感” - 不靠像素字体替代真正的像素场景 - 不在没有素材体系前继续微调现有程序化块体配色 本轮进一步确定: - `Office` 像素画布的世界坐标固定为 `400x225` - 内部渲染分辨率提升为 `800x450` - 最终显示采用整数倍缩放,不做非整数拉伸 - 调色板方向参考 `openclaw-theme-stardew` 的冬季主题 - 视觉氛围关键词: - 冬夜深蓝 - 冰霜蓝灰 - 雪白文字 - 壁炉橙点缀 冬季基础调色板: - 背景主色:`#1a2433` - 背景次色:`#232f42` - 背景抬升:`#2c3a52` - 卡片/家具中间色:`#3a4a62` - 边框色:`#4a5a72` - 边框高亮:`#5a7092` - 文本主色:`#e8f0f8` - 文本强色:`#f5faff` - 弱文本:`#8aa0b8` - 主强调色:`#ff8a65` - 强调高亮:`#ffab91` - 焦点/冷光:`#90caf9` 场景层建议的派生色: - 地板深色:`#243041` - 地板亮格:`#31425b` - 墙体阴影:`#17202d` - 窗外夜色:`#101827` - 屏幕冷光:`#8fd3ff` - 完成态绿:`#78c27a` - 阻塞态红:`#d96c75` - 等待态米黄:`#d8c79a` 第一批素材最小集合: - 房间 tileset - 墙体 - 墙角 - 地板 - 地毯 - 窗户 - 家具 sprites - 工位桌 - 椅子 - 会议桌 - 沙发 - 茶几 - 服务器柜 - 归档箱 - 植物 - 灯 - 角色 sprites - 主 agent - 通用 sub-agent - 访客 / 等待角色 - 状态 sprites - 感叹号 - 任务气泡 - 屏幕闪烁 - 告警灯 - 完成勾标 角色第一版动画最小集合: - `idle` - `walk` - `type` - `blocked` - `done` 素材制作顺序: 1. 先做房间 tileset,再做家具 2. 家具完成后先在 `Tiled` 里搭出固定办公室 3. 最后再做角色和状态动画 4. 没有房间和家具前,不先做复杂角色细节 第一批素材规格书: 基础尺寸约定: - 基础 tile 尺寸统一为 `16x16` - 大件家具优先按 `16` 的倍数出图,避免后续拼接时出现半格对齐 - 角色单帧基准尺寸先定为 `16x24` - 小型状态图标基准尺寸先定为 `8x8` 或 `12x12` - UI 面板仍由 React / CSS 承载,不纳入本批像素素材制作 地图网格约定: - 逻辑分辨率:`400x225` - 地图绘制网格:`16x16` - 可视区域理论格数约为: - 宽 `25` 格 - 高 `14` 格 - 允许顶部或底部保留少量非满格安全边距,但房间主体尽量在整数格内完成 tileset 规格: - 文件:`public/office/tiles/office-winter-tileset.png` - 单格:`16x16` - 首版建议内容: - 墙面浅色 tile - 墙面阴影 tile - 内墙/隔断 tile - 地板深色 tile - 地板亮色 tile - 地毯边缘 tile - 地毯中心 tile - 窗框 tile - 窗外夜景 tile - 踢脚线 / 边框 tile 家具 sprite 规格: - 文件目录:`public/office/sprites/furniture/` - 命名规则: - `desk-workstation.png` - `chair-office.png` - `table-meeting.png` - `sofa-2seat.png` - `table-coffee.png` - `rack-server.png` - `crate-archive.png` - `lamp-floor.png` - `plant-office.png` - 建议尺寸: - 工位桌:`32x16` - 椅子:`16x16` - 会议桌:`32x24` - 双人沙发:`32x16` - 茶几:`16x16` - 服务器柜:`16x32` - 归档箱:`16x16` - 落地灯:`16x32` - 植物:`16x24` 角色 sprite 规格: - 文件目录:`public/office/sprites/agents/` - 命名规则: - `agent-main.png` - `agent-worker.png` - `agent-visitor.png` - 单帧尺寸:`16x24` - 第一版朝向: - 先只做正面 / 背面 / 侧面三向 - 第一版动画帧数建议: - `idle`: `2` 帧 - `walk`: `4` 帧 - `type`: `2` 帧 - `blocked`: `2` 帧 - `done`: `2` 帧 状态图标规格: - 文件目录:`public/office/sprites/status/` - 命名规则: - `icon-alert.png` - `icon-task.png` - `icon-done.png` - `icon-wait.png` - `light-warning.png` - 建议尺寸: - 感叹号:`8x8` - 任务气泡:`12x12` - 完成标记:`8x8` - 等待气泡:`8x8` - 告警灯:`8x8` 第一张办公室地图必须包含的区域: - 左侧工位区 - 中央协作区 - 右侧服务器 / 监控区 - 下侧归档区 - 上侧窗户 / 夜景区 第一张办公室地图不做的内容: - 多房间切换 - 可拖拽家具 - 复杂门禁 - 楼梯 / 二层结构 - 动态天气 Tiled 第一版图层建议: - `bg-floor` - `bg-rug` - `walls` - `windows` - `furniture-base` - `furniture-top` - `markers` - `collision` 第一批最先开画的 10 个资产: 1. `floor-dark` 2. `floor-light` 3. `wall-main` 4. `window-night` 5. `desk-workstation` 6. `chair-office` 7. `sofa-2seat` 8. `rack-server` 9. `crate-archive` 10. `agent-worker` 实现约束: - 所有像素图禁止后期模糊缩放 - 输出时保留原始整数像素,不做抗锯齿 - 家具和角色优先保证轮廓清晰,再补内部细节 - 首版宁可资产少,也不要为了“丰富”而拉杂 仓库骨架已创建: - [public/office/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/README.md) - [public/office/tiles/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/tiles/README.md) - [public/office/sprites/furniture/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/sprites/furniture/README.md) - [public/office/sprites/agents/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/sprites/agents/README.md) - [public/office/sprites/status/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/sprites/status/README.md) - [public/office/atlas/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/atlas/README.md) - [public/office/maps/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/maps/README.md) - [public/office/maps/office-winter-v1-sketch.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/maps/office-winter-v1-sketch.md) - [public/office/maps/office-winter-v1-grid.txt](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/maps/office-winter-v1-grid.txt) - [public/office/maps/office-winter-v1.tmj](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/maps/office-winter-v1.tmj) 占位 Tiled 地图已生成: - 使用 `25x14` 网格和 `16x16` tile - 直接引用未来的 `../tiles/office-winter-tileset.png` - 图层已预拆为: - `bg-floor` - `bg-rug` - `walls` - `windows` - `markers` - `furniture-anchors` - `collision` - 家具、服务器、归档箱、装饰点暂时先落为 `object layer anchors` - 后续真实素材接入时,可优先替换 `tileset` 和 `furniture-anchors` 的渲染映射,不必重画地图结构 首版占位 tileset 已生成: - [public/office/tiles/office-winter-tileset.png](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/tiles/office-winter-tileset.png) - [public/office/tiles/office-winter-tileset-preview.png](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/tiles/office-winter-tileset-preview.png) - 当前是“可用占位图块”,目标是: - 让 `Tiled` 和 `Phaser` 不再处于缺图状态 - 让布局讨论从纯色块进入“基础像素质感”阶段 - 后续真实美术可以逐格替换,不影响现有地图编号 当前地图预览已切换为真实 tileset 拼接: - [public/office/maps/office-winter-v1-preview.png](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/maps/office-winter-v1-preview.png) 已引入 `pixel-agents` 资产作为当前内用原型素材库: - [public/office/vendor/pixel-agents/README.md](/home/ivan/xuan/nano_project/app-instance/frontend/public/office/vendor/pixel-agents/README.md) - 资产已整体落入: - `public/office/vendor/pixel-agents/assets/furniture/` - `public/office/vendor/pixel-agents/assets/floors/` - `public/office/vendor/pixel-agents/assets/walls/` - `public/office/vendor/pixel-agents/assets/characters/` - 当前已经在 `OfficePhaserCanvas` 中接入的是真实家具 sprite,不再只显示锚点框 - 当前已映射的主要锚点类型: - `desk-anchor` - `chair-anchor` - `sofa-anchor` - `coffee-anchor` - `meeting-anchor` - `server-anchor` - `archive-anchor` - `decor-anchor` - `pixel-agents` 角色素材也已接入当前运行态画布 - 当前做法是: - 每个成员按顺序分配一套角色皮肤 - 先裁切站立帧作为第一版展示 - 状态变化仍通过徽标、灯光、告警符号和轻微动画表达 - 下一阶段再考虑把角色切到真正的多帧动画: - `idle` - `walk` - `type` - `blocked` - `done` 第一版 tileset 逐格设计表: 文件约定: - 文件名:`public/office/tiles/office-winter-tileset.png` - 单格尺寸:`16x16` - 第一版先做 `4 x 4` 共 `16` 格 - 编号顺序按从左到右、从上到下 首版 16 格 tile 清单: 1. `tile-00-wall-main` - 主墙面 - 用于绝大多数内墙和外墙主体 2. `tile-01-wall-shadow` - 主墙面的阴影变体 - 用于墙角、隔断背光面 3. `tile-02-wall-trim-top` - 墙顶部边线 / 踢脚线风格装饰 - 用于形成冬季室内木质边框感 4. `tile-03-window-night` - 窗框 + 夜色 - 允许少量雪蓝高光 5. `tile-04-floor-dark` - 深色地板主格 - 为办公室主走道和大部分可行走区域提供底色 6. `tile-05-floor-light` - 地板亮格 - 与深格交替,形成轻微棋盘层次 7. `tile-06-floor-shadow` - 地板压暗格 - 用于家具下方、墙边和角落 8. `tile-07-rug-center` - 地毯中心格 - 适合中部休息区 9. `tile-08-rug-edge-top` - 地毯上边缘 10. `tile-09-rug-edge-bottom` - 地毯下边缘 11. `tile-10-rug-edge-left` - 地毯左边缘 12. `tile-11-rug-edge-right` - 地毯右边缘 13. `tile-12-corner-inner` - 内转角 - 用于墙体或地毯转角收边 14. `tile-13-corner-outer` - 外转角 - 用于房间边界和隔断端点 15. `tile-14-border-accent` - 装饰边框格 - 用于壁炉橙 / 冷蓝点缀的少量节奏 16. `tile-15-placeholder-debug` - 调试占位格 - 首版用于 Tiled 中快速查错,不进入最终视觉 每格绘制要求: - 所有 tile 都必须在 `16x16` 内完成,不允许跨格画主体 - 第一版每格内部色彩控制在 `3-5` 个主色内 - 轮廓优先用较深色,不用纯黑硬描整圈 - 高光只落在上边或左上角,统一光源方向 - 地板和墙面的纹理必须克制,不能把 tile 画成噪点块 首版 tileset 排版建议: - 第 1 行:墙体相关 - 第 2 行:地板相关 - 第 3 行:地毯相关 - 第 4 行:角落 / 边框 / 调试格 首版绘制优先级: 1. `tile-04-floor-dark` 2. `tile-05-floor-light` 3. `tile-00-wall-main` 4. `tile-03-window-night` 5. `tile-07-rug-center` 6. `tile-08/09/10/11-rug-edge-*` 首版完成标准: - 能在 `Tiled` 里先拼出完整房间外轮廓 - 能区分墙、地板、窗户、地毯 4 个层次 - 即使还没有家具和角色,也能看出“冬季办公室”的空间氛围 最新运行时视觉约束: - 场景里不显示分区标题 - 场景里不显示彩色分区框 - 分区只用于成员落点和状态映射,不在画布上显式标注 最新机器人表现约束: - 机器人整体尺寸上调一档 - 主 agent 比普通成员再大一档 - running 态不使用走路朝向,统一正面朝下 - 运行感主要靠轻微上下浮动和状态灯,不靠横向行走 - 运行时角色必须使用 spritesheet frame 渲染,不允许再用整张角色图错误裁切 - `public/office/maps/office-winter-v1-scene-preview.png` 必须跟随当前地图、家具和角色资源重导,不能长期作为过期效果图保留 最新画布简化约束: - 场景顶部左侧标题条移除 - 场景顶部右侧阶段条移除 - 场景底部 focus/点击提示牌匾移除 最新运行态规则修正: - 已完成成员不单独停留在完成区,而是返回讨论区(collab)