From ba13a28016ef08f1929389cd6e1c082d2c609c75 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Fri, 15 May 2026 11:16:09 +0800 Subject: [PATCH] auto-sync: 2026-05-15 11:16:09 --- docs/design/architecture-v2.6.md | 578 ++++++++++++++++++++----------- 1 file changed, 373 insertions(+), 205 deletions(-) diff --git a/docs/design/architecture-v2.6.md b/docs/design/architecture-v2.6.md index 94149fa..ec9f67f 100644 --- a/docs/design/architecture-v2.6.md +++ b/docs/design/architecture-v2.6.md @@ -1,8 +1,8 @@ # AI原生DevOps Platform 架构设计 v2.6 -**版本**: v2.6(Shared Workspace + Blackboard 架构) +**版本**: v2.6(Shared Workspace + Blackboard 架构) **基于**: architecture-v2.md + v2.0 AI Native 调研 + 技术验证 -**作者**: 庞统(副军师) +**作者**: 庞统(副军师) **日期**: 2026-05-15 --- @@ -11,36 +11,37 @@ | 版本 | 日期 | 变更内容 | |------|------|---------| -| v2.0 | 2026-05-04 | 初始版本:SQLite 4表 + 状态机 + DAG 引擎 | -| v2.6 | 2026-05-15 | **架构重构**:Shared Workspace(Blackboard)取代 DAG 引擎为编排核心 | +| 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三件套、分级审查矩阵 | --- -## 1. v2.6 核心变革:从 DAG 状态机到 Shared Workspace +## 1. v2.6 核心变革:从 DAG 状态机到 Shared Workspace -### 1.1 为什么变? +### 1.1 为什么变? -v2.0 的核心是 **DAG 引擎 + 状态机 + 邮件通信**,本质是给 AI 团队做了一套 ERP: -- 编排是确定性状态机(固定流程) -- 交互是点按钮(Dashboard) -- Agent 间靠邮件异步通信(信息分散在 mail 目录) -- 人的参与密度不变(全程驾驶) +v2.0 的核心是 **DAG 引擎 + 状态机 + 邮件通信**,本质是给 AI 团队做了一套 ERP: +- 编排是确定性状态机(固定流程) +- 交互是点按钮(Dashboard) +- Agent 间靠邮件异步通信(信息分散在 mail 目录) +- 人的参与密度不变(全程驾驶) -v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemon 投递**: -- 编排是 AI agent 在黑板上自主领活(动态协作) +v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemon 投递**: +- 编排是 AI agent 在黑板上自主领活(动态协作) - 交互是自然语言对话 -- Agent 间通过黑板共享一切(信息集中在任务空间) +- Agent 间通过黑板共享一切(信息集中在任务空间) - 人只做方向决策和验收 ### 1.2 核心原则 -> **黑板是唯一真相源,所有 agent 读它、想、行动,写回结果。Daemon 是投递员,不是决策者。** +> **黑板是唯一真相源,所有 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 --session-id ` spawn 隔离 session,执行完 daemon 存档 jsonl 并清理 sessions.json。 +1. **Agent 决策,Daemon 执行** - 庞统做 plan、张飞领任务、关羽发现风险,都写在黑板上。Daemon 读黑板,执行 spawn/通知。 +2. **产出在黑板,不在邮件** - 所有任务产出、讨论、观察都在任务的黑板空间里,Sanguo Mail 不介入任务协作。 +3. **Daemon 不阻塞 Agent** - Daemon 是常驻管家,定期 tick 检查黑板,spawn agent 执行,不占用任何 agent 的主 session。 +4. **Session 用完即清** - Agent 通过 `openclaw agent --agent --session-id ` spawn 隔离 session,执行完 daemon 存档 jsonl 并清理 sessions.json。 --- @@ -53,12 +54,12 @@ v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemo └──────────────────────────┬──────────────────────────────────┘ │ 写入黑板或触发 daemon ┌──────────────────────────▼──────────────────────────────────┐ -│ Shared Workspace(黑板) │ +│ Shared Workspace(黑板) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ SQLite (blackboard.db) │ │ │ │ tasks / comments / outputs / agents / events │ │ -│ │ 原子读写(propose→validate→commit 或 SQLite 事务) │ │ +│ │ 原子读写(propose→validate→commit 或 SQLite 事务) │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ @@ -67,7 +68,7 @@ v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemo └──────────────────────────┬──────────────────────────────────┘ │ daemon tick 读写 ┌──────────────────────────▼──────────────────────────────────┐ -│ Daemon(管家) │ +│ Daemon(管家) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Tick 循环 │ │ Session 管理 │ │ 健康检查 │ │ @@ -75,17 +76,17 @@ v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemo │ │ 读黑板→决策 │ │ /cleanup │ │ /stale 任务 │ │ │ └──────┬───────┘ └──────┬───────┘ └──────────────────┘ │ │ │ │ │ -│ Daemon 只做三件事: │ │ -│ 1. 读黑板,发现需要介入的 │ │ +│ Daemon 只做三件事: │ │ +│ 1. 读黑板,发现需要介入的 │ │ │ 2. Spawn 对应 agent │ │ │ 3. 清理完成的 session │ │ └──────────────────────────┬──────────────────────────────────┘ │ openclaw agent --agent --session-id │ 执行完 → 存档 jsonl → 清理 sessions.json ┌──────────────────────────▼──────────────────────────────────┐ -│ Agent 层(将军们) │ +│ Agent 层(将军们) │ │ │ -│ Agent 不常驻。被 spawn 时: │ +│ Agent 不常驻。被 spawn 时: │ │ 1. 读黑板 → 了解全局状态 │ │ 2. 想和做 → 根据职责自主决策 │ │ 3. 写回黑板 → 产出、评论、领任务 │ @@ -97,32 +98,32 @@ v2.6 的核心是 **Shared Workspace(Blackboard)+ Agent 自主决策 + Daemo │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ 每个 Agent: SOUL.md + IDENTITY.md + Skills + Workspace │ -│ Agent 主 session 不参与任务执行(不被污染) │ +│ Agent 主 session 不参与任务执行(不被污染) │ └─────────────────────────────────────────────────────────────┘ ``` -### 关键区别:v2.0 vs v2.6 +### 关键区别: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,用完即清 | +| 编排核心 | 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. Shared Workspace(黑板)设计 ### 3.1 参考系统对比 | 系统 | 存储 | 原子性 | 讨论 | 状态机 | 发现 | |------|------|--------|------|--------|------| -| Claude Code Agent Teams | JSON 文件 | 无(last-write-wins) | inbox 点对点 | pending/in_progress/completed | Agent 轮询 | +| 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 分发 | @@ -138,19 +139,19 @@ CREATE TABLE IF NOT EXISTS tasks ( 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 - + assigned_by TEXT, -- 谁分配的:pangtong-fujunshi / user + -- 依赖 depends_on TEXT, -- JSON array of task IDs - parent_task TEXT, -- 父任务(子任务分解时) - + 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')), @@ -158,10 +159,15 @@ CREATE TABLE IF NOT EXISTS tasks ( started_at TEXT, completed_at TEXT, deadline TEXT, - + -- 重试 retry_count INTEGER NOT NULL DEFAULT 0, - max_retries INTEGER NOT NULL DEFAULT 2 + 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); @@ -169,7 +175,7 @@ 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:追加写入,所有参与者可见 +-- 参考 Hermes kanban_comment:追加写入,所有参与者可见 CREATE TABLE IF NOT EXISTS comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL, @@ -177,14 +183,14 @@ CREATE TABLE IF NOT EXISTS comments ( 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 关联表优化。 +-- 注意:mentions 是 JSON 数组,无法直接建索引。daemon tick 查询用 json_each(mentions)。 +-- 数据量小时够用,后续可拆 comment_mentions 关联表优化。 -- ===== 产出表 ===== CREATE TABLE IF NOT EXISTS outputs ( @@ -193,11 +199,11 @@ CREATE TABLE IF NOT EXISTS outputs ( agent TEXT NOT NULL, -- 谁写的 output_type TEXT NOT NULL, -- code/document/data/config/other title TEXT NOT NULL, - content_path TEXT, -- 文件路径(产出物在 task 目录下) + 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) ); @@ -209,11 +215,11 @@ 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 方案内存开销更大" + 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) ); @@ -231,11 +237,11 @@ CREATE TABLE IF NOT EXISTS observations ( 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, @@ -248,13 +254,13 @@ CREATE TABLE IF NOT EXISTS events ( 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, +-- 合法 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 +-- 协作: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 ( @@ -266,13 +272,32 @@ CREATE TABLE IF NOT EXISTS agents ( 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' ``` -**连接配置:** +**连接配置:** ```python def get_connection(): @@ -290,13 +315,13 @@ def get_connection(): pending → claimed → working → review → done ↑ │ ├→ blocked ──┘ ├→ failed │ │ └→ failed └→ cancelled - └─────────┘ - (review→pending: 审核不通过,打回重做) + └─────────┘ + (review→pending: 审核不通过,打回重做) (blocked→pending: 阻塞解除) (failed→pending: 重试) ``` -**与 v2.0 的区别:** v2.0 有 9 个状态(spawning, ready, reporting 等),v2.6 简化为 8 个。原因是 spawn 逻辑从状态机移到了 daemon——daemon tick 发现黑板需要某人介入就 spawn,不需要 spawning/ready 这些中间状态。 +**与 v2.0 的区别:** v2.0 有 9 个状态(spawning, ready, reporting 等),v2.6 简化为 8 个。原因是 spawn 逻辑从状态机移到了 daemon--daemon tick 发现黑板需要某人介入就 spawn,不需要 spawning/ready 这些中间状态。 | 状态 | 含义 | 谁触发 | |------|------|--------| @@ -309,7 +334,7 @@ pending → claimed → working → review → done | failed | 失败 | Agent 或 daemon | | cancelled | 取消 | 用户 | -**完整合法流转矩阵:** +**完整合法流转矩阵:** ```python VALID_TRANSITIONS = { @@ -326,7 +351,7 @@ VALID_TRANSITIONS = { ### 3.4 原子操作 -**任务认领(claim)** — 原子 CAS,防止两个人同时领: +**任务认领(claim)** - 原子 CAS,防止两个人同时领: ```python def claim_task(task_id: str, agent_id: str) -> bool: @@ -343,7 +368,7 @@ def claim_task(task_id: str, agent_id: str) -> bool: conn.close() ``` -**产出写入** — SQLite 事务保证原子: +**产出写入** - SQLite 事务保证原子: ```python def write_output(task_id: str, agent_id: str, output: dict): @@ -353,7 +378,7 @@ def write_output(task_id: str, agent_id: str, output: dict): conn.execute( "INSERT INTO outputs (task_id, agent, output_type, title, content_path, summary, metadata) " "VALUES (?, ?, ?, ?, ?, ?, ?)", - (task_id, agent_id, output['type'], output['title'], + (task_id, agent_id, output['type'], output['title'], output['path'], output['summary'], json.dumps(output.get('metadata', {}))) ) conn.execute( @@ -365,9 +390,9 @@ def write_output(task_id: str, agent_id: str, output: dict): conn.close() ``` -### 3.5 评论线程(讨论机制) +### 3.5 评论线程(讨论机制) -参考 Hermes 的 `kanban_comment` 模式: +参考 Hermes 的 `kanban_comment` 模式: ```python def add_comment(task_id: str, author: str, body: str, mentions: list = None): @@ -386,37 +411,37 @@ def add_comment(task_id: str, author: str, body: str, mentions: list = None): conn.close() ``` -**讨论示例:** +**讨论示例:** ``` -[16:30 庞统] 张飞,你的实现方案我看了,回测数据量大时内存会爆。 - 关羽,从风控角度也看看? @关羽 @张飞 -[16:35 关羽] 同意。建议加分批加载机制,单批不超过 50 万条。 -[16:40 张飞] 收到,改成分批加载。预计 30 分钟。 -[16:55 庞统] @张飞 注意止损逻辑也需要同步改,分批后止损触发时机变了。 +[16:30 庞统] 张飞,你的实现方案我看了,回测数据量大时内存会爆。 + 关羽,从风控角度也看看? @关羽 @张飞 +[16:35 关羽] 同意。建议加分批加载机制,单批不超过 50 万条。 +[16:40 张飞] 收到,改成分批加载。预计 30 分钟。 +[16:55 庞统] @张飞 注意止损逻辑也需要同步改,分批后止损触发时机变了。 [17:10 张飞] 完成。产出在 output-zhangfei-v2.md。 ``` -**核心原则:评论都在黑板上,不在任何 agent 的 session 里。Agent 的 session 是临时的。** +**核心原则:评论都在黑板上,不在任何 agent 的 session 里。Agent 的 session 是临时的。** ### 3.6 竞态解决 -任务认领的竞态通过 SQLite 原子 CAS 解决(先到先得)。 +任务认领的竞态通过 SQLite 原子 CAS 解决(先到先得)。 -职责冲突的解决(张飞和关羽都认为自己该做某个任务): -1. **默认:先到先得** — SQLite CAS,谁先 claim 谁做 -2. **升级:庞统仲裁** — 如果争议,评论中 @庞统 请求仲裁 -3. **最终:用户拍板** — @user 请求用户决定 +职责冲突的解决(张飞和关羽都认为自己该做某个任务): +1. **默认:先到先得** - SQLite CAS,谁先 claim 谁做 +2. **升级:庞统仲裁** - 如果争议,评论中 @庞统 请求仲裁 +3. **最终:用户拍板** - @user 请求用户决定 -不需要复杂的分布式共识——职责分工已经自然避免了大部分冲突。 +不需要复杂的分布式共识--职责分工已经自然避免了大部分冲突。 --- -## 4. Daemon(管家)设计 +## 4. Daemon(管家)设计 ### 4.1 Daemon 的角色定位 -> **Daemon 是投递员,不是决策者。所有决策发生在黑板上,daemon 只执行。** +> **Daemon 是投递员,不是决策者。所有决策发生在黑板上,daemon 只执行。** Daemon 做三件事: 1. **读黑板** — 定期 tick,检查黑板状态 @@ -428,44 +453,63 @@ Daemon **不做**: - ❌ 不维护状态机(黑板就是状态) - ❌ 不做业务逻辑(不解析产出、不做评审) +**三层执行模型**:Daemon 的操作按成本和复杂度分为三层: + +| 层级 | 方式 | 成本 | 适用场景 | 例子 | +|------|------|------|---------|------| +| **L1 Daemon 直接操作** | SQLite 读写、文件操作 | 几乎为零 | 纯机械动作 | 更新状态、记录事件、机械验证(文件存在、JSON格式、字段非空) | +| **L2 spawn sub** | `openclaw agent --agent --session-id ` | 轻量(隔离 session,单任务) | 轻量 AI 判断 | scope guard、格式校验、快速评估、假死 reminder | +| **L3 run agent** | spawn 完整 Agent 到黑板上工作 | 完整(读黑板+思考+写回) | 重度 AI 工作 | 庞统拆解、张飞编码、司马懿 review、庞统纠错 | + +关键区别: +- L2 的 sub 是一次性、单任务的("帮我检查这个输出是否在 scope 内"),执行完就退出 +- L3 的 agent 是完整的黑板参与者(读全局、自主决策、写回多个表) + ### 4.2 Daemon Tick 循环 -参考 Hermes Dispatcher,但更轻量: +参考 Hermes Dispatcher,但更轻量: -**Tick 频率:60 秒(默认),可通过 CLI 手动触发立即执行。** +**Tick 频率:60 秒(默认),可通过 CLI 手动触发立即执行。** ```bash # 手动触发一次 tick(用于需要立即响应的场景) python3 ~/.sanguo_projects/sanguo_moziplus/cli/daemon.py tick ``` +**§4.2.1 Tick 循环中的 L1/L2/L3 标注说明:** +- L1(几乎零成本):健康检查、状态回收、session 清理、SQLite 查询、文件存档 +- L2(轻量):spawn 单任务 sub,如 @mention 通知、待领取任务通知、scope guard +- L3(完整):spawn 完整 Agent,如庞统仲裁 blocked 任务、AI 纠错协商、审核 + ```python async def daemon_tick(): """每 60 秒执行一次""" - - # 1. 健康检查 - reclaim_stale_tasks() # 超时的 working 任务回收 - detect_zombie_sessions() # 进程死了但 session 还在的 - - # 2. 读黑板 + + # 1. 健康检查(L1: Daemon 直接操作) + # 详见 §4.5 续杯与心跳 + check_agent_heartbeats() # L1: 检查 observations 有无新进展 + reclaim_stale_tasks() # L1: 超时的 working 任务回收(硬上限 = 3x 预估工时) + detect_zombie_sessions() # L1: 进程死了但 session 还在的 + + # 2. 读黑板(L1: SQLite 查询) board = read_blackboard() # SQLite 查询 - - # 3. 处理评论中的 @mention + + # 3. 处理评论中的 @mention(L2/L3: spawn agent) # 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}),请查看。") + async_spawn_agent(target_agent, + message=f"黑板上有给你的新评论(task-{comment.task_id}),请查看。") mark_comment_processed(comment.id) - - # 4. 处理待领取的任务(庞统在黑板上分配了但 agent 还没领) + + # 4. 处理待领取的任务(L2: spawn agent)庞统在黑板上分配了但 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 请求帮助) + message=f"黑板上有分配给你的任务({task.title}),请查看并认领。") + + # 5. 处理 blocked 任务(L3: spawn 庞统决策)agent 请求帮助) for task in board.get_blocked_tasks(): mentions = get_latest_comment_mentions(task.id) if mentions: @@ -473,13 +517,13 @@ async def daemon_tick(): for agent_id in mentions: if not is_agent_active(agent_id): async_spawn_agent(agent_id, - message=f"任务 {task.id} 被 block,需要你的协助。") + message=f"任务 {task.id} 被 block,需要你的协助。") else: # 没有 @ → spawn 庞统来决定找谁帮忙 async_spawn_agent('pangtong-fujunshi', - message=f"任务 {task.id} 被 block,没有指定协助者,请决定如何处理。") - - # 6. 清理完成的 session + message=f"任务 {task.id} 被 block,没有指定协助者,请决定如何处理。") + + # 6. 清理完成的 session(L1: 文件操作) for session in get_completed_sessions(): archive_session(session) # mv jsonl → task 目录 cleanup_sessions_json(session) # 编辑 sessions.json 删除记录 @@ -493,11 +537,11 @@ async def daemon_tick(): --message "请检查黑板 task-001..." ↓ 2. Agent 执行 - - 读黑板(SQLite 查询) - - 做任务(编码/审核/数据分析) - - 写回黑板(产出、评论、状态更新) + - 读黑板(SQLite 查询) + - 做任务(编码/审核/数据分析) + - 写回黑板(产出、评论、状态更新) ↓ -3. Agent 退出(自然结束) +3. Agent 退出(自然结束) ↓ 4. Daemon 清理 - mv .jsonl → task-001/archive/ @@ -505,36 +549,135 @@ async def daemon_tick(): - 编辑 sessions.json 删除该 session 记录 ``` -**技术验证结论:** +**技术验证结论:** - `openclaw agent --agent --session-id ` 可创建完全隔离的 session ✅ -- 直接编辑 `sessions.json` 可安全删除 session 记录 ✅(已验证) -- Gateway WS `sessions.delete` 需要 `operator.admin` scope(token 模式不授予,不可用)❌ -- 回退方案:直接编辑 `sessions.json` 是安全可靠的 ✅ +- 直接编辑 `sessions.json` 可安全删除 session 记录 ✅(已验证) +- Gateway WS `sessions.delete` 需要 `operator.admin` scope(token 模式不授予,不可用)❌ +- 回退方案:直接编辑 `sessions.json` 是安全可靠的 ✅ ### 4.4 Agent Spawn 后的消息内容 -Agent 被 spawn 时,daemon 传递的消息应包含足够的上下文让 agent 知道该做什么: +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" - + + 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" + msg += f"\n最近评论:\n" for c in recent: msg += f" [{c.created_at} {c.author}] {c.body[:200]}\n" - + msg += f"\n请读取黑板获取完整信息。" return msg ``` +### 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 | +| timeout(session idle)+ 产出达标 | session idle + outputs 表有内容 | 幻觉门控验证产出 → 通过则继续流转 | L1→L2 | +| timeout + 产出不达标 | session idle + 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分钟)提供了超时回收的参考值 + +### 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 连续失败 N 次(默认3次)→ 自动 block + 通知用户 + +**失败记录**:谁记录什么? + +| 记录者 | 记录内容 | 黑板位置 | +|--------|---------|---------| +| Daemon | 机械类失败(进程退出码、超时) | events 表,event_type=task_failed | +| 司马懿 | 内容类失败(评审不通过) | reviews 表(verdict=needs_revision + issues) | +| 庞统 | 方向类失败(需求偏离) | decisions 表(重规划原因) | + +**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 写 scope_declaration 时 | L2 sub 对比 scope_declaration vs task.truths | 写 observation(severity=warning)→ Daemon 触发后续判断链 | +| **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 节点上 + --- ## 5. Agent 与黑板的交互 @@ -545,15 +688,18 @@ def build_spawn_message(task_id: str, trigger_reason: str, comments_since: str = Agent 被 spawn ↓ 1. 读黑板 → 了解任务全局状态 - - 读 tasks 表:当前任务的状态、描述、依赖 - - 读 comments 表:讨论历史 - - 读 outputs 表:已有产出 - - 读 observations 表:已知风险 + - 读 tasks 表:当前任务的状态、描述、依赖 + - 读 comments 表:讨论历史 + - 读 outputs 表:已有产出 + - 读 observations 表:已知风险 ↓ 2. 想 → 根据自己的职责自主决策 - 我是编码先锋,这个 pending 任务适合我 → claim - 我是风控守将,这个 comment @ 我 → 回复 - 我是副军师,这个任务需要分解 → 创建子任务 + - Agent claim 任务后、开始工作前,写 scope_declaration 到 decisions 表: + "我计划做什么,产出什么" + Scope Guard(L2 sub)会对比 scope_declaration vs task.truths ↓ 3. 做 → 执行任务 - 编码、审核、数据分析等 @@ -565,25 +711,29 @@ Agent 被 spawn - 写 comments 表:完成说明 - 写 decisions 表:关键决策(哪怕自己的决策也要填一条) - 更新 tasks 表:status → done/review + - must_haves 三件套(任务创建时由庞统定义): + - truths:用户视角的可观测行为("用户能看到回测结果"),不是实现步骤("编写回测脚本") + - artifacts:必须存在的产出文件 + - constraints:继承的约束(如"不超过500行"、"必须用vnpy") ↓ 5. 退出 → daemon 自动清理 session ``` ### 5.2 Agent 工具集 -Agent 通过 `exec` 工具调用 CLI 命令操作黑板: +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 条) +# 读黑板(过滤:只读最近 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 # 认领任务 @@ -592,7 +742,7 @@ python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py claim --task task-0 # 写产出 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万条" + --summary "实现分批加载,单批50万条" # 写评论 python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py comment --task task-001 --author zhangfei-dev \ @@ -604,9 +754,9 @@ python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py observe --task task # 记录决策 python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py decide --task task-001 --decider zhangfei-dev \ - --decision "使用分批加载而非流式" --rationale "流式需要改底层框架,分批只需改回测模块" + --decision "使用分批加载而非流式" --rationale "流式需要改底层框架,分批只需改回测模块" -# 创建任务(任何 Agent 都可以创建) +# 创建任务(任何 Agent 都可以创建) python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py create --title "分钟线数据下载" \ --creator zhaoyun-data --task-type data ``` @@ -618,13 +768,13 @@ python3 ~/.sanguo_projects/sanguo_moziplus/cli/blackboard.py create --title "分 ### 6.1 庞统规划 + Agent 领任务 ``` -用户 → 庞统(主session):"设计一个动量因子策略" +用户 → 庞统(主session):"设计一个动量因子策略" ↓ -庞统在黑板上写: - - 创建 task-001(数据准备,pending) - - 创建 task-002(因子计算,pending,depends_on: [task-001]) - - 创建 task-003(回测验证,pending,depends_on: [task-002]) - - 评论:"建议赵云领 001,张飞领 002 和 003" +庞统在黑板上写: + - 创建 task-001(数据准备,pending) + - 创建 task-002(因子计算,pending,depends_on: [task-001]) + - 创建 task-003(回测验证,pending,depends_on: [task-002]) + - 评论:"建议赵云领 001,张飞领 002 和 003" ↓ Daemon tick 发现 task-001 pending + 庞统评论建议赵云 ↓ @@ -634,7 +784,7 @@ Daemon tick 发现 task-001 done → task-002 depends_on 满足 ↓ Daemon spawn 张飞 → 张飞读黑板 → claim task-002 → 执行 → 写产出 → 退出 ↓ -(同理 task-003) +(同理 task-003) ``` ### 6.2 Agent 间协作讨论 @@ -642,14 +792,14 @@ Daemon spawn 张飞 → 张飞读黑板 → claim task-002 → 执行 → 写产 ``` 张飞执行 task-002 时发现需要分钟线数据 ↓ -张飞写评论:"@赵云 task-002 需要分钟线数据,能帮忙下载吗?" +张飞写评论:"@赵云 task-002 需要分钟线数据,能帮忙下载吗?" 张飞更新任务状态 → blocked ↓ Daemon tick 发现 task-002 blocked + 评论 @ 赵云 ↓ Daemon spawn 赵云 → 赵云读黑板 → 看到评论 → 下载数据 -赵云写评论:"分钟线数据已下载到 /path/to/data" + 写产出 -赵云写评论:"@张飞 数据就绪,可以继续" +赵云写评论:"分钟线数据已下载到 /path/to/data" + 写产出 +赵云写评论:"@张飞 数据就绪,可以继续" ↓ Daemon tick 发现评论 @ 张飞 ↓ @@ -661,9 +811,9 @@ Daemon spawn 张飞 → 张飞读黑板 → 看到数据就绪 → 继续 task-0 ``` 张飞在 task-002 中发现止损逻辑有 bug ↓ -张飞写 observation(severity: warning): +张飞写 observation(severity: warning): "止损逻辑在分批模式下可能漏触发" -张飞写评论:"@关羽 止损逻辑需要你从风控角度确认" +张飞写评论:"@关羽 止损逻辑需要你从风控角度确认" ↓ Daemon tick 发现 observation + 评论 @ 关羽 ↓ @@ -675,7 +825,7 @@ Daemon spawn 关羽 → 关羽读黑板 → 审查 → 写评论 + observation ``` 用户读黑板 → 发现 task-002 进度慢 ↓ -用户在黑板上写评论:"task-002 优先级提高,需要今天完成" +用户在黑板上写评论:"task-002 优先级提高,需要今天完成" ↓ Daemon tick 发现用户评论 → 如果张飞未 active → spawn 张飞通知 ``` @@ -689,7 +839,7 @@ Daemon tick 发现用户评论 → 如果张飞未 active → spawn 张飞通知 ```python class SessionManager: def async_spawn_agent(self, agent_id: str, message: str) -> str: - """异步 spawn 隔离 session,不等待完成。返回 session_id。""" + """异步 spawn 隔离 session,不等待完成。返回 session_id。""" session_id = str(uuid.uuid4()) cmd = [ "openclaw", "agent", @@ -698,35 +848,35 @@ class SessionManager: "--message", message, "--json" ] - # Popen 异步启动,不阻塞 daemon tick + # 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 并发写入冲突) + + # 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: @@ -743,15 +893,15 @@ class SessionManager: | 并行 spawn 成功 | ✅ 通过 | | 直接编辑 sessions.json 删除记录安全 | ✅ 通过 | | jsonl 存档后从原目录删除 | ✅ 通过 | -| Gateway WS sessions.delete(需 admin scope) | ❌ 不可用 | +| Gateway WS sessions.delete(需 admin scope) | ❌ 不可用 | | `openclaw sessions cleanup --fix-missing --enforce` | ❌ 对 agent main session 报错 | -| Agent 主 session 对 CLI spawn 的 sub 完全无感 | ✅ 确认(设计如此)| +| Agent 主 session 对 CLI spawn 的 sub 完全无感 | ✅ 确认(设计如此)| --- -## 8. Sanguo Mail:退役 +## 8. Sanguo Mail:退役 -v2.6 中 Mail 完全退役。黑板的两个操作替代了 Mail 的所有功能: +v2.6 中 Mail 完全退役。黑板的两个操作替代了 Mail 的所有功能: | Mail 功能 | 黑板替代 | |----------|--------| @@ -760,29 +910,29 @@ v2.6 中 Mail 完全退役。黑板的两个操作替代了 Mail 的所有功能 | 结果回传 | 产出写入 outputs 表 + 评论通知 | | 讨论 | 评论线程 | -黑板比 Mail 更可靠:信息集中在 SQLite(不分散在 mail 目录)、有状态追踪、评论线程保持上下文完整、SQLite 读写比 Mail poller 更可靠。 +黑板比 Mail 更可靠:信息集中在 SQLite(不分散在 mail 目录)、有状态追踪、评论线程保持上下文完整、SQLite 读写比 Mail poller 更可靠。 -如果需要系统级通知(daemon 异常、Gateway 状态),在黑板上创建 `system` 类型任务处理。 +如果需要系统级通知(daemon 异常、Gateway 状态),在黑板上创建 `system` 类型任务处理。 --- -## 9. 质量门控(任务完成标准) +## 9. 质量门控(任务完成标准) ### 9.1 任务完成 = 司马懿评审通过 + 所有问题达成一致 + 修改完成 -一个任务要标记为 `done`,必须满足: +一个任务要标记为 `done`,必须满足: -1. **产出已提交** — Agent 写入 outputs 表 -2. **决策已记录** — 关键决策写入 decisions 表(哪怕是自己的决策也要填一条) -3. **司马懿审核通过** — spawn 司马懿审核产出,评论中明确写“通过” -4. **所有问题达成一致** — 审核中提出的问题全部解决,讨论达成共识 -5. **修改已完成** — 审核问题对应的修改已写入产出 +1. **产出已提交** - Agent 写入 outputs 表 +2. **决策已记录** - 关键决策写入 decisions 表(哪怕是自己的决策也要填一条) +3. **司马懿审核通过** - spawn 司马懿审核产出,评论中明确写"通过" +4. **所有问题达成一致** - 审核中提出的问题全部解决,讨论达成共识 +5. **修改已完成** - 审核问题对应的修改已写入产出 ### 9.2 达成一致的原则 -- **以用户意图为导向** — 任何人(包括司马懿、庞统)说的都不一定对,最终以用户的原始意图为准 -- **讨论达成共识** — 不同意见通过黑板评论讨论解决,不是谁职位高谁说了算 -- **用户有最终裁量权** — 讨论无法达成一致时,@user 请用户裁定 +- **以用户意图为导向** - 任何人(包括司马懿、庞统)说的都不一定对,最终以用户的原始意图为准 +- **讨论达成共识** - 不同意见通过黑板评论讨论解决,不是谁职位高谁说了算 +- **用户有最终裁量权** - 讨论无法达成一致时,@user 请用户裁定 ### 9.3 流程 @@ -791,33 +941,51 @@ Agent 完成产出 → status: review ↓ Daemon tick → spawn 司马懿审核 ↓ -司马懿读黑板(产出 + 决策 + 观察) +司马懿读黑板(产出 + 决策 + 观察) ↓ -司马懿写评论: - - “通过” → status: done - - “不通过,原因:XXX” → status: pending(打回重做)+ 评论说明问题 +司马懿写评论: + - "通过" → status: done + - "不通过,原因:XXX" → status: pending(打回重做)+ 评论说明问题 ↓ -如果有争议: +如果有争议: - 评论中讨论 - 讨论不清 → @user 请求裁定 ``` ### 9.4 决策记录 -Agent 执行过程中的每个关键决策都必须记录在黑板的 decisions 表中: +Agent 执行过程中的每个关键决策都必须记录在黑板的 decisions 表中: | 字段 | 含义 | |------|------| | decider | 谁做的决策 | -| decision | 决策内容(选了什么) | +| decision | 决策内容(选了什么) | | rationale | 为什么这样选 | | alternatives | 被排除的选项 | **哪怕是自己做的决策也要填一条。** 目的: -- 后续复盘时能追溯“当时为什么这样选” +- 后续复盘时能追溯"当时为什么这样选" - 审核时司马懿能理解决策背后的思考 - 经验沉淀的原始素材 +### 9.5 分级审查矩阵 + +不是所有任务都值得同等深度的审查。按任务风险等级决定审查深度。 + +| 风险等级 | 任务类型 | 审查深度 | 参与者 | +|---------|---------|---------|--------| +| **高风险** | 量化策略、生产部署、数据删除 | 三阶段审查(方案审查→Output Guardrail→产出审查)+ 可选多视角对抗 | 庞统+司马懿+对应执行者 | +| **标准** | 编码、数据处理、配置修改 | 二阶段(Output Guardrail + 产出审查) | 司马懿+执行者 | +| **低风险** | 调研报告、文档更新、日志查看 | 一阶段(Output Guardrail 机械检查) | Daemon 自动 | +| **调研** | 技术调研、方案探索 | 一阶段(庞统确认方向) | 庞统 | + +**风险等级由庞统在创建任务时标注**(task_type 字段),Daemon 据此决定审查流程。 + +**设计推导**: +- v1.0 实践:每个节点都要司马懿审查,简单任务过重 +- superpowers:三阶段审查(implementer → spec reviewer → code quality reviewer),不同阶段不同深度 +- Hermes:per-task retry budget,任务级别差异化 + --- ## 10. 产出物目录约定 @@ -825,46 +993,46 @@ Agent 执行过程中的每个关键决策都必须记录在黑板的 decisions ``` ~/.sanguo_projects/sanguo_moziplus/artifacts/ └── {task-id}/ - ├── outputs/ # Agent 产出物(代码、文档、数据) + ├── outputs/ # Agent 产出物(代码、文档、数据) ├── archive/ # session jsonl 存档 └── data/ # 数据文件 ``` -Agent 写产出时,`content_path` 指向此目录。Daemon 存档 session jsonl 时也写入 `archive/` 子目录。 +Agent 写产出时,`content_path` 指向此目录。Daemon 存档 session jsonl 时也写入 `archive/` 子目录。 --- ## 11. 保留 v2.0 的设计 -以下 v2.0 的设计在 v2.6 中保留: +以下 v2.0 的设计在 v2.6 中保留: -1. **SQLite WAL 模式** — 黑板数据库同样使用 WAL -2. **结构化产出规范** — output.md frontmatter + 结论 JSON(写在黑板 outputs 表中) -3. **观察机制** — v2.0 Report Watcher 的思路升级为 observations 表 -4. **证据原则** — 结论必须有证据(代码行号、日志、文件内容) -5. **审核流程** — 可通过黑板评论 + 状态机实现 +1. **SQLite WAL 模式** - 黑板数据库同样使用 WAL +2. **结构化产出规范** - output.md frontmatter + 结论 JSON(写在黑板 outputs 表中) +3. **观察机制** - v2.0 Report Watcher 的思路升级为 observations 表 +4. **证据原则** - 结论必须有证据(代码行号、日志、文件内容) +5. **审核流程** - 可通过黑板评论 + 状态机实现 --- -## 12. Phase 规划(v2.6) +## 12. Phase 规划(v2.6) ### Phase 1: 黑板基础设施 -1. SQLite blackboard.db(5 表 + WAL) -2. blackboard.py CLI(读写操作) -3. Daemon tick 循环(读黑板 + spawn + 清理) -4. Session 管理(spawn + 存档 + 清理) +1. SQLite blackboard.db(5 表 + 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 检测) +8. 健康检查(stale reclaim + zombie 检测) ### Phase 3: 智能化 -9. 庞统 AI 规划(读需求 → 创建任务 + 分配建议) -10. Agent 自主领活(读黑板 → 匹配职责 → claim) +9. 庞统 AI 规划(读需求 → 创建任务 + 分配建议) +10. Agent 自主领活(读黑板 → 匹配职责 → claim) 11. 产出验证门禁 -12. 经验沉淀(observation → knowledge base) +12. 经验沉淀(observation → knowledge base) --- @@ -873,11 +1041,11 @@ Agent 写产出时,`content_path` 指向此目录。Daemon 存档 session json | 需求 | 参考系统 | 我们的方案 | 理由 | |------|---------|-----------|------| | 共享状态 | Hermes SQLite + Network-AI flock | SQLite WAL + 事务 CAS | 原子性 + 无外部依赖 | -| 讨论 | Hermes kanban_comment | comments 表 + @mention | 简单追加写入,所有人可见 | -| 调度 | Hermes Dispatcher 60s tick | Daemon 60s tick | 同设计,更轻量 | +| 讨论 | 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 CAS(first-commit-wins) | SQLite 事务足够 | +| 竞态 | Network-AI propose→validate→commit | SQLite CAS(first-commit-wins) | SQLite 事务足够 | | Session | Hermes process-per-worker | openclaw agent --session-id | OpenClaw 原生隔离 | | 清理 | 无参考 | 编辑 sessions.json | 已验证可行 | @@ -887,9 +1055,9 @@ Agent 写产出时,`content_path` 指向此目录。Daemon 存档 session json | 风险 | 概率 | 缓解 | |------|------|------| -| Agent 上下文不足(隔离 session 没有历史)| 中 | spawn 时传递黑板关键信息 + agent 可主动读黑板 | +| Agent 上下文不足(隔离 session 没有历史)| 中 | spawn 时传递黑板关键信息 + agent 可主动读黑板 | | Daemon 单点故障 | 低 | PM2 自动重启 + tick 无状态 | | SQLite 并发写入 | 中 | WAL + busy_timeout + BEGIN IMMEDIATE | -| 黑板膨胀(大量评论/产出)| 低 | 定期 archive + agent 只读最近 N 条 | +| 黑板膨胀(大量评论/产出)| 低 | 定期 archive + agent 只读最近 N 条 | | Agent 不知道该做什么 | 中 | Skill 指导 + 庞统 plan 评论 + daemon 消息含上下文 | | Sanguo Mail 退役后的系统通知 | 低 | 黑板 system 类型任务替代 |