Files
sanguo_moziplus_v2/docs/design/topic9-dashboard-design.md
T
2026-05-17 12:52:48 +08:00

17 KiB
Raw Blame History

课题9:Dashboard 前后端完整设计方案

版本: v2.0(评审修订版) 作者: 庞统(副军师)🐦 日期: 2026-05-17 状态: 待司马懿确认 评审记录: Mail #278(司马懿 P0+P1 评审)→ 已全部修复 → Mail #279(验证通过)


一、核心定位

1.1 Dashboard 不是管理控制台,是 AI 工作可视化窗口

传统 Dashboard AI Native Dashboardv2.0
用户主动操作(点按钮、填表单) AI 主动展示(推送状态、产出、决策请求)
用户监控一切 AI 只在关键点拉人,其余自主
表格+表单为主 时间线+卡片+对话为主
用户是驾驶员 用户是乘客,偶尔导航

1.2 参考的优秀实践

实践 借鉴点
Edict MiniPipe 任务卡片上的状态管线可视化
Hermes Kanban 卡片 + 结果区 + 评论线程 + 事件流 + Nudge 按钮
Claude Code App 多 Tab 切换详情面板(产出/评审/事件)
Linear 精准通知不噪音;状态流转按钮守卫
Devin AI 自主执行的观察窗口理念
OpenClaw Control Center 双入口对等(对话 + Dashboard

二、数据模型

2.1 V2Task 类型(对齐后端 tasks 表)

前端核心类型,字段完全对齐后端 src/blackboard/db.pytasks 表:

interface V2Task {
  // 基础字段(直接对应 tasks 表列)
  id: string;
  title: string;
  description: string;
  status: TaskStatus;          // 8 状态枚举,见 §2.2
  assignee: string | null;
  assigned_by: string | null;
  depends_on: string | null;   // 依赖任务 ID
  parent_task: string | null;  // 父任务 ID
  priority: number;            // 1-10 整数(P0 修复:前端曾用 string,已改 number
  task_type: string;
  created_at: string;
  updated_at: string;
  claimed_at: string | null;
  completed_at: string | null;
  started_at: string | null;
  deadline: string | null;
  retry_count: number;
  max_retries: number;
  risk_level: 'low' | 'standard' | 'high' | 'critical';
  escalated: number;           // 0/1 整数(与后端 INTEGER 一致)

  // 聚合字段(后端 summary API 或前端多次查询)
  comments_count: number;
  outputs_count: number;
  review_status: 'none' | 'pending' | 'approved' | 'rejected' | 'rebuttal';
  latest_event: string | null;
  project_id: string;
}

2.2 状态机(8 状态,对齐后端 VALID_STATUSES

pending ──→ claimed ──→ working ──→ review ──→ done
  │           │           │           │
  │           │           ├→ blocked  ├→ failed
  │           │           │           │
  └→ cancelled←┴───────────┴───────────┘

线性管线5 步):pending → claimed → working → review → done

旁路状态failed(标记在 working 位置)、blocked(标记在 working 位置)

终态donecancelled

2.3 状态流转守卫(对齐后端 VALID_TRANSITIONS

const VALID_TRANSITIONS: Record<string, string[]> = {
  pending:   ['claimed', 'cancelled'],
  claimed:   ['working', 'pending', 'cancelled'],
  working:   ['review', 'blocked', 'failed', 'cancelled'],
  review:    ['done', 'pending', 'failed', 'cancelled'],
  blocked:   ['pending', 'cancelled'],
  failed:    ['pending'],
  done:      [],
  cancelled: [],
};

与后端 src/blackboard/db.pyVALID_TRANSITIONS 完全一致。

2.4 Priority 映射

后端 priority 是 INTEGER1-10),前端映射:

const PRIORITY_META: Record<number, { color: string; label: string }> = {
  1: { color: '#6b7280', label: '低' },
  2: { color: '#6b7280', label: '低' },
  3: { color: '#3b82f6', label: '中' },
  4: { color: '#3b82f6', label: '中' },
  5: { color: '#f59e0b', label: '高' },
  6: { color: '#f59e0b', label: '高' },
  7: { color: '#ff5270', label: '紧急' },
  8-10: { color: '#ff5270', label: '紧急' },
};

三、前端页面设计

3.1 页面结构

页面 组件 优先级 状态
📜 任务看板 EdictBoard P0 Mock UI 已完成,待对接 API
🔍 任务详情 TaskModal P0 Mock UI 已完成,待对接 API
🏛️ 朝堂议政 CourtDiscussion P1 迁移自 v1.0
🔌 编排调度 MonitorPanel P1 迁移自 v1.0
🤺 将军总览 OfficialPanel P1 迁移自 v1.0
🔔 通知中心 NotificationCenter P1 占位(Header 铃铛)
📊 AI Briefing AIBriefing P2 占位页面
⚙️ 系统设置 SettingsPanel P1 迁移自 v1.0

3.2 任务看板(EdictBoard

顶部统计(4 格)

内容 数据来源
活跃任务 working + claimed + review 数 tasks 表 count
已完成 done 数 tasks 表 count
失败/阻塞 failed + blocked 数 tasks 表 count
审查中 review 数 tasks 表 count

状态管线可视化(5 步线性)

📋待认领 → 👤已认领 → ⚔️执行中 → 🔍审查中 → ✅已完成
  • 管线上游步骤显示 ✓(绿色),当前步骤高亮(蓝色),下游步骤灰显
  • failed 状态:在 working 位置显示 ✗(红色)
  • blocked 状态:在 working 位置显示 🚧(黄色)
  • cancelled 状态:管线全部灰显

筛选栏

8 个状态筛选按钮(all/pending/claimed/working/review/done/failed/blocked+ 搜索框。

任务卡片

每张卡片展示:

  • 状态管线5 步 MiniPipe
  • ID + 标题
  • 标签行:状态标签 + 负责人(agent emoji)+ 优先级 + 升级标记 + 依赖标记
  • 元信息行:更新时间 + 产出数 + 评论数 + 审查状态图标
  • 最新事件:单行文本截断
  • 底部:风险等级 + 重试次数 + 截止时间 + 详情按钮

3.3 任务详情面板(TaskModal

点击任务卡片弹出 Modal,4 Tab 布局:

Tab 1: 📋 总览

区域 内容 数据来源
需求描述 task.description tasks 表
状态操作 基于 VALID_TRANSITIONS 的操作按钮 §2.3 守卫
任务信息 类型/优先级/风险/重试/时间/分配人(8 字段网格) tasks 表
事件时间线 逆序事件列表(图标 + 描述 + 时间 + agent events 表
评论/交接 handoff/progress/review/rebuttal/debate/observation 6 种类型 comments 表
决策记录 决策人 + 决策类型 + 理由 decisions 表
Checkpoint 占位(虚线框 + "v2.7 提供" 后端未实现

Tab 2: 📦 产出

产出物列表:agent + 内容 + 类型标签 + 尝试次数 + 时间。数据来源:outputs 表。

Tab 3: 🔍 审查

审查意见:评审人 + verdictAPPROVE/REJECT+ 置信度 + 风险等级 + 辩论轮次 + 摘要。数据来源:reviews 表。

Tab 4: 🧠 经验

关联经验:标题 + 摘要 + 标签。左侧彩色边框区分 best_practice(绿)/ pitfall(红)/ environment(蓝)。数据来源:experiences 表。

3.4 通知中心(Header 铃铛)

🔔 (3)  → 点击展开通知面板
  • 按 4 级分组(🔴 Critical > 🟡 Warning > 🟢 Info > 🔵 Silent
  • 每条:时间 + 级别图标 + 内容摘要 + 关联任务链接
  • 🔴 可展开操作按钮
  • 支持标记已读/全部已读

当前状态SSE broker 已实现(F17),ticker 未推送事件。前端做铃铛 UI + 占位。

3.5 AI Briefing

日报/周报自动生成。格式:

📊 今日战报 (2026-05-17)
━━━━━━━━━━━━━━━━━━━━
📋 完成任务:3 个
🔄 进行中:2 个
⚠️ 需关注:1 个
💰 Token 消耗:127K
🧠 新增经验:2 条

当前状态:后端未实现。前端做占位页面。


四、后端 API 设计

4.1 已有 APIF5 实现)

端点 方法 说明
/api/projects/{pid}/tasks GET 任务列表(支持 status 筛选)
/api/projects/{pid}/tasks POST 创建任务
/api/projects/{pid}/tasks/{tid} GET 单任务基本信息
/api/projects/{pid}/tasks/{tid} PATCH 更新任务
/api/projects/{pid}/tasks/{tid}/status POST 状态流转
/api/projects/{pid}/tasks/{tid}/claim POST 认领任务
/api/projects/{pid}/tasks/{tid}/comments GET/POST 评论 CRUD
/api/projects/{pid}/tasks/{tid}/outputs GET/POST 产出 CRUD
/api/projects/{pid}/tasks/{tid}/decisions GET/POST 决策 CRUD
/api/projects/{pid}/tasks/{tid}/observations GET/POST 观察 CRUD
/api/projects/{pid}/tasks/{tid}/reviews GET/POST 审查 CRUD
/api/events GET (SSE) SSE 事件流
/api/daemon/status GET Daemon 状态
/api/projects GET/POST 项目管理

4.2 需新增的 API

API-1: GET /api/projects/{pid}/tasks/{tid}?expand=comments,outputs,reviews,events,decisions

优先级: P0 说明: 任务详情聚合,一次返回全部关联数据,避免前端多次请求。 实现方案: 在 blackboard_routes.pyget_task 端点增加 expand 查询参数。

@router.get("/tasks/{task_id}")
async def get_task(
    task_id: str,
    expand: str = "",  # 逗号分隔: "comments,outputs,reviews,events,decisions"
    db_path: Path = Depends(get_db_path),
):
    task = queries.get_task(db_path, task_id)
    if not task:
        raise HTTPException(404, "Task not found")

    result = dict(task)

    if expand:
        for rel in expand.split(","):
            rel = rel.strip()
            if rel == "comments":
                result["comments"] = queries.list_comments(db_path, task_id)
            elif rel == "outputs":
                result["outputs"] = queries.list_outputs(db_path, task_id)
            elif rel == "reviews":
                result["reviews"] = queries.list_reviews(db_path, task_id)
                # 聚合 review_status
                if result["reviews"]:
                    latest = result["reviews"][-1]
                    result["review_status"] = "approved" if latest["verdict"] == "APPROVE" else "rejected"
                else:
                    result["review_status"] = "none"
            elif rel == "events":
                result["events"] = queries.list_events(db_path, task_id)
                # 聚合 latest_event
                if result["events"]:
                    result["latest_event"] = result["events"][-1]["detail"]
            elif rel == "decisions":
                result["decisions"] = queries.list_decisions(db_path, task_id)

    # 聚合计数
    result["comments_count"] = queries.count_comments(db_path, task_id)
    result["outputs_count"] = queries.count_outputs(db_path, task_id)

    return result

工作量: 30minqueries.py 已有基础方法,只需组合)

API-2: GET /api/projects/{pid}/tasks/{tid}/events

优先级: P0 说明: 任务事件时间线查询。 实现方案: 新增路由端点。

@router.get("/tasks/{task_id}/events")
async def get_task_events(
    task_id: str,
    limit: int = 50,
    offset: int = 0,
    db_path: Path = Depends(get_db_path),
):
    return queries.list_events(db_path, task_id, limit=limit, offset=offset)

前置: queries.list_events() 已存在(events 表 CRUD 已实现)。 工作量: 15min

API-3: GET /api/projects/{pid}/tasks/{tid}/experiences

优先级: P1 说明: 查询与任务关联的经验(通过 task_id 检索 experiences.jsonl)。 实现方案: 新增路由端点 + queries 方法。

@router.get("/tasks/{task_id}/experiences")
async def get_task_experiences(
    task_id: str,
    project_id: str,
):
    # 从 experiences.jsonl 读取,过滤 source_task == task_id
    experiences = experience_store.list_by_task(project_id, task_id)
    return experiences

工作量: 30min(需在 experience_store 增加 list_by_task 方法)

4.3 已有 API 但需增强

端点 增强 说明
GET /api/projects/{pid}/tasks 增加 counts 返回 任务列表 API 返回各状态计数,供统计卡片使用
GET /api/events (SSE) ticker 事件推送 ticker 状态变更时自动通过 SSE 推送(F17 SSEBroker 已实现,只需在 ticker 中调用 publish

4.4 完整路由表

/api/projects                                    GET/POST
/api/projects/{pid}                              GET/DELETE
/api/projects/{pid}/tasks                        GET/POST
/api/projects/{pid}/tasks/{tid}                  GET/PATCH/DELETE
/api/projects/{pid}/tasks/{tid}/status           POST
/api/projects/{pid}/tasks/{tid}/claim            POST
/api/projects/{pid}/tasks/{tid}/comments         GET/POST
/api/projects/{pid}/tasks/{tid}/outputs          GET/POST
/api/projects/{pid}/tasks/{tid}/decisions        GET/POST
/api/projects/{pid}/tasks/{tid}/observations     GET/POST
/api/projects/{pid}/tasks/{tid}/reviews          GET/POST
/api/projects/{pid}/tasks/{tid}/events           GET          ← 新增 API-2
/api/projects/{pid}/tasks/{tid}/experiences      GET          ← 新增 API-3
/api/events                                      GET (SSE)
/api/daemon/status                               GET

五、实现方案

5.1 后端改动清单

# 改动 文件 优先级 工作量
B1 get_task 增加 expand 参数 blackboard_routes.py + queries.py P0 30min
B2 新增 GET /tasks/{tid}/events 路由 blackboard_routes.py P0 15min
B3 新增 GET /tasks/{tid}/experiences 路由 blackboard_routes.py + experience_store.py P1 30min
B4 任务列表 API 返回状态计数 blackboard_routes.py P1 15min
B5 ticker 状态变更推送 SSE ticker.py P1 1h

5.2 前端改动清单

# 改动 文件 优先级 工作量
F1 EdictBoard Mock → 对接真实 API EdictBoard.tsx P0 1h
F2 TaskModal Mock → 对接真实 API TaskModal.tsx P0 1.5h
F3 api.ts 补新 API 方法 api.ts P0 30min
F4 store.ts 补状态字段 store.ts P0 15min
F5 SSE EventSource 替代轮询 App.tsx + store.ts P1 1h
F6 通知中心铃铛占位 NotificationCenter.tsx + App.tsx P1 30min
F7 AI Briefing 占位页面 AIBriefing.tsx P2 15min
F8 其余 v1.0 Tab 适配 各 Panel 组件 P1 2h

5.3 执行顺序

Phase 1: 后端补 APIB1-B3         ~1.5h
Phase 2: 前端核心对接(F1-F4         ~3h
Phase 3: SSE + 通知占位(B5+F5+F6    ~2.5h
Phase 4: 其余 Tab 适配(F8          ~2h
Phase 5: 联调 + E2E 验证              ~1h
总计: ~10h

5.4 不在本轮范围

功能 原因 计划
Checkpoint(验证/决策/执行) 后端无数据模型,需新设计 v2.7 专项
AI Briefing 生成 后端聚合逻辑未设计 v2.7 专项
Artifact 文件预览/下载 后端无文件存储机制 v2.7 评估
朝堂议政迁移 需要 CourtDiscussion 适配 v2.6 API v2.7

六、UI 组件文件结构

src/frontend/src/
├── App.tsx                    # 主布局 + Header + Tab 路由
├── api.ts                     # API 层(所有后端调用)
├── store.ts                   # Zustand 全局状态
├── types.ts                   # V2Task 等类型定义(从 EdictBoard 提取)
└── components/
    ├── EdictBoard.tsx         # 任务看板(卡片 + 筛选 + 搜索)
    ├── TaskModal.tsx          # 任务详情(4 Tab
    ├── NotificationCenter.tsx # 通知铃铛(占位)
    ├── CourtDiscussion.tsx    # 朝堂议政(迁移自 v1.0
    ├── MonitorPanel.tsx       # 编排调度(迁移自 v1.0
    ├── OfficialPanel.tsx      # 将军总览(迁移自 v1.0
    ├── ModelConfig.tsx        # 模型配置(迁移自 v1.0
    ├── SkillsConfig.tsx       # 技能配置(迁移自 v1.0
    ├── SessionsPanel.tsx      # 传令巡哨(迁移自 v1.0
    ├── MemorialPanel.tsx      # 奏折阁(迁移自 v1.0
    ├── SettingsPanel.tsx      # 系统设置(迁移自 v1.0
    └── GlobalSearch.tsx       # 全局搜索

七、评审修订记录

司马懿 Mail #278 评审 → 修复

项目 级别 修复内容
P0: priority 类型 必修 stringnumber1-10),前端 PRIORITY_META 按整数映射
P1: 状态管线 必修 4 步 → 5 步线性 + blocked 旁路,8 状态全覆盖
P1: VALID_TRANSITIONS 必修 完全对齐后端 db.py 的 8×N 映射
P2: 遗漏字段 建议 V2Task 补 depends_on, parent_task

司马懿 Mail #279 确认

P0 priority: number P1 STATUS_ORDER 含 review/blocked P1 TaskModal 状态映射含 reviewing/blocked/cancelled17 处引用) 构建产物 12:37 部署在 8083,前端可访问。