Files
sanguo_moziplus_v2/docs/design/architecture-v2.6.md
T
2026-05-15 02:05:48 +08:00

896 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI原生DevOps Platform 架构设计 v2.6
**版本**: v2.6Shared Workspace + Blackboard 架构)
**基于**: architecture-v2.md + v2.0 AI Native 调研 + 技术验证
**作者**: 庞统(副军师)
**日期**: 2026-05-15
---
## 变更历史
| 版本 | 日期 | 变更内容 |
|------|------|---------|
| v2.0 | 2026-05-04 | 初始版本:SQLite 4表 + 状态机 + DAG 引擎 |
| v2.6 | 2026-05-15 | **架构重构**Shared WorkspaceBlackboard)取代 DAG 引擎为编排核心 |
| v2.6.1 | 2026-05-15 | 司马懿评审反馈 + Mail 退役决策 + 质量门控 + 决策记录 + 工程修正 |
---
## 1. v2.6 核心变革:从 DAG 状态机到 Shared Workspace
### 1.1 为什么变?
v2.0 的核心是 **DAG 引擎 + 状态机 + 邮件通信**,本质是给 AI 团队做了一套 ERP:
- 编排是确定性状态机(固定流程)
- 交互是点按钮(Dashboard
- Agent 间靠邮件异步通信(信息分散在 mail 目录)
- 人的参与密度不变(全程驾驶)
v2.6 的核心是 **Shared WorkspaceBlackboard+ Agent 自主决策 + Daemon 投递**
- 编排是 AI agent 在黑板上自主领活(动态协作)
- 交互是自然语言对话
- Agent 间通过黑板共享一切(信息集中在任务空间)
- 人只做方向决策和验收
### 1.2 核心原则
> **黑板是唯一真相源,所有 agent 读它、想、行动,写回结果。Daemon 是投递员,不是决策者。**
1. **Agent 决策,Daemon 执行** — 庞统做 plan、张飞领任务、关羽发现风险,都写在黑板上。Daemon 读黑板,执行 spawn/通知。
2. **产出在黑板,不在邮件** — 所有任务产出、讨论、观察都在任务的黑板空间里,Sanguo Mail 不介入任务协作。
3. **Daemon 不阻塞 Agent** — Daemon 是常驻管家,定期 tick 检查黑板,spawn agent 执行,不占用任何 agent 的主 session。
4. **Session 用完即清** — Agent 通过 `openclaw agent --agent <id> --session-id <uuid>` spawn 隔离 session,执行完 daemon 存档 jsonl 并清理 sessions.json。
---
## 2. 架构总览
```
┌─────────────────────────────────────────────────────────────┐
│ 用户 / 触发器 │
│ (Web / CLI / Cron) │
└──────────────────────────┬──────────────────────────────────┘
│ 写入黑板或触发 daemon
┌──────────────────────────▼──────────────────────────────────┐
│ Shared Workspace(黑板) │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ SQLite (blackboard.db) │ │
│ │ tasks / comments / outputs / agents / events │ │
│ │ 原子读写(propose→validate→commit 或 SQLite 事务) │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 任务列表 │ │ 评论线程 │ │ 产出空间 │ │ 讨论区域 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│ daemon tick 读写
┌──────────────────────────▼──────────────────────────────────┐
│ Daemon(管家) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Tick 循环 │ │ Session 管理 │ │ 健康检查 │ │
│ │ (60s 轮询) │ │ spawn/archive │ │ zombie/reclaim │ │
│ │ 读黑板→决策 │ │ /cleanup │ │ /stale 任务 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────────────┘ │
│ │ │ │
│ Daemon 只做三件事: │ │
│ 1. 读黑板,发现需要介入的 │ │
│ 2. Spawn 对应 agent │ │
│ 3. 清理完成的 session │ │
└──────────────────────────┬──────────────────────────────────┘
│ openclaw agent --agent <id> --session-id <uuid>
│ 执行完 → 存档 jsonl → 清理 sessions.json
┌──────────────────────────▼──────────────────────────────────┐
│ Agent 层(将军们) │
│ │
│ Agent 不常驻。被 spawn 时: │
│ 1. 读黑板 → 了解全局状态 │
│ 2. 想和做 → 根据职责自主决策 │
│ 3. 写回黑板 → 产出、评论、领任务 │
│ 4. 退出 → session 被 daemon 清理 │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │庞统 │ │司马懿│ │姜维 │ │关羽 │ │张飞 │ │赵云 │ │
│ │策划 │ │质量 │ │平台 │ │风控 │ │编码 │ │数据 │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ 每个 Agent: SOUL.md + IDENTITY.md + Skills + Workspace │
│ Agent 主 session 不参与任务执行(不被污染) │
└─────────────────────────────────────────────────────────────┘
```
### 关键区别:v2.0 vs v2.6
| 维度 | v2.0 | v2.6 |
|------|------|------|
| 编排核心 | DAG 引擎 + 状态机 | BlackboardShared Workspace |
| 决策者 | Daemon(状态机驱动) | Agent(在黑板上自主决策) |
| Daemon 角色 | 调度器(决定谁干什么) | 投递员(执行黑板上的决策) |
| Agent 通信 | Sanguo Mail(异步邮件) | 黑板 Comment 线程(共享空间) |
| 信息位置 | 分散(mail + task目录 + session | 集中(黑板 SQLite |
| Agent 生命周期 | 固定节点执行 | Spawn 隔离 session,用完即清 |
| 通知机制 | Mail 轮询 | Daemon tick + spawn |
| 协作模式 | 指令式(庞统分配→将军执行) | 自主式(看黑板→领活→写回) |
---
## 3. Shared Workspace(黑板)设计
### 3.1 参考系统对比
| 系统 | 存储 | 原子性 | 讨论 | 状态机 | 发现 |
|------|------|--------|------|--------|------|
| Claude Code Agent Teams | JSON 文件 | 无(last-write-wins | inbox 点对点 | pending/in_progress/completed | Agent 轮询 |
| Hermes Kanban v0.13 | SQLite | SQLite 事务 | Comment 线程 | 7 状态完整机 | Dispatcher 60s tick |
| Network-AI | Markdown 文件 | flock 三阶段提交 | signal key | 无 | Agent 主动读 |
| agent-blackboard | SQLite + Ontology | SQLite 事务 | 本体条目 | 无 | Coordinator 分发 |
| **我们的方案** | **SQLite** | **SQLite 事务** | **Comment 线程** | **简化状态机** | **Daemon tick** |
### 3.2 SQLite Schema
```sql
-- ===== 任务表 =====
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY, -- task-001
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'pending',
CHECK (status IN ('pending','claimed','working','review','done','failed','blocked','cancelled')),
-- 分配(谁领了或被指派)
assignee TEXT, -- agent id: zhangfei-dev
assigned_by TEXT, -- 谁分配的:pangtong-fujunshi / user
-- 依赖
depends_on TEXT, -- JSON array of task IDs
parent_task TEXT, -- 父任务(子任务分解时)
-- 优先级和类型
priority INTEGER NOT NULL DEFAULT 5, -- 1(最高)-10(最低)
task_type TEXT, -- coding/review/data/deploy/research/discuss
-- 时间
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
claimed_at TEXT,
started_at TEXT,
completed_at TEXT,
deadline TEXT,
-- 重试
retry_count INTEGER NOT NULL DEFAULT 0,
max_retries INTEGER NOT NULL DEFAULT 2
);
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);
-- ===== 评论线程表 =====
-- 参考 Hermes kanban_comment:追加写入,所有参与者可见
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
author TEXT NOT NULL, -- agent id 或 'user'
body TEXT NOT NULL,
mentions TEXT, -- JSON array: ["zhangfei-dev", "guanyu-dev"]
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (task_id) REFERENCES tasks(id)
);
CREATE INDEX IF NOT EXISTS idx_comments_task ON comments(task_id);
CREATE INDEX IF NOT EXISTS idx_comments_author ON comments(author);
-- 注意:mentions 是 JSON 数组,无法直接建索引。daemon tick 查询用 json_each(mentions)。
-- 数据量小时够用,后续可拆 comment_mentions 关联表优化。
-- ===== 产出表 =====
CREATE TABLE IF NOT EXISTS outputs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
agent TEXT NOT NULL, -- 谁写的
output_type TEXT NOT NULL, -- code/document/data/config/other
title TEXT NOT NULL,
content_path TEXT, -- 文件路径(产出物在 task 目录下)
summary TEXT, -- 一句话摘要
metadata TEXT, -- JSON: {files_changed, lines_added, ...}
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (task_id) REFERENCES tasks(id)
);
CREATE INDEX IF NOT EXISTS idx_outputs_task ON outputs(task_id);
-- ===== 决策记录表 =====
-- Agent 执行过程中的关键决策必须记录。哪怕是自己做的决策也要填一条。
CREATE TABLE IF NOT EXISTS decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
decider TEXT NOT NULL, -- 谁做的决策
decision TEXT NOT NULL, -- 决策内容:"选 A 方案"
rationale TEXT NOT NULL, -- 为什么:"B 方案内存开销更大"
alternatives TEXT, -- JSON array: 被排除的选项
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (task_id) REFERENCES tasks(id)
);
CREATE INDEX IF NOT EXISTS idx_decisions_task ON decisions(task_id);
-- ===== 观察表 =====
-- Agent 执行过程中发现的问题、风险、建议
CREATE TABLE IF NOT EXISTS observations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
observer TEXT NOT NULL, -- 谁观察到的
severity TEXT NOT NULL DEFAULT 'info',
CHECK (severity IN ('blocking','warning','info','audit')),
body TEXT NOT NULL,
resolved_by TEXT, -- 谁处理的
resolved_at TEXT, -- 何时处理的
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (task_id) REFERENCES tasks(id)
);
-- ===== 事件日志(审计追踪)=====
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT,
agent TEXT,
event_type TEXT NOT NULL,
detail TEXT, -- JSON
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_events_task ON events(task_id);
CREATE INDEX IF NOT EXISTS idx_events_time ON events(created_at);
-- 合法 event_type 清单:
-- 任务:task_created, task_claimed, task_started, task_completed, task_failed,
-- task_blocked, task_unblocked, task_reviewed, task_cancelled, task_retried
-- 协作:comment_added, output_written, observation_added, decision_recorded
-- Agentagent_spawned, agent_completed, agent_zombie_detected
-- Sessionsession_spawned, session_archived, session_cleanup
-- 系统:daemon_tick, daemon_manual_tick
-- ===== Agent 注册表 =====
CREATE TABLE IF NOT EXISTS agents (
agent_id TEXT PRIMARY KEY,
role TEXT,
current_status TEXT DEFAULT 'idle', -- idle/working/offline
current_task TEXT,
last_active TEXT,
capabilities TEXT -- JSON array: ["coding", "review", "deploy"]
);
-- agents 表更新规则:
-- Agent claim 任务时:自己更新 current_status='working', current_task=task_id
-- Agent 完成退出时:daemon 更新 current_status='idle', current_task=NULL
-- Daemon tick 检测到 zombiedaemon 更新 current_status='offline'
```
**连接配置:**
```python
def get_connection():
conn = sqlite3.connect(str(DB_PATH))
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")
conn.execute("PRAGMA busy_timeout=5000")
return conn
```
### 3.3 简化状态机
```
pending → claimed → working → review → done
↑ │ ├→ blocked ──┘ ├→ failed
│ │ └→ failed └→ cancelled
└─────────┘
(review→pending: 审核不通过,打回重做)
(blocked→pending: 阻塞解除)
(failed→pending: 重试)
```
**与 v2.0 的区别:** v2.0 有 9 个状态(spawning, ready, reporting 等),v2.6 简化为 8 个。原因是 spawn 逻辑从状态机移到了 daemon——daemon tick 发现黑板需要某人介入就 spawn,不需要 spawning/ready 这些中间状态。
| 状态 | 含义 | 谁触发 |
|------|------|--------|
| pending | 待领取 | 任何 Agent 或用户创建 |
| claimed | 已认领 | Agent 自己或被指派 |
| working | 执行中 | Agent |
| review | 待审核 | Agent 完成产出 |
| blocked | 需要帮助 | Agent |
| done | 完成 | **审核通过且所有问题达成一致** |
| failed | 失败 | Agent 或 daemon |
| cancelled | 取消 | 用户 |
**完整合法流转矩阵:**
```python
VALID_TRANSITIONS = {
"pending": {"claimed", "cancelled"},
"claimed": {"working", "pending", "cancelled"}, # pending: 放弃认领
"working": {"review", "blocked", "failed", "cancelled"},
"review": {"done", "pending", "failed", "cancelled"}, # pending: 审核不通过打回
"blocked": {"pending", "cancelled"}, # pending: 阻塞解除
"done": set(), # 终态
"failed": {"pending"}, # pending: 重试
"cancelled": set(), # 终态
}
```
### 3.4 原子操作
**任务认领(claim** — 原子 CAS,防止两个人同时领:
```python
def claim_task(task_id: str, agent_id: str) -> bool:
conn = get_connection()
try:
cursor = conn.execute(
"UPDATE tasks SET status='claimed', assignee=?, claimed_at=datetime('now') "
"WHERE id=? AND status='pending' AND (assignee IS NULL OR assignee=?)",
(agent_id, task_id, agent_id)
)
conn.commit()
return cursor.rowcount > 0 # 0 表示被别人抢了或不是指定分配给自己的人
finally:
conn.close()
```
**产出写入** — SQLite 事务保证原子:
```python
def write_output(task_id: str, agent_id: str, output: dict):
conn = get_connection()
try:
conn.execute("BEGIN IMMEDIATE") # 立即获取写锁
conn.execute(
"INSERT INTO outputs (task_id, agent, output_type, title, content_path, summary, metadata) "
"VALUES (?, ?, ?, ?, ?, ?, ?)",
(task_id, agent_id, output['type'], output['title'],
output['path'], output['summary'], json.dumps(output.get('metadata', {})))
)
conn.execute(
"INSERT INTO events (task_id, agent, event_type, detail) VALUES (?, ?, 'output_written', ?)",
(task_id, agent_id, json.dumps({'output_id': output['title']}))
)
conn.commit()
finally:
conn.close()
```
### 3.5 评论线程(讨论机制)
参考 Hermes 的 `kanban_comment` 模式:
```python
def add_comment(task_id: str, author: str, body: str, mentions: list = None):
conn = get_connection()
try:
conn.execute(
"INSERT INTO comments (task_id, author, body, mentions) VALUES (?, ?, ?, ?)",
(task_id, author, body, json.dumps(mentions or []))
)
conn.execute(
"INSERT INTO events (task_id, agent, event_type, detail) VALUES (?, ?, 'commented', ?)",
(task_id, author, json.dumps({'body_preview': body[:100], 'mentions': mentions}))
)
conn.commit()
finally:
conn.close()
```
**讨论示例:**
```
[16:30 庞统] 张飞,你的实现方案我看了,回测数据量大时内存会爆。
关羽,从风控角度也看看? @关羽 @张飞
[16:35 关羽] 同意。建议加分批加载机制,单批不超过 50 万条。
[16:40 张飞] 收到,改成分批加载。预计 30 分钟。
[16:55 庞统] @张飞 注意止损逻辑也需要同步改,分批后止损触发时机变了。
[17:10 张飞] 完成。产出在 output-zhangfei-v2.md。
```
**核心原则:评论都在黑板上,不在任何 agent 的 session 里。Agent 的 session 是临时的。**
### 3.6 竞态解决
任务认领的竞态通过 SQLite 原子 CAS 解决(先到先得)。
职责冲突的解决(张飞和关羽都认为自己该做某个任务):
1. **默认:先到先得** — SQLite CAS,谁先 claim 谁做
2. **升级:庞统仲裁** — 如果争议,评论中 @庞统 请求仲裁
3. **最终:用户拍板**@user 请求用户决定
不需要复杂的分布式共识——职责分工已经自然避免了大部分冲突。
---
## 4. Daemon(管家)设计
### 4.1 Daemon 的角色定位
> **Daemon 是投递员,不是决策者。所有决策发生在黑板上,daemon 只执行。**
Daemon 做三件事:
1. **读黑板** — 定期 tick,检查黑板状态
2. **Spawn Agent** — 根据黑板上的指示,spawn 对应的 agent
3. **清理 Session** — agent 执行完后,存档 jsonl + 清理 sessions.json
Daemon **不做**
- ❌ 不决定谁做什么(agent 自己决定或庞统在黑板上分配)
- ❌ 不维护状态机(黑板就是状态)
- ❌ 不做业务逻辑(不解析产出、不做评审)
### 4.2 Daemon Tick 循环
参考 Hermes Dispatcher,但更轻量:
**Tick 频率:60 秒(默认),可通过 CLI 手动触发立即执行。**
```bash
# 手动触发一次 tick(用于需要立即响应的场景)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/daemon.py tick
```
```python
async def daemon_tick():
"""每 60 秒执行一次"""
# 1. 健康检查
reclaim_stale_tasks() # 超时的 working 任务回收
detect_zombie_sessions() # 进程死了但 session 还在的
# 2. 读黑板
board = read_blackboard() # SQLite 查询
# 3. 处理评论中的 @mention
# Agent A 在评论中 @AgentB → daemon spawn AgentB 来看评论
for comment in board.get_unprocessed_mentions():
target_agent = comment.mentioned_agent
if not is_agent_active(target_agent):
async_spawn_agent(target_agent,
message=f"黑板上有给你的新评论(task-{comment.task_id}),请查看。")
mark_comment_processed(comment.id)
# 4. 处理待领取的任务(庞统在黑板上分配了但 agent 还没领)
for task in board.get_assigned_unclaimed():
if not is_agent_active(task.assignee):
async_spawn_agent(task.assignee,
message=f"黑板上有分配给你的任务({task.title}),请查看并认领。")
# 5. 处理 blocked 任务(agent 请求帮助)
for task in board.get_blocked_tasks():
mentions = get_latest_comment_mentions(task.id)
if mentions:
# 评论中 @ 了某人 → spawn 那个人
for agent_id in mentions:
if not is_agent_active(agent_id):
async_spawn_agent(agent_id,
message=f"任务 {task.id} 被 block,需要你的协助。")
else:
# 没有 @ → spawn 庞统来决定找谁帮忙
async_spawn_agent('pangtong-fujunshi',
message=f"任务 {task.id} 被 block,没有指定协助者,请决定如何处理。")
# 6. 清理完成的 session
for session in get_completed_sessions():
archive_session(session) # mv jsonl → task 目录
cleanup_sessions_json(session) # 编辑 sessions.json 删除记录
```
### 4.3 Session 生命周期
```
1. Daemon spawn
openclaw agent --agent zhangfei-dev --session-id <uuid> \
--message "请检查黑板 task-001..."
2. Agent 执行
- 读黑板(SQLite 查询)
- 做任务(编码/审核/数据分析)
- 写回黑板(产出、评论、状态更新)
3. Agent 退出(自然结束)
4. Daemon 清理
- mv <session_id>.jsonl → task-001/archive/
- mv <session_id>.trajectory.jsonl → task-001/archive/
- 编辑 sessions.json 删除该 session 记录
```
**技术验证结论:**
- `openclaw agent --agent <id> --session-id <uuid>` 可创建完全隔离的 session ✅
- 直接编辑 `sessions.json` 可安全删除 session 记录 ✅(已验证)
- Gateway WS `sessions.delete` 需要 `operator.admin` scope(token 模式不授予,不可用)❌
- 回退方案:直接编辑 `sessions.json` 是安全可靠的 ✅
### 4.4 Agent Spawn 后的消息内容
Agent 被 spawn 时,daemon 传递的消息应包含足够的上下文让 agent 知道该做什么:
```python
def build_spawn_message(task_id: str, trigger_reason: str, comments_since: str = None):
task = get_task(task_id)
msg = f"黑板任务通知:\n"
msg += f"- 任务:{task.title}{task.id}\n"
msg += f"- 状态:{task.status}\n"
msg += f"- 触发原因:{trigger_reason}\n"
if comments_since:
recent = get_comments_since(task_id, comments_since)
if recent:
msg += f"\n最近评论:\n"
for c in recent:
msg += f" [{c.created_at} {c.author}] {c.body[:200]}\n"
msg += f"\n请读取黑板获取完整信息。"
return msg
```
---
## 5. Agent 与黑板的交互
### 5.1 Agent 被_spawn_后的工作流程
```
Agent 被 spawn
1. 读黑板 → 了解任务全局状态
- 读 tasks 表:当前任务的状态、描述、依赖
- 读 comments 表:讨论历史
- 读 outputs 表:已有产出
- 读 observations 表:已知风险
2. 想 → 根据自己的职责自主决策
- 我是编码先锋,这个 pending 任务适合我 → claim
- 我是风控守将,这个 comment @ 我 → 回复
- 我是副军师,这个任务需要分解 → 创建子任务
3. 做 → 执行任务
- 编码、审核、数据分析等
- 过程中发现风险 → 写 observation
- 需要其他人协助 → 写 comment @mention
4. 写回黑板 → 产出、评论、状态更新、决策记录
- 写 outputs 表:产出文件路径 + 摘要
- 写 comments 表:完成说明
- 写 decisions 表:关键决策(哪怕自己的决策也要填一条)
- 更新 tasks 表:status → done/review
5. 退出 → daemon 自动清理 session
```
### 5.2 Agent 工具集
Agent 通过 `exec` 工具调用 CLI 命令操作黑板:
```bash
# 读黑板(全部)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py read --task task-001
# 读黑板(过滤:只读和自己相关的)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py read --task task-001 --agent zhangfei-dev
# 读黑板(过滤:只读最近 20 条)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py read --task task-001 --last 20
# 读黑板(过滤:只读特定类型)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py read --task task-001 --type comments
# 认领任务
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py claim --task task-001 --agent zhangfei-dev
# 写产出
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py output --task task-001 --agent zhangfei-dev \
--type code --title "分批加载实现" --path task-001/output-zhangfei.md \
--summary "实现分批加载,单批50万条"
# 写评论
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py comment --task task-001 --author zhangfei-dev \
--body "完成分批加载实现" --mentions "[]"
# 写观察
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py observe --task task-001 --observer guanyu-dev \
--severity warning --body "止损逻辑需适配分批模式"
# 记录决策
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py decide --task task-001 --decider zhangfei-dev \
--decision "使用分批加载而非流式" --rationale "流式需要改底层框架,分批只需改回测模块"
# 创建任务(任何 Agent 都可以创建)
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py create --title "分钟线数据下载" \
--creator zhaoyun-data --task-type data
```
---
## 6. 关键场景流程
### 6.1 庞统规划 + Agent 领任务
```
用户 → 庞统(主session)"设计一个动量因子策略"
庞统在黑板上写:
- 创建 task-001(数据准备,pending
- 创建 task-002(因子计算,pendingdepends_on: [task-001]
- 创建 task-003(回测验证,pendingdepends_on: [task-002]
- 评论:"建议赵云领 001,张飞领 002 和 003"
Daemon tick 发现 task-001 pending + 庞统评论建议赵云
Daemon spawn 赵云 → 赵云读黑板 → claim task-001 → 执行 → 写产出 → 退出
Daemon tick 发现 task-001 done → task-002 depends_on 满足
Daemon spawn 张飞 → 张飞读黑板 → claim task-002 → 执行 → 写产出 → 退出
(同理 task-003
```
### 6.2 Agent 间协作讨论
```
张飞执行 task-002 时发现需要分钟线数据
张飞写评论:"@赵云 task-002 需要分钟线数据,能帮忙下载吗?"
张飞更新任务状态 → blocked
Daemon tick 发现 task-002 blocked + 评论 @ 赵云
Daemon spawn 赵云 → 赵云读黑板 → 看到评论 → 下载数据
赵云写评论:"分钟线数据已下载到 /path/to/data" + 写产出
赵云写评论:"@张飞 数据就绪,可以继续"
Daemon tick 发现评论 @ 张飞
Daemon spawn 张飞 → 张飞读黑板 → 看到数据就绪 → 继续 task-002
```
### 6.3 Agent 发现风险
```
张飞在 task-002 中发现止损逻辑有 bug
张飞写 observationseverity: warning):
"止损逻辑在分批模式下可能漏触发"
张飞写评论:"@关羽 止损逻辑需要你从风控角度确认"
Daemon tick 发现 observation + 评论 @ 关羽
Daemon spawn 关羽 → 关羽读黑板 → 审查 → 写评论 + observation
```
### 6.4 用户直接参与
```
用户读黑板 → 发现 task-002 进度慢
用户在黑板上写评论:"task-002 优先级提高,需要今天完成"
Daemon tick 发现用户评论 → 如果张飞未 active → spawn 张飞通知
```
---
## 7. Session 隔离与清理
### 7.1 技术实现
```python
class SessionManager:
def async_spawn_agent(self, agent_id: str, message: str) -> str:
"""异步 spawn 隔离 session,不等待完成。返回 session_id。"""
session_id = str(uuid.uuid4())
cmd = [
"openclaw", "agent",
"--agent", agent_id,
"--session-id", session_id,
"--message", message,
"--json"
]
# Popen 异步启动,不阻塞 daemon tick
subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
log_event(agent=agent_id, event_type='agent_spawned', detail={'session_id': session_id})
return session_id
def cleanup_session(self, agent_id: str, session_id: str, archive_dir: str):
"""存档 jsonl + 文件锁保护下清理 sessions.json"""
sessions_dir = f"/Users/chufeng/.openclaw/agents/{agent_id}/sessions"
store_path = f"{sessions_dir}/sessions.json"
lock_path = f"{sessions_dir}/.cleanup.lock"
# 1. 存档 jsonl 文件
os.makedirs(archive_dir, exist_ok=True)
for ext in ['.jsonl', '.trajectory.jsonl', '.trajectory-path.json']:
src = f"{sessions_dir}/{session_id}{ext}"
if os.path.exists(src):
shutil.move(src, f"{archive_dir}/{session_id}{ext}")
# 2. 文件锁保护下编辑 sessions.json(防止和 Gateway 并发写入冲突)
with open(lock_path, 'w') as lock_file:
fcntl.flock(lock_file, fcntl.LOCK_EX)
try:
with open(store_path) as f:
store = json.load(f)
keys_to_remove = [k for k in store if session_id in k]
for k in keys_to_remove:
del store[k]
with open(store_path, 'w') as f:
json.dump(store, f, indent=2)
finally:
fcntl.flock(lock_file, fcntl.LOCK_UN)
os.unlink(lock_path)
```
### 7.2 验证结论
| 验证项 | 结果 |
|--------|------|
| `openclaw agent --session-id <uuid>` 创建隔离 session | ✅ 通过 |
| 连续 spawn 多个 session 互不干扰 | ✅ 通过 |
| 并行 spawn 成功 | ✅ 通过 |
| 直接编辑 sessions.json 删除记录安全 | ✅ 通过 |
| jsonl 存档后从原目录删除 | ✅ 通过 |
| Gateway WS sessions.delete(需 admin scope | ❌ 不可用 |
| `openclaw sessions cleanup --fix-missing --enforce` | ❌ 对 agent main session 报错 |
| Agent 主 session 对 CLI spawn 的 sub 完全无感 | ✅ 确认(设计如此)|
---
## 8. Sanguo Mail:退役
v2.6 中 Mail 完全退役。黑板的两个操作替代了 Mail 的所有功能:
| Mail 功能 | 黑板替代 |
|----------|--------|
| 庞统分配任务 | 庞统在黑板创建 task + 评论 @指定 agent |
| Agent 间通信 | 评论 @mention |
| 结果回传 | 产出写入 outputs 表 + 评论通知 |
| 讨论 | 评论线程 |
黑板比 Mail 更可靠:信息集中在 SQLite(不分散在 mail 目录)、有状态追踪、评论线程保持上下文完整、SQLite 读写比 Mail poller 更可靠。
如果需要系统级通知(daemon 异常、Gateway 状态),在黑板上创建 `system` 类型任务处理。
---
## 9. 质量门控(任务完成标准)
### 9.1 任务完成 = 司马懿评审通过 + 所有问题达成一致 + 修改完成
一个任务要标记为 `done`,必须满足:
1. **产出已提交** — Agent 写入 outputs 表
2. **决策已记录** — 关键决策写入 decisions 表(哪怕是自己的决策也要填一条)
3. **司马懿审核通过** — spawn 司马懿审核产出,评论中明确写“通过”
4. **所有问题达成一致** — 审核中提出的问题全部解决,讨论达成共识
5. **修改已完成** — 审核问题对应的修改已写入产出
### 9.2 达成一致的原则
- **以用户意图为导向** — 任何人(包括司马懿、庞统)说的都不一定对,最终以用户的原始意图为准
- **讨论达成共识** — 不同意见通过黑板评论讨论解决,不是谁职位高谁说了算
- **用户有最终裁量权** — 讨论无法达成一致时,@user 请用户裁定
### 9.3 流程
```
Agent 完成产出 → status: review
Daemon tick → spawn 司马懿审核
司马懿读黑板(产出 + 决策 + 观察)
司马懿写评论:
- “通过” → status: done
- “不通过,原因:XXX” → status: pending(打回重做)+ 评论说明问题
如果有争议:
- 评论中讨论
- 讨论不清 → @user 请求裁定
```
### 9.4 决策记录
Agent 执行过程中的每个关键决策都必须记录在黑板的 decisions 表中:
| 字段 | 含义 |
|------|------|
| decider | 谁做的决策 |
| decision | 决策内容(选了什么) |
| rationale | 为什么这样选 |
| alternatives | 被排除的选项 |
**哪怕是自己做的决策也要填一条。** 目的:
- 后续复盘时能追溯“当时为什么这样选”
- 审核时司马懿能理解决策背后的思考
- 经验沉淀的原始素材
---
## 10. 产出物目录约定
```
~/.sanguo_projects/sanguo_moziplus/artifacts/
└── {task-id}/
├── outputs/ # Agent 产出物(代码、文档、数据)
├── archive/ # session jsonl 存档
└── data/ # 数据文件
```
Agent 写产出时,`content_path` 指向此目录。Daemon 存档 session jsonl 时也写入 `archive/` 子目录。
---
## 11. 保留 v2.0 的设计
以下 v2.0 的设计在 v2.6 中保留:
1. **SQLite WAL 模式** — 黑板数据库同样使用 WAL
2. **结构化产出规范** — output.md frontmatter + 结论 JSON(写在黑板 outputs 表中)
3. **观察机制** — v2.0 Report Watcher 的思路升级为 observations 表
4. **证据原则** — 结论必须有证据(代码行号、日志、文件内容)
5. **审核流程** — 可通过黑板评论 + 状态机实现
---
## 12. Phase 规划(v2.6
### Phase 1: 黑板基础设施
1. SQLite blackboard.db5 表 + WAL
2. blackboard.py CLI(读写操作)
3. Daemon tick 循环(读黑板 + spawn + 清理)
4. Session 管理(spawn + 存档 + 清理)
### Phase 2: Agent 交互
5. Agent 黑板操作 Skill
6. 评论 + @mention 通知链路
7. 任务依赖自动推进
8. 健康检查(stale reclaim + zombie 检测)
### Phase 3: 智能化
9. 庞统 AI 规划(读需求 → 创建任务 + 分配建议)
10. Agent 自主领活(读黑板 → 匹配职责 → claim)
11. 产出验证门禁
12. 经验沉淀(observation → knowledge base
---
## 13. 技术选型
| 需求 | 参考系统 | 我们的方案 | 理由 |
|------|---------|-----------|------|
| 共享状态 | Hermes SQLite + Network-AI flock | SQLite WAL + 事务 CAS | 原子性 + 无外部依赖 |
| 讨论 | Hermes kanban_comment | comments 表 + @mention | 简单追加写入,所有人可见 |
| 调度 | Hermes Dispatcher 60s tick | Daemon 60s tick | 同设计,更轻量 |
| 通知 | Claude Code idle notification | Daemon spawn + message | OpenClaw 原生能力 |
| 通信 | Hermes kanban_comment + Claude Code inbox | 黑板 comments + @mention | 替代 Sanguo Mail |
| 竞态 | Network-AI propose→validate→commit | SQLite CASfirst-commit-wins | SQLite 事务足够 |
| Session | Hermes process-per-worker | openclaw agent --session-id | OpenClaw 原生隔离 |
| 清理 | 无参考 | 编辑 sessions.json | 已验证可行 |
---
## 14. 风险和缓解
| 风险 | 概率 | 缓解 |
|------|------|------|
| Agent 上下文不足(隔离 session 没有历史)| 中 | spawn 时传递黑板关键信息 + agent 可主动读黑板 |
| Daemon 单点故障 | 低 | PM2 自动重启 + tick 无状态 |
| SQLite 并发写入 | 中 | WAL + busy_timeout + BEGIN IMMEDIATE |
| 黑板膨胀(大量评论/产出)| 低 | 定期 archive + agent 只读最近 N 条 |
| Agent 不知道该做什么 | 中 | Skill 指导 + 庞统 plan 评论 + daemon 消息含上下文 |
| Sanguo Mail 退役后的系统通知 | 低 | 黑板 system 类型任务替代 |