diff --git a/docs/design/technical-design-v2.6.md b/docs/design/technical-design-v2.6.md index acd46d8..182afac 100644 --- a/docs/design/technical-design-v2.6.md +++ b/docs/design/technical-design-v2.6.md @@ -822,7 +822,7 @@ cd src/frontend && npm run build # FastAPI 直接托管静态文件(port 8083) ``` -详见 `topic7-9-interaction-dashboard-proposal.md`。 +详见 `topic9-dashboard-design.md`(课题9 Dashboard 前后端完整设计方案)。 --- diff --git a/docs/design/topic9-dashboard-design.md b/docs/design/topic9-dashboard-design.md new file mode 100644 index 0000000..dbdc37b --- /dev/null +++ b/docs/design/topic9-dashboard-design.md @@ -0,0 +1,466 @@ +# 课题9:Dashboard 前后端完整设计方案 + +**版本**: v2.0(评审修订版) +**作者**: 庞统(副军师)🐦 +**日期**: 2026-05-17 +**状态**: 待司马懿确认 +**评审记录**: Mail #278(司马懿 P0+P1 评审)→ 已全部修复 → Mail #279(验证通过) + +--- + +## 一、核心定位 + +### 1.1 Dashboard 不是管理控制台,是 AI 工作可视化窗口 + +| 传统 Dashboard | AI Native Dashboard(v2.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.py` 的 `tasks` 表: + +```typescript +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 位置) + +**终态**:`done`、`cancelled` + +### 2.3 状态流转守卫(对齐后端 VALID_TRANSITIONS) + +```typescript +const VALID_TRANSITIONS: Record = { + 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.py` 的 `VALID_TRANSITIONS` 完全一致。 + +### 2.4 Priority 映射 + +后端 `priority` 是 INTEGER(1-10),前端映射: + +```typescript +const PRIORITY_META: Record = { + 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: 🔍 审查 + +审查意见:评审人 + verdict(APPROVE/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 已有 API(F5 实现) + +| 端点 | 方法 | 说明 | +|------|------|------| +| `/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.py` 的 `get_task` 端点增加 `expand` 查询参数。 + +```python +@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 +``` + +**工作量**: 30min(queries.py 已有基础方法,只需组合) + +#### API-2: `GET /api/projects/{pid}/tasks/{tid}/events` + +**优先级**: P0 +**说明**: 任务事件时间线查询。 +**实现方案**: 新增路由端点。 + +```python +@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 方法。 + +```python +@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: 后端补 API(B1-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 类型 | 必修 | `string` → `number`(1-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/cancelled(17 处引用) +> 构建产物 12:37 部署在 8083,前端可访问。