# #04 黑板协作模型设计

> 版本: v1.1  
> 日期: 2026-05-30  
> 作者: 庞统（副军师）  
> 状态: v1.1 修订（司马懿评审意见已纳入）  
> 前置: #02 Main Session + Delegation, #03 Prompt 进化  

---

## 一、问题陈述

当前黑板有 7 种信息类型（Task/Comment/Output/Event/Review/Decision/Observation），全部通过 `task_id` 平铺关联。E2E 测试暴露了三个设计缺陷：

### 问题 1：assignee 和 @mention 功能重叠

| 机制 | 触发方式 | 路由行为 |
|------|---------|---------|
| assignee 字段 | 创建任务时传 `assignee: "zhangfei-dev"` | 确定性路由 |
| @mention | Comment 中 `mentions: ["zhaoyun-data"]` | mention_queue → spawn |

**问题**：两者都能触发"找人做事"，语义不同但边界模糊。
- Agent 不知道什么时候用 assignee，什么时候用 @
- assignee 是单人的，但任务可能需要多人协作
- @mention 需要额外传 `mentions` 数组，容易遗漏

### 问题 2：一个任务只能有一个 assignee

当前 `assignee` 是单个字符串。但现实中：
- 一个数据准备任务可能需要赵云（获取）+ 关羽（风控审核）
- 一个部署任务可能需要姜维（部署）+ 关羽（安全检查）
- 庞统创建子任务时需要同时指定多人

### 问题 3：信息组织是平铺的，缺乏结构化视图

当前 `expand=all` 只展开当前 task 的子表，不递归子任务。用户想看"数据准备子任务的所有 comments 和成果物"，需要：
1. 先查父任务，找到子任务 ID
2. 再逐个查每个子任务的 `expand=all`

信息之间的关联（比如 Output #3 是 Comment #2 的结果）无法表达。

---

## 二、优秀实践调研

### 2.1 项目管理工具的共识

**Jira / Linear / Asana 共同模式**：

| 洞察 | 来源 | 启示 |
|------|------|------|
| 单表存所有任务 + 类型字段区分 | Jira `issuetype` | 不需要为不同任务类型建不同表 |
| `parent_id` + `topmost_id` 实现层级 | Jira | 父子关系用指针，不是嵌套 |
| Project 是逻辑隔离边界 | Jira/Linear | 一个 Project 一个黑板 DB |
| 一个 Task 可属于多个 Project | Asana | 多对多关系（cross-project） |

### 2.2 AI Agent 编排系统的共识

**LangGraph / CrewAI / AutoGen 共同模式**：

| 洞察 | 来源 | 启示 |
|------|------|------|
| 子任务隔离 + 结果摘要回传 | LangGraph Subgraph | 子任务有独立状态，完成后折叠 |
| 共享 Memory + Crew 边界 | CrewAI | 同一 Crew 共享记忆 |
| 对话历史即上下文 | AutoGen | 不需要额外机制，消息本身就是上下文 |
| 扁平 Handoff + Context Variables | OpenAI Swarm | 最简模型，适合简单场景 |

### 2.3 学术和实践前沿

| 洞察 | 来源 | 启示 |
|------|------|------|
| **共享产出物 > 共享消息** | O'Reilly 2026 | 围绕 artifact 协作，不是围绕消息 |
| **Phase gates + shared artifacts + final supervisor** | O'Reilly 2026 | 2026 存活系统的共同特征 |
| **共享工作区 + 结构化产出 > 消息传递** | Claude Code 实践 | 文件系统 + JSON 接口 |
| **对话历史传递 surprisingly effective** | OpenAI Handoff | 转交时携带完整上下文 |
| **bMAS 迭代收敛到共识** | arXiv:2507.01701 | Agent 轮流行动直到黑板达成共识 |
| **propose→validate→commit 原子写入** | Network-AI | 消除 split-brain |
| **Auftragstaktik 任务式指挥** | ClawTeam #10 | Intent→End State→Constraints，不指定步骤 |

### 2.4 核心设计原则（从调研提炼）

1. **黑板是唯一真相源** — 所有 Agent 通过黑板共享信息
2. **产出物 > 消息** — 围绕共享产出物协作，不是围绕消息传递
3. **结构对称** — 不把任何 Agent 当"主"，Canonical IR 原则
4. **单一职责** — 每种信息类型只表达一件事
5. **可追溯** — 不可变事件日志，写入即审计
6. **任务式指挥** — Intent→End State→Constraints，Agent 自主决定怎么做

---

## 三、设计方案

### 3.1 统一路由：assignee 降级为显示字段，路由统一走 @mention

**核心决策**：保留 `assignee` 字段作为「负责人」显示字段，但路由触发统一走 @mention。

| 之前 | 之后 |
|------|------|
| 创建任务传 `assignee` → 确定性路由 | 创建任务时在 description 中 `@zhangfei-dev` → daemon 自动推断 assignee + 路由 |
| Agent 不知道用 assignee 还是 @mention | 只有一种路由入口：@ |
| assignee 是单人的 | @ 可以 @ 多人 |

**为什么保留 assignee 字段（不取消）**：
1. **前端展示**：「这个任务谁负责」是刚需
2. **Mail 特殊处理**：Mail 的 assignee 是收件人，不能取消
3. **向后兼容**：存量任务已有 assignee 值
4. **查询效率**：`WHERE assignee=?` 比扫描 description 中的 @ 快

**assignee 从「路由入口」变为「路由结果」**：
- API 调用方不再直接设置 assignee（废弃）
- daemon 从 description/comment 中的 @mention 自动推断 assignee
- router 快速路径保持不变（仍然读 assignee 字段，只是赋值来源变了）

#### 迁移方案

**阶段 1（#04 Phase 1）**：双轨并行
- 创建任务时仍接受 `assignee` 参数（向后兼容，标记为 deprecated）
- 新增：daemon 从 description 中提取 @mention → 如果有匹配 → 自动覆盖 assignee
- router 快速路径保持不变（读 assignee 字段路由）
- 无 assignee 且无 @mention → 广播认领

**阶段 2（#04 Phase 2）**：全面切换
- API 不再接受 `assignee` 参数（返回 deprecation warning）
- assignee 完全由 daemon 从 @mention 推断
- router 快速路径不变（仍然读 assignee 字段，只是赋值来源变了）

**阶段 3（未来）**：清理
- 移除 API 的 assignee 参数
- 存量任务：assignee 值保留不动（已经是正确的负责人信息）

**代码影响评估**：
| 文件 | 影响 | 改动 |
|------|------|------|
| router.py L155-160 | assignee 快速路径 | ✅ 保持不变（仍然读 assignee 字段） |
| dispatcher.py L427-446 | assignee 作为 dispatch 依据 | ✅ 保持不变 |
| ticker.py _transition_status | pending 时清空 assignee | ✅ 保持不变 |
| API POST /tasks | 接受 assignee 参数 | Phase 1 保留，Phase 2 废弃 |
| ticker.py _tick_project | 新增：创建后扫描 description 提取 @mention | 新代码 |

**API 变化**：
```python
# Phase 1：两种方式都支持，@mention 优先
POST /tasks {
  "title": "写 hello.py",
  "description": "@zhangfei-dev 写一个 hello.py"
}
# daemon 自动提取 assignee=zhangfei-dev
# 也可以旧方式：assignee="zhangfei-dev"（deprecated）

# Phase 2：只用 @mention
POST /tasks {
  "title": "写 hello.py",
  "description": "@zhangfei-dev 写一个 hello.py"
}
```

### 3.2 @mention 语义增强

**当前问题**：Comment 需要**同时**写 `body` 中的 `@zhaoyun-data` **和** `mentions` 数组。Agent 容易遗漏。

**改进方案**：daemon 自动从 `body` 中提取 `@Agent` 模式，自动填充 `mentions`。

```python
# Agent 只需要写 body
POST /comments {
  "author": "pangtong-fujunshi",
  "body": "@zhaoyun-data 你来获取沪深300行情数据，@guanyu-dev 数据到了你做风控检查"
}

# daemon 自动提取 → mentions: ["zhaoyun-data", "guanyu-dev"]
# 写入 mention_queue → 两人都收到 mention spawn
```

**提取规则**（D4 决策：只支持 @agent-id 格式）：
- 正则 `@([a-z]+-[a-z]+-[a-z]+)` 匹配已知 Agent ID 列表
- 匹配到的 Agent 写入 `mentions` 字段（如果前端也传了 `mentions`，取并集）
- 不认识的 @ 目标忽略（可能是 @文档 @链接）
- **不支持中文昵称**（@张飞）—— agent-id 是系统唯一标识，无歧义。未来如需支持，加 alias 映射即可

### 3.3 多人协作模式

@ 多人 ≠ 多人同时执行同一任务。而是**每人收到 mention spawn，自主协调**：

| @ 模式 | 含义 | Daemon 行为 |
|--------|------|------------|
| `@zhangfei-dev` | 指定张飞做这个任务 | 确定性路由给张飞 |
| `@zhaoyun-data @guanyu-dev` | 需要两人协作 | 创建两个子任务，分别路由 |
| `@所有人` | 不知道谁合适 | 广播认领 |
| 无 @ | 不需要特定人 | 广播认领 |

**@多人 → 自动拆子任务**（Phase 2，暂不实现）：

```
庞统创建任务：@zhaoyun-data 获取数据，@zhangfei-dev 写策略代码

Daemon 自动拆解：
├── 子任务 1: 获取数据 → assignee: zhaoyun-data
└── 子任务 2: 写策略代码 → assignee: zhangfei-dev, depends_on: [子任务 1]
```

Phase 1 只做：@多人 → 每个人收到 mention spawn，各自决定如何协作。

### 3.4 信息关联模型重构

**当前**：7 种信息平铺挂在 `task_id` 上，互相之间无关联。

**改进**：引入 `parent_id` 串联信息流。

#### Output 关联 Comment

```python
# 当前
POST /outputs {agent, content_type, summary}
# 产出物和讨论没有关联

# 改进：output 关联到触发它的 comment
POST /outputs {
  agent: "zhaoyun-data",
  content_type: "data",
  summary: "沪深300日频行情",
  triggered_by_comment: 42    # ← 新字段，关联到 @赵云 的那条 comment
}
```

#### 反向索引

```sql
CREATE INDEX IF NOT EXISTS idx_outputs_triggered_by ON outputs(triggered_by_comment);
```

支持查询「这条 comment 触发了哪些 outputs」。

#### Review 关联 Output

当前 `Review.output_id` 已有关联，保持不变。

#### 信息流视图

```
Comment #1: "@zhaoyun-data 获取沪深300行情"
  └── mention_queue → spawn 赵云
       └── Comment #2: "数据已获取，见附件"
            └── Output #1: content_path=/data/market.csv, triggered_by_comment=#1
                 └── Review #1: verdict=approved, output_id=#1
```

### 3.5 层级查询 API

**当前痛点**：想看"数据准备子任务的所有信息"需要多次查询。

**改进**：新增聚合查询 API（D3 决策：Phase 2 实施，含子任务的完整版 Phase 3）。

#### Phase 2：单任务 timeline

```
GET /api/projects/{pid}/tasks/{task_id}/timeline?limit=50&offset=0

返回：
{
  "task": {...},
  "timeline": [
    {
      "task": {...},
      "comments": [...],
      "outputs": [...],
      "reviews": [...]
    }
  ],
  "timeline": [
    {"ts": "...", "type": "comment", "author": "pangtong", "body": "@zhaoyun-data ..."},
    {"ts": "...", "type": "mention", "agent": "zhaoyun-data", "status": "notified"},
    {"ts": "...", "type": "status", "from": "pending", "to": "claimed"},
    {"ts": "...", "type": "comment", "author": "zhaoyun-data", "body": "数据已获取"},
    {"ts": "...", "type": "output", "agent": "zhaoyun-data", "summary": "沪深300行情"},
    {"ts": "...", "type": "review", "verdict": "approved"}
  ],
  "total": 42,
  "has_more": true
}
```

- 只聚合当前任务（不含子任务）的 comments + outputs + reviews + events + mentions
- 支持 `limit` + `offset` 分页
- 按 `created_at` DESC 排序

#### Phase 3：含子任务的完整 timeline

聚合子任务信息，考虑 materialized view 或 cache 优化性能（WARN-3）。

---

## 四、实施计划

### Phase 1（与 #02/#03 一起验证）

| 改动 | 影响 |
|------|------|
| daemon 自动从 body 提取 @mention | Agent 不再需要手动传 `mentions` 数组 |
| claim prompt 补充完整 API（RC-2） | Agent 知道怎么写产出物 |
| assignee 保留但降级为显示字段 | 向后兼容 |

### Phase 2（#04 独立实施）

| 改动 | 影响 |
|------|------|
| 创建任务时从 description 提取 @ → 自动 assignee | 统一路由入口 |
| API assignee 参数废弃（返回 deprecation warning） | 向后兼容过渡 |
| @mention 自动提取（blackboard_routes.py） | Agent 不再需要手动传 mentions |
| Output 新增 triggered_by_comment 字段 + 索引 | 信息关联 |
| /timeline 聚合查询 API（单任务版） | 结构化视图 |

### Phase 3（未来）

| 改动 | 影响 |
|------|------|
| @多人自动拆子任务（LLM 辅助） | 复杂协作 |
| @所有人 广播语法 | 统一广播入口 |
| /timeline 含子任务完整版 + materialized view | 全景视图 |
| Context Folding（子任务完成后折叠） | 上下文管理 |
| Fidelity 三档信息路由 | 按 Agent 角色分档展示 |

---

## 五、设计决策（已确认）

| # | 决策 | 结论 | 理由 |
|---|------|------|------|
| D1 | assignee 是否完全取消？ | **保留为显示字段，路由统一走 @mention** | 前端展示/Mail/向后兼容/查询效率 |
| D2 | @多人 Phase 1 是否自动拆子任务？ | **不自动拆，Phase 3 再做** | daemon 无法可靠推断依赖关系 |
| D3 | timeline API 是否 Phase 1 就做？ | **Phase 2 做（单任务版）** | 先让 E2E 跑通 |
| D4 | @ 模式支持中文昵称？ | **只支持 @agent-id** | 无歧义，低维护成本 |

---

## 附录：调研来源

- Jira 数据库设计（单表 + issuetype + parent_id）
- Linear/Asana 层级模型（Workspace 隔离 + 逻辑分组）
- LangGraph Subgraph 模式（子任务隔离 + State 映射回传）
- CrewAI 共享 Memory（Crew 边界）
- bMAS 黑板架构（Control Unit 动态路由 + 迭代收敛）
- O'Reilly 2026（共享产出物 > 共享消息 + Phase gates）
- OpenAI Handoff（对话历史传递 surprisingly effective）
- Network-AI（propose→validate→commit 原子写入）
- ClawTeam #10（Auftragstaktik 任务式指挥）
- Opal-Bridge（Canonical IR + Fidelity 三档）
