Files
sanguo_moziplus_v2/docs/design/v2.7-subtask-model.md
T
2026-05-18 08:24:10 +08:00

11 KiB
Raw Blame History

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 表 DDLtasks 表移除 card_idstage
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):

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 idsSubTask 间依赖)
    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 表基础上,新增字段:

ALTER TABLE tasks ADD COLUMN stages_json TEXT DEFAULT '[]';   -- Stage 定义(AI 生成)
ALTER TABLE tasks ADD COLUMN plan_json TEXT DEFAULT NULL;      -- 完整分解计划(庞统生成)

stages_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 格式

{
  "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 字段

  • NULLSubTask 在当前 Project 内执行
  • "sanguo_vnpy":SubTask 实际工作在另一个 Project 目录

跨项目 SubTask 的执行流程:

  1. 庞统在 Task Asanguo_quant_live)分解时,识别到需要回测引擎
  2. 创建 SubTask st-004parent_project=sanguo_vnpy
  3. Ticker 在 sanguo_vnpy 项目也扫描到这个 SubTask(通过 cross-project 查询)
  4. 姜维在 sanguo_vnpy 执行完成后,结果写回 Task A 的 SubTask

注意:跨项目执行是复杂场景,v2.7 先做数据模型支持(字段存在),执行逻辑后续迭代。

3.4 现有表关联 SubTask

以下表新增 subtask_id 字段(可选,向后兼容):

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                 → 创建 SubTaskAI/手动)
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 扫描逻辑

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 栏 + 内容区)
  • 项目选择器保持下拉菜单
  • EdictBoardTask 看板)基本不变

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)