# v2.7 数据模型重新设计:Project → Task → SubTask > 日期:2026-05-18 > 版本:v1.0(基于 2026-05-18 08:13 ~ 08:21 讨论定稿) > 作者:庞统 > 状态:待确认 > 前置:回滚 v2.7 Card 层(设计文档 + 代码) --- ## 一、概念对齐 | 层级 | 语义 | 例子 | 谁创建 | 生命周期 | |------|------|------|--------|---------| | **Project** | 仓库/组织 | sanguo_quant_live、sanguo_vnpy | 系统(自动发现)+ 用户 | 长期 | | **Task** | 用户的一条需求/意图/目标 | "动量策略v1"、"下载分钟线"、"v2.7开发" | 用户 / AI | 天~周 | | **SubTask** | Agent 领走执行的原子动作 | 张飞编码、赵云备数据、司马懿评审 | AI(庞统分解)| 小时~天 | ### 关键认知 1. **Task 是用户视角的最小单位**:用户说"帮我做动量策略v1",这就是一个 Task 2. **SubTask 是 Agent 视角的最小单位**:庞统把 Task 拆解后分发给各将军 3. **SubTask 可以跨 Project**:姜维在 sanguo_vnpy 搭回测引擎,服务的是 sanguo_quant_live 的 Task 4. **SubTask ≈ Stage**:同一个 Task 的 SubTask 按执行顺序形成 Stage 链 5. **Task 间有依赖**:策略编码依赖数据准备,实盘依赖模拟验证 --- ## 二、回滚范围 ### 回滚 Card 相关代码(开发目录) | 文件 | 操作 | |------|------| | `src/blackboard/models.py` | 删除 Card 相关代码(CARD_VALID_STATUSES 等 + Card dataclass) | | `src/blackboard/db.py` | 删除 `_CARDS_SCHEMA`、`_migrate_v27()`、cards 表 DDL;tasks 表移除 `card_id`、`stage` 列 | | `src/blackboard/operations.py` | 删除 CardOps 类(create_card / list_cards / fork_card 等) | | `src/blackboard/queries.py` | 移除 card_id 过滤参数 | | `src/api/card_routes.py` | **删除整个文件** | | `src/api/mail_routes.py` | **删除整个文件**(Mail 后续重新设计) | | `src/blackboard/registry.py` | 保留 ProjectRegistry(不涉及 Card) | | `src/daemon/ticker.py` | 移除 per-card 扫描逻辑,恢复 v2.6 per-project 扫描 | | `src/main.py` | 移除 card_routes、mail_routes 注册;移除 v2.7 迁移调用 | | `tests/test_v27_cards.py` | **删除整个文件** | ### 回滚设计文档 | 文件 | 操作 | |------|------| | `docs/design/v2.7-three-tier-hierarchy.md` | 归档到 `docs/design/archive/` | ### 保留 | 文件/功能 | 原因 | |-----------|------| | `registry.db` + ProjectRegistry + 自动发现 | Project 管理需要 | | per-project `blackboard.db` | 多项目隔离需要 | | Project API 路由 | 前端项目选择器需要 | | Product 方向备忘录 | 新增,记录未来方向 | --- ## 三、新增:SubTask 层 ### 3.1 数据模型 **SubTask 表**(加到 per-project blackboard.db): ```sql CREATE TABLE IF NOT EXISTS subtasks ( id TEXT PRIMARY KEY, -- 自动生成(如 st-a1b2c3) task_id TEXT NOT NULL REFERENCES tasks(id), -- 所属 Task title TEXT NOT NULL, -- 如 "策略编码"、"数据清洗" description TEXT DEFAULT '', status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','claimed','working','review','done','failed','blocked','cancelled')), assignee TEXT, -- 执行 Agent(如 zhangfei-dev) assigned_by TEXT, -- 分配者(通常是 pangtong-fujunshi) stage TEXT DEFAULT NULL, -- 所属 Stage 标签(如 "research"、"coding") stage_order INTEGER DEFAULT 0, -- Stage 内排序 depends_on TEXT, -- JSON array of subtask ids(SubTask 间依赖) output_summary TEXT DEFAULT NULL, -- 产出物摘要 output_path TEXT DEFAULT NULL, -- 产出物路径 parent_project TEXT DEFAULT NULL, -- 实际工作所在 Project(跨项目时用) retry_count INTEGER NOT NULL DEFAULT 0, max_retries INTEGER NOT NULL DEFAULT 2, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), started_at TEXT, completed_at TEXT ); CREATE INDEX IF NOT EXISTS idx_subtasks_task ON subtasks(task_id); CREATE INDEX IF NOT EXISTS idx_subtasks_status ON subtasks(status); CREATE INDEX IF NOT EXISTS idx_subtasks_assignee ON subtasks(assignee); CREATE INDEX IF NOT EXISTS idx_subtasks_stage ON subtasks(task_id, stage); ``` ### 3.2 Task 表变更 在现有 tasks 表基础上,新增字段: ```sql ALTER TABLE tasks ADD COLUMN stages_json TEXT DEFAULT '[]'; -- Stage 定义(AI 生成) ALTER TABLE tasks ADD COLUMN plan_json TEXT DEFAULT NULL; -- 完整分解计划(庞统生成) ``` **`stages_json` 格式**: ```json [ {"id": "research", "label": "因子研究", "order": 1}, {"id": "data_prep", "label": "数据准备", "order": 2}, {"id": "coding", "label": "策略编码", "order": 3}, {"id": "backtest", "label": "历史回测", "order": 4}, {"id": "optimize", "label": "参数优化", "order": 5} ] ``` **`plan_json` 格式**: ```json { "version": 1, "created_by": "pangtong-fujunshi", "created_at": "2026-05-18T08:00:00Z", "stages": [...], "subtasks": [ { "id": "st-001", "title": "因子研究", "assignee": "zhangfei-dev", "stage": "research", "depends_on": [], "parent_project": null }, { "id": "st-002", "title": "分钟线数据下载", "assignee": "zhaoyun-data", "stage": "data_prep", "depends_on": [], "parent_project": null }, { "id": "st-003", "title": "策略编码", "assignee": "zhangfei-dev", "stage": "coding", "depends_on": ["st-001", "st-002"], "parent_project": null }, { "id": "st-004", "title": "回测引擎搭建", "assignee": "jiangwei-infra", "stage": "backtest", "depends_on": [], "parent_project": "sanguo_vnpy" } ] } ``` ### 3.3 跨项目 SubTask **`parent_project` 字段**: - `NULL`:SubTask 在当前 Project 内执行 - `"sanguo_vnpy"`:SubTask 实际工作在另一个 Project 目录 跨项目 SubTask 的执行流程: 1. 庞统在 Task A(sanguo_quant_live)分解时,识别到需要回测引擎 2. 创建 SubTask st-004,`parent_project=sanguo_vnpy` 3. Ticker 在 sanguo_vnpy 项目也扫描到这个 SubTask(通过 cross-project 查询) 4. 姜维在 sanguo_vnpy 执行完成后,结果写回 Task A 的 SubTask **注意**:跨项目执行是复杂场景,v2.7 先做数据模型支持(字段存在),执行逻辑后续迭代。 ### 3.4 现有表关联 SubTask 以下表新增 `subtask_id` 字段(可选,向后兼容): ```sql ALTER TABLE events ADD COLUMN subtask_id TEXT; ALTER TABLE comments ADD COLUMN subtask_id TEXT; ALTER TABLE outputs ADD COLUMN subtask_id TEXT; ALTER TABLE task_attempts ADD COLUMN subtask_id TEXT; ``` 这样 Events、Comments、Outputs 既可以关联到 Task 级别,也可以精确到 SubTask 级别。 --- ## 四、Task 状态 = SubTask 状态聚合 Task 不独立维护复杂状态机,核心状态从 SubTask 推导(类似之前 Card 的聚合逻辑,但语义更自然): | Task 状态 | 推导规则 | |-----------|---------| | `pending` | 所有 SubTask 都是 pending,或无 SubTask | | `working` | 有 SubTask 在 working | | `review` | 有 SubTask 在 review | | `done` | 所有 SubTask 都是 done | | `failed` | 有 SubTask failed 且无 active SubTask | | `blocked` | 有 SubTask blocked 且无 active SubTask | | `cancelled` | 用户/AI 取消(手动设置) | **额外状态**(不属于聚合,手动设置): - `planning`:庞统正在分解 Task(生成 plan_json) - `challenging`:司马懿正在审核分解方案 --- ## 五、API 路由 ### 5.1 Task 级(已有,微调) ``` GET /api/projects/{pid}/tasks → Task 列表(含 SubTask 聚合状态) POST /api/projects/{pid}/tasks → 创建 Task(用户需求) GET /api/projects/{pid}/tasks/{tid} → Task 详情 + Stage 进度 PATCH /api/projects/{pid}/tasks/{tid} → 更新 Task(如取消、改优先级) DELETE /api/projects/{pid}/tasks/{tid} → 删除 Task ``` ### 5.2 SubTask 级(新增) ``` GET /api/projects/{pid}/tasks/{tid}/subtasks → Task 下所有 SubTask(按 Stage 分组) POST /api/projects/{pid}/tasks/{tid}/subtasks → 创建 SubTask(AI/手动) GET /api/projects/{pid}/tasks/{tid}/subtasks/{stid} → SubTask 详情 PATCH /api/projects/{pid}/tasks/{tid}/subtasks/{stid} → 更新 SubTask(状态、指派) POST /api/projects/{pid}/tasks/{tid}/subtasks/{stid}/status → 更新状态(Agent 回调) POST /api/projects/{pid}/tasks/{tid}/subtasks/{stid}/outputs → 提交产出 ``` ### 5.3 便利路由 ``` GET /api/projects/{pid}/tasks/{tid}/progress → Stage 进度条数据 GET /api/projects/{pid}/subtasks → 项目级所有 SubTask(跨 Task 查询) ``` ### 5.4 向后兼容 - 旧路由 `/api/projects/{pid}/tasks` 保留 - 不传 SubTask 相关参数时行为不变 - Task 的 status 仍然可用(聚合推导) --- ## 六、Ticker 变更 ### 6.1 扫描逻辑 ```python for project in registry.list_projects(status='active'): db_path = data_root / project.id / "blackboard.db" tasks = queries.tasks_by_status(db_path, ['pending', 'working', 'planning']) for task in tasks: # 1. 如果 Task 在 planning → 触发庞统分解 if task.status == 'planning': dispatch_planning(task) continue # 2. 扫描 Task 下 pending 的 SubTask pending = queries.pending_subtasks(db_path, task.id) for subtask in pending: if deps_satisfied(subtask): dispatch_subtask(subtask) # 3. 聚合刷新 Task 状态 refresh_task_status(db_path, task.id) ``` ### 6.2 调度粒度 - **调度单位**:SubTask(不是 Task) - **状态聚合**:每次 tick 刷新 Task 的聚合状态 - **max_dispatch_per_tick**:全局限制(恢复 v2.6 语义) --- ## 七、前端改动(最小) ### 7.1 不改的 - 整体布局不变(Tab 栏 + 内容区) - 项目选择器保持下拉菜单 - EdictBoard(Task 看板)基本不变 ### 7.2 改的 1. **Task 详情弹窗(TaskModal.tsx)**: - 新增 Stage 进度条(从 stages_json 读取) - SubTask 列表(按 Stage 分组) - 每个 SubTask 显示:标题、Agent、状态、产出 2. **Task 看板卡片**: - 加 Stage 进度指示器(小进度条) 3. **Mail Tab**(后续独立实现): - 新增 Tab,列表展示 Sanguo Mail ### 7.3 暂不做的 - 独立的 SubTask 看板/管理页面 - 跨 Project 的 Product 视图 - Card 看板 --- ## 八、实施计划 | 序号 | 任务 | 预计 | |------|------|------| | 1 | 回滚 Card 层(代码 + 设计文档归档) | 2h | | 2 | 新增 SubTask 表 + models + operations | 3h | | 3 | Task 表加 stages_json/plan_json + 聚合状态 | 2h | | 4 | SubTask API 路由 | 2h | | 5 | Ticker 适配(per-SubTask 调度 + 状态聚合) | 2h | | 6 | 前端 Task 详情弹窗改造(Stage 进度 + SubTask 列表) | 3h | | 7 | 测试 + 司马懿评审 | 2h | | **合计** | | **~16h** | ### 优先级 先回滚(1)→ 再建 SubTask(2-5)→ 前端(6)→ 评审测试(7)