Files
sanguo_moziplus_v2/docs/design/v2.7-three-tier-hierarchy.md
T
2026-05-17 22:21:50 +08:00

12 KiB
Raw Blame History

v2.7 三级层次结构设计:Project / Card / Task

日期:2026-05-17 作者:庞统 + 司马懿(共识)→ 用户确认逻辑隔离方案 状态:待用户确认最终版


一、核心设计决策

1.1 三层定义

Project(项目)
  └── Card(卡片/策略)
        ├── Task(任务)
        ├── Output(产出)
        ├── Review(审查)
        ├── Comment(评论)
        ├── Event(事件)
        └── routing_decisions(路由审计)
层级 语义 例子 生命周期
Project 业务领域/产品线 sanguo_quant_live、sanguo_vnpy 长期(月/年)
Card 独立的工作流/策略 动量策略v1、数据平台P3 中期(天/周)
Task 具体执行单元 编写回测脚本、清洗分钟线 短期(小时/天)

1.2 隔离策略:一个 Project 一个 DB + card_id 逻辑隔离

物理隔离(一 Card 一 DBvs 逻辑隔离(一 Project 一 DB + card_id 字段):

维度 物理隔离 逻辑隔离 选定
业界参考 无主流先例 Jira(单表+类型字段)、Linear(单DB+team_id)、Asana(单DB+workspace_id
DB 文件数 Card 数量级(可能上百) Project 数量级(个位数)
跨 Card 查询 不可能(跨库) 天然支持(同一 DB
事务 Card 内完整,跨 Card 不可能 Project 内任意事务
归档 关闭连接即可 按 card_id UPDATE status
Agent prompt 天然干净 查询时加 WHERE card_id=? 过滤
复杂度 DB 文件管理复杂 所有表加一列,查询统一
备份 按 Card 备份(碎片化) 按 Project 备份(完整)

选逻辑隔离的理由:

  1. 优秀实践一致Jira/Linear/Asana 都用逻辑隔离,说明这是成熟模式
  2. 调研报告建议5.4 节):"单实例 + Card 级命名空间"
  3. 跨 Card 查询有需求:用户看 Project 概览时需要聚合所有 Card 的 tasks
  4. 运维简单:一个 Project 一个文件,备份/迁移/清理都方便
  5. Card 数量可控Ticker 查询 WHERE card_id IN (active_cards) 性能无问题

1.3 归档/恢复 — 只改 metadata

  • cards 表 status 字段:active / closed / archived / deleted
  • 归档 = UPDATE cards SET status='archived' → Ticker 跳过 → 不 dispatch 新任务
  • 恢复 = UPDATE cards SET status='active'
  • 删除 = status=deleted + 7 天后异步清理关联数据

1.4 跨 Card 依赖 — 不支持

  • 业务上策略之间应该独立
  • 如果策略B 用策略A 的数据,那是数据层面的事(artifacts 路径引用),不是编排层面的事

二、数据模型

2.1 Registry DB(全局唯一,管理 Project 列表)

路径:data/registry.db

CREATE TABLE IF NOT EXISTS projects (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    description TEXT DEFAULT '',
    status      TEXT DEFAULT 'active',     -- active/archived/deleted
    config_json TEXT DEFAULT '{}',         -- agents、routing 等项目级配置
    created_at  TEXT NOT NULL,
    updated_at  TEXT
);

职责:只管 Project 列表 + 指向 DB 文件路径。Card 信息在 Project DB 内部管理。

2.2 Project DBper-project,逻辑隔离)

路径:data/{pid}/blackboard.db

新增 cards 表:

CREATE TABLE IF NOT EXISTS cards (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    description TEXT DEFAULT '',
    status      TEXT DEFAULT 'active',     -- active/closed/archived/deleted
    card_type   TEXT DEFAULT 'default',    -- default/strategy/data_pipeline/research
    labels_json TEXT DEFAULT '[]',
    created_at  TEXT NOT NULL,
    updated_at  TEXT,
    archived_at TEXT
);

CREATE INDEX IF NOT EXISTS idx_cards_status ON cards(status);

现有所有表加 card_id 字段:

-- tasks 表
ALTER TABLE tasks ADD COLUMN card_id TEXT;
CREATE INDEX IF NOT EXISTS idx_tasks_card ON tasks(card_id);

-- outputs 表
ALTER TABLE outputs ADD COLUMN card_id TEXT;
CREATE INDEX IF NOT EXISTS idx_outputs_card ON outputs(card_id);

-- reviews 表
ALTER TABLE reviews ADD COLUMN card_id TEXT;

-- comments 表
ALTER TABLE comments ADD COLUMN card_id TEXT;

-- events 表
ALTER TABLE events ADD COLUMN card_id TEXT;
CREATE INDEX IF NOT EXISTS idx_events_card ON events(card_id);

-- task_attempts 表
ALTER TABLE task_attempts ADD COLUMN card_id TEXT;

-- routing_decisions 表
ALTER TABLE routing_decisions ADD COLUMN card_id TEXT;

DDL 变更策略:

  • CREATE TABLE 语句直接包含 card_id(新 DB)
  • 旧 DB 通过 _migrate_v27() 添加列 + 创建默认 Card + 回填 card_id
  • 每个查询都加 WHERE card_id=? 过滤

2.3 默认 Card

迁移策略:每个旧 DB 自动创建一个默认 Card(id=default),将所有现有数据的 card_id 设为 default

新建 Project 时也自动创建一个默认 Card,用户可以直接在默认 Card 上工作(不感知 Card 层),也可以手动创建新 Card。

2.4 目录结构

data/
├── registry.db                              ← Project 列表
├── sanguo_quant_live/
│   ├── blackboard.db                        ← Project 级 DB(含 cards 表 + card_id 隔离)
│   └── artifacts/                           ← 产出物文件
│       ├── strategy-momentum-v1/
│       ├── strategy-mean-reversion/
│       └── default/
├── sanguo_vnpy/
│   ├── blackboard.db
│   └── artifacts/
└── inbox/                                   ← 快捷单 Card 项目
    └── fetch-data-001/
        ├── blackboard.db
        └── artifacts/

三、API 路由变更

3.1 新路由

# Project 级
GET    /api/projects                              → 列出所有项目
POST   /api/projects                              → 创建项目(自动创建默认 Card
GET    /api/projects/{pid}                        → 项目详情
PATCH  /api/projects/{pid}                        → 更新项目配置
DELETE /api/projects/{pid}                        → 删除项目

# Card 级
GET    /api/projects/{pid}/cards                  → 列出项目下所有 Card
POST   /api/projects/{pid}/cards                  → 创建 Card
GET    /api/projects/{pid}/cards/{cid}            → Card 详情(含聚合统计)
PATCH  /api/projects/{pid}/cards/{cid}            → 更新 Card(改名/归档/恢复)
DELETE /api/projects/{pid}/cards/{cid}            → 删除 Card

# Task 级(嵌套在 Card 下)
GET    /api/projects/{pid}/cards/{cid}/tasks              → 列出 Card 下所有 Task
POST   /api/projects/{pid}/cards/{cid}/tasks              → 创建 Task
GET    /api/projects/{pid}/cards/{cid}/tasks/{tid}        → Task 详情
POST   /api/projects/{pid}/cards/{cid}/tasks/{tid}/status → 更新状态
POST   /api/projects/{pid}/cards/{cid}/tasks/{tid}/outputs → 添加产出
POST   /api/projects/{pid}/cards/{cid}/tasks/{tid}/reviews → 添加审查
POST   /api/projects/{pid}/cards/{cid}/tasks/{tid}/comments → 添加评论
GET    /api/projects/{pid}/cards/{cid}/events             → Card 事件流

# 便利路由(前端兼容/项目概览)
GET    /api/projects/{pid}/tasks                  → 该 Project 所有 active cards 的 tasks(合并)
GET    /api/projects/{pid}/events                 → 该 Project 所有 active cards 的事件(合并)

3.2 向后兼容

  • GET /api/projects/{pid}/tasks 保留,内部查所有 active cards 的 tasks
  • 旧前端不传 card_id 时,自动使用 default Card
  • 渐进迁移:前端可以先不感知 Card 层,按需升级

四、Ticker 变更

4.1 当前逻辑

for project_id in registry.projects:
    db_path = data_root / project_id / "blackboard.db"
    _tick_project(db_path, ...)

4.2 变更后

for project_id in registry.list_projects(status='active'):
    db_path = data_root / project_id / "blackboard.db"
    # 查出所有 active cards
    active_cards = queries.list_cards(db_path, status='active')
    for card in active_cards:
        _tick_card(db_path, card.id, ...)

4.3 性能

  • 单 DB 内按 card_id 查询,SQLite 索引命中,微秒级
  • 无打开/关闭多个 DB 的开销
  • active cards 过多时加 LIMIT 分批

五、Blackboard 操作变更

5.1 现有接口签名变更

所有 Blackboard/Queries 方法加 card_id 参数:

# Before
bb.create_task(task)

# After
bb.create_task(task, card_id="strategy-momentum-v1")

# Before
q.tasks_by_status("pending")

# After
q.tasks_by_status("pending", card_id="strategy-momentum-v1")

5.2 Card 管理

class CardOps:
    """Card CRUD(操作 Project DB 内的 cards 表)"""
    def create_card(self, db_path, card_id, name, card_type="default") -> dict
    def get_card(self, db_path, card_id) -> dict | None
    def list_cards(self, db_path, status=None) -> list[dict]
    def update_card(self, db_path, card_id, **kwargs) -> bool
    def archive_card(self, db_path, card_id) -> bool
    def restore_card(self, db_path, card_id) -> bool
    def delete_card(self, db_path, card_id) -> bool

    def card_stats(self, db_path, card_id) -> dict:
        """聚合统计:task数、完成率、活跃agent等"""

六、路由方案兼容性

完全兼容:

  • routing_decisions 表加 card_id,审计按 Card 隔离
  • current_agent / previous_agent 在 task 级别,通过 card_id 自然隔离
  • agent_profiles 和 routing 配置在 Project 级(registry.db 的 config_json 或项目级配置文件)
  • AgentRouter 实例 per-ProjectCard 只是查询过滤条件

七、前端变更

7.1 导航结构

当前:  Project List → Task List
变更:  Project List → Card List → Task List

7.2 新增组件

  • CardList:卡片式展示(名称、状态 badge、task 进度条、操作按钮)
  • CardDetail:单 Card 概览(聚合统计 + 事件流)
  • 新建 Card 对话框

7.3 便利模式

Project 概览页:展示所有 active cards 的 tasks 合并视图(按 Card 分组折叠)

7.4 渐进迁移

  • Phase 1:后端支持 Card,前端不感知(所有操作走 default Card
  • Phase 2:前端加 CardList 导航
  • Phase 3:Card 模板、归档管理、可视化

八、数据迁移

8.1 迁移脚本

def migrate_v27(db_path: Path):
    """将旧 DB 升级到 v2.7 三级结构"""
    # 1. 创建 cards 表
    # 2. 所有表加 card_id 列
    # 3. 创建默认 Cardid='default'
    # 4. 回填所有现有数据的 card_id = 'default'
    # 5. 创建索引

8.2 Registry 迁移

def migrate_registry(data_root: Path):
    """将 _registry.yaml 迁移为 registry.db"""
    # 读 _registry.yaml → 写 registry.db projects 表
    # 每个 project 目录下已有 blackboard.db,路径不变

九、实施计划

v2.7.0 范围

序号 任务 预计
1 registry.db + ProjectRegistry 类 0.5 天
2 cards 表 + 所有表加 card_id + 迁移 0.5 天
3 CardOps + Blackboard/Queries 接口加 card_id 1 天
4 API 路由(Card CRUD + Task 嵌套) 1 天
5 Ticker 适配(per-card 扫描) 0.5 天
6 前端 CardList + 导航 1 天
7 端到端测试 + 迁移验证 0.5 天
合计 5 天

前置条件

  • v2.6.1 路由方案已落地并 E2E 验证

不在 v2.7.0 范围

  • 跨 Card 依赖(不支持)
  • Card 模板
  • 向量检索/摘要(Phase 2
  • 冷存储迁移脚本

十、与调研报告的对齐

调研发现 方案对齐
Jira:单表 + 类型字段 + project_id 隔离 单 DB + card_id 字段,完全一致
Linear:单DB + team_id 过滤 单 DB + card_id 过滤,完全一致
LangGraphSubgraph 独立 State Card 是查询隔离,非物理隔离
Context Folding:子任务折叠为摘要 Card 归档时折叠,保留元数据索引
Hot/Cold Memory 分离 active=热、archived=冷,按需加载