From df54c1440a1c45e182864befd3a59e08f7b3197b Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 17 May 2026 21:14:52 +0800 Subject: [PATCH] auto-sync: 2026-05-17 21:14:52 --- docs/research/task_hierarchy_research.md | 345 +++++++++++++++++++++++ src/blackboard/db.py | 6 +- 2 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 docs/research/task_hierarchy_research.md diff --git a/docs/research/task_hierarchy_research.md b/docs/research/task_hierarchy_research.md new file mode 100644 index 0000000..2610ed2 --- /dev/null +++ b/docs/research/task_hierarchy_research.md @@ -0,0 +1,345 @@ +# 多层级任务组织最佳实践调研报告 + +> 调研日期:2026-05-17 +> 调研目的:为 moziplus v2 三级任务层次(Project / Card(Epic) / Task)设计提供业界参考 +> 调研范围:项目管理工具、AI Agent 编排系统、黑板架构、数据库模型 + +--- + +## 一、项目管理工具的层级组织 + +### 1.1 Jira 的层级模型 + +**层级结构:** +``` +Project → Epic → Story/Bug/Task → Sub-task +``` + +Jira 支持自定义层级扩展(Premium/Enterprise),标准配置为 3-4 层。Jira 8+ 允许在 Epic 上方增加 Initiative、Theme 等层级。 + +**数据库设计要点:** +- **单表 + `project_id` 过滤**:Jira 的核心表是 `jiraissue`,所有 issue 类型(Epic/Story/Task/Sub-task)存储在同一张表,通过 `issuetype` 字段区分 +- **层级通过 `parent_id` + `topmost_issue_id` 实现**:每个 issue 有 `parent_id` 指向上级,`topmost_issue_id` 指向层级树的根节点 +- **Project 是隔离边界**:Project 持有独立的 workflow、permission scheme、notification scheme 配置,但数据物理上共享表 +- **多租户隔离策略**:小租户用共享 schema(加 `tenant_id`),大企业客户可提供独立 schema 或独立数据库实例 + +**关键洞察:** +> Jira 选择"单表存所有 issue + 类型字段区分 + project_id 隔离"的模型。这说明在项目管理领域,**灵活的类型系统比物理隔离更重要**。Project 是逻辑隔离边界,不是物理隔离边界。 + +### 1.2 Linear 的层级模型 + +**层级结构:** +``` +Workspace → Team → Project → Issue(可设 parent issue) +``` + +Linear 更扁平,没有原生的 Epic 概念,而是通过"Project"(时间框定的倡议)+ Issue 的 parent/child 关系来实现层级。 + +**数据库设计要点:** +- **Workspace 级共享数据库**:一个 Workspace 对应一个数据库实例(或共享实例+行级隔离) +- **Team 是主要的分组单元**:每个 Team 有自己的 Issue 列表视图 +- **Project 是跨 Team 的聚合**:一个 Project 可以包含多个 Team 的 Issue +- **UUID + organization_id 行级隔离**:类似 SaaS 标准做法 + +### 1.3 Asana 的层级模型 + +**层级结构:** +``` +Workspace/Organization → Project → Section → Task → Subtask +``` + +Asana 支持多项目(Portfolio/Program)聚合,Section 作为 Task 的分组。 + +**数据库设计要点:** +- **共享数据库 + 行级隔离**:所有 workspace 共享同一个数据库,通过 `workspace_id` 过滤 +- **Task 是核心实体**:Project、Section 都是 Task 的"容器标签",而非独立实体 +- **多对多关系**:一个 Task 可以属于多个 Project(通过 `project_membership` 关联表) + +### 1.4 小结:项目管理工具的共同模式 + +| 工具 | 层级数 | DB 隔离边界 | 类型区分方式 | +|------|--------|------------|------------| +| Jira | 3-7 层(可扩展) | Project(逻辑隔离) | 单表 + issuetype 字段 | +| Linear | 3-4 层 | Workspace | 单表 + parent 关系 | +| Asana | 4-5 层 | Workspace | 单表 + 容器标签 | + +**核心结论:项目管理工具几乎都采用"单表存所有任务 + 类型/层级字段区分 + 逻辑隔离"的模型,而非物理隔离。** + +--- + +## 二、AI Agent 编排系统的多层级任务组织 + +### 2.1 LangGraph + +**层级组织方式:** +- **Graph → Subgraph → Node**:LangGraph 通过嵌套的 StateGraph 实现层级 +- **State 是核心**:每个 Graph 有自己的 State schema,Subgraph 可以有独立的 State +- **State 传递机制**: + - 父 Graph 的 State 可以通过 `state_schema` 映射传递给 Subgraph + - Subgraph 执行完毕后,State 可以回传给父 Graph + - 使用 `thread_id` 实现持久化,同一 `thread_id` 下的 State 共享持久化存储 + +**共享上下文 vs 隔离:** +- **默认行为**:Subgraph 的 State 是隔离的,需要显式映射才能与父 Graph 共享 +- **共享策略**:通过 State 的 key 映射(`state_key_mapping`)实现有限共享 +- **持久化**:通过 Checkpointer(MemorySaver / SqliteSaver)实现 State 持久化,`thread_id` 是隔离边界 + +**关键模式:** +``` +ParentGraph (全局 State) + ├── Subgraph A (局部 State,映射父 State 的部分 key) + ├── Subgraph B (独立的局部 State) + └── Subgraph C (与 A 共享部分 State key) +``` + +### 2.2 CrewAI + +**层级组织方式:** +- **Crew → Agent → Task**:Crew 是编排单元,包含多个 Agent 和 Task +- **共享内存**:Crew 内的 Agent 共享一个 Memory 系统(支持 Short-term、Long-term、Entity Memory) +- **内置 SQLite 持久化**:Crew 的执行状态和 Memory 可以持久化到 SQLite + +**共享上下文 vs 隔离:** +- **Crew 级别共享**:同一 Crew 内的 Agent 默认共享上下文 +- **跨 Crew 隔离**:不同 Crew 的 Memory 默认隔离 +- **RAG 增强**:支持向量检索来管理大规模上下文,避免全量加载 + +### 2.3 AutoGen + +**层级组织方式:** +- **Conversation → Agent Group → Agent**:以对话为核心,Agent 组通过多轮对话协作 +- **多对话模式**:支持 two-agent chat、group chat、nested chat(层级嵌套) +- **Context 传递**:通过消息列表(chat history)传递上下文 + +**共享上下文 vs 隔离:** +- **Group Chat 共享**:同一 Group Chat 内的所有 Agent 共享完整的对话历史 +- **Nested Chat 隔离**:嵌套对话有独立的消息历史,可通过闭包传递摘要结果 +- **自定义 State**:支持通过 `ConversableAgent` 的 `context_variables` 传递结构化状态 + +### 2.4 OpenAI Swarm / Agents SDK + +**层级组织方式:** +- **Agent → Handoff → Agent**:极简模型,Agent 之间通过 Handoff 传递控制权 +- **Context Variables**:通过共享的字典传递上下文 +- **无显式层级**:没有 Project/Task 概念,是扁平的 Agent 网络 + +**关键洞察:** +> OpenAI Swarm 的哲学是"最小抽象"——不引入层级概念,让开发者在代码层面自行组织。这适合简单场景,但在复杂工作流中缺乏结构化管理。 + +### 2.5 小结:AI Agent 编排系统的模式对比 + +| 系统 | 层级机制 | 共享上下文方式 | 隔离边界 | 持久化 | +|------|---------|-------------|---------|-------| +| LangGraph | Graph/Subgraph 嵌套 | State key 映射 | thread_id | Checkpointer | +| CrewAI | Crew/Agent/Task | 共享 Memory | Crew 边界 | SQLite | +| AutoGen | Nested Chat | 对话历史 | Chat 边界 | 自定义 | +| OpenAI Swarm | 扁平 | Context Variables | 无显式边界 | 无 | + +**核心结论:AI Agent 编排系统的"共享 vs 隔离"权衡通常是"子任务隔离 + 结果摘要回传"。LangGraph 的 Subgraph 模式最接近我们的 Card/Epic 概念——每个 Subgraph 有自己的 State(类似黑板),执行完毕后通过 State 映射将关键结果传回父 Graph。** + +--- + +## 三、黑板系统(Blackboard Architecture)的上下文管理 + +### 3.1 经典黑板架构回顾 + +黑板架构的核心组成: +- **Blackboard(黑板)**:共享的数据空间,存储问题求解的中间状态 +- **Knowledge Sources(知识源)**:独立的处理模块,观察黑板并响应变化 +- **Controller(控制器)**:决定哪个知识源在何时执行 + +### 3.2 大规模场景下的挑战:上下文膨胀 + +黑板系统在大规模场景下面临的核心问题: +1. **数据增长**:随着任务执行,黑板上的数据持续累积 +2. **相关性衰减**:早期数据对当前决策的相关性逐渐降低 +3. **检索效率**:大量数据导致查找变慢 +4. **上下文窗口限制**:LLM 的上下文窗口有限,不能无限加载 + +### 3.3 业界解决方案 + +#### 方案一:分层黑板(Hierarchical Blackboard) + +将黑板分为多个层级,每层有不同的数据保留策略: +- **L0 热层(Working Memory)**:当前活跃任务的数据,完整保留,常驻内存 +- **L1 温层(Context Buffer)**:近期完成的任务结果,保留摘要+关键指标 +- **L2 冷层(Archive)**:历史数据,仅保留元数据+索引,需要时通过向量检索召回 + +#### 方案二:Context Folding(上下文折叠) + +来自 ByteDance 2025 年的研究: +- Agent 执行子任务时进入独立的子轨迹(sub-trajectory) +- 子任务完成后,将中间步骤"折叠"为简洁的摘要 +- 只保留最终结果和关键决策点,丢弃中间过程 + +> 这与我们的 Card 概念高度契合:Card 完成后,将整个黑板的详细数据折叠为摘要,只保留对上层有用的结论。 + +#### 方案三:Hot/Cold Memory 分离 + +来自学术论文 "Codified Context"(2026)的实践: +- **Hot Memory**:始终加载的约定和规则(类似 system prompt),体积小但高频访问 +- **Cold Memory**:按需加载的规范和文档,通过触发表(trigger table)自动路由 +- **触发表**:定义什么条件下加载什么冷数据,避免全量加载 + +#### 方案四:摘要 + 向量检索 + +业界最常用的上下文管理策略: +- 历史对话嵌入向量存储 +- 处理新查询时,通过语义检索召回相关历史 +- 保留最近 5-7 轮完整上下文,更早的只保留摘要 +- 合并相似度 > 0.95 的重复记忆 + +#### 归档策略推荐 + +| 数据类型 | 热保留期 | 归档方式 | 召回方式 | +|---------|---------|---------|---------| +| 活跃任务数据 | 任务执行期间 | 完成后折叠 | 直接访问 | +| 任务结果摘要 | Card 生命周期内 | Card 完成后归档 | 向量检索 | +| 决策记录 | Project 生命周期内 | Project 结束后归档 | 元数据索引 | +| 执行日志 | 7 天 | 压缩存储 | 按需加载 | + +--- + +## 四、Notion / Linear 的数据库模型 + +### 4.1 Notion 的数据模型 + +**核心概念:** +- **Page**:原子内容单元,可以是独立页面或数据库的一行 +- **Database(Data Source)**:结构化的 Page 集合,有自己的 schema(属性定义) +- **Relation**:数据库之间的关联关系 +- **Linked Database View**:一个数据库在另一个位置的过滤视图 + +**一个项目一个 DB vs 一个大 DB?** + +Notion 社区的争论和实践表明: + +| 方案 | 优点 | 缺点 | +|------|------|------| +| **一个大 DB + 过滤** | 统一 schema,跨项目视图,维护简单 | 属性膨胀(不同类型任务属性不同),过滤复杂 | +| **多个 DB(按类型分)** | 每个 DB 有专用 schema,更整洁 | 跨 DB 查询困难,rollup 受限(只支持一层) | +| **混合方案** | 任务用一个大 DB,项目/目标用独立 DB | 需要维护 Relation 关系 | + +**Notion 2025 的改进——Multiple Data Sources:** +- 一个视图可以合并多个 Data Source +- 每个 Data Source 可以有独立的 schema +- 在视图层面进行统一展示和过滤 + +**这启示我们:多类型实体可以用独立的 DB,通过关联关系连接,在视图层统一展示。** + +### 4.2 Linear 的数据模型 + +**核心概念:** +- **Workspace → Team → Project → Issue** +- 所有 Issue 存储在同一数据集中,通过 `team_id` 和 `project_id` 过滤 +- Project 可以跨 Team,是"倡议"级别的聚合 + +**Linear 的设计哲学:** +- **一个巨大的表**:所有 Issue 共享同一 schema +- **自定义字段**:通过 Custom Fields 扩展属性,而非不同的表 +- **Cycle/Project 作为视图**:不是数据隔离,而是数据视图 + +### 4.3 数据库模型选型建议 + +| 场景 | 推荐模型 | +|------|---------| +| 实体类型统一,属性差异小 | 单表 + 类型字段 + 过滤 | +| 实体类型差异大,属性结构不同 | 多表 + 关联关系 | +| 需要跨类型聚合查询 | 视图/物化视图层统一 | + +--- + +## 五、最佳实践总结 + +### 5.1 什么粒度最适合做 DB 隔离边界? + +**核心原则:隔离边界应该在"需要独立生命周期管理"的粒度上。** + +| 层级 | 是否适合做 DB 隔离 | 理由 | +|------|-------------------|------| +| **Project** | ❌ 不适合 | 数量少,生命周期长,需要跨项目查询和聚合 | +| **Card/Epic** | ✅ **最适合** | 数量适中,有明确的开始/结束,需要独立的上下文空间 | +| **Task** | ❌ 不适合 | 数量大,生命周期短,频繁创建/完成,独立 DB 管理成本过高 | + +**推荐方案:Card/Epic 级别的逻辑隔离(共享数据库实例 + Card 级别的数据分区/命名空间)** + +具体实现: +- 所有 Card 共享一个数据库实例 +- 每个 Card 有独立的黑板命名空间(如 `blackboard:{card_id}:*`) +- Card 完成后,其黑板数据归档(折叠为摘要),释放存储空间 +- Project 级别维护一个聚合黑板,存储所有 Card 的摘要结果 + +### 5.2 黑板(共享上下文)的隔离策略 + +``` +Project(全局聚合黑板) + ├── Card A(独立黑板空间) + │ ├── Task A1 的产出 + │ ├── Task A2 的产出 + │ └── Card A 摘要(完成后回传给 Project) + ├── Card B(独立黑板空间) + │ ├── Task B1 的产出 + │ └── Card B 摘要 + └── Project 聚合结果 +``` + +**关键规则:** +1. **Card 间默认隔离**:不同 Card 的黑板空间不互相访问 +2. **结果向上流动**:Card 完成后,摘要结果写入 Project 级黑板 +3. **依赖显式声明**:Card B 需要 Card A 的结果时,在定义中声明依赖,系统自动注入摘要 +4. **禁止向下传递全部数据**:Task 只能看到自己 Card 的黑板,不会加载整个 Project 的数据 + +### 5.3 归档策略设计 + +``` +阶段一:任务执行中 + → 黑板数据全量保留在热层 + → 每个 Task 完成后产出写入黑板 + +阶段二:Card 完成 + → 黑板数据"折叠":中间步骤摘要化 + → 保留:最终结果、关键决策、错误记录 + → 归档:原始日志、中间计算过程 + → 摘要写入 Project 级黑板 + +阶段三:Project 完成 + → 所有 Card 摘要聚合为 Project 报告 + → 全部黑板数据归档到冷存储 + → 保留可搜索的元数据索引 +``` + +### 5.4 对 moziplus v2 的具体建议 + +1. **数据库模型**:采用"单实例 + Card 级命名空间"方案 + - 所有任务共享一个数据库实例(SQLite 或 PostgreSQL) + - 每张表通过 `card_id` 字段区分归属 + - 黑板数据使用 `blackboard_entries` 表,key 为 `{card_id}:{entry_key}` + +2. **Task 类型**:使用"单表 + 类型字段"方案 + - `tasks` 表存储所有 Task,`type` 字段区分类型 + - 不按 Task 类型分表,保持查询灵活性 + +3. **Card 级隔离**: + - 每个 Card 的黑板空间独立 + - Card 间通过"依赖声明 + 摘要注入"传递上下文 + - Card 完成时触发归档流程 + +4. **上下文管理**: + - 热层:活跃 Card 的完整黑板数据 + - 温层:已完成 Card 的摘要数据(保留 7-30 天) + - 冷层:归档数据,通过向量检索按需召回 + +--- + +## 六、参考资料 + +1. Jira Issue Hierarchy - Atlassian Community: https://community.atlassian.com/forums/Jira-questions/ +2. Jira Database Schema - Atlassian Developer: https://developer.atlassian.com/server/jira/platform/database-schema/ +3. Multi-Tenant Database Architecture Patterns - Bytebase: https://www.bytebase.com/blog/multi-tenant-database-architecture-patterns-explained/ +4. LangGraph Subgraphs Documentation: https://docs.langchain.com/oss/python/langgraph/use-subgraphs +5. CrewAI Memory Documentation: https://docs.crewai.com/en/concepts/memory +6. Context Folding (ByteDance, Oct 2025): https://medium.com/@najeebkan/context-management-for-ai-agents-d68716a37965 +7. Codified Context - Hot/Cold Memory Separation (2026): https://arxiv.org/html/2602.20478v1 +8. Azure AI Agent Design Patterns: https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns +9. Notion Data Sources (2025): https://www.notionapps.com/blog/notion-data-sources-update-2025 +10. Agent Memory & State Management: https://www.techaheadcorp.com/blog/agent-memory-state/ diff --git a/src/blackboard/db.py b/src/blackboard/db.py index 6155f7d..4016bfd 100644 --- a/src/blackboard/db.py +++ b/src/blackboard/db.py @@ -119,11 +119,15 @@ _SCHEMA_STATEMENTS = [ must_haves TEXT, risk_level TEXT DEFAULT 'standard', estimated_duration_minutes INTEGER, - escalated INTEGER DEFAULT 0 + escalated INTEGER DEFAULT 0, + current_agent TEXT, + previous_agent TEXT, + next_capability TEXT )""", "CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)", "CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee)", "CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task)", + "CREATE INDEX IF NOT EXISTS idx_tasks_current_agent ON tasks(current_agent)", # comments """CREATE TABLE IF NOT EXISTS comments (