Files
sanguo_moziplus_v2/docs/design/architecture-v2.6.md
T
2026-05-15 22:55:14 +08:00

85 KiB
Raw Blame History

AI原生DevOps Platform 架构设计 v2.6

版本: v2.6(Shared 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 Workspace(Blackboard)取代 DAG 引擎为编排核心
v2.6.1 2026-05-15 司马懿评审反馈 + Mail 退役决策 + 质量门控 + 决策记录 + 工程修正
v2.6.2 2026-05-15 课题1设计决策:三层执行模型、续杯机制、AI驱动retry、Guardrail体系、must_haves三件套、分级审查矩阵
v2.6.2.1 2026-05-15 司马懿评审反馈:L2/L3区分标准、timeout修正、outputs关联attempt、Scope Guard异步、risk_level自动
v2.6.3 2026-05-15 课题2设计决策:Tick核心+Inbox JSONL加速、Handoff Comment无缝接手、L1/L2/L3对应Opal-Bridge Fidelity、黑板AI Native内容规范+三层约束体系(Schema校验+Skill引导+L1截取)、依赖驱动并行/串行、Phase规划更新
v2.6.4 2026-05-15 课题3设计决策:分级审查流水线(四级风险→三/二/一阶段)、审查协议注册表(Review Protocol Registry)、反驳权(Rebuttal Phase)、reviews表结构化存储、声明式guardrails.yaml、Full Agent vs Subagent vs Daemon直接执行判据、对抗辩论模式
v2.6.5 2026-05-15 课题4设计决策:模板组件库(+custom)替代固定DAG、四层上下文架构(L0铁律/L1角色/L2引擎注入三段式/L3被动参考)、prompt_templates按角色拼装、L2按角色精确注入不多不少、L3 Skill description四要素优化写法

课题 1-2 遗留 TODO(需后续课题解决)

# 待解决事项 归属课题 说明
T1-1 spawn sub 是否阻塞?需要调查 课题 2 课题 2 解决:不阻塞,signal file 异步
T1-2 事件驱动取代 polling tick 课题 2 课题 2 解决:双层事件架构
T1-3 依赖推进(done→自动解锁下游) 课题 2 课题 2 解决:task_completed 事件即时解锁
T2-1 files_modified 冲突检测 课题 2 D2-4 决策:不做,Agent 评论自然协调
T2-2 Auto-compact 课题 2 D2-6 决策:不做,隔离 session 天然无 context rot
T1-4 Agent 间自主协商机制 课题 3 课题3解决:审查协议+反驳权+评论协商
T1-5 Scope Guard 的 Skill 定义 课题 4 scope_declaration 格式、检查 prompt 模板
T1-6 truths 验证的具体实现 课题 4 AI 级别验证,怎么让 AI 判断 truths 达成
T1-7 outputs attempt_number 过滤规则 课题 4 重试时 Agent 看到之前 attempt output 的规则
T1-8 状态机细化(review 轮次、sub_status) 课题 3 课题3解决:§9.7状态机对齐
T2-3 blackboard.py 写操作自动写 inbox JSONL Phase 1 实现 CLI 层自动完成
T2-4 Tick Loop + Inbox JSONL Watcher 实现 Phase 1 实现 Daemon 核心改造
T2-5 L2/L3 分层读取 API Phase 2 实现 blackboard.py read --level L2/L3
T2-6 仓库级上下文(Agent Context Pack Phase 3 参考 agent-chorus Context Pack:结构化仓库情报让 Agent 不需要自己探索。量化项目可抽象为项目结构/代码规范/常见陷阱
T2-7 Handoff Comment 的 Skill 引导 Phase 2 Agent Skill 中明确“结束前必须写 Handoff Comment”的行为规范
T2-8 Handoff Comment 的 Skill 解析规则 课题 4 下一个 Agent 的 Skill 中如何解析 Handoff 格式

课题 4 Skill 体系设计 TODO

# Skill 内容 适用角色 Phase 来源
S-01 blackboard.py CLI 使用手册 所有 Agent P1 课题1 §5.2
S-02 L1→L2/L3 按需读取判断 所有 Agent P2 课题2 §4.4
S-03 写 Handoff Comment(格式+时机) 所有 Agent P1 课题2 §5.1
S-04 读 Handoff Comment(利用上一个 Agent 交接) 所有 Agent P2 课题2 §5.1
S-05 写 observation(时机+severity 格式) 所有 Agent P2 课题1 §4.7
S-06 写 decision(时机+rationale 格式) 所有 Agent P2 课题1 §9.4
S-07 写 output 的 Schema 约束 所有 Agent P1 课题2 §3.7
S-08 Guardrail 打回时的处理流程 执行者 P2 课题3 §9.3
S-09 @mention 使用规范 所有 Agent P2 课题1 §5.2
S-10 claim 后写 scope_declaration(格式+内容) 执行者 P1 课题1 §4.7
S-11 must_haves 三件套自检(truths/artifacts/constraints 执行者 P1 课题1 §9
S-12 收到 review needs_revision 的反驳流程(ACCEPT/REJECT/PARTIAL 执行者 P2 课题3 §9.5
S-13 审查者 Investigation Protocol 五阶段执行 审查者 P2 课题3 §9.4
S-14 多视角审查方法(代码/方案/分析三套视角集) 审查者 P2 课题3 §9.4
S-15 写 review 的 Schema 约束(verdict+evidence 审查者 P2 课题3 §9.6
S-16 信心度自评(confidence 打分标准) 审查者 P2 课题3 §9.6
S-17 收到反驳(REJECT)后的评估方法 审查者 P2 课题3 §9.5
S-18 plan_review 协议(假设提取/pre-mortem/依赖审计) 审查者 P2 课题3 §9.4
S-19 output_review 协议(需求追踪/缺口分析) 审查者 P2 课题3 §9.4
S-20 analysis_review 协议(逻辑跳跃/数据来源) 审查者 P2 课题3 §9.4
S-21 创建任务(truths/artifacts/constraints 定义) 庞统 P1 课题1 §9
S-22 风险等级自动判断(task_type→risk_level 庞统 P1 课题3 §9.2
S-23 挑战者选择(按任务类型选挑战者) 庞统 P2 课题3 §9.10
S-24 对抗辩论裁决方法 庞统 P3 课题3 §9.10
S-25 escalated 任务的用户沟通 庞统 P3 课题3 §9.7
S-26 confidence 低时的升级判断 庞统 P2 课题3 T3-5
S-27 任务拆解方法(依赖声明/子任务创建) 庞统 P2 课题1 §5.1
S-28 L1 消息构建逻辑 庞统/Daemon P1 课题2 §4.4
T2-9 inbox 并发写入的竞态处理 Phase 1 验证 多 Agent 同时写 inbox 文件时的安全性(agent-chorus 同样用追回写入,无锁)
T2-10 inbox 文件的 rotate/truncate 策略 Phase 2 长期运行后文件膨胀的防范
T2-11 Tick 频率 30s vs 60s 的性能验证 Phase 1 验证 黑板查询量的实际测算
T2-12 CLI Schema 校验的 schemas/ 目录定义 Phase 1 实现 handoff/output/decide/observe 四个 schema 文件

课题 3 遗留 TODO

# 待解决事项 归属 说明
T3-1 reviews 表的 CLI 命令(blackboard.py review Phase 2 写入/查询评审结果
T3-2 Guardrail YAML 解析 + 执行引擎 Phase 2 读取 guardrails.yaml 并按 layer 执行
T3-3 对抗辩论模式的具体黑板协议 Phase 3 正方/反方如何在黑板上交互
T3-4 挑战者池的选择策略 Phase 2 按任务类型自动选择挑战者
T3-5 confidence 低于阈值自动升级 Phase 2 如 confidence < 0.7 升级庞统
T3-6 评审详情文件的 Schema 定义 Phase 2 detail_path 指向的 JSON 结构
T3-7 low 风险任务 Guardrail 自动通过的流控 Phase 2 自动跳过 review 状态
T3-8 Review Protocol 模板文件编写 Phase 2 4个 YAML 协议文件 + Daemon 加载逻辑
T3-9 反驳权(Rebuttal Phase)的 Daemon 流控 Phase 2 review 提交后自动 spawn 原执行者反驳
T3-10 Full Agent vs Subagent 的 Daemon 调度逻辑 Phase 2 根据任务类型自动选择 spawn 方式

1. v2.6 核心变革:从 DAG 状态机到 Shared Workspace

1.1 为什么变?

v2.0 的核心是 DAG 引擎 + 状态机 + 邮件通信,本质是给 AI 团队做了一套 ERP:

  • 编排是确定性状态机(固定流程)
  • 交互是点按钮(Dashboard)
  • Agent 间靠邮件异步通信(信息分散在 mail 目录)
  • 人的参与密度不变(全程驾驶)

v2.6 的核心是 Shared Workspace(Blackboard)+ 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 引擎 + 状态机 Blackboard(Shared 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

-- ===== 任务表 =====
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,

    -- must_haves 与风险等级(课题1设计决策)
    must_haves TEXT,                          -- JSON: {truths: [], artifacts: [], constraints: []}
    risk_level TEXT DEFAULT 'standard',       -- high/standard/low/research
    estimated_duration_minutes INTEGER        -- 预估工时(续杯硬上限 = 3x 此值)
);

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, ...}
    attempt_number INTEGER DEFAULT 1,       -- 关联 task_attempts.attempt_number
    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
-- Agent:agent_spawned, agent_completed, agent_zombie_detected
-- Session:session_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"]
);

-- ===== 任务尝试记录(参考 Hermes task_runs)=====
CREATE TABLE IF NOT EXISTS task_attempts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    task_id TEXT NOT NULL,
    attempt_number INTEGER NOT NULL,
    agent TEXT NOT NULL,
    outcome TEXT NOT NULL,  -- completed/blocked/crashed/timed_out/spawn_failed/reclaimed
    exit_code INTEGER,
    log_path TEXT,
    summary TEXT,
    metadata TEXT,  -- JSON: {duration_seconds, token_count, ...}
    started_at TEXT NOT NULL DEFAULT (datetime('now')),
    completed_at TEXT,

    FOREIGN KEY (task_id) REFERENCES tasks(id)
);

CREATE INDEX IF NOT EXISTS idx_attempts_task ON task_attempts(task_id);

-- agents 表更新规则:
-- Agent claim 任务时:自己更新 current_status='working', current_task=task_id
-- Agent 完成退出时:daemon 更新 current_status='idle', current_task=NULL
-- Daemon tick 检测到 zombie:daemon 更新 current_status='offline'

连接配置:

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 取消 用户

完整合法流转矩阵:

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,防止两个人同时领:

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 事务保证原子:

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 模式:

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 请求用户决定

不需要复杂的分布式共识--职责分工已经自然避免了大部分冲突。

3.7 黑板是索引不是仓库(AI Native 内容规范)

核心原则:黑板只存元数据 + 摘要 + 文件路径,不存大段文本内容。

设计推导(课题 2):

  • Network-AI 的核心洞察:Agent 只读黑板摘要,详细数据在文件中
  • Claude Code 的 file reference 模式:不内联,只引用
  • agent-chorus 的 Context Pack 实验证明:结构化上下文让 Agent 文件打开量降 70%、token 消耗降 60%、零生产风险答案
  • Opal-Bridge 的 Fidelity 三档:无损/摘要/混合,传递时按需降档
  • 一个典型任务全量黑板信息 ~1100-1750 tokens,极端 ~4000 tokens--远小于 128K context
  • 问题不是空间不够,而是信号噪声比:全量注入让 Agent 在无关信息上浪费注意力

AI Native 内容规范--不做硬限制,做软引导:

传统做法是给每个字段设长度上限(如 comments.body ≤ 2000 字符),这是 CRUD 应用的思维。 AI Native 的做法是:Agent 是智能体,有能力判断"这段分析应该写文件还是直接写评论"。规范是指导性的,不是强制性的。

  • 不做硬限制--不设字段长度上限,不截断,不报错
  • 做软引导--Agent 的 Skill 中写"评论应简洁明了,大段分析写文件后在评论中给路径"
  • 做传递优化--L1 传递时自动截取(最近 3 条评论、每条 100 字符),这是传递层面的优化,不是存储层面的限制
  • 做信息分层--黑板上的 comments 表存完整内容(不截断),但 L1 传递时只取摘要

为什么这样做是 AI Native:

  1. Agent 是智能体,不是 API 客户端--它有能力判断"这段分析应该写文件还是直接写评论"
  2. 如果硬限制导致信息丢失,Agent 会绕过限制(拆成多条评论、用文件存储),反而更混乱
  3. 真正需要控制的是传递时的信息量(L1 预算),不是存储时的信息量

黑板上"必要信息"的定义(指导性):

类别 上黑板 不上黑板
决策 谁、选了什么、为什么 完整备选方案对比表
产出 title + summary + content_path 代码全文、数据文件
状态 当前 status + 最新 observation 完整事件日志(可归档)
讨论 结论 + 关键论据 来回辩论的完整过程
风险 severity + 一句话描述 详细影响分析报告

防爆炸机制:

  • 产出物只存路径(outputs.content_path)
  • 事件日志有 TTL(events 表定期归档旧数据)
  • 大段分析建议写文件,黑板只存摘要+路径

三层约束体系(AI Native 结构化约束)

Skill 软引导的问题是"可看可不看",等于没有约束。数据库硬限制是传统 CRUD 思维。中间地带是 CLI 层 Schema 校验——参考 agent-chorus 的 JSON Schema 模式(每个操作都有 schemas/*.schema.json),在写入接口层校验结构。

机制 约束力 适用对象
Schema 校验 blackboard.py CLI 写入时校验 JSON Schema 强(不符合返回明确错误) 结构化操作(handoff / output / decide / observe
Skill 引导 Agent Skill 中的行为规范文本 弱(可看可不看) 非结构化操作(普通评论、讨论)
L1 截取 Daemon 构建 L1 时自动截取 代码层面,Agent 无感 传递优化

为什么这是 AI Native

  1. Schema 是可执行文档——Agent 不需要读 Skill 就能知道格式要求(CLI 错误信息会告诉它缺了什么)
  2. 错误信息是建设性的——"Handoff must include 'completed' field" 让 Agent 知道该补什么
  3. 约束的是结构,不是内容——不限制写多长,只限制必须包含哪些字段
  4. Agent 可以自主决定深度——可选字段可以不写
  5. 和 OpenAI Agent SDK 的 handoff input_type 同理——Pydantic schema 校验交接数据

Schema 定义schemas/ 目录):

// schemas/handoff.schema.json
{
  "type": "object",
  "required": ["completed", "artifacts"],
  "properties": {
    "completed": { "type": "string", "description": "完成了什么" },
    "artifacts": {
      "type": "array",
      "items": { "type": "string" },
      "description": "产出物路径列表"
    },
    "remaining": { "type": "string", "description": "未完成事项(可选)" },
    "next_steps": { "type": "string", "description": "对接手者的建议(可选)" }
  }
}

// schemas/observe.schema.json
{
  "type": "object",
  "required": ["severity", "body"],
  "properties": {
    "severity": {
      "type": "string",
      "enum": ["info", "warning", "critical"],
      "description": "info=只记录不触发; warning=下次tick触发庞统; critical=立即spawn庞统"
    },
    "body": { "type": "string", "description": "风险描述" }
  }
}

severity 枚举与处理方式对齐(与 §4.7 Guardrail 体系一致):

severity Daemon 处理 对应 Guardrail 行为
info 只记录,不触发 不介入
warning 下次 tick 统一处理 spawn 庞统(L3)判断
critical inbox 通知 → 立即 spawn 庞统 立即介入
操作 Schema 文件 必填字段 Schema 校验 CLI 附加校验
--handoff handoff.schema.json completed + artifacts 结构完整,类型正确 artifacts 中路径是否存在
--output output.schema.json summary + content_path summary 非空字符串 content_path 文件是否存在
--decide decide.schema.json decision + rationale 两个字段非空字符串
--observe observe.schema.json severity + body severity 枚举值(info/warning/critical)
--comment(普通) body 无 Schema 校验

Agent 使用方式

# 结构化操作:CLI 校验 Schema
blackboard.py comment --task task-001 --author zhangfei-dev \
  --handoff '{"completed": "分批加载实现", "artifacts": ["task-001/output.md"], "remaining": "止损逻辑"}'
# 校验失败 → 返回具体错误:"Handoff must include 'completed' field"
# 校验通过 → 写入 comments 表,body 自动格式化为结构化文本

# 普通评论:无 Schema 校验
blackboard.py comment --task task-001 --author zhangfei-dev \
  --body "@赵云 task-002 需要分钟线数据"

落地到 schema(存储层):

  • outputs 表:content_path + summary,不存文件内容
  • comments 表:body 存完整内容(不截断),handoff 类型自动格式化
  • decisions 表:decision + rationale 是结构化文本
  • observations 表:body 是风险描述

Agent 获取信息的分层策略(L1/L2/L3)详见 §4.4,对应 Opal-Bridge Fidelity 三档。


4. Daemon(管家)设计

4.1 Daemon 的角色定位

Daemon 是投递员,不是决策者。所有决策发生在黑板上,daemon 只执行。

Daemon 做三件事:

  1. 读黑板 - 定期 tick,检查黑板状态
  2. Spawn Agent - 根据黑板上的指示,spawn 对应的 agent
  3. 清理 Session - agent 执行完后,存档 jsonl + 清理 sessions.json

Daemon 不做:

  • 不决定谁做什么(agent 自己决定或庞统在黑板上分配)
  • 不维护状态机(黑板就是状态)
  • 不做业务逻辑(不解析产出、不做评审)

三层执行模型:Daemon 的操作按成本和复杂度分为三层:

层级 方式 成本 适用场景 例子
L1 Daemon 直接操作 SQLite 读写、文件操作 几乎为零 纯机械动作 更新状态、记录事件、机械验证(文件存在、JSON格式、字段非空)
L2 spawn sub openclaw agent --agent <id> --session-id <uuid> 轻量(隔离 session,单任务) 轻量 AI 判断 scope guard、格式校验、快速评估、假死 reminder
L3 run agent spawn 完整 Agent 到黑板上工作 完整(读黑板+思考+写回) 重度 AI 工作 庞统拆解、张飞编码、司马懿 review、庞统纠错

关键区别:

  • L2 的 sub 是一次性、单任务的("帮我检查这个输出是否在 scope 内"),执行完就退出
  • L3 的 agent 是完整的黑板参与者(读全局、自主决策、写回多个表)

L2 与 L3 的区分标准:是否读黑板全局。

  • L2:不读黑板全局上下文,只拿当前任务的特定字段做判断。spawn 时传递局部数据(如 scope_declaration 文本 + task.truths),sub 返回结果后退出。
  • L3:读黑板全局(tasks + comments + outputs + decisions + observations),做全局决策。spawn 时只传任务 ID + 触发原因,Agent 自己读黑板。

这个区分决定了 spawn 时的消息内容--L2 传数据,L3 传指针。

4.2 事件驱动架构(课题 2 设计决策)

设计推导过程

三个参考系统的做法:

系统 架构 事件通知方式 启示
open-multi-agent 单进程 TypeScript 纯 EventEmitter--queue.on('task:ready', handler)。TaskQueue 内部维护 listeners Map,complete() 时同步触发 emit。零基础设施 和我们的黑板同构:TaskQueue.complete() = 我们的任务完成,unblockDependents() = 我们的依赖解锁
agent-chorus 多进程(Claude/Codex/Gemini 各自独立) 本地 JSONL 文件队列--chorus send 写入 .agent-chorus/messages/<target>.jsonl,chorus messages --agent <self> --clear 读并清空。纯文件系统,无网络 Standup+Conclude 模式:Agent 开始时读 inbox,结束时广播状态。JSONL inbox 做跨进程通信
Edict 分布式(API Gateway + Orchestrator + Agent Pool) Redis Streams Event Bus--task.created 等主题 + WebSocket 推送 Dashboard 我们是单机单进程,不需要 Redis

推导结论:

  1. open-multi-agent 证明:单进程内 EventEmitter 完全够用,但它是单进程,我们是跨进程
  2. agent-chorus 证明:跨进程通信用 JSONL 文件就行,不需要 HTTP/Redis/MQ
  3. Edict 的 Redis Streams 是分布式场景所需,我们不需要
  4. 真正需要即时响应的场景只有 4 个:task_completed / task_failed / @mention / user_action。其他 ≤30s 延迟完全可接受
  5. 60s Tick 本身不是问题,问题是 Tick 的效率--应该 Tick 是核心,加速器可选

用户反馈与设计迭代:

  • 初始设计:Signal File 跨进程通知 → 用户质疑"Signal File 存在的意义是什么"
  • 第二版:HTTP 端点 → 用户要求"基于优秀实践推导,不是拍脑袋换方案"
  • 最终版:Tick 核心 + Inbox JSONL 加速(agent-chorus 模式)--基于三个参考系统的实际代码推导

D2-1:Tick 核心 + Inbox 加速(最终方案)

┌──────────────────────────────────────────────────────────────────┐
│                    Daemon (Python asyncio)                       │
│                                                                   │
│  核心:Tick Loop(30s 主循环)                                     │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │ 读黑板全量状态(SQLite 查询)                                  │ │
│  │ 发现需要处理的(mention / blocked / done → pending)           │ │
│  │ 执行对应操作(spawn / 通知 / 清理)                             │ │
│  │ 健康检查(zombie / stale / heartbeat)                         │ │
│  │                                                               │ │
│  │ 设计推导:Hermes 60s tick 证明 polling 可靠稳定。              │ │
│  │ 我们从 60s 降到 30s,因为黑板查询比 Hermes 轻量。              │ │
│  └─────────────────────────────────────────────────────────────┘ │
│          ↑                                                        │
│  加速:Inbox JSONL(agent-chorus 模式)                           │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │ Agent 写黑板后,可选:追加一行 JSON 到 daemon inbox             │ │
│  │ Daemon 主循环每 1s 检查 inbox 是否有新内容                     │ │
│  │ 有新内容 → 立即执行一次 mini-tick(只处理触发的事件)            │ │
│  │ 处理完清空 inbox                                               │ │
│  │                                                               │ │
│  │ 设计推导:agent-chorus 用 JSONL inbox 做跨 Agent 通信,         │ │
│  │ 我们用 JSONL inbox 做 Agent→Daemon 通知。同理同构。            │ │
│  │ inbox 是加速器,不是核心。Tick 兜底所有场景。                    │ │
│  └─────────────────────────────────────────────────────────────┘ │
│          ↑                                                        │
│  恢复:启动时全量扫描                                              │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │ Daemon 重启后立即做一次完整 Tick(PM2 自动重启)                │ │
│  │ 消除重启后的 30s 空窗                                          │ │
│  │ 不需要 EventBus 持久化--黑板(SQLite)是唯一真相源              │ │
│  └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

为什么不选的替代方案:

  • EventBus + Signal File(初始设计):Signal File 需要额外的扫描/读/删循环,增加了耦合链
  • HTTP 端点(第二版):引入网络依赖,Daemon 需要跑 HTTP 服务,不够简单
  • Redis pub/sub(Edict 方案):引入新依赖,v2.6 已去掉 Redis;我们不需要分布式
  • SQLite update-hook:需要 C API 绑定
  • fswatch/watchdog:跨平台兼容性差

Inbox JSONL 具体实现(参考 agent-chorus chorus send 模式):

# blackboard.py 写完 SQLite 后,可选追加一行 JSON
INBOX_PATH = Path("~/.sanguo_projects/sanguo_moziplus_v2/inbox/daemon.jsonl")

# 写入格式(参考 agent-chorus message schema: from/to/timestamp/content/cwd)
async def notify_daemon(event_type: str, task_id: str, agent: str):
    line = json.dumps({
        "from": agent,
        "to": "daemon",
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "event": event_type,     # comment_added / task_completed / task_failed
        "task_id": task_id,
    })
    async with aiofiles.open(INBOX_PATH, mode='a') as f:
        await f.write(line + '\n')

# Daemon 主循环中检查 inbox
async def check_inbox():
    if not INBOX_PATH.exists():
        return
    lines = INBOX_PATH.read_text().strip().split('\n')
    INBOX_PATH.unlink()  # 原子读取+删除(参考 agent-chorus --clear 模式)
    for line in lines:
        msg = json.loads(line)
        await handle_event(msg['event'], msg['task_id'], msg['from'])

Daemon 主循环:

async def daemon_main_loop():
    # 启动时全量扫描
    await full_tick()

    while True:
        # 1. 检查 inbox(每 1s)
        await check_inbox()  # 有内容则立即执行 mini-tick

        # 2. 定期 Tick(每 30s)
        if time.time() - last_tick > 30:
            await full_tick()
            last_tick = time.time()

        await asyncio.sleep(1)

D2-2:依赖声明的并行/串行自动决策

设计推导:open-multi-agent 的 TaskQueue.complete() → unblockDependents() 是核心模式--complete→auto-unlock,纯依赖声明驱动。其 scheduler.ts 还提供了 4 种调度策略(round-robin / least-busy / capability-match / dependency-first)。

串行触发链(Tick + Inbox 加速版):

Agent A 完成 task-001
  → 写黑板 outputs + 更新 status → done + 写 handoff comment
  → 通知 Daemon(inbox JSONL)
  → Daemon 下次循环(~1s 内)收到通知 → mini-tick:
      查询所有 depends_on 包含 task-001 的 pending 任务
      → task-002 depends_on: [task-001],检查 task-001 done ✅
      → spawn Agent B 执行 task-002
  (如果 inbox 通知丢失 → 30s Tick 兜底补上)

并行:depends_on 为空且 assignee 不同的任务,自然并行(Daemon 分别 spawn)。不需要额外逻辑。

不做 files_modified 冲突检测(D2-4):Agent 通过黑板评论自然协调("我在改 main.py,你别碰"),不需要系统强制。Scope Guard(课题 1)作为兜底。实际覆盖:depends_on 覆盖 80%+ 的显式依赖场景,边角场景通过黑板评论 + 庞统仲裁补充。

与课题 1 的兼容性

课题 1 设计 事件驱动后变化 改善
续杯机制 task_completed 通知加速依赖解锁 @mention 从 ≤60s 降到 ≤1s
retry 由 AI 决策 task_failed 通知加速 retry 链 庞统更快介入
Guardrail 吹哨人 observation 写入后通知 Daemon Daemon 即时感知问题
三层执行模型 不变,Tick/inbox 处理仍按 L1/L2/L3 分层 一致

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 的上下文分层传递(课题 2 设计决策)

设计推导:GSD Wave Execution 证明隔离 session + 新鲜 context > 单一 session + 压缩。Claude Code 的 file reference 模式证明"引用而非内联"是最优策略。Opal-Bridge 的 Fidelity 三档提供了理论框架。agent-chorus 的 Context Pack 实验证明结构化上下文让 Agent 效率提升 60-70%。问题不是 context 不够大,而是信号噪声比。

L1/L2/L3 对应 Opal-Bridge Fidelity 三档:

Opal-Bridge Fidelity 我们的映射 场景
Mode A:无损(完整上下文) L1 + L2 + L3 复杂任务,Agent 需要完整了解讨论历史和产出
Mode B:LLM 摘要 L1 + L2 标准任务,核心信息 + 扩展摘要
Mode C:混合保留最近N条 L1 简单任务,只传核心,Agent 按需取更多

Agent 自己决定用哪个 Fidelity 档位--收到 L1 后判断信息是否足够,不够就 L2/L3。

D2-5:三层上下文传递(L1 必传 / L2 按需 / L3 按需)

层级 内容 Token 估算 谁决定
L1(spawn message) 任务核心 + 角色 + 触发原因 + 依赖状态 + 最近评论 + must_haves ~300-500 Daemon 自动
L2(CLI 按需) 完整评论线程 + 产出摘要 + 决策记录 + 观察记录 ~500-1500 Agent 自主决定
L3(文件按需) 产出物文件完整内容 + 完整事件日志 + 子任务详情 ~2000-10000 Agent 自主决定

L1 Spawn Message 模板:

def build_spawn_message_L1(task_id: str, agent_id: str, trigger: str) -> str:
    task = get_task(task_id)

    # 依赖状态摘要(1行/依赖任务)
    deps_status = []
    for dep_id in json.loads(task['depends_on'] or '[]'):
        dep = get_task(dep_id)
        deps_status.append(f"  {dep_id}: {dep['status']} - {dep['title']}")

    # 最近 3 条评论摘要(截断 100 字符)
    recent_comments = get_comments(task_id, limit=3)
    comments_str = ""
    for c in recent_comments:
        comments_str += f"  [{c['created_at'][:16]} {c['author']}] {c['body'][:100]}\n"

    # must_haves 摘要
    must_haves = json.loads(task.get('must_haves') or '{}')
    truths_str = ', '.join(must_haves.get('truths', []))

    return f"""黑板任务通知(L1):
任务:{task['title']}({task['id']})
状态:{task['status']} | 类型:{task['task_type']} | 风险:{task['risk_level']}
触发原因:{trigger}
描述:{task['description'] or '(无)'}
验收标准(truths):{truths_str or '(未定义)'}

依赖状态:
{chr(10).join(deps_status) if deps_status else '  (无依赖)'}

最近评论:
{comments_str if comments_str else '  (无评论)'}

请使用以下命令获取更多信息:
  L2(扩展):blackboard.py read --task {task_id} --level L2
  L3(全量产出):blackboard.py read --task {task_id} --type outputs
"""

D2-6:不需要 Auto-compact:v2.6 的 Agent 每次 spawn 都是隔离的新鲜 session,天然没有 context rot。唯一可能有累积的是庞统主 session(长期在线协调),属 Phase 3 优化。

D2-7:Context 预算分配(128K 模型):

组件 预算 说明
System Prompt + SOUL.md + IDENTITY.md ~3K-5K tokens 固定开销
Skills + AGENTS.md ~2K-4K tokens 固定开销
L1 spawn message ~300-500 tokens 必传
L2 黑板扩展(按需) ~500-1500 tokens Agent 自主决定
L3 产出物文件(按需) ~2K-10K tokens Agent 自主决定
工作空间(Agent 思考+输出) ~30K-50K tokens 预留
总计 ~40K-70K tokens 远小于 128K,安全

4.5 续杯与心跳

参考 v1.0 实践 + Hermes v0.13 Claim TTL。

正常流(大多数情况):

  1. Agent spawn → 开始工作
  2. Agent 每个关键进展写黑板 observation(既是进度汇报,也是心跳信号)
  3. Daemon tick 看到 working 状态 + 有新 observation → 不干预(健康状态)
  4. Agent 完成产出 → 写 output + 状态流转 → Daemon 检测到继续下一步

异常流:

情况 Daemon 检测到 行为 层级
Agent 有进展 黑板有新 observations 不干预(无限续) L1
Agent 没进展但 session 活跃 无新 observations 但 session 还在 不干预(可能正在思考) L1
↑ 判断信号:observations 最后写入时间 < estimated_duration_minutes,纯 L1 查询,不依赖 AI 判断
timeout(agent run 返回超时)+ 产出达标 agent run 返回超时 + outputs 表有内容 幻觉门控验证产出 → 通过则继续流转 L1→L2
timeout(agent run 返回超时)+ 产出不达标 agent run 返回超时 + outputs 为空 L2 spawn sub 发 reminder 让 Agent 继续(假死处理) L2
timeout + 产出不达标 + reminder 后仍无进展 二次 timeout 回收到 pending,记录 failure_detail L1
非timeout 错误(进程退出) 进程已死 进入 AI 纠错流程 L3
硬上限超时 working 状态超过 3x 预估工时 强制回收,记录事件 L1

设计推导:

  • v1.0 实践证明:看结果不看过程(即使 CLI 报错/超时,产出文件存在且有效就算成功)
  • 续命和重试是两个独立预算:续命(Agent有进度→无限续),重试(Agent真挂→有限次)
  • Hermes 的 Claim TTL(默认15分钟)提供了超时回收的参考值

timeout 的检测:timeout 信号来自 openclaw agent --agent <id> 的返回值(阻塞调用)。Agent 在执行过程中通过写黑板 observations 维持活跃信号--Daemon tick 检查 observations 的最后写入时间,如果有新 observation 说明 Agent 还在工作。但最终判断 Agent 是否超时,以 agent run 的返回值为准。

reminder 后的硬时间上限:reminder 后如果超过 estimated_duration_minutes 仍未完成(从 reminder 时间算起),才回收任务。

4.6 AI 驱动的 Retry(纠错协商)

参考 v1.0 _handle_blocked_node() + Hermes task_runs + Claude Code Teams "before retrying, answer what failed"。

核心原则:Retry 原因由 AI 判断,Daemon 只执行。

流程:

  1. Agent 失败(产出 status=failed 或 Daemon 检测到异常终止)
  2. Daemon 不判断原因,只在黑板上记录这次 attempt(task_runs 模式,每次 attempt 独立记录)
  3. Daemon spawn 庞统(L3)看黑板上的失败记录 + 之前所有 attempts
  4. 庞统在黑板上写决策(四种选择之一):
    • "同一 Agent 重试" + 失败原因分析 + 改进建议
    • "换 Agent 重试" + 为什么换 + 新 Agent 优势
    • "任务需要用户介入" + 卡在哪 + 建议
    • "任务无法完成,建议取消" + 为什么
  5. Daemon 读庞统决策,执行对应操作
  6. 如果重试后仍失败 → 新 attempt 记录 → 再次 spawn 庞统
  7. Circuit breaker:同一 task 总 attempt 数达到 N 次(默认3次,不区分是哪个 Agent)→ 自动 block + 通知用户。理由:3 次尝试都不成功说明问题在任务本身而非 Agent 能力。

失败记录:谁记录什么?

记录者 记录内容 黑板位置
Daemon 机械类失败(进程退出码、超时) events 表,event_type=task_failed
司马懿 内容类失败(评审不通过) reviews 表(verdict=needs_revision + issues)
庞统 方向类失败(需求偏离) decisions 表(重规划原因)
Agent 自己 能力不足/专业外,主动报告失败 comments 表(说明原因)+ tasks status→failed
Agent(重试时) 新 attempt 的产出 outputs 表(带 attempt_number)

Agent 重试时能看到什么:黑板上的 events(失败记录)+ reviews(评审意见)+ comments(讨论)。全部在黑板上,spawn 时自然读到。

设计推导:

  • v1.0 实践:庞统分析原因 → 司马懿 challenge → 三轮协商 → 执行,方向正确但由引擎硬编码调用
  • v2.6 改进:Agent 在黑板上自主协商(需要事件驱动支持,见课题2),Daemon 只 spawn 不调度
  • Hermes task_runs:每次 attempt 独立记录(attempt_number, outcome, log_path, exit_code),可追溯可审计

4.7 Guardrail 体系(吹哨人机制)

参考 OpenAI Agent SDK OutputGuardrail + GSD must_haves + v1.0 M4 Guard 机制。

核心原则:Guardrail 是吹哨人不是终结者。检测到问题写黑板(observation),触发后续 AI 判断链。决策权在黑板上,执行权在 Daemon。

三个 Guardrail:

Guardrail 触发时机 检测方式 发现问题后
Scope Guard Agent claim 任务后在工作过程中写 decisions(scope 相关)时 L2 sub 异步对比 scope_declaration vs task.truths 写 observation(severity=warning),Daemon 下次 tick 触发庞统判断
Output Guard Agent 写 output 时 L1 机械检查(文件存在、格式正确、字段非空)+ L2 语义检查 机械失败直接打回,语义问题写 observation
Format Guard Agent 写任何结构化数据时 L1 JSON Schema 校验 格式错误直接打回重做

后续动作链(问题升级):

Guardrail 检测到问题 → 写黑板 observation
  ↓
Daemon tick 读到 observation
  ↓
根据 severity 分级处理:
  - blocking → L3 立即 spawn 庞统
  - warning → L3 spawn 庞统(下次 tick 统一处理)
  - info → 只记录,不触发
  ↓
庞统在黑板上写决策:
  - "确认偏离,打回" → Daemon 改状态回 pending
  - "方向扩展合理,批准继续" → 继续
  - "需要用户判断" → 通知用户

设计推导:

  • OpenAI Agent SDK:Guardrail 本身是轻量 AI Agent(并行运行,专门做检查),不是 if-else 规则
  • GSD must_haves truths:面向可观测行为,不是实现步骤
  • v1.0 M4 Guard:entry/exit guard + skill 化检查逻辑,方向正确但绑定在 DAG 节点上

Scope Guard(异步检查,不阻塞 Agent 执行):

  • 触发时机:Agent claim 任务后在工作过程中写 decisions(scope 相关)时
  • 检查方式:L2 sub 异步对比 scope_declaration vs task.truths
  • 不阻塞:Agent 写完 scope_declaration 后继续工作,不等 Guard 结果
  • 发现问题:写 observation(severity=warning),Daemon 下次 tick 触发庞统判断
  • 兜底:即使 Scope Guard 漏报,庞统在 review 阶段仍会检查方向正确性

5. Agent 与黑板的交互

5.1 Agent 被_spawn_后的工作流程

Agent 被 spawn
    ↓
1. 读黑板 → 了解任务全局状态
   - 读 tasks 表:当前任务的状态、描述、依赖
   - 读 comments 表:讨论历史
   - 读 outputs 表:已有产出
   - 读 observations 表:已知风险
    ↓
2. 想 → 根据自己的职责自主决策
   - 我是编码先锋,这个 pending 任务适合我 → claim
   - 我是风控守将,这个 comment @ 我 → 回复
   - 我是副军师,这个任务需要分解 → 创建子任务
   - Agent claim 任务后、开始工作前,写 scope_declaration 到 decisions 表:
     "我计划做什么,产出什么"
     Scope Guard(L2 sub)会对比 scope_declaration vs task.truths
    ↓
3. 做 → 执行任务
   - 编码、审核、数据分析等
   - 过程中发现风险 → 写 observation
   - 需要其他人协助 → 写 comment @mention
    ↓
4. 写回黑板 → 产出、评论、状态更新、决策记录
   - 写 outputs 表:产出文件路径 + 摘要
   - 写 comments 表:完成说明
   - 写 decisions 表:关键决策(哪怕自己的决策也要填一条)
   - 更新 tasks 表:status → done/review
   - must_haves 三件套(任务创建时由庞统定义):
     - truths:用户视角的可观测行为("用户能看到回测结果"),不是实现步骤("编写回测脚本")
     - artifacts:必须存在的产出文件
     - constraints:继承的约束(如"不超过500行"、"必须用vnpy")
    ↓
5. 写 Handoff Comment → 退出
   - Agent 结束前必须写一条结构化的交接评论(借鉴 agent-chorus checkpoint)
   - 内容:完成什么、产出在哪、还剩什么、建议下一步
   - 这条 comment 会出现在下一个 Agent 的 L1 消息中(最近 3 条评论),实现无缝接手
   - 示例:
     ```
     blackboard.py comment --task task-001 --author zhangfei-dev \
       --body "## Handoff\n完成:分批加载实现\n产出:task-001/output-zhangfei-v2.md\n未完成:止损逻辑分批适配\n建议下一步:关羽 review 止损逻辑"
     ```
    ↓
6. Daemon 自动清理 session
   - 通知 Daemon(inbox JSONL)
   - Daemon 检测到完成 → 继续下一步(解锁下游 / spawn review / 清理 session)

设计推导(Handoff Comment):

  • agent-chorus 的核心机制是 Standup + Conclude:Agent 开始时读 inbox,结束时广播状态
  • 映射到黑板:Standup = Agent spawn 后读黑板(L1),Conclude = Agent 结束时写 handoff comment
  • agent-chorus 的 checkpoint 广播给所有其他 Agent → 我们的 handoff comment 通过 L1 自然传递给下一个 Agent
  • 关键价值:黑板上的状态足够让 Agent B 无缝接手 Agent A 的工作--这正是 agent-chorus 解决的核心问题

5.2 Agent 工具集

Agent 通过 exec 工具调用 CLI 命令操作黑板:

# 读黑板(全部)
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

# 写 Handoff Comment(结构化,CLI 校验 Schema
python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py comment --task task-001 --author zhangfei-dev \
  --handoff '{"completed": "分批加载实现", "artifacts": ["task-001/output.md"], "remaining": "止损逻辑分批适配"}'
# --handoff 使用 schemas/handoff.schema.json 校验
# 缺必填字段 → CLI 返回具体错误,如 "Handoff must include 'completed' field"
# 校验通过 → 自动格式化为结构化评论写入 comments 表

6. 关键场景流程

6.1 庞统规划 + Agent 领任务(事件驱动版)

用户 → 庞统(主session):"设计一个动量因子策略"
    ↓
庞统在黑板上写:
  - 创建 task-001(数据准备,pending,无依赖)
  - 创建 task-002(因子计算,pending,depends_on: [task-001])
  - 创建 task-003(回测验证,pending,depends_on: [task-002])
  - 评论:"建议赵云领 001,张飞领 002 和 003"
    ↓
庞统写 inbox 通知: task_created
    ↓
Daemon Tick 发现 task-001 pending + 庞统评论建议赵云
  → spawn 赵云(L1 消息含任务核心 + 庞统建议)
    ↓
赵云读黑板 → claim task-001 → 执行 → 写产出
  → 写 Handoff Comment: "完成:分钟线数据下载 | 产出:task-001/data/ | 无未完成事项"
  → 更新 status→done → 通知 Daemon(inbox JSONL)
    ↓
Daemon ~1s 内收到 inbox 通知 → mini-tick:
  → 查询 depends_on 包含 task-001 的 pending 任务 → task-002
  → task-002 的依赖全部满足 → spawn 张飞(L1 消息含赵云的 handoff 摘要)
    ↓
(同理 task-002 done → 即时触发 task-003)

对比 polling 版:task-001 done 到 task-002 spawn 的延迟从 ≤60s 降到 ≤1s。张飞的 L1 消息中包含赵云的 Handoff Comment,无需额外查询即可无缝接手。

6.2 Agent 间协作讨论(事件驱动版)

张飞执行 task-002 时发现需要分钟线数据
    ↓
张飞写评论:"@赵云 task-002 需要分钟线数据,能帮忙下载吗?"
张飞更新任务状态 → blocked
  → 通知 Daemon(inbox JSONL)
    ↓
Daemon ~1s 内收到 inbox 通知 → mini-tick:
  → 解析 @mention → 赵云
  → spawn 赵云(L1 消息含评论摘要)
    ↓
赵云读黑板 → 看到评论 → 下载数据 → 写产出
赵云写评论:"@张飞 数据就绪,可以继续" + 写产出
  → 通知 Daemon(inbox JSONL)
    ↓
Daemon 收到通知 → @mention → spawn 张飞
    ↓
张飞读黑板 → 看到数据就绪 → 继续 task-002

对比 polling 版:@mention 响应从 ≤60s 降到 ≤1s。

6.3 Agent 发现风险

张飞在 task-002 中发现止损逻辑有 bug
    ↓
张飞写 observation(severity: warning):
  "止损逻辑在分批模式下可能漏触发"
张飞写评论:"@关羽 止损逻辑需要你从风控角度确认"
    ↓
Daemon tick 发现 observation + 评论 @ 关羽
    ↓
Daemon spawn 关羽 → 关羽读黑板 → 审查 → 写评论 + observation

6.4 用户直接参与

用户读黑板 → 发现 task-002 进度慢
    ↓
用户在黑板上写评论:"task-002 优先级提高,需要今天完成"
    ↓
Daemon tick 发现用户评论 → 如果张飞未 active → spawn 张飞通知

7. Session 隔离与清理

7.1 技术实现

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 设计目标

课题1定义了"谁来判断"(双轨制)和"判断什么"must_haves三件套、Guardrail吹哨人)。课题3解决"判断结果怎么记录、怎么协商、怎么流转"。

v1.0 三层体系(PAV自律 → 司马懿review → 庞统+司马懿终审)的问题:

  1. PAV 形同虚设——Skill是"菜单"不是"触发器"Agent可忽略
  2. 每个节点都审过重——没有分级,简单/复杂任务同一个审查标准
  3. 方案阶段没审查——只审产出不审方案
  4. 评审结果不结构化——自然语言评论,无法机器判断
  5. 司马懿角色重叠——节点审一次+终审一次

9.2 分级审查流水线

参考实践superpowers 串行双审、TradingAgents 对抗辩论、v1.0 PRD 三层体系

任务风险等级 流水线 方案审查 产出审查 审查模式 最大轮次
high(部署/策略/资金) 三阶段 独立审查 独立审查 + Guardrail 对抗辩论 5
standard(编码/设计) 二阶段 方案过挑战 产出过挑战 单审 3
low(文档/格式化/搬运) 一阶段 跳过 Guardrail 自动检查 自动 0
research(调研/分析) 一阶段 跳过 庞统确认方向 单审 2

风险等级:庞统创建任务时标注。默认 standard。task_type 为 strategy/deploy 自动设 highresearch 自动设 research

9.3 三阶段审查流程

参考实践superpowersimplementer → spec reviewer → code quality reviewer串行)、TradingAgentsBull vs Bear辩论)

阶段 1:方案审查(Plan Review——high/standard 任务

执行者提交 scope_declaration 到黑板 decisions 表
    ↓
挑战者审查方案
    ├── high:对抗辩论(正方 vs 反方 → 庞统裁决)
    └── standard:单审(指定挑战者,默认司马懿)
    ↓
通过 → 进入执行
未通过 → 协商修改(max_rounds 轮)
    └── 超轮次 → 升级用户

方案审查 vs Scope Guard(课题1):

  • Scope Guard 是过程中的软检查(L2 sub 异步,发现问题写 observation
  • 方案审查是正式评审(L3 Agent 审查,通过/打回有 verdict

阶段 2:执行 + Guardrail 自动检查——所有任务

执行者按方案执行
    ↓
每次写 output 时 Daemon 自动触发 Guardrail
    ├── L1 机械检查(文件存在/JSON格式/字段非空)→ 不通过直接打回
    ├── L2 轻量 AI 检查(Schema校验/artifacts路径验证)→ 不通过写 observation
    └── L3 安全红线(tripwire:如直接操作生产环境)→ 立即中断
    ↓
Guardrail 通过 → 进入产出审查

阶段 3:产出审查(Output Review——high/standard 任务

执行者提交产出到黑板 outputs 表
    ↓
挑战者审查产出(质量/完整性/与方案一致性)
    ↓
评审结果结构化记录到 reviews 表
    ↓
通过 → status: review → done
未通过 → status: review → working(打回重做)
    └── 协商在 comments 表
    └── 超轮次 → 升级用户

9.4 审查协议注册表(Review Protocol Registry

参考实践

  • superpowers:三个独立 prompt 文件(implementer/spec-reviewer/code-quality-reviewer),每个角色有专属模板
  • oh-my-claudecode CriticInvestigation Protocol 分 Phase(预判→验证→多视角→缺口分析→自审),不同 artifact type 自动切换视角
  • superpowers spec-reviewerprompt 注入对抗性指令("DO NOT trust the report. Read the actual code."

问题:审查者容易陷入局部审查(编码规范/编译通过),漏掉本质问题(需求一致性/语义正确性)。被挑战后一律照改不加思考。

方案:审查协议是代码注入的,不是靠 Agent 自己找 Skill。Daemon spawn 审查者时动态加载协议模板。

review_protocols/
├── plan_review.yaml          # 方案审查协议
├── output_review.yaml        # 产出审查协议
├── guardrail_l2.yaml         # L2 轻量AI检查协议
└── analysis_review.yaml      # 分析/调研审查协议

每个协议定义四个维度:

维度 内容 参考
审查维度(审什么) dimensions 列表,每个有 weightcritical/major/minor)和 method superpowers 三种 reviewer 各自的关注点
审查方法(怎么审) investigation_protocol 分阶段执行 oh-my-claudecode Critic 五阶段 Protocol
多视角 按 artifact type 切换视角集 Critic 代码视角(安全/新人/运维) vs 方案视角(执行者/利益相关者/怀疑论者)
对抗性指令 adversarial_instructions 防止走过场 superpowers spec-reviewer "DO NOT trust" 指令

Investigation Protocol 通用模式(参考 Critic,各协议按场景裁剪):

Phase 1 预判(Pre-commitment):先不读产出,凭领域经验预测3-5个最可能出问题的点
Phase 2 验证(Verification):读实际产出(不是报告),逐条验证
Phase 3 多视角(Multi-perspective):从不同角色看产出
Phase 4 缺口分析(Gap Analysis):不只看"什么有问题",还看"什么缺失了"
Phase 5 自审(Self-audit):给每个 finding 打 confidence,低 confidence 降级

多视角矩阵

审查类型 多视角集合
output_review(代码) 安全 / 新人 / 运维
plan_review(方案) 执行者 / 利益相关者 / 怀疑论者
analysis_review(调研) 领域专家 / 实践者 / 反方辩手
guardrail_l2(轻量) 无多视角

9.5 反驳权(Rebuttal Phase

参考实践TradingAgents Bull vs Bear 模式——双方都有表达权

审查不是单向的。但不是每次都触发反驳——有跳过条件:

跳过条件(不需 spawn 反驳):

  • 审查者 verdict=approved → 直接 done,跳过 rebuttal
  • 审查者 verdict=needs_revision,但 issues 全是 minor severity → 执行者自然在 comments 接受并修改,不 spawn 反驳

触发条件spawn 反驳):

  • 审查者 verdict=needs_revision,且 issues 中有 critical 或 major severity → spawn 原执行者反驳
  • 审查者 verdict=rejected → spawn 原执行者反驳
审查者提交 review
    ↓
verdict=approved → 直接 done(跳过 rebuttal
verdict=needs_revision 且只有 minor → 执行者直接修改(跳过 rebuttal
verdict=needs_revision 且有 critical/major → spawn 反驳
    ↓
Daemon spawn 原执行者,注入反驳指令:
    "对每个 issue,明确表态:ACCEPT / REJECT / PARTIAL
     不允许全部接受不加思考。"
    ↓
执行者 response 写入 comments 表
    ↓
有 REJECT → Daemon spawn 审查者看 response → 继续协商
全部 ACCEPT → 修改后重提交 → 审查者 re-review

9.6 评审结果结构化存储(reviews 表)

参考实践

  • HermesComment 的 metadata JSON 承载结构化数据
  • SWE-agentTrajectory 线性追加,不修改历史
  • GitHub PR ReviewAPPROVE / REQUEST_CHANGES / COMMENT 三种 verdict

设计原则:追加写入不修改历史(SWE-agent),黑板索引+文件详情(课题2),结构化 verdictGitHub PR Review)。

CREATE TABLE IF NOT EXISTS reviews (
    id TEXT PRIMARY KEY,
    task_id TEXT NOT NULL,
    output_id TEXT,
    reviewer TEXT NOT NULL,              -- agent id 或 'system'
    review_type TEXT NOT NULL,
    CHECK (review_type IN ('plan_review', 'output_review', 'guardrail', 'final_review')),
    verdict TEXT NOT NULL,
    CHECK (verdict IN ('approved', 'rejected', 'needs_revision')),
    confidence REAL,                     -- 0.0-1.0
    round INTEGER NOT NULL DEFAULT 1,
    max_rounds INTEGER NOT NULL DEFAULT 3,
    consensus_reached BOOLEAN DEFAULT FALSE,
    summary TEXT NOT NULL,               -- 一句话结论(黑板索引)
    detail_path TEXT,                    -- 完整评审报告文件
    created_at TEXT NOT NULL DEFAULT (datetime('now')),
    FOREIGN KEY (task_id) REFERENCES tasks(id),
    FOREIGN KEY (output_id) REFERENCES outputs(id)
);

CREATE INDEX IF NOT EXISTS idx_reviews_task ON reviews(task_id);
CREATE INDEX IF NOT EXISTS idx_reviews_output ON reviews(output_id);

Guardrail 检查结果也入 reviews 表reviewer='system', review_type='guardrail')。

comments 表和 reviews 表职责分离

职责 内容 verdict
comments 讨论、@mention、协商过程 自然语言
reviews 正式评审结论 结构化 JSON 必须有

9.7 协商流程与状态机对齐

现有状态机pending → claimed → working → review → done

状态转换 触发条件 对应阶段
working → working 方案审查 needs_revision 阶段 1
working → review 写 output + Guardrail 通过 阶段 2→3
working → working Guardrail rejected(自动打回) 阶段 2
review → done 产出审查 approved 阶段 3
review → working 产出审查 needs_revision 阶段 3
review → review(加 escalated flag 超轮次升级用户 阶段 3

escalated flagtasks 表增加 escalated BOOLEAN DEFAULT FALSE 字段。超轮次时设为 TRUE 并通知用户,任务停在 review 状态等用户裁定。用户裁定后清除 flag 并推进状态。不新增 blocked 状态。

low 风险任务:working → [Guardrail 自动检查] → done(跳过 review 状态) research 任务:working → [庞统确认] → review → done

9.8 声明式 Guardrail 配置

参考实践OpenAI Agent SDK @output_guardrail 装饰器、v1.0 M4 Guard、SonarQube rule_id 模式

# guardrails.yaml
#
# L1 check 用 assert 字段(Python 表达式,Daemon eval 执行)
# L2 check 用 prompt 字段(传给 subagent 的检查指令)
# 两者本质不同,不用同一个字段

task_types:
  coding:
    output_guardrails:
      - name: file_exists
        assert: "len(output.get('files', [])) > 0"
        severity: blocking
        layer: L1
      - name: json_valid
        assert: "output.get('json_schema_valid', False) == True"
        severity: blocking
        layer: L1
      - name: artifacts_exist
        assert: "all(os.path.exists(p) for p in output.get('artifacts_paths', []))"
        severity: blocking
        layer: L1
      - name: scope_alignment
        prompt: "Compare the agent's scope_declaration against task truths. Check: is every truth covered? Are there deviations not declared?"
        severity: warning
        layer: L2
    output_review:
      required: true
      mode: single_reviewer
      max_rounds: 3

  deploy:
    plan_review:
      required: true
      mode: debate
      max_rounds: 5
    output_guardrails:
      - name: no_direct_production
        assert: "output.get('target_env') != 'production'"
        severity: tripwire
        layer: L1
      - name: rollback_plan_exists
        assert: "output.get('rollback_plan') is not None"
        severity: blocking
        layer: L1
    output_review:
      required: true
      mode: debate
      max_rounds: 5

  data:
    output_guardrails:
      - name: format_check
        assert: "output.get('format') in ['csv', 'parquet', 'json']"
        severity: blocking
        layer: L1
    output_review:
      required: false

  research:
    output_review:
      required: true
      reviewer: "pangtong-fujunshi"
      mode: single_reviewer
      max_rounds: 2

9.9 Full Agent vs Subagent vs Daemon 直接执行

参考实践

  • superpowersImplementer/Spec Reviewer/Code Quality Reviewer 都是 Task tool dispatch 的 subagent,各自独立 prompt 和模型
  • oh-my-claudecodeCritic 被禁止 Write/Edit 工具(disallowedTools),角色隔离
  • open-multi-agentTaskQueue 维护 agent poolscheduler 按 capability-match 分配

判据

问题 Full Agent Subagent Daemon 直接执行
需要独立身份/人格?
需要 Agent 专属工具? 通用工具 不需要 AI
任务复杂度 编码/审查/调研/决策 单一检查/快速评估 格式校验/文件检查
需要写黑板? 只返回 pass/fail 只改状态
需要多轮交互? 一次性 一次性

场景映射

场景 方式 OpenClaw API 理由
执行者编码/数据/部署 Full Agent openclaw agent --agent <id> 需要身份+专属工具
挑战者审查 Full Agent openclaw agent --agent simayi-challenger 需要质量守门人角色+多轮
执行者反驳 Full Agent(原 Agent openclaw agent --agent <原执行者> 需要原执行者身份
庞统规划/裁决 Full Agent(隔离session openclaw agent --agent pangtong-fujunshi --session-id <new> 避免主session上下文膨胀
L2 Guardrail AI 检查 Subagent sessions_spawn(task=...) 单一检查、无身份
Scope Guard 异步检查 Subagent sessions_spawn(task=...) 轻量一次性
L1 机械校验 不走 AI Daemon 直接执行 纯机械操作

简化规则:黑板上有名字的角色走 Full Agent。无名字的一次性检查走 Subagent。纯机械检查 Daemon 自己做。

庞统主 session 隔离策略:主 session 做轻量调度(L1构建、状态检查)。复杂的任务拆解和裁决 spawn 隔离 session,避免上下文膨胀。

9.10 对抗辩论模式(high 风险任务)

参考实践TradingAgents Bull vs Bear → Research Manager 裁决

正方(执行者):scope_declaration,论证方案可行性
反方(挑战者池):找方案漏洞、风险、遗漏
    ↓
庞统裁决:综合双方观点
    ├── 认可正方 → 方案通过
    ├── 认可反方 → 方案打回
    └── 综合意见 → 要求修改后重新辩论

挑战者池(按任务类型选择,不是固定司马懿):

任务类型 默认挑战者
编码 司马懿
风控 关羽
数据 赵云(数据质量视角)
部署 姜维

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.db(5 表 + WAL)
  2. blackboard.py CLI(读写操作 + inbox JSONL 通知)
  3. Daemon 核心循环(Tick 30s + Inbox 检查 + 启动全量扫描)
  4. Session 管理(spawn + 存档 + 清理)
  5. L1 spawn message 模板

Phase 2: 事件驱动 + Agent 交互

  1. Agent 黑板操作 Skill(含 Handoff Comment 规范)
  2. Inbox JSONL 即时通知(task_completed → 解锁下游、@mention → spawn
  3. 任务依赖自动推进(complete→auto-unlock
  4. 评论 + @mention 通知链路(Inbox 加速版)
  5. 健康检查(stale reclaim + zombie 检测)
  6. L2/L3 分层读取 APIblackboard.py read --level

Phase 3: 智能化

  1. 庞统 AI 规划(读需求 → 创建任务 + 分配建议 + must_haves)
  2. Agent 自主领活(读黑板 → 匹配职责 → claim + scope_declaration)
  3. 产出验证门禁(Output Guard + Scope Guard)
  4. AI 驱动 Retry + Circuit breaker
  5. 经验沉淀(observation → knowledge base)

13. 技术选型

需求 参考系统 我们的方案 理由
共享状态 Hermes SQLite + Network-AI flock SQLite WAL + 事务 CAS 原子性 + 无外部依赖
讨论 Hermes kanban_comment comments 表 + @mention 简单追加写入,所有人可见
事件驱动 open-multi-agent EventEmitter + agent-chorus JSONL inbox Tick 核心 + Inbox JSONL 加速 + 启动全量扫描 Tick 兜底可靠,inbox 加速即时响应,零新依赖
调度 Hermes Dispatcher 60s tick Tick 30s + Inbox JSONL 加速 + 启动全量扫描 Tick 可靠 + inbox 即时
上下文传递 GSD Wave Execution + Claude Code file ref + Opal-Bridge Fidelity L1 必传 + L2/L3 按需读取 + Handoff Comment 信号噪声比优化 + 无缝接手
通知 Claude Code idle notification Daemon spawn + L1 message OpenClaw 原生能力
通信 Hermes kanban_comment + Claude Code inbox 黑板 comments + @mention 替代 Sanguo Mail
竞态 Network-AI propose→validate→commit SQLite CAS(first-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 类型任务替代