diff --git a/docs/design/24-compact-detection-fix.md b/docs/design/24-compact-detection-fix.md index 431febf..8e577bf 100644 --- a/docs/design/24-compact-detection-fix.md +++ b/docs/design/24-compact-detection-fix.md @@ -293,7 +293,48 @@ trajectory jsonl 路径 = `{sessionFile}.trajectory.jsonl`,其中 sessionFile - §07 Spawner Acquire-First(§4.5 O5 compact 扫描条件收紧) - §08 Classify Outcome Optimization(compact_hanging 处理) -## 8. 评审记录 +## 8. 实验验证(2026-06-13) + +### 8.1 Manual Compact 实验 + +对 `jiangwei-infra` 执行 `/compact`,每秒采样 4 个维度,完整时间线: + +| 时间 | 事件 | 维度 | +|---|---|---| +| 08:08:57 | 基线:lock=0, status=done, compact=2, jsonl=1942 | 全部 | +| 08:09:22 | **Lock file 出现** + JSONL +1 行 | lock, jsonl | +| 08:09:22~08:12:31 | Lock 持有中,status=done 不变 | lock | +| 08:12:32 | **Lock 释放** + compaction+1 + GW 2 条新日志 | 全部 | +| 08:12:32.035 | GW: `[compaction] rotated active transcript` | gateway log | +| 08:12:32.057 | GW: `⇄ res ✓ sessions.compact 191267ms` | gateway log | + +**Compact 耗时**:191267ms ≈ 3 分 11 秒。 + +### 8.2 关键结论 + +1. **Lock file 是最通用的 compact 运行时标记**:manual compact 开始时立即出现,结束时释放,覆盖全程。 +2. **Gateway log 无开始标记**:manual compact 不产生 `context-overflow-precheck` 或 `route=compact_then_truncate`,只有结束标记(`rotated active transcript` + `sessions.compact` WS 响应)。 +3. **sessions.json status 不变**:全程 `done`,不出现 `compacting` 之类的中间状态。 +4. **JSONL compaction entry 是可靠的结束标记**:所有路径(precheck/manual/threshold)完成后都会写入。 +5. **V5 安全性**:manual/threshold 无开始标记 → V5 不会误判为进行中 → 不会在完成后误阻塞。Phase 2 的 `session_locked`(lock file 检查)覆盖了 compact 期间的阻塞需求。 + +### 8.3 实际防御体系 + +| Phase 2 检查 | 检测信号 | 覆盖路径 | +|---|---|---| +| `session_compacting` | Gateway log precheck 开始标记 + jsonl 结束标记 | precheck | +| `session_locked` | Lock file 存在 + PID 活 | 全部(precheck + manual + threshold) | + +两条路径互补,先命中哪个就 skip。V5 compact 检测是附加保险,不是唯一防线。 + +### 8.4 自身 compact 验证(庞统) + +同日 08:00 对庞统自身执行 `/compact` 的结果一致: +- Gateway log 无开始标记,只有 `08:00:12.213` 的 `rotated active transcript`(结束) +- `sessions.compact 238358ms` ≈ 3 分 58 秒 +- 结论相同 + +## 9. 评审记录 - **v1**:trajectory jsonl 间接推断 → 仲达指出 trajectoryPath 不可用、需多文件等 3 个问题 - **v2**:gateway 日志 precheck 开始标志 → 仲达指出开始标志覆盖率仅 30%,建议 rotation-only diff --git a/docs/design/agent-api-contract.md b/docs/design/agent-api-contract.md deleted file mode 100644 index d80d6ec..0000000 --- a/docs/design/agent-api-contract.md +++ /dev/null @@ -1,198 +0,0 @@ -# Agent-Backend API 契约 (v2.6) - -Agent 通过 HTTP API 与黑板交互。本文档是 prompt 模板和后端路由的共同 Source of Truth。 - -## 通用约定 - -- Base URL: `http://{api_host}:{api_port}` -- Content-Type: `application/json` -- 错误响应格式(422/409): - ```json - { - "error": "error_code", - "detail": "人类+Agent 可读的错误描述", - "valid_values": { "field": ["allowed", "values"] }, - "hint": "修复建议" - } - ``` - ---- - -## 1. POST /api/projects/{project_id}/tasks/{task_id}/outputs - -写入产出物。 - -### 请求体 - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `agent` | string | ✅ | Agent ID(如 `zhangfei-dev`) | -| `type` | string | ✅ | 产出类型。合法值:`code`, `document`, `data`, `config`, `other` | -| `title` | string | ✅ | 产出标题(简要描述) | -| `content` | string | ⬜ | 产出内容(文本直传模式)。与 `content_path` 二选一 | -| `content_path` | string | ⬜ | 产出文件路径(路径引用模式)。与 `content` 二选一 | -| `summary` | string | ⬜ | 内容摘要 | -| `metadata` | object | ⬜ | 附加元数据 | - -### 行为 - -- 如果传了 `content` 而没传 `content_path`:后端自动将内容写入 `artifacts/{task_id}/{title}` 并填充 `content_path` -- `type` 的别名 `content_type` 也被接受(兼容),但优先用 `type` - -### 成功响应 (200) - -```json -{"ok": true, "output_id": 42} -``` - -### 错误响应 - -- **422**: 字段缺失或值不合法。`valid_values` 会列出合法值 -- **500**: 内部错误(不应出现,出现说明后端有 bug) - -### 示例 - -```bash -curl -X POST http://127.0.0.1:8083/api/projects/demo/tasks/task-001/outputs \ - -H 'Content-Type: application/json' \ - -d '{ - "agent": "zhangfei-dev", - "type": "code", - "title": "csv_sorter.py", - "content": "import csv\nimport argparse\n...", - "summary": "CSV排序小程序,支持按任意列降序/升序排序" - }' -``` - ---- - -## 2. POST /api/projects/{project_id}/tasks/{task_id}/status - -更新任务状态。 - -### 状态机 - -``` -pending ──→ claimed ──→ working ──→ review ──→ done - │ │ │ │ - │ │ │ └──→ pending(驳回重做) - │ │ ├──→ blocked ──→ pending - │ │ ├──→ failed ──→ pending(重试) - │ │ └──→ cancelled - │ ├──→ pending(超时回收) - │ └──→ cancelled - └──→ cancelled -``` - -### 请求体 - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `status` | string | ✅ | 目标状态 | -| `agent` | string | ✅ | Agent ID | -| `detail` | string | ⬜ | 状态变更说明(失败原因、驳回理由等) | - -### Agent 常用转换 - -| 从 | 到 | 何时 | -|----|-----|------| -| claimed | working | 开始执行任务 | -| working | review | 完成任务,提交审查 | -| working | failed | 执行失败 | - -### 成功响应 (200) - -```json -{"ok": true, "old_status": "working", "new_status": "review"} -``` - -### 错误响应 - -- **409**: 状态转换不合法。响应会包含合法的目标状态列表 - -```json -{ - "error": "invalid_transition", - "detail": "Cannot transition from claimed to review", - "valid_transitions": {"claimed": ["working", "pending", "cancelled"]}, - "hint": "You must go through 'working' before 'review'" -} -``` - -### 示例 - -```bash -# 开始工作 -curl -X POST http://127.0.0.1:8083/api/projects/demo/tasks/task-001/status \ - -H 'Content-Type: application/json' \ - -d '{"status": "working", "agent": "zhangfei-dev"}' - -# 完成,提交审查 -curl -X POST http://127.0.0.1:8083/api/projects/demo/tasks/task-001/status \ - -H 'Content-Type: application/json' \ - -d '{"status": "review", "agent": "zhangfei-dev"}' - -# 失败 -curl -X POST http://127.0.0.1:8083/api/projects/demo/tasks/task-001/status \ - -H 'Content-Type: application/json' \ - -d '{"status": "failed", "agent": "zhangfei-dev", "detail": "无法连接数据库"}' -``` - ---- - -## 3. GET /api/projects/{project_id}/tasks/{task_id}?expand=all - -获取任务完整信息(含产出、评论、事件、审查)。 - -### 查询参数 - -| 参数 | 类型 | 说明 | -|------|------|------| -| `expand` | string | `all` 返回所有关联数据 | - -### 响应字段 - -包含任务基本信息 + 聚合字段: - -| 字段 | 说明 | -|------|------| -| `status` | 当前状态 | -| `outputs` | 产出列表 | -| `reviews` | 审查记录 | -| `events` | 事件时间线 | -| `comments` | 评论 | -| `review_status` | 审查状态(null/pending/approved/rejected/rebuttal) | - ---- - -## 4. POST /api/projects/{project_id}/tasks/{task_id}/comments - -添加评论(Agent fallback 时可用)。 - -### 请求体 - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `author` | string | ✅ | 评论者 | -| `body` | string | ✅ | 评论内容 | - ---- - -## 5. GET /api/projects/{project_id}/tasks - -列出项目任务。 - -### 查询参数 - -| 参数 | 类型 | 说明 | -|------|------|------| -| `status` | string | 按状态过滤 | -| `assignee` | string | 按负责人过滤 | - ---- - -## 变更日志 - -| 日期 | 变更 | -|------|------| -| 2026-05-17 | 初始版本。基于端到端实测发现的问题制定 | diff --git a/docs/design/agent-integration-v2.6.md b/docs/design/agent-integration-v2.6.md deleted file mode 100644 index a8afa36..0000000 --- a/docs/design/agent-integration-v2.6.md +++ /dev/null @@ -1,344 +0,0 @@ -# Agent 编排集成方案 — D1+D2+D4 - -> 版本: v2.6.1 -> 日期: 2026-05-17 -> 作者: 庞统 🐦 -> 状态: 待评审 - -## 1. 背景 - -v2.6 已完成 F1-F18 全部模块编码(301 测试通过),前端 Mock UI 已就绪。当前断层: - -- **Ticker** 在跑,但不调 Dispatcher/Spawner(只做依赖推进 + daemon_tick) -- **Spawner** 代码有,`spawn_full_agent()` 走 `openclaw agent` CLI,但从未被 Ticker 调用过 -- **Agent** 不知道 v2.0 API 的存在,无法读写黑板 -- **前端** 展示 mock 数据,未对接真实 API - -本文档设计:Ticker → Dispatcher → Spawner → Agent → 回写黑板的完整闭环。 - -## 2. 设计目标 - -1. **端到端可跑通**:用户通过 API 创建任务 → Agent 被调起 → Agent 执行 → Agent 回写产出 → 审查流水线触发 → 前端实时展示 -2. **最小改动**:复用现有 Spawner/Dispatcher 代码,只补 Ticker 的调度触发和 Agent 端的 API 调用 -3. **不破坏现有测试**:301 测试全部继续通过 -4. **渐进式上线**:先跑通 Full Agent(注册角色),Subagent 后续完善 - -## 3. 架构总览 - -``` -┌──────────────────────────────────────────────────────────────────┐ -│ Daemon (uvicorn) │ -│ │ -│ Ticker (30s) │ -│ ├─ 1. 依赖推进 (blocked → pending) ← 已有 │ -│ ├─ 2. 扫描 pending 任务 ← 新增 │ -│ ├─ 3. Dispatcher.decide() 决策 ← 新增调用 │ -│ ├─ 4. Spawner.spawn() 调起 Agent ← 新增调用 │ -│ └─ 5. 状态 pending → claimed/working ← 新增 │ -│ │ -│ API (FastAPI) │ -│ ├─ POST /api/projects/{id}/tasks ← 已有 │ -│ ├─ GET /api/projects/{id}/tasks/{tid}?expand=all ← 已有 │ -│ ├─ POST /api/projects/{id}/tasks/{tid}/outputs ← 已有 │ -│ └─ PATCH /api/projects/{id}/tasks/{tid}/status ← 已有 │ -│ │ -│ SSE (/api/events/stream) ← 已有 │ -└──────────────────────────────────────────────────────────────────┘ - │ - │ openclaw agent --agent {id} --message "..." - ▼ -┌──────────────────────────────────────────────────────────────────┐ -│ OpenClaw Agent (独立 session) │ -│ │ -│ 1. 读任务:GET /api/projects/{pid}/tasks/{tid}?expand=all │ -│ 2. 执行任务:根据 task_type + description 自主完成 │ -│ 3. 写产出:POST /api/projects/{pid}/tasks/{tid}/outputs │ -│ 4. 更新状态:PATCH /api/projects/{pid}/tasks/{tid}/status │ -│ → working → review(触发审查流水线) │ -└──────────────────────────────────────────────────────────────────┘ -``` - -## 4. 详细设计 - -### 4.1 Ticker 集成调度(D4) - -**改动文件**: `src/daemon/ticker.py` 的 `_tick_project()` - -**当前流程**: -``` -tick → 依赖推进 → daemon_tick event → 结束 -``` - -**新增流程**: -``` -tick → 依赖推进 → 调度扫描 → daemon_tick event → 结束 - │ - ├─ 扫描所有 pending 任务 - ├─ 对每个 pending 任务调 Dispatcher.decide() - ├─ 如果是 FULL_AGENT → Spawner.spawn_full_agent() - ├ 同时将任务状态改为 claimed - └─ 如果是 LOCAL → 直接执行 -``` - -**关键约束**: -- 每个 tick 最多调度 `max_dispatch_per_tick` 个任务(默认 3,可配置) -- 只调度 `pending` 状态的任务(`claimed`/`working` 说明已有 Agent 在处理) -- 检查并发限制(`ActiveAgentCounter.can_acquire()`) -- dispatch 失败不阻塞 tick 循环 - -**代码改动(伪代码)**: -```python -# ticker.py _tick_project() 新增步骤 -async def _tick_project(self, project_id, project_info): - # ... 现有: 依赖推进 ... - - # 新增: 调度扫描 - if self.dispatcher and self.spawner: - await self._dispatch_pending(project_id, db_path) - - # ... 现有: daemon_tick event ... - -async def _dispatch_pending(self, project_id, db_path): - """扫描 pending 任务并调度""" - queries = Queries(db_path) - pending = queries.tasks_by_status("pending") - - dispatched = 0 - for task in pending[:self.max_dispatch_per_tick]: - try: - result = await self.dispatcher.dispatch(task, project_config={...}) - if result["status"] == "dispatched" and result["level"] in ("full", "escalate"): - # 标记为 claimed - self._transition_status(conn, task.id, "claimed", - agent="daemon", - detail={"dispatched_to": result["agent_id"], - "session_id": result.get("session_id")}) - dispatched += 1 - except Exception: - logger.exception("Dispatch failed for %s", task.id) -``` - -**Ticker 构造器新增参数**: -```python -def __init__(self, ..., dispatcher=None, spawner=None, max_dispatch_per_tick=3): - self.dispatcher = dispatcher - self.spawner = spawner - self.max_dispatch_per_tick = max_dispatch_per_tick -``` - -### 4.2 Spawner 对接 OpenClaw(D1) - -**改动文件**: `src/daemon/spawner.py` - -**现状分析**: `spawn_full_agent()` 已经调用 `openclaw agent` CLI,参数完全匹配: -```python -cmd = ["openclaw", "agent", - "--agent", agent_id, - "--session-id", session_id, - "--message", message, - "--json"] -``` - -`openclaw agent --help` 确认支持 `--agent`、`--session-id`、`--message`、`--json`。 - -**改动点**: - -1. **丰富 message 内容** — 当前 `_build_message()` 只传 title + description。需要补充: - - 项目 ID、任务 ID(让 Agent 知道调哪个 API) - - API 端点信息(让 Agent 知道怎么回写) - - `--deliver` 标志(可选,让 Agent 回复发送到频道) - -2. **新增 prompt 模板** — Agent 收到的 message 需要包含完整的执行指令: - -```python -SPAWN_PROMPT_TEMPLATE = """你收到一个 v2.6 黑板任务: - -项目: {project_id} -任务ID: {task_id} -标题: {title} -描述: {description} -类型: {task_type} -优先级: {priority} -必要条件: {must_haves} - -请按以下步骤执行: - -1. 了解任务需求,确认理解无误 -2. 执行任务(编码/回测/数据检查等) -3. 完成后,调用以下 API 写入产出: - POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/outputs - Body: {{"agent": "{agent_id}", "content": "<产出内容>", "content_type": "report"}} -4. 更新任务状态为 review: - PATCH http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status - Body: {{"status": "review", "agent": "{agent_id}"}} -5. 如果遇到问题,更新状态为 failed: - PATCH http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status - Body: {{"status": "failed", "agent": "{agent_id}", "detail": "<失败原因>"}} - -注意: -- API 地址: http://{api_host}:{api_port} -- 你可以通过 GET /api/projects/{project_id}/tasks/{task_id}?expand=all 查看完整任务信息 -- 产出写入后,审查流水线会自动触发 -""" -``` - -3. **配置化 API 地址** — 从 `config/default.yaml` 或环境变量读取: -```yaml -# config/default.yaml 新增 -daemon: - api_host: "127.0.0.1" - api_port: 8083 -``` - -### 4.3 Agent 执行回写流程(D2) - -**Agent 端无需代码改动** — Agent 本身已有 `exec` 工具可以调用 `curl`,也有 `web_fetch` 可以调 HTTP API。关键是在 spawn 的 prompt 里告诉 Agent: - -1. 任务详情(title、description、must_haves) -2. API 端点和调用方式 -3. 回写产出的具体步骤 - -**回写 API 已就绪**: - -| API | 方法 | 用途 | -|-----|------|------| -| `/api/projects/{id}/tasks/{tid}/outputs` | POST | 写入产出 | -| `/api/projects/{id}/tasks/{tid}/status` | PATCH | 更新状态 | -| `/api/projects/{id}/tasks/{tid}/comments` | POST | 添加评论 | -| `/api/projects/{id}/tasks/{tid}?expand=all` | GET | 查看完整信息 | - -**审查触发**: Agent 将状态改为 `review` 后,Ticker 在下次 tick 检测到 `review` 状态的任务,触发审查流水线(F12 + F13)。 - -### 4.4 审查流水线集成 - -**改动文件**: `src/daemon/ticker.py` 的 `_tick_project()` - -**新增逻辑**: -```python -# 检测 review 状态的任务,触发审查 -review_tasks = queries.tasks_by_status("review") -for task in review_tasks: - # 检查是否已有 review 记录 - reviews = bb.get_reviews(task.id) - if not reviews: - # 调度审查 Agent(司马懿) - await self.dispatcher.dispatch(task, action_type="review") -``` - -### 4.5 僵尸检测 + 超时处理 - -**已有基础**: F8 Health Monitor 有 zombie 检测逻辑。 - -**新增**: Ticker 在每次 tick 检查 `claimed`/`working` 状态超时的任务: -- `claimed` 超过 `claim_timeout`(默认 5 分钟)→ 重置为 `pending` -- `working` 超过 `task_timeout`(从任务 `deadline` 或默认 30 分钟计算)→ 标记 `failed` - -这部分复用现有 `_transition_status()` 方法。 - -## 5. 配置变更 - -```yaml -# config/default.yaml 新增字段 -daemon: - # Agent 调度 - max_dispatch_per_tick: 3 - claim_timeout_minutes: 5 - default_task_timeout_minutes: 30 - - # API(供 Agent 回写) - api_host: "127.0.0.1" - api_port: 8083 -``` - -## 6. main.py 启动集成 - -**改动文件**: `src/main.py` - -当前 Ticker 只接收 `registry` 参数。需要: -1. 创建 `Dispatcher` 实例(传入 registered_agents、spawner、counter) -2. 创建 `AgentSpawner` 实例 -3. 创建 `ActiveAgentCounter` 实例 -4. 将 dispatcher + spawner 传给 Ticker - -```python -# main.py lifespan 中 -from src.daemon.spawner import AgentSpawner -from src.daemon.dispatcher import Dispatcher -from src.daemon.counter import ActiveAgentCounter - -spawner = AgentSpawner(dry_run=False, agent_timeout=600) -counter = ActiveAgentCounter(max_global=5, max_per_agent=2) -dispatcher = Dispatcher( - registered_agents=["pangtong-fujunshi", "simayi-challenger", ...], - spawner=spawner, - counter=counter, -) - -ticker = Ticker( - registry=registry, - dispatcher=dispatcher, - spawner=spawner, - max_dispatch_per_tick=config.get("daemon", {}).get("max_dispatch_per_tick", 3), -) -``` - -## 7. 改动量评估 - -| 文件 | 改动类型 | 行数估计 | 风险 | -|------|---------|---------|------| -| `src/daemon/ticker.py` | 新增 `_dispatch_pending()` + 构造器参数 | +80 行 | 低(纯新增方法) | -| `src/daemon/spawner.py` | 新增 prompt 模板 + 配置读取 | +40 行 | 低 | -| `src/main.py` | 注入 dispatcher/spawner | +20 行 | 低 | -| `config/default.yaml` | 新增配置项 | +8 行 | 无 | -| `tests/test_ticker.py` | 新增调度测试 | +60 行 | 无 | - -**总改动**: ~210 行,全部是新增代码,不修改现有逻辑。 - -## 8. 不在本方案范围内 - -| 项目 | 原因 | -|------|------| -| Subagent spawn (sessions_spawn) | 留后续,先跑通 Full Agent | -| 前端对接真实 API | 依赖本方案完成后才有真实数据 | -| Checkpoint | v2.7 | -| Agent bootstrap prompt 定制 | 当前用通用模板,后续按角色定制 | - -## 9. 端到端流程示例 - -用户通过 API 创建一个回测任务: - -``` -1. POST /api/projects/quant-demo/tasks - → 任务入库,status=pending,assignee=zhangfei-dev - -2. Ticker tick(30s 内) - → 扫描 pending 任务 - → Dispatcher: assignee 是注册角色 → FULL_AGENT - → Spawner: openclaw agent --agent zhangfei-dev --message "..." - → 任务 status → claimed - -3. 张飞 Agent 收到 prompt - → 读取任务详情(GET expand=all) - → 执行回测 - → 写入产出(POST /outputs) - → 更新状态为 review - -4. Ticker 下次 tick - → 检测 review 状态任务 - → 调度司马懿审查 - → 司马懿 Agent 收到 prompt → 查看产出 → 写 review → APPROVE/REJECT - -5. 审查通过 → status → done - 审查驳回 → status → pending(重做) - -6. 前端 SSE 实时收到事件更新 -``` - -## 10. 验收标准 - -1. 通过 API 创建任务 → 30s 内 Agent 被调起 -2. Agent 能读取任务详情、执行、写入产出 -3. Agent 更新状态为 review → 审查 Agent 被调起 -4. 审查通过 → 任务完成 -5. 前端展示真实数据(非 mock) -6. 现有 301 测试全部继续通过 diff --git a/docs/design/agent-routing-redesign.md b/docs/design/agent-routing-redesign.md deleted file mode 100644 index 9c52b74..0000000 --- a/docs/design/agent-routing-redesign.md +++ /dev/null @@ -1,518 +0,0 @@ -# Agent 路由机制重设计方案 - -**版本**: v2.0 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-17 -**状态**: 待评审 -**触发**: E2E 测试暴露 review 阶段派错 Agent(张飞被派去审查自己),根因是 Daemon 硬编码路由 -**评审**: 司马懿 - ---- - -## 1. 问题诊断 - -### 1.1 Bug 根因 - -任务生命周期中 `assignee` 只在执行阶段被设置(张飞 claim → assignee="zhangfei-dev")。到 review 阶段,`decide()` 走 Level 2:`task.assignee` 在注册列表中 → 又派给张飞。 - -### 1.2 更深层的问题 - -**Daemon 在做 AI 该做的决策。** v2.6 架构明确定义: - -| 维度 | v2.6 设计目标 | 当前实现 | -|------|-------------|---------| -| 决策者 | Agent(在黑板上自主决策) | Daemon(if-else 硬编码) | -| Daemon 角色 | 投递员(执行黑板上的决策) | 调度器(决定谁干什么) | -| 编排方式 | AI agent 在黑板上自主领活 | 配置表驱动(非 AI 判断) | - -T3-10 设计原文写着"配置表驱动非 AI 判断"——与 v2.6 核心原则矛盾。 - ---- - -## 2. 调研发现 - -### 2.1 学术前沿 - -| 来源 | 核心发现 | 对我们的价值 | -|------|---------|-------------| -| **bMAS** arXiv 2507.01701 | Control Unit(LLM 驱动)根据黑板当前内容动态选择 Agent | 路由本身可以是 LLM 调用,不是 if-else | -| **Self-Selection** arXiv 2510.01285 | 任务不显式分配,Agent 根据自己能力自主决定是否参与 | 最 AI Native 的模式,我们的演进目标 | -| **MasRouter** arXiv 2601.04861 | 根据任务复杂度动态选模型规模 + confidence 机制 | confidence 阈值 + 历史表现动态评分 | -| **AgentGate** arXiv 2604.06696 | 3B-7B 小模型做结构化路由决策 | 验证"路由可以是 AI"的可行性 | - -### 2.2 生产实践 - -| 项目 | 模式 | 启发 | -|------|------|------| -| **Microsoft Conductor**(2026.05 开源) | YAML 确定性编排 | 确定性流程 + LLM 动态路由分层混合 | -| **Azure Agent Patterns** | 5 种模式:顺序/并发/群聊/**Handoff**/Magentic | **Handoff**:Agent 完成后自己决定交接给谁 | -| **AWS 动态分派** | 事件驱动 + 上下文感知路由 | 路由变成事件,不是轮询 | -| **Claude Code Agent Teams** | Lead coordinator + context 隔离 | Lead 做分解+分配+监控,subagent 只拿相关 context | - -### 2.3 已有调研的线索 - -- architecture-v2.6.md:**"Agent 决策,Daemon 执行"**;Daemon 是投递员不是决策者 -- shared-consciousness-research.md:Control Unit 是 LLM 驱动的,不是规则路由 -- v2.6-research-01:Hermes 幻觉门控——不信任 Agent 完成声明 - ---- - -## 3. 设计原则 - -| # | 原则 | 说明 | -|---|------|------| -| P1 | 路由决策在 Agent 层,不在 Daemon 层 | "谁该做这个任务"由 Agent 自己或 LLM 决定,Daemon 只执行 | -| P2 | 当前 Agent 最清楚下一步需要谁 | 刚做完工作的人最清楚该交接给谁(Azure Handoff) | -| P3 | 路由可审计 | 每次路由决策记录到黑板,可回溯 | - ---- - -## 4. 三种路由模式 - -### 4.1 模式总览 - -``` -┌───────────────────────────────────────────────────────────┐ -│ 路由决策入口 │ -│ Dispatcher.decide(task) │ -└────────┬──────────────┬──────────────────┬────────────────┘ - │ │ │ - ┌────▼────┐ ┌────▼─────┐ ┌────────▼────────┐ - │ Mode A │ │ Mode B │ │ Mode C │ - │ LLM路由 │ │ Agent交接 │ │ Agent自主领活 │ - │(中心化) │ │(去中心化) │ │ (去中心化) │ - └────┬────┘ └────┬─────┘ └────────┬────────┘ - │ │ │ - LLM选Agent 执行者说需要谁 Agent自己来领 -``` - -### 4.2 Mode A:LLM 路由(中心化) - -**场景**:首次分配(pending → claimed)、异常升级(failed/blocked)、无明确 handoff 指令时。 - -**机制**:Daemon 调用一次轻量 LLM API,传入任务信息 + Agent 能力画像 + 负载状态,LLM 返回选择的 Agent + 理由 + 置信度。 - -**关键**:不是 spawn 一个 Agent session,是一次 ~300 token 的 API 调用(~1-2s,<¥0.01)。 - -``` -输入: 任务描述 + 6个Agent画像 + 负载 -输出: {"agent_id": "xxx", "reason": "...", "confidence": 0.9} -约束: ~200 token response, temperature=0.1 -``` - -### 4.3 Mode B:Agent 声明式交接(去中心化)⭐ 最高频 - -**场景**:Agent 完成当前阶段后,明确声明下一步需要什么。 - -**机制**:Agent 在 POST /status 时附带 `next_capability` 字段: - -```json -{ - "status": "review", - "agent": "zhangfei-dev", - "next_capability": "review", - "handoff_note": "代码已实现,请审查质量和安全性" -} -``` - -Daemon 读 `next_capability`,查 Agent 能力画像找到匹配者(排除当前执行者),直接 spawn。 - -**这是最 AI Native 的模式**——刚做完工作的人最清楚下一步需要谁。不需要 LLM 调用,0ms 延迟。 - -### 4.4 Mode C:Agent 自主领活(去中心化)— 未来演进 - -**场景**:Daemon 广播任务需求,Agent 自己决定是否 claim。 - -**当前阶段不实现**,保留演进空间。数据结构(agent_profiles、capabilities)不变,只需把"Daemon 查表派发"改为"Daemon 广播 + Agent 自己 claim"。 - -### 4.5 模式选择逻辑 - -```python -def decide(self, task, action_type=""): - # 确定性快速路径(0ms,不调 LLM) - if self._is_deterministic(task, action_type): - return self._deterministic_route(task, action_type) - - # Mode B: Agent 声明了 next_capability → 直接匹配 - if task.next_capability: - return self._match_capability(task.next_capability, - exclude={task.assignee}) - - # Mode A: 无明确 handoff → LLM 路由 - return self._llm_route(task, action_type) -``` - -**确定性快速路径**包括: -- 机械检查(L1_guardrail、format_check)→ Daemon 本地执行 -- 已有 assignee 且非生命周期流转(如 crashed → retry 同一人)→ 直接用 - ---- - -## 5. 核心组件设计 - -### 5.1 Agent 能力画像(Agent Profile) - -每个 Agent 在配置中声明自己的能力(**不是 Daemon 硬编码**): - -```yaml -# config/default.yaml → agents 段扩展 -agents: - zhangfei-dev: - capabilities: [coding, implementation, scripting] - can_review: false - max_concurrent: 1 - - simayi-challenger: - capabilities: [review, quality_check, debate] - can_review: true - max_concurrent: 2 - - guanyu-dev: - capabilities: [risk, compliance, position_check] - can_review: true - max_concurrent: 1 - - zhaoyun-data: - capabilities: [data, acquisition, cleaning, verification] - can_review: false - max_concurrent: 1 - - jiangwei-infra: - capabilities: [deploy, infrastructure, docker, vnpy] - can_review: false - max_concurrent: 1 - - pangtong-fujunshi: - capabilities: [planning, coordination, escalation, strategy] - can_review: true - is_fallback: true - max_concurrent: 3 -``` - -Daemon 启动时读取配置,写入黑板 `agent_profiles` 表。未来可演进为 Agent 自己注册。 - -### 5.2 LLM 路由器(LLMDriver) - -```python -class LLMDriver: - """bMAS Control Unit — 轻量 LLM 路由决策""" - - def __init__(self, model: str, api_base: str, api_key: str): - self.model = model - self.client = OpenAI(base_url=api_base, api_key=api_key) - - def route(self, task, agent_profiles, active_agents) -> RouteDecision: - prompt = self._build_prompt(task, agent_profiles, active_agents) - - response = self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": prompt}], - response_format={"type": "json_object"}, - max_tokens=200, - temperature=0.1, - ) - - result = json.loads(response.choices[0].message.content) - return RouteDecision( - agent_id=result["agent_id"], - reason=result["reason"], - confidence=result.get("confidence", 0.5), - ) -``` - -**Routing Prompt 模板**: - -``` -你是任务路由器。根据任务需求和 Agent 能力,选择最合适的 Agent。 - -## 当前任务 -- ID: {task_id} -- 标题: {task_title} -- 状态: {task_status} -- 描述: {task_description} -- 上一步执行者: {previous_assignee} - -## 可用 Agent -{每个Agent: ID, 能力列表, 当前负载} - -## 约束 -1. review/quality_check 不能选上一步执行者 -2. 同等能力优先选负载最低的 -3. 必须匹配任务所需能力 - -## 输出 -{"agent_id": "...", "reason": "...", "confidence": 0.0-1.0} -``` - -### 5.3 Dispatcher 重写 - -```python -class Dispatcher: - def __init__(self, config, counter): - self.counter = counter - self.agent_profiles = config.get("agent_profiles", {}) - self.llm = LLMDriver( - model=config.get("routing", {}).get("model", "zhipu/glm-5.1"), - api_base=config.get("routing", {}).get("api_base", ""), - api_key=config.get("routing", {}).get("api_key", ""), - ) - self.LOCAL_ACTIONS = {"L1_guardrail", "format_check", "file_exists_check"} - - def decide(self, task, action_type="") -> dict: - # ── 快速路径:确定性路由 ── - if action_type in self.LOCAL_ACTIONS: - return {"level": "local", "reason": "机械检查,Daemon本地执行"} - - # retry 同一人 - if action_type == "retry" and task.assignee: - return {"level": "full_agent", "agent_id": task.assignee, - "reason": "retry原执行者", "mode": "deterministic"} - - # ── Mode B: Agent 声明式交接 ── - if task.next_capability: - agent = self._match_capability(task.next_capability, - exclude={task.assignee}) - if agent: - return {"level": "full_agent", "agent_id": agent, - "reason": f"执行者handoff: 需要{task.next_capability}", - "mode": "agent_handoff"} - - # ── Mode A: LLM 路由 ── - decision = self.llm.route(task, self.agent_profiles, - self.counter.active_agents) - - # 合法性校验 - if (decision.agent_id not in self.agent_profiles - or decision.confidence < 0.7): - return {"level": "full_agent", "agent_id": "pangtong-fujunshi", - "reason": f"LLM低置信度({decision.confidence}): {decision.reason}", - "mode": "fallback"} - - return {"level": "full_agent", "agent_id": decision.agent_id, - "reason": decision.reason, "mode": "llm_route", - "confidence": decision.confidence} - - def _match_capability(self, capability, exclude=None): - """从能力画像中匹配 Agent""" - candidates = [ - aid for aid, prof in self.agent_profiles.items() - if aid not in (exclude or set()) - and capability in prof.get("capabilities", []) - ] - if not candidates: - return None - if len(candidates) == 1: - return candidates[0] - return min(candidates, key=lambda a: self.counter.active_agents.get(a, 0)) -``` - -### 5.4 assignee 语义变更 - -| 维度 | 当前 | 改为 | -|------|------|------| -| `assignee` 含义 | 任务负责人(贯穿全生命周期) | **当前阶段执行者**(随状态流转更新) | -| 新增 `previous_assignee` | 无 | 保存前一阶段执行者(用于排除和审计) | - -```python -# 状态流转时更新 -task.previous_assignee = task.assignee -task.assignee = new_agent_id -``` - ---- - -## 6. 路由审计 - -每次路由决策写入黑板 `routing_decisions` 表。 - -### 6.1 表结构 - -```sql -CREATE TABLE IF NOT EXISTS routing_decisions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - from_status TEXT, -- 前一状态 - to_status TEXT, -- 目标状态 - mode TEXT NOT NULL, -- deterministic / agent_handoff / llm_route / fallback - selected_agent TEXT NOT NULL, - previous_agent TEXT, -- 前一阶段执行者 - reason TEXT, -- 路由理由 - confidence REAL, -- LLM 置信度(Mode A 才有) - model TEXT, -- 使用的 LLM 模型(Mode A 才有) - latency_ms INTEGER, -- 路由耗时 - created_at TEXT DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id) -); - -CREATE INDEX idx_routing_task ON routing_decisions(task_id); -``` - -### 6.2 审计日志示例 - -``` -task=test-e2e-001 | pending→claimed | mode=llm_route - → zhangfei-dev (confidence=0.95, reason="编码任务匹配coding能力") - → model=zhipu/glm-5.1, latency=1200ms - -task=test-e2e-001 | working→review | mode=agent_handoff - → simayi-challenger (reason="执行者handoff: 需要review") - → latency=2ms - -task=test-e2e-001 | review→done | mode=agent_handoff - → pangtong-fujunshi (reason="审查通过,交接给协调者收尾") - → latency=1ms -``` - ---- - -## 7. 路由模型配置 - -### 7.1 后端配置 - -```yaml -# config/default.yaml 新增 -routing: - model: "zhipu/glm-5.1" # 默认路由模型 - api_base: "" # 空=用 OpenClaw Gateway - api_key: "" # 空=用 OpenClaw 默认 - confidence_threshold: 0.7 # 低于此值 fallback - max_tokens: 200 - temperature: 0.1 -``` - -### 7.2 前端配置入口 - -在现有 `ModelConfig.tsx` 页面顶部新增"路由模型"配置区域: - -``` -┌─────────────────────────────────────────────┐ -│ 🎯 路由模型(Control Unit) │ -│ ┌─────────────────────┐ ┌────┐ │ -│ │ zhipu/glm-5.1 ▾ │ │应用│ │ -│ └─────────────────────┘ └────┘ │ -│ 任务路由使用的 LLM(推荐轻量快速模型) │ -├─────────────────────────────────────────────┤ -│ 🐦 庞统 pangtong-fujunshi │ -│ 当前: zhipu/glm-5.1 │ -│ ... │ -``` - -- 模型下拉列表复用 OpenClaw 已注册的 `knownModels`(和 Agent 模型选的是同一个数据源) -- 通过后端 API `PATCH /api/config/routing-model` 保存 -- 调用 `api.setModel` 同理,走 Gateway 模型配置 - -### 7.3 API - -```python -# blackboard_routes.py 新增 -@api_route("GET", "/api/config/routing") -def get_routing_config(request): - return {"model": config.routing.model, - "confidence_threshold": config.routing.confidence_threshold} - -@api_route("PATCH", "/api/config/routing") -def set_routing_config(request): - new_model = request.json.get("model") - # 校验模型在 OpenClaw 已注册模型列表中 - config.routing.model = new_model - config.save() - return {"ok": True} -``` - ---- - -## 8. 改动清单 - -### 8.1 数据模型 - -| 变更 | 类型 | 说明 | -|------|------|------| -| 新增 `agent_profiles` 配置段 | 配置 | 每个 Agent 声明能力列表 | -| 新增 `routing` 配置段 | 配置 | 路由模型 + 参数 | -| tasks 新增 `next_capability` 字段 | DDL | Agent 声明下一步需要的能力 | -| tasks 新增 `previous_assignee` 字段 | DDL | 保存前一阶段执行者 | -| 新增 `routing_decisions` 表 | DDL | 路由审计日志 | -| `assignee` 语义变更 | 逻辑 | 从"任务负责人"改为"当前阶段执行者" | - -### 8.2 代码 - -| 文件 | 变更 | -|------|------| -| `dispatcher.py` | 重写:新增 LLMDriver + Mode A/B/C 路由逻辑 | -| `config/default.yaml` | 新增 `agent_profiles` + `routing` 配置段 | -| `blackboard_routes.py` | status API 接受 `next_capability`;新增路由配置 API | -| `ticker.py` | 使用新 dispatcher;路由结果写 routing_decisions | -| `blackboard/db.py` | 新增 routing_decisions 表 DDL;tasks 表新增字段 | -| `ModelConfig.tsx` | 新增路由模型配置区域 | - -### 8.3 不变 - -| 不变 | 原因 | -|------|------| -| 状态机(pending→claimed→working→review→done) | 状态流转语义正确 | -| Agent prompt 模板(S2) | Agent 仍按 4 步流程,只在 POST /status 时多传一个字段 | -| Spawner 逻辑 | spawn 机制不变 | -| 前端 Dashboard 核心布局 | 只在 ModelConfig 加一个区域 | - ---- - -## 9. 和现有实践的对标 - -| 实践 | 本方案对应 | -|------|----------| -| bMAS Control Unit(LLM 驱动) | Mode A: LLMDriver 实现,轻量 API 调用 | -| Azure Handoff(Agent 交接) | Mode B: next_capability + handoff_note | -| 自主选择(arXiv 2510.01285) | Mode C: 未来演进,数据结构预留 | -| MasRouter(confidence) | confidence 阈值 + fallback 机制 | -| Microsoft Conductor(确定性 + 动态混合) | 快速路径(确定性)+ LLM 路由(动态)分层 | -| 幻觉门控(Hermes) | LLM 输出合法性校验 + confidence 阈值 | -| "Agent 决策,Daemon 执行"(v2.6 原则) | Mode B 是最直接的实现:Agent 自己决定交接给谁 | - ---- - -## 10. 演进路线 - -``` -Phase 1(本次实现): Mode A + Mode B - - LLMDriver 路由(首次分配、异常场景) - - Agent 声明式交接(最高频场景) - - 路由审计表 - - 前端路由模型配置 - -Phase 2(未来): Mode C - - 同样的 agent_profiles 和 capabilities 数据结构 - - Daemon 广播需求 → Agent 自己 claim - - 迁移成本极低(数据结构不变,只改消费方式) - -Phase 3(更远): 经验驱动的路由 - - 路由审计数据反哺 LLM prompt(历史匹配成功率) - - Agent 可靠性评分(参考 MasRouter) - - 动态能力发现(Agent 完成新类型任务后自动更新画像) -``` - ---- - -## 11. 司马懿评审要点 - -请重点关注: - -1. **LLMDriver 的异常处理**:API 超时/失败时的 fallback 策略是否合理 -2. **Mode B 的安全性**:Agent 声明 `next_capability` 时是否需要校验(防恶意指定) -3. **assignee 语义变更**的影响范围:是否有其他模块依赖"assignee = 任务负责人" -4. **routing_decisions 表设计**:字段是否充分,索引是否合理 -5. **配置 API 的安全性**:修改路由模型是否需要鉴权 -6. **性能影响**:Mode A 的 ~2s 延迟在 tick cycle 中是否可接受 - ---- - -## 12. 参考 - -- bMAS: arXiv 2507.01701 — Blackboard LLM Multi-Agent System -- Self-Selection: arXiv 2510.01285 — Agent 自主选择模式 -- MasRouter: arXiv 2601.04861 — Confidence-Aware Routing -- AgentGate: arXiv 2604.06696 — 结构化路由引擎 -- Microsoft Conductor: github.com/microsoft/conductor — 确定性编排 -- Azure Agent Patterns: learn.microsoft.com — Handoff 模式 -- v2.6 调研报告: docs/research/shared-consciousness-research.md -- v2.6 架构设计: docs/design/architecture-v2.6.md -- T3-10 调度判据: docs/design/topic3-challenge-review-proposal.md §5.4 diff --git a/docs/design/architecture-v2.6.md b/docs/design/architecture-v2.6.md deleted file mode 100644 index bb23a0b..0000000 --- a/docs/design/architecture-v2.6.md +++ /dev/null @@ -1,2070 +0,0 @@ -# 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四要素优化写法 | -| v2.6.6 | 2026-05-15 | 课题7+9设计决策:四种交互模式(沉浸观察/轻触确认/即时对话/被动通知)、推送级别分级(🔴🟡🟢🔵)、三层信息架构(L1一眼/L2看板/L3详情)、5页Dashboard(任务看板/全局监控/产出档案/系统配置/AI Briefing) | -| v2.6.7 | 2026-05-15 | 课题6设计决策:经验三种载体(Memory→Skill→Rule)、两级蒸馏(实时一级+周期二级)、验证(格式+内容+实用性)、四层架构反哺(Daemon注入+被动参考)、experiences表、Skill生命周期(draft→active→deprecated) | -| v2.6.8 | 2026-05-16 | 课题10结论:不需要复杂压缩/摘要、续杯问题已由课题2+4解决、单黑板单Daemon多任务并行、Context预算35-60K远小于128K、用户级多项目待课题11解决 | -| v2.6.9 | 2026-05-16 | 7项目调研结论纳入:Guardrail验证脚本层(Aider)、对抗性审计映射(claude-goal→L1/L2/L3)、防偏离三防线+Runaway Guard、双重Hook(OpenClaw+moziplus)、Shadow Checkpoint(Cline)、潜在课题(模型策略/信任分级/Worktree隔离)、Blackboard Map按需触发 | -| v2.6.9.1 | 2026-05-16 | 司马懿评审回应:PRD目标不改+架构记录差距、blocked状态保留+设计完整、verification_commands安全模型明确(exec approvals)、Daemon逻辑健康自检纳入§14 | -| v2.6.10 | 2026-05-16 | 课题11多项目:方案C单Daemon多数据库 + per-project线程并发 + ActiveAgentCounter异步计数器 + session命名规则(sequential复用/parallel独立) + 三层资源控制 + 项目归档/删除安全流程 | -| v2.6.11 | 2026-05-16 | 课题7+9方案完成:v1.0已有11个Tab全部保留(仅任务看板重设计)、Checkpoint三种交互、推送SSE+降级轮询、AI Briefing日报/周报、项目切换器、推送通知中心 | -| v2.6.12 | 2026-05-16 | 全量遗留清理(51项→0) + 课题3四项新方案(对抗辩论/评审Schema/反驳权/调度判据) + 司马懿评审通过(comments表comment_type) + 工具链课题初始化 + 技术架构/部署架构重写(v2.6.2) | -| v2.8 | 2026-05-18 | v2.8 状态增强:新增 paused/escalated/waiting_human 3状态(共11个)+ 归档机制 + M3 Checkpoint/Artifact + 前端两行筛选栏 + 卡片快捷按钮 | - -### 课题 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~~ | ✅ 课题4解决:executor.md 步骤2(scope_declaration) | -| ~~T1-6~~ | ~~truths 验证的具体实现~~ | ~~课题 4~~ | ✅ 课题4解决:executor.md 步骤1+Plan Checker(plan_check.yaml) | -| ~~T1-7~~ | ~~outputs attempt_number 过滤规则~~ | ~~课题 4~~ | ✅ 课题4解决:executor.md 步骤4(output提交)+Daemon build_bootstrap() | -| ~~T1-8~~ | ~~状态机细化(review 轮次、sub_status)~~ | ~~课题 3~~ | ✅ 课题3解决:§9.7状态机对齐 | -| ~~T2-3~~ | ~~blackboard.py 写操作自动写 inbox JSONL~~ | ~~Phase 1~~ | ✅ 开发实现 | -| ~~T2-4~~ | ~~Tick Loop + Inbox JSONL Watcher~~ | ~~Phase 1~~ | ✅ 开发实现 | -| ~~T2-5~~ | ~~L2/L3 分层读取 API~~ | ~~Phase 2~~ | ✅ 开发实现 | -| ~~T2-6~~ | ~~仓库级上下文(Agent Context Pack)~~ | ~~Phase 3~~ | ✅ P3 保留,不合并不关闭 | -| ~~T2-7~~ | ~~Handoff Comment 的 Skill 引导~~ | ~~Phase 2~~ | ✅ 课题4解决:executor.md 步骤5写死 | -| ~~T2-8~~ | ~~Handoff Comment 的 Skill 解析规则~~ | ~~课题 4~~ | ✅ 课题4解决:executor.md "前序信息"部分+schemas/handoff.schema.json | - -### 课题 4 Skill 体系设计 TODO - -> **课题4设计决策**:关键操作不靠 Skill 被动触发,走引擎注入(L2 prompt_templates)。以下 28 项全部通过 prompt_templates/ + review_protocols/ + schemas/ 覆盖。此表保留作为设计推导留痕。 -> -> **覆盖映射**: -> - S-01~S-12(执行者操作)→ prompt_templates/executor.md -> - S-13~S-20(审查者操作)→ prompt_templates/reviewer.md + review_protocols/*.yaml -> - S-21~S-27(庞统操作)→ prompt_templates/planner.md / adjudicator.md -> - S-28(L1 消息构建)→ Daemon build_bootstrap() 代码 -> -> 详见课题4方案:`docs/design/topic4-decomposition-skill-proposal.md` D4-6/D4-7 - -| # | Skill 内容 | 适用角色 | Phase | 来源 | 覆盖方式 | -|---|----------|---------|-------|------|----------| -| S-01 | blackboard.py CLI 使用手册 | 所有 Agent | P1 | 课题1 §5.2 | ✅ executor.md 各步骤中写死 | -| S-02 | L1→L2/L3 按需读取判断 | 所有 Agent | P2 | 课题2 §4.4 | ✅ executor.md "可选参考"部分 | -| S-03 | 写 Handoff Comment(格式+时机) | 所有 Agent | P1 | 课题2 §5.1 | ✅ executor.md 步骤5 | -| S-04 | 读 Handoff Comment(利用上一个 Agent 交接) | 所有 Agent | P2 | 课题2 §5.1 | ✅ executor.md "前序信息"部分 | -| S-05 | 写 observation(时机+severity 格式) | 所有 Agent | P2 | 课题1 §4.7 | ✅ executor.md 步骤3 | -| S-06 | 写 decision(时机+rationale 格式) | 所有 Agent | P2 | 课题1 §9.4 | ✅ executor.md 步骤2 | -| S-07 | 写 output 的 Schema 约束 | 所有 Agent | P1 | 课题2 §3.7 | ✅ executor.md 步骤4 + schemas/ | -| S-08 | Guardrail 打回时的处理流程 | 执行者 | P2 | 课题3 §9.3 | ✅ executor.md + guardrails.yaml | -| S-09 | @mention 使用规范 | 所有 Agent | P2 | 课题1 §5.2 | ✅ executor.md 步骤3 | -| S-10 | claim 后写 scope_declaration(格式+内容) | 执行者 | P1 | 课题1 §4.7 | ✅ executor.md 步骤2(含JSON格式) | -| S-11 | must_haves 三件套自检(truths/artifacts/constraints) | 执行者 | P1 | 课题1 §9 | ✅ executor.md 步骤1 | -| S-12 | 收到 review needs_revision 的反驳流程(ACCEPT/REJECT/PARTIAL) | 执行者 | P2 | 课题3 §9.5 | ✅ rebuttal.md | -| S-13 | 审查者 Investigation Protocol 五阶段执行 | 审查者 | P2 | 课题3 §9.4 | ✅ reviewer.md 五步骤 | -| S-14 | 多视角审查方法(代码/方案/分析三套视角集) | 审查者 | P2 | 课题3 §9.4 | ✅ reviewer.md + review_protocols/ | -| S-15 | 写 review 的 Schema 约束(verdict+evidence) | 审查者 | P2 | 课题3 §9.6 | ✅ reviewer.md + schemas/ | -| S-16 | 信心度自评(confidence 打分标准) | 审查者 | P2 | 课题3 §9.6 | ✅ reviewer.md 步骤5(自审) | -| S-17 | 收到反驳(REJECT)后的评估方法 | 审查者 | P2 | 课题3 §9.5 | ✅ reviewer.md(反方回应处理) | -| S-18 | plan_review 协议(假设提取/pre-mortem/依赖审计) | 审查者 | P2 | 课题3 §9.4 | ✅ review_protocols/plan_review.yaml | -| S-19 | output_review 协议(需求追踪/缺口分析) | 审查者 | P2 | 课题3 §9.4 | ✅ review_protocols/output_review.yaml | -| S-20 | analysis_review 协议(逻辑跳跃/数据来源) | 审查者 | P2 | 课题3 §9.4 | ✅ review_protocols/analysis_review.yaml | -| S-21 | 创建任务(truths/artifacts/constraints 定义) | 庞统 | P1 | 课题1 §9 | ✅ planner.md | -| S-22 | 风险等级自动判断(task_type→risk_level) | 庞统 | P1 | 课题3 §9.2 | ✅ planner.md(映射规则) | -| S-23 | 挑战者选择(按任务类型选挑战者) | 庞统 | P2 | 课题3 §9.10 | ✅ planner.md(选择表) | -| S-24 | 对抗辩论裁决方法 | 庞统 | P3 | 课题3 §9.10 | ✅ adjudicator.md | -| S-25 | escalated 任务的用户沟通 | 庞统 | P3 | 课题3 §9.7 | ✅ planner.md / adjudicator.md | -| S-26 | confidence 低时的升级判断 | 庞统 | P2 | 课题3 T3-5 | ✅ reviewer.md(< 0.7 升级庞统) | -| S-27 | 任务拆解方法(依赖声明/子任务创建) | 庞统 | P2 | 课题1 §5.1 | ✅ planner.md(四步+组件库+PlanChecker) | -| S-28 | L1 消息构建逻辑 | 庞统/Daemon | P1 | 课题2 §4.4 | ✅ Daemon build_bootstrap() 代码 | -| ~~T2-9~~ | ~~inbox 并发写入的竞态处理~~ | ~~Phase 1 验证~~ | ✅ 已解决:truncate(清空不删除)替代 unlink(删除),实测 200 次并发写入 0 丢失。演进方向:向 webhook 实时化走 | -| ~~T2-10~~ | ~~inbox 文件的 rotate/truncate 策略~~ | ~~Phase 2~~ | ✅ 关闭:Inbox 每秒被清空,正常运行不膨胀。Daemon 崩溃积压也仅 ~200KB/1000条 | -| ~~T2-11~~ | ~~Tick 频率 30s vs 60s 的性能验证~~ | ~~开发后实测~~ | ✅ 开发后实测 | -| ~~T2-12~~ | ~~CLI Schema 校验的 schemas/ 目录定义~~ | ~~Phase 1~~ | ✅ 开发实现 | - -### 课题 3 遗留 TODO - -| # | 待解决事项 | 归属 | 说明 | -|---|----------|------|------| -| ~~T3-1~~ | ~~reviews 表的 CLI 命令~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-2~~ | ~~Guardrail YAML 解析 + 执行引擎~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-3~~ | ~~对抗辩论模式的具体黑板协议~~ | ~~Phase 3~~ | ✅ 方案已定(topic3 §5.1),仅用户明确要求时启用 | -| ~~T3-4~~ | ~~挑战者池的选择策略~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-5~~ | ~~confidence 低于阈值自动升级~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-6~~ | ~~评审详情文件的 Schema 定义~~ | ~~Phase 2~~ | ✅ 方案已定(topic3 §5.2),待司马懿评审确认 | -| ~~T3-7~~ | ~~low 风险任务 Guardrail 自动通过~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-8~~ | ~~Review Protocol 模板文件编写~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-9~~ | ~~反驳权 Daemon 流控~~ | ~~Phase 2~~ | ✅ 方案已定(topic3 §5.3),轮次上限兜底不设超时 | -| ~~T3-10~~ | ~~Agent 调度判据~~ | ~~Phase 2~~ | ✅ 方案已定(topic3 §5.4),配置表驱动非 AI 判断 | - ---- - -## 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 --session-id ` spawn 隔离 session,执行完 daemon 存档 jsonl 并清理 sessions.json。 -5. **双入口,对等地位** - Agent 对话和 Dashboard 是两个对等入口,共享同一套黑板数据。Dashboard 是 AI Native 的可视化入口,不是降级的监控面板。 - ---- - -## 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 --session-id - │ 执行完 → 存档 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 - -```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, - - -- 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' - comment_type TEXT NOT NULL DEFAULT 'general', -- general/handoff/observation/rebuttal/rebuttal_response/debate_argument/debate_rebuttal/debate_judgment - 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), - CHECK (comment_type IN ('general', 'handoff', 'observation', 'rebuttal', 'rebuttal_response', 'debate_argument', 'debate_rebuttal', 'debate_judgment')) -); - -CREATE INDEX IF NOT EXISTS idx_comments_task ON comments(task_id); -CREATE INDEX IF NOT EXISTS idx_comments_type ON comments(task_id, comment_type); -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' -``` - -**连接配置:** - -```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 状态机(v2.8:11 个状态) - -``` -pending → claimed → working → review → done - ↑ │ ↕ ├→ blocked ──┘ ├→ failed - │ │ paused ├→ paused └→ cancelled - └─────────┘ ├→ escalated - └→ waiting_human ← M3 Checkpoint - - (review→pending: 审核不通过,打回重做) - (review→waiting_human: 审查阶段需人工裁定) - (blocked→pending: 阻塞解除) - (blocked→escalated: 阻塞升级求助) - (failed→pending: 重试) - (failed→escalated: 失败升级求助) - (escalated→working: 用户介入后恢复) - (waiting_human→working: Checkpoint reject) - (waiting_human→done: verify Checkpoint approve) -``` - -**v2.8 变更:** v2.6 有 8 个状态,v2.8 新增 3 个: paused(用户暂停)、escalated(Agent 升级求助)、waiting_human(Checkpoint 人工确认)。 - -| 状态 | 含义 | 谁触发 | 调度 | 聚合 | -|------|------|--------|------|------| -| pending | 待领取 | 任何 Agent 或用户创建 | ✅ 正常调度 | ✅ 参与 | -| claimed | 已认领 | Agent 自己或被指派 | ✅ 超时回收 | ✅ 参与 | -| working | 执行中 | Agent | ✅ 超时监控 | ✅ 参与 | -| review | 待审核 | Agent 完成产出 | ✅ review 调度 | ✅ 参与 | -| **paused** | **已暂停** | **用户主动** | **❌ 跳过** | **❌ 不参与** | -| **escalated** | **已升级** | **Agent 求助** | **❌ 跳过** | **✅ 视同 working** | -| **waiting_human** | **等人工** | **Agent Checkpoint** | **❌ 跳过** | **✅ 视同 working** | -| done | 完成 | 审核通过 | 终态 | ✅ 参与 | -| failed | 失败 | Agent 或 daemon | ❌ 不调度 | ✅ 参与 | -| blocked | 需要帮助 | Agent | ❌ 不调度 | ✅ 参与 | -| cancelled | 取消 | 用户 | 终态 | ❌ 不参与 | - -**聚合规则(v2.8):** 优先级 escalated > waiting_human > review > working/claimed > pending > failed > blocked。paused/cancelled 不参与。全部 done → done。 - -**完整合法流转矩阵:** - -```python -VALID_TRANSITIONS = { - "pending": {"claimed", "cancelled"}, - "claimed": {"working", "paused", "pending", "cancelled"}, - "working": {"review", "blocked", "failed", "paused", "escalated", "waiting_human", "cancelled"}, - "paused": {"working", "cancelled"}, - "review": {"done", "pending", "failed", "escalated", "waiting_human", "cancelled"}, - "blocked": {"pending", "escalated", "cancelled"}, - "failed": {"pending", "escalated", "cancelled"}, - "escalated": {"working", "pending", "cancelled"}, - "waiting_human": {"working", "done", "cancelled"}, - "done": set(), # 终态 - "cancelled": set(), # 终态 -} -``` - -**字段迁移(v2.8):** `escalated` 从布尔字段改为 status 判断。DB 保留 `escalated INTEGER` 做向后兼容,前端/Ticker 改用 `task.status == 'escalated'`。 - -**归档机制(v2.8):** tasks 表新增 `archived INTEGER DEFAULT 0`、`archived_at TEXT`。归档不改变状态,只影响前端显示(默认不显示已归档任务)。 - -**Checkpoint 机制(M3):** Agent 执行到需人工确认的节点 → 创建 checkpoint + 设置 waiting_human → 用户 approve/reject → 自动推进状态。详见 `docs/design/v2.8-state-enhancement.md`。 - -### 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 请求用户决定 - -不需要复杂的分布式共识--职责分工已经自然避免了大部分冲突。 - -### 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/` 目录): - -```json -// 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 使用方式**: -```bash -# 结构化操作: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 --session-id ` | 轻量(隔离 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/.jsonl`,`chorus messages --agent --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` 模式): - -```python -# 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.write_text('') # truncate:清空内容不删除文件,避免并发追加写入时文件不存在 - for line in lines: - msg = json.loads(line) - await handle_event(msg['event'], msg['task_id'], msg['from']) -``` - -**Daemon 主循环**: - -```python -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 \ - --message "请检查黑板 task-001..." - ↓ -2. Agent 执行 - - 读黑板(SQLite 查询) - - 做任务(编码/审核/数据分析) - - 写回黑板(产出、评论、状态更新) - ↓ -3. Agent 退出(自然结束) - ↓ -4. Daemon 清理 - - mv .jsonl → task-001/archive/ - - mv .trajectory.jsonl → task-001/archive/ - - 编辑 sessions.json 删除该 session 记录 -``` - -**技术验证结论:** -- `openclaw agent --agent --session-id ` 可创建完全隔离的 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 模板**: - -```python -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 ` 的返回值(阻塞调用)。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 命令操作黑板: - -```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 - -# 写 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 技术实现 - -```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 ` 创建隔离 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` 自动设 `high`,`research` 自动设 `research`。 - -### 9.3 三阶段审查流程 - -> **参考实践**:superpowers(implementer → spec reviewer → code quality reviewer串行)、TradingAgents(Bull 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 Critic**:Investigation Protocol 分 Phase(预判→验证→多视角→缺口分析→自审),不同 artifact type 自动切换视角 -> - **superpowers spec-reviewer**:prompt 注入对抗性指令("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 列表,每个有 weight(critical/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 表) - -> **参考实践**: -> - **Hermes**:Comment 的 metadata JSON 承载结构化数据 -> - **SWE-agent**:Trajectory 线性追加,不修改历史 -> - **GitHub PR Review**:APPROVE / REQUEST_CHANGES / COMMENT 三种 verdict - -**设计原则**:追加写入不修改历史(SWE-agent),黑板索引+文件详情(课题2),结构化 verdict(GitHub PR Review)。 - -```sql -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 协商流程与状态机对齐(v2.8 更新) - -**现有状态机(v2.8,11个状态)**:`pending → claimed → working → review → done` - -| 状态转换 | 触发条件 | 对应阶段 | -|---------|---------|--------| -| working → working | 方案审查 needs_revision | 阶段 1 | -| working → review | 写 output + Guardrail 通过 | 阶段 2→3 | -| working → working | Guardrail rejected(自动打回) | 阶段 2 | -| working → escalated | Agent 遇到无法处理的问题 | 任意阶段 | -| working → waiting_human | Agent 触发 Checkpoint | 任意阶段 | -| review → done | 产出审查 approved | 阶段 3 | -| review → working | 产出审查 needs_revision | 阶段 3 | -| review → escalated | 超轮次升级用户 | 阶段 3 | -| review → waiting_human | 审查阶段需人工裁定 | 阶段 3 | -| waiting_human → working | Checkpoint reject(驳回) | 阶段 3 | -| waiting_human → done | verify Checkpoint approve(通过) | 阶段 3 | - -**v2.8 变更**: `escalated` 从布尔标志改为独立状态(`status="escalated"`),不再使用 `review + escalated flag` 模式。超轮次时直接进入 `escalated` 状态,通知用户裁定。用户裁定后恢复到 `working` 或 `pending`。 - - -low 风险任务:working → [Guardrail 自动检查] → done(跳过 review 状态) -research 任务:working → [庞统确认] → review → done - -### 9.8 声明式 Guardrail 配置 - -> **参考实践**:OpenAI Agent SDK `@output_guardrail` 装饰器、v1.0 M4 Guard、SonarQube rule_id 模式 - -```yaml -# 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 直接执行 - -> **参考实践**: -> - **superpowers**:Implementer/Spec Reviewer/Code Quality Reviewer 都是 Task tool dispatch 的 subagent,各自独立 prompt 和模型 -> - **oh-my-claudecode**:Critic 被禁止 Write/Edit 工具(disallowedTools),角色隔离 -> - **open-multi-agent**:TaskQueue 维护 agent pool,scheduler 按 capability-match 分配 - -**判据**: - -| 问题 | Full Agent | Subagent | Daemon 直接执行 | -|------|-----------|---------|----------------| -| 需要独立身份/人格? | ✅ | ❌ | ❌ | -| 需要 Agent 专属工具? | ✅ | ❌ 通用工具 | 不需要 AI | -| 任务复杂度 | 编码/审查/调研/决策 | 单一检查/快速评估 | 格式校验/文件检查 | -| 需要写黑板? | ✅ | ❌ 只返回 pass/fail | 只改状态 | -| 需要多轮交互? | ✅ | ❌ 一次性 | 一次性 | - -**场景映射**: - -| 场景 | 方式 | OpenClaw API | 理由 | -|------|------|-------------|------| -| 执行者编码/数据/部署 | Full Agent | `openclaw agent --agent ` | 需要身份+专属工具 | -| 挑战者审查 | Full Agent | `openclaw agent --agent simayi-challenger` | 需要质量守门人角色+多轮 | -| 执行者反驳 | Full Agent(原 Agent) | `openclaw agent --agent <原执行者>` | 需要原执行者身份 | -| 庞统规划/裁决 | Full Agent(隔离session) | `openclaw agent --agent pangtong-fujunshi --session-id ` | 避免主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. **审核流程** - 可通过黑板评论 + 状态机实现 - ---- - -## 11. 上下文管理策略(课题10结论) - -> **核心结论**:不需要复杂的压缩/摘要机制。课题2+4的设计已覆盖上下文管理的主要场景。 - -### 11.1 黑板信息量测算 - -| 信息类型 | 单个任务估算 | -|---------|------------| -| 任务基础信息(tasks 表) | ~50-60 tokens | -| 10 条评论(comments 表) | ~300-500 tokens | -| 5 个产出摘要(outputs 表) | ~150-250 tokens | -| 10 条事件日志(events 表) | ~150-250 tokens | -| 3 条决策记录(decisions 表) | ~150-200 tokens | -| **单个任务全量** | **~1000-1500 tokens** | - -即使 10 个任务全量注入也仅 ~1-1.5 万 tokens,远小于 128K context window。 - -### 11.2 三个场景的结论 - -**场景1:续杯时上下文膨胀** - -已在课题2+4中解决: -- 黑板保留完整讨论链(comments/decisions/observations),新 Agent 随时可读 -- 课题4 D4-7 三段式注入:操作规范 + 任务上下文 + 前序信息(depends_on 产出) -- 比 GSD Wave Execution 更优——GSD 给每个 executor 全新 context 但丢弃思考过程,我们保留在黑板中 - -**场景2:多任务并行** - -所有优秀实践的共识:单黑板、单 Daemon、多任务并行调度。 -- open-multi-agent:一个 TaskQueue,事件驱动,无依赖任务自然并行 -- Hermes:一个 Dispatcher + 一个 SQLite,60s tick 扫描 -- GSD:一个 orchestrator,同 wave 并行,跨 wave 串行 - -moziplus 同此模式:一个 SQLite 黑板、一个 Daemon 进程、Tick 扫描可推进的任务并行 spawn。 -每个 Agent spawn 时只注入自己任务的信息(课题4 D4-7 按角色注入),不受其他任务干扰。 - -**⚠️ 待解决**:用户级多任务(跨项目/跨域)需要项目级隔离机制,与课题11一起设计。 - -**场景3:Agent 自主决定读 L3** - -在 L2 bootstrap 末尾注入"可选参考"提示(课题4 D4-11 已在示例中体现): -``` -═══ 可选参考 ═══ -根据任务需要,你可以用 read 加载以下 Skill: -- 编码:~/.openclaw/skills/coding-implementation/SKILL.md -- 回测:~/.openclaw/skills/quant-backtest/SKILL.md -只在需要时加载,不需要全部读取。 -``` - -未来增强:SkillRouter 检索 + OpenClaw 动态关键词触发(见课题12 SkillRouter 调研)。 - -### 11.3 Context 预算分配 - -| 组件 | 预算 | 说明 | -|------|------|------| -| System Prompt + SOUL.md + IDENTITY.md | ~3000-5000 | 固定开销(L0+L1) | -| L2 引擎注入(操作规范 + 任务上下文) | ~1000-2000 | 每次 spawn 强制注入 | -| L2 历史经验提醒 | ~150 | 最多 3 条 experience | -| L3 可选参考 | ~100 | Skill 列表提示 | -| 工作空间(Agent 思考+输出) | ~30000-50000 | 预留给 Agent | -| **总计** | **~35K-60K** | 远小于 128K,安全 | - -### 11.4 设计原则 - -1. 黑板是索引(做什么 + 在哪找),不是仓库(详细内容) -2. 产出物在文件中,黑板只存路径 -3. Agent spawn 时传最小充分上下文(L2),按需获取更多(L3) -4. 不做复杂压缩——信息量本身就很小 - ---- - -## 12. 开发策略(v2.6) - -> **核心原则:不分阶段,不妥协,直奔 AI Native。** 每个部分设计到清楚为止。 - -### 12.1 设计推进方式 - -按课题逐个推进。课题之间允许并行,不强求串行。每个课题设计清楚就定稿。 - -**已完成设计的课题**: -- ✅ 课题1:三层执行模型(v2.6.2) -- ✅ 课题2:事件驱动 + Inbox JSONL(v2.6.3) -- ✅ 课题3:挑战/评审体系(v2.6.4) -- ✅ 课题4:拆解 + 上下文架构(v2.6.5) -- ✅ 课题6:经验沉淀闭环(v2.6.7) -- ✅ 课题7+9:交互模式 + Dashboard(v2.6.6 → 方案文档 v2.6.11) -- ✅ 课题11:多项目支持(v2.6.10) - -### 待推进课题 - -| 课题 | 内容 | 方向 | 状态 | -|------|------|------|------| -| 课题7 | AI Native 人机交互 | 4种交互模式+推送4级+信息3层+双入口对等+Checkpoint交互+AI Briefing | 📐 方案完成 | -| 课题9 | Dashboard 设计 | v1.0已有11个Tab全部保留,任务看板重设计,SSE实时推送,新增AI Briefing Tab | 📐 方案完成 | -| 课题10 | 上下文管理 | — | ⏸️ 暂缓 | -| 课题11 | 用户级多项目 | 方案C:单Daemon多数据库 + per-project线程并发 + 全局LLM信号量 + per-agent互斥锁(借鉴open-multi-agent AgentPool) | 📐 方案完成(v2) | -| 节点级模型策略 | Aider architect/editor/weak 三层分工 | 审查节点用强模型、执行节点用快模型,成本与质量平衡 | P2 | -| 信任分级系统 | DeepSeek-TUI Plan/Agent/YOLO 三级 | Dashboard 按信任等级展示不同操作按钮 | P2 | -| Worktree 项目隔离 | Superset + Claude Squad | 同项目内多Agent并行改代码的物理隔离 | P1 | -| Blackboard Map | Aider Repo Map | 黑板结构化索引,Agent按任务ID只读关联子图,不全量读取 | P3(触发条件:单任务>5K tokens 或并行>15任务) | -| 工具链集成 | PRD C10 | DevOps工具链(代码管理/CI/CD/测试/沙箱/运维)集成到v2.0 | 📋 信息收集(`docs/design/toolchain-proposal.md`) | - -### 司马懿 v2.6.9 评审结论与回应 - -**评分**: 55/70。最高:上下文管理 9/10。最低:自主协作能力 + 异常自愈 7/10。 - -**3 个评审意见的回应**: - -| # | 司马懿意见 | 回应 | 处理 | -|---|-----------|------|------| -| 1 | PRD §2.1 B3"共享意识"与架构有差距,改PRD对齐 | PRD 目标正确不改。当前架构已基本实现共享意识(黑板+CLI读写+@mention+Handoff),唯一差距是 L2 prompt 需要告诉 Agent"你可以查全局" | 在 §15.1 借鉴4 的 prompt_template 中加入全局查询提示 | -| 2 | blocked 状态转换图不完整 + mentions/experience_tags 风格不统一 | blocked 定义完整(§3.3 转换矩阵含 blocked,§6.2 场景清晰:Agent执行中被卡→blocked→他人帮忙→pending)。mentions 做通知路由(轻查询),experience_tags 做统计分析(重查询),场景不同不需要统一格式 | 不修改 | -| 3 | verification_commands 安全性 | Agent 已有 exec 权限,verification_commands 走 Daemon exec,由 OpenClaw exec approvals 机制(allowlist+approval)保障安全,不引入新攻击面。参考:Claude Code 同模型、Aider 直接信任、NVIDIA 深度防御指南 | 在 §15.1 借鉴1 明确安全模型 | - -**采纳的改进建议**: -- ✅ Daemon 逻辑健康自检(连续 N tick 无变更则告警)→ 纳入 §14 风险缓解 -- ✅ PRD 与架构对齐描述 → 在架构文档中记录当前状态而非修改 PRD -- 📋 多项目 scope → 课题11 设计 -- 📋 AI Retry 基本版 → Phase 1 考虑 -- 📋 SkillRouter → 课题12 - -### 已完成调研 - -- ✅ 7 个新项目调研(Claude Squad / Superset Terminal / OpenCode / claude-goal / Cline / Aider / DeepSeek-TUI) - - 综合报告:`docs/research/research-seven-projects-synthesis.md` - - 详细报告:`docs/research/research-batch{1,2,3}-*.md` - -### 12.2 开发启动条件 - -所有核心课题设计完成后启动开发。开发顺序由设计依赖关系决定,不由阶段划分决定。 - -### 12.3 核心依赖链(设计 → 开发) - -``` -黑板基础设施(SQLite + CLI + Daemon) - → 事件驱动(Inbox + 依赖推进) - → Agent 交互(Handoff + 评论 + Observation) - → 审查体系(Review + Guardrail + 反驳) - → 智能化(AI规划 + 自主领活 + 经验沉淀) - → Dashboard 前端 -``` - -每层依赖前一层,但不等所有层设计完才开发。前层设计清楚即可开发。 - ---- - -## 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 类型任务替代 | -| Daemon 逻辑死循环 | 中 | Daemon 逻辑健康自检:连续 N tick(默认 20)无任何任务状态变更则写 observation 告警 + 通知用户。PM2 只能检测进程崩溃,逻辑死循环需应用层检测 | - ---- - -## 15. 7项目调研结论与设计借鉴 - -> **调研日期**: 2026-05-16 -> **调研范围**: Claude Squad / Superset Terminal / OpenCode / claude-goal / Cline / Aider / DeepSeek-TUI -> **详细报告**: `docs/research/research-seven-projects-synthesis.md` - -### 15.1 已纳入设计的借鉴点 - -#### 借鉴1:Guardrail 验证脚本层(纳入课题3) - -**来源**: Aider lint→test→commit 质量闭环 - -**当前设计**(topic3 方案 阶段2): -``` -L1 机械检查(文件存在、JSON格式)→ 不通过直接打回 -L2 轻量 AI 检查(Schema校验)→ 不通过写 observation -L3 安全红线(tripwire)→ 立即中断 -``` - -**增强后**:L1 增加验证脚本层 -``` -L1 机械检查 + 验证脚本 - ├── 文件存在性检查(原有) - ├── JSON 格式检查(原有) - └── 🆕 verification_commands 执行(新增) - ├── 任务 Plan 中声明的验证命令(如 pytest, flake8, mypy) - ├── 命令 exit 0 = 通过,非 0 = 打回 - └── stdout/stderr 附在打回原因中 -``` - -**黑板 Schema 变更**:tasks 表新增 `verification_commands` JSON 字段 -```json -{ - "verification_commands": [ - {"cmd": "pytest tests/test_foo.py -x", "description": "单元测试必须通过"}, - {"cmd": "flake8 src/", "description": "代码规范检查"} - ] -} -``` - -**触发时机**:Daemon 检测到 output 写入 → 执行 Guardrail → L1 验证脚本 → 脚本失败则自动打回。 - -**安全模型**:verification_commands 通过 Daemon 调用 `exec`,走 OpenClaw exec approvals 机制(allowlist + approval)。Agent 已有 exec 权限,verification_commands 不引入新的攻击面。参考:Claude Code 同模型(Agent 可执行命令,宿主机 exec approvals 拦截危险操作)、Aider 直接信任、NVIDIA 深度防御指南。 - -> 参考实践:Aider 的 lint→test→commit 是原子操作——验证不通过就不 commit。我们的 Guardrail 也是原子门控——验证不通过就不进入 review。 - -#### 借鉴2:对抗性审计映射(纳入课题3) - -**来源**: claude-goal 三种审计模式(none/adversarial/double) - -**映射到 Guardrail L1/L2/L3**: - -| claude-goal 模式 | moziplus Guardrail | 触发条件 | 行为 | -|-----------------|-------------------|---------|------| -| none(跳过审计) | L1 only(机械+脚本) | low 风险任务 | 自动检查通过即完成 | -| adversarial(对抗审计) | L1 + L2(AI审查) | standard 风险任务 | 一轮 AI 审查,通过/打回 | -| double(双重审计) | L1 + L2 + 反驳权 | high 风险任务 | 双轮审查 + 执行者可反驳 + 最多5轮 | - -> claude-goal 的完成审计 6 步流程(还原交付物→对照表→检查证据→找缺失→继续/完成)对 Output Guard 的 AI 审查层有参考价值,但不硬编码步骤——AI 审查者按 Skill 指导灵活执行。 - -#### 借鉴3:防偏离改进(纳入课题3+4) - -**来源**: claude-goal Stop Hook + 完成审计 - -**v2.0 的防偏离三层防线**: - -| 防线 | claude-goal 机制 | moziplus v2.0 对应 | 实现方式 | -|------|-----------------|-------------------|---------| -| 每轮重新注入目标 | Stop Hook 注入 `` | L2 引擎注入任务上下文 | Daemon spawn 时注入 truths + acceptance_criteria | -| 不信任 Agent 自述 | 完成审计 6 步(查证据) | Output Guard + Scope Guard | L1 验证脚本 + L2 AI 审查(prompt→artifact 对照) | -| Runaway Guard | 500 次续杯上限 | ❌ 当前缺失 | 新增:每个任务最大 tick 上限(配置项 `max_ticks`,默认 100) | - -**新增设计**:tasks 表增加 `max_ticks` 字段(默认 100)。Daemon 每次 tick 处理某任务时递增 `tick_count`,超过 `max_ticks` 时: -1. 自动暂停任务 -2. 写入 system observation:“任务超过最大 tick 上限,可能陷入循环” -3. 通知用户决定继续/取消 - -#### 借鉴4:双重 Hook 机制(纳入 Daemon 设计) - -**来源**: claude-goal 的 Claude Code Stop Hook + mozi v1 的 HookRegistry - -**v2.0 双重 Hook 架构**: - -``` -┌─────────────────────────────────────────┐ -│ Layer 1: OpenClaw Hook(系统级) │ -│ • Heartbeat(定时轮询,读 HEARTBEAT.md) │ -│ • Webhook(HTTP 入口,外部事件触发) │ -│ • Cron(定时任务) │ -│ → 触发 Agent session 内行为 │ -└─────────────────────────────────────────┘ - ↓ 事件到达 -┌─────────────────────────────────────────┐ -│ Layer 2: moziplus Hook(编排级) │ -│ • NODE_AFTER_EXECUTE → 触发 Guardrail │ -│ • NODE_ON_SUCCESS → 触发经验沉淀 │ -│ • TASK_AFTER_COMPLETE → 触发 Scope Guard │ -│ • CONCLUSION_AFTER_PARSE → 写入黑板 │ -│ → 触发编排层业务逻辑 │ -└─────────────────────────────────────────┘ -``` - -**mozi v1 已有 HookRegistry**(20 个 hook 点,覆盖任务/节点/结论/路由全生命周期),v2.0 继承并精简: -- v1 的 HookPoint 枚举 → v2 的 Daemon event handler -- v1 的 priority + stop_propagation → v2 保留(Hook 链可控) -- v1 的两级失败隔离(关键型暂停/辅助型跳过)→ v2 保留 -- 新增:Guardrail 验证脚本作为 NODE_AFTER_EXECUTE 的默认 Hook - -**与 claude-goal 的对比**: -| 维度 | claude-goal | moziplus v2.0 | -|------|-----------|-------------| -| Hook 注册位置 | Claude Code settings.json | Daemon HookRegistry | -| Hook 触发点 | Agent Stop 事件 | 节点完成/任务完成/产出写入 | -| Hook 实现 | Python 脚本 | Daemon event handler + 可插拔 Hook | -| 防偏离手段 | 拦截停止+注入目标 | L2注入上下文+Guardrail验证+Runaway Guard | - -#### 借鉴5:Shadow Checkpoint(纳入课题6 经验沉淀) - -**来源**: Cline Shadow Git Checkpoint - -**设计**:每次 Agent 产出写入黑板时,Daemon 自动对产出目录做 git commit(带 agent_id + task_id + timestamp tag)。 - -- **与课题4的关系**:不依赖课题4。Shadow Checkpoint 是 Daemon 层行为(产出写入后触发),课题4是 Agent spawn 时的上下文注入。两者独立。 -- **与课题6的关系**:Shadow Checkpoint 的变更历史是经验蒸馏的输入(课题6 DISCOVER 阶段可以 diff 两个 checkpoint 看Agent改了什么) -- **回滚能力**:用户可以在 Dashboard 上选择任意 checkpoint 一键回滚 -- **存储开销**:git commit 增量存储,开销可控 - -**实现**:在 `NODE_AFTER_EXECUTE` Hook 中增加 git checkpoint 逻辑。 - -### 15.2 经评估暂不纳入的借鉴点 - -#### Blackboard Map(按需触发,不预设) - -**来源**: Aider Repo Map - -**结论**:当前单个任务黑板信息 ~1000-1500 tokens(§11.1 测算),10 个任务全量注入 ~1-1.5 万 tokens,远小于 128K。**现阶段不需要 Blackboard Map**。 - -**触发条件**(达到以下任一时启动设计): -- 单个任务黑板信息超过 5000 tokens -- 并行任务数超过 15 个 -- Agent spawn 时注入上下文超过 10K tokens -- 或者 L2 注入需要做精准裁剪(只取关联子图而非全量) - -**预留设计**:当需要时,基于黑板 comments/outputs/decisions 的拓扑关系构建图索引,Agent 按任务 ID 只读关联子图。 - -### 15.3 技术选型更新 - -| 需求 | 原参考 | 新增参考 | 我们的方案 | -|------|--------|---------|----------| -| 质量门控 | Hermes 幻觉门控 | Aider lint→test→commit | L1 机械+脚本 → L2 AI → L3 红线 | -| 防偏离 | 无 | claude-goal Stop Hook + 完成审计 | L2 注入 + Guardrail + Runaway Guard | -| 产出追溯 | 无 | Cline Shadow Git | NODE_AFTER_EXECUTE Hook 自动 git commit | -| Hook 机制 | mozi v1 HookRegistry | claude-goal Stop Hook + OpenClaw Heartbeat/Webhook | 双重 Hook(系统级+编排级) | -| 多项目隔离 | 无 | Superset Worktree | 课题11 中设计(Worktree 物理隔离) | diff --git a/docs/design/architecture-v2.md b/docs/design/architecture-v2.md deleted file mode 100644 index 9cca4c8..0000000 --- a/docs/design/architecture-v2.md +++ /dev/null @@ -1,2757 +0,0 @@ -# moziplus v2.0 — AI 原生多Agent编排平台 架构设计 - -**版本**: v2.6(实现前终版) -**日期**: 2026-05-14 -**作者**: 庞统(副军师) -**状态**: 实现前终版,防降级机制 + 上下文管理 + spawn 首选 + Skill AI native -**调研基础**: `docs/research/shared-consciousness-research.md` -**变更记录**: -- v2.0 初版(四相循环 + Blackboard 设计) -- v2.1 技术架构修订(Daemon+HTTP API+SQLite, 废弃sessions_send/cron) -- v2.2 完整设计(物理结构、API定义、经验沉淀、状态机、实现清单) -- v2.3 评审修订(司马懿两轮评审:openclaw agent CLI调度、质量治理框架、双写简化) -- v2.4 AI native 修正(恢复三个被妥协的核心目标:Agent 自主协作、实时共享感知、AI 持续参与) -- v2.5 AI native 完善(Skill 三层体系 + 调度五原则 + Iterative Refinement + Observation 自由格式 + spawn 实验 + 双模式调度) -- v2.6 实现前终版(spawn 首选 + 庞统上下文管理 + 防降级机制 + Skill AI native 方案 + 并行感知识别机制) - ---- - -## 0. 设计哲学 - -> "可预测的骨架 + AI 驱动的填充" —— 不是纯 DAG,也不是纯 ReAct,而是混合模式。 - -六个核心信念: - -1. **AI 参与每一个决策层** —— 编排/路由/渲染/异常处理/经验沉淀都有 AI 参与 -2. **黑板是唯一真相源** —— 所有 Agent 通过黑板共享信息,没有私下通信 -3. **产出物 > 消息** —— 共享产出物比共享消息更重要 -4. **验证才算完** —— 不验证产出不算完成 -5. **有界并行** —— 默认最多 4 个 Agent 并行(有学术依据) -6. **闭环学习** —— 执行→经验沉淀→下次改进 - ---- - -## 1. 系统总览 - -### 1.1 宏观架构 - -``` -用户(自然语言) - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 对话入口(Conversation Layer) │ -│ 庞统的持久 session,用户唯一交互点 │ -│ 支持:WebChat / CLI / Cron 触发 / API 调用 │ -└───────────────────────┬─────────────────────────────────────┘ - │ -┌───────────────────────▼─────────────────────────────────────┐ -│ 庞统 AI 指挥官(Control Unit) │ -│ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Phase 1 │ │ Phase 2 │ │ Phase 3 │ │ Phase 4 │ │ -│ │ 需求探索 │ │ 动态规划 │ │ 自主执行 │ │ 主动汇报 │ │ -│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ -│ │ -│ 内置机制: │ -│ - /goal Ralph Loop:持久目标跨 turn 保持 │ -│ - Scope Reduction Detection:防偷懒 │ -│ - 幻觉门控:验证产出再算完成 │ -│ - Fidelity 路由:按需分发信息 │ -│ - Boids 规则注入:Agent 协作行为塑造 │ -│ │ -│ ┌──────────────────────────────────────────────────────┐ │ -│ │ Blackboard(共享意识空间) │ │ -│ │ │ │ -│ │ TaskCtx │ Moments │ Artifacts │ Decisions │ Plan │ │ -│ │ AgentStates │ Experience │ EventLog │ │ -│ └──────────────────────────────────────────────────────┘ │ -└───────────────────────┬─────────────────────────────────────┘ - │ Fidelity 三档读写 - ┌─────────────┼─────────────┐ - │ │ │ - ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ - │ 张飞 │ │ 关羽 │ │ 赵云 │ ... - │ 编码 │ │ 风控 │ │ 数据 │ - └─────────┘ └─────────┘ └─────────┘ - 每个 Agent: isolated session + SOUL.md + Skills - 写入保护: propose → validate → commit -``` - -### 1.2 与 v1.0 的关系 - -| 维度 | v1.0 | v2.0 | -|------|------|------| -| 编排 | 固定 DAG 模板 + 确定性状态机 | 庞统 AI 动态规划 + 持续指挥 | -| 通信 | Sanguo Mail(异步邮件轮询) | Blackboard(实时共享读写) | -| 入口 | CLI + Dashboard | 自然语言对话 | -| 计划 | 一次性生成不可变 | 持续演进,可随时调整 | -| Agent 调度 | 按模板固定分配 | 按能力画像动态选择 | -| 信息可见性 | 每个 Agent 只看自己 | Fidelity 三档按需 | -| 异常处理 | Report Watcher(规则) | 庞统 AI 判断 | -| 验证 | output.md frontmatter | 幻觉门控 + AI 验证 | -| 经验 | 无闭环 | DISCOVER→DISTILL→APPLY→IMPROVE | - -**v2.0 独立仓库、独立代码、独立部署**。v1.0 继续运行,互不干扰。 - ---- - -## 2. 核心模块详细设计 - -### 2.1 Blackboard(共享意识空间) - -#### 2.1.1 物理结构 - -``` -~/.sanguo_projects/sanguo_moziplus_v2/ -├── blackboard/ # 共享意识空间根目录 -│ ├── tasks/ # 任务空间 -│ │ └── {task-id}/ # 每个任务独立目录 -│ │ ├── context.json # 任务上下文(目标/约束/状态) -│ │ ├── moments.jsonl # 原子事件流(追加写入) -│ │ ├── plan.json # 动态计划图谱 -│ │ ├── decisions.jsonl # 决策记录(不可变) -│ │ ├── agents/ # 各 Agent 工作区 -│ │ │ ├── {agent-id}/ -│ │ │ │ ├── state.json # Agent 当前状态 -│ │ │ │ ├── output/ # Agent 产出物 -│ │ │ │ └── inbox/ # Agent 专属信箱(通知类) -│ │ │ └── ... -│ │ └── artifacts/ # 共享产出物索引 -│ │ └── index.json # 产出物注册表 -│ ├── global/ # 全局共享空间 -│ │ ├── agent-registry.json # Agent 能力画像注册表 -│ │ ├── experience/ # 跨任务经验库 -│ │ │ ├── {domain}.jsonl # 按领域组织 -│ │ │ └── index.json # 经验索引 -│ │ └── templates/ # 任务模板库 -│ │ └── {template-id}.json -│ ├── events/ # 不可变全局事件日志 -│ │ └── {date}.jsonl # 按日期分文件 -│ ├── inbox/ # 用户需求入口 -│ │ └── {req-id}.json # 待处理需求 -│ └── locks/ # 写入锁目录 -│ └── {resource-path}.lock # 文件锁 -├── daemon/ # 守护进程代码 -├── skills/ # Skill 包 -├── docs/ # 文档 -└── config/ # 配置 -``` - -#### 2.1.2 数据结构定义 - -**context.json** — 任务上下文: -```json -{ - "task_id": "task-20260514-001", - "title": "均线策略回测", - "goal": "对双均线交叉策略在沪深300上进行5年回测", - "intent": "验证该策略在A股市场的有效性", - "end_state": "回测报告完整,包含收益曲线、最大回撤、夏普比率", - "constraints": [ - "数据范围:2020-2025", - "标的:沪深300指数", - "初始资金:100万" - ], - "state": "executing", // exploring → planning → executing → reviewing → completed - "phase": 3, // 当前四相阶段 - "created_at": "2026-05-14T08:00:00+08:00", - "updated_at": "2026-05-14T08:15:00+08:00", - "parent_task": null, // 子任务指向父任务 - "tags": ["backtest", "strategy", "moving-average"], - "confidence": 0.0 // 庞统对"需求理解程度"的自评 -} -``` - -**moments.jsonl** — 原子事件流(每行一个 JSON): -```json -{"type":"task_created","ts":"...","agent":"pangtong","data":{"goal":"..."}} -{"type":"requirement_clarified","ts":"...","agent":"pangtong","data":{"clarifications":[...]}} -{"type":"plan_generated","ts":"...","agent":"pangtong","data":{"plan_id":"p1","steps":5}} -{"type":"plan_approved","ts":"...","agent":"pangtong","data":{"approved_by":"user"}} -{"type":"agent_assigned","ts":"...","agent":"pangtong","data":{"agent":"zhaoyun","step":"s1"}} -{"type":"agent_started","ts":"...","agent":"zhaoyun","data":{"step":"s1"}} -{"type":"artifact_produced","ts":"...","agent":"zhaoyun","data":{"file":"data.csv","summary":"5年日线数据","confidence":0.95}} -{"type":"agent_completed","ts":"...","agent":"zhaoyun","data":{"step":"s1","status":"success"}} -{"type":"anomaly_detected","ts":"...","agent":"pangtong","data":{"type":"data_quality","severity":"warning"}} -{"type":"plan_adjusted","ts":"...","agent":"pangtong","data":{"reason":"数据质量问题","added_step":{...}}} -{"type":"consensus_reached","ts":"...","agent":"pangtong","data":{"result":"回测通过"}} -{"type":"task_completed","ts":"...","agent":"pangtong","data":{"final_state":"success"}} -``` - -**plan.json** — 动态计划图谱: -```json -{ - "plan_id": "p1", - "task_id": "task-20260514-001", - "version": 3, - "steps": [ - { - "id": "s1", - "type": "data_fetch", - "intent": "获取沪深300的5年日线数据", - "end_state": "数据文件就绪,通过质量检查", - "constraints": ["使用AKShare", "保存为CSV"], - "agent": "zhaoyun", - "status": "completed", - "artifacts": ["data/hs300_daily.csv"], - "started_at": "...", - "completed_at": "...", - "confidence": 0.95 - }, - { - "id": "s2", - "type": "strategy_implementation", - "intent": "实现双均线交叉策略", - "end_state": "策略代码可运行,通过单元测试", - "constraints": ["使用vnpy框架", "参数可配置"], - "agent": "zhangfei", - "status": "executing", - "depends": ["s1"], - "started_at": "..." - }, - { - "id": "s2.5", - "type": "data_cleaning", - "intent": "清洗异常数据点", - "end_state": "异常值处理完毕,数据连续无缺失", - "agent": "zhaoyun", - "status": "pending", - "depends": [], - "added_dynamically": true, - "add_reason": "s1 完成后发现数据有缺失值" - } - ], - "changelog": [ - {"version":1,"change":"初始计划","ts":"..."}, - {"version":2,"change":"添加 s2.5 数据清洗步骤","reason":"发现数据质量问题","ts":"..."}, - {"version":3,"change":"调整 s3 约束","reason":"用户要求改为vnpy框架","ts":"..."} - ] -} -``` - -**agent-registry.json** — Agent 能力画像: -```json -{ - "agents": { - "zhaoyun-data": { - "name": "赵云", - "role": "数据总管", - "capabilities": ["data_fetch", "data_cleaning", "data_validation", "quality_check"], - "tools": ["exec", "read", "write", "web_fetch"], - "model_preference": "auto", - "max_parallel_tasks": 1, - "priority": 2, - "performance": { - "tasks_completed": 42, - "avg_confidence": 0.91, - "avg_duration_minutes": 8, - "strengths": ["data_quality", "python"], - "last_active": "..." - }, - "session_key": "agent:zhaoyun-data:main" - }, - "zhangfei-dev": { - "name": "张飞", - "role": "编码先锋", - "capabilities": ["coding", "backtest", "strategy_implementation", "scripting"], - "tools": ["exec", "read", "write", "edit"], - "model_preference": "auto", - "max_parallel_tasks": 1, - "priority": 1, - "performance": { ... }, - "session_key": "agent:zhangfei-dev:main" - }, - "guanyu-dev": { - "name": "关羽", - "role": "风控守将", - "capabilities": ["risk_check", "position_sizing", "stop_loss", "live_audit"], - "tools": ["exec", "read", "write", "edit"], - "model_preference": "auto", - "max_parallel_tasks": 1, - "priority": 3, // 风控最高优先级 - "performance": { ... }, - "session_key": "agent:guanyu-dev:main" - }, - "simayi-challenger": { - "name": "司马懿", - "role": "质量总监", - "capabilities": ["code_review", "challenge", "final_acceptance"], - "tools": ["exec", "read", "write", "edit"], - "model_preference": "auto", - "max_parallel_tasks": 1, - "priority": 2, - "performance": { ... }, - "session_key": "agent:simayi-challenger:main" - }, - "jiangwei-infra": { - "name": "姜维", - "role": "平台总督", - "capabilities": ["deployment", "docker", "nas", "backtest_server", "vnpy"], - "tools": ["exec", "read", "write", "edit"], - "model_preference": "auto", - "max_parallel_tasks": 1, - "priority": 1, - "performance": { ... }, - "session_key": "agent:jiangwei-infra:main" - } - } -} -``` - -**decisions.jsonl** — 决策记录(不可变): -```json -{"ts":"...","agent":"pangtong","decision":"assign_s2_to_zhangfei","reason":"张飞擅长策略编码","alternatives_considered":["关羽(风控优先)"]} -{"ts":"...","agent":"pangtong","decision":"add_s2.5","reason":"数据清洗步骤缺失","trigger":"s1 confidence=0.7 低于阈值"} -``` - -#### 2.1.3 写入保护:propose → validate → commit - -借鉴 Network-AI 的三阶段原子写入: - -``` -Agent A: - 1. propose: 写入 agents/{agent-id}/proposed/{change-id}.json - 包含:target_path, proposed_content, priority, reason - -Control Unit (庞统): - 2. validate: - a. 格式校验(JSON schema) - b. 冲突检测(target 是否被其他 propose 锁定) - c. 优先级检查(是否有更高优先级的 propose) - d. 业务校验(状态流转是否合法) - - 3. commit: - a. 获取文件锁 (locks/{resource}.lock) - b. 原子写入(tmp → rename) - c. 追加 Moment 事件 - d. 释放锁 - - 或 abort: - a. 记录拒绝原因 - b. 通知提议 Agent -``` - -**简化规则**: -- Agent 对自己工作区(`agents/{agent-id}/`)的写入:**自动 commit**,不需要 propose -- Agent 对共享区域(`artifacts/`, `plan.json`, `context.json`)的写入:**必须 propose → commit** -- Agent 对其他 Agent 工作区的写入:**禁止**(覆盖保护原则) - -#### 2.1.4 文件锁实现 - -借鉴 ClawTeam 的 fcntl + 原子 rename: - -```python -import fcntl, tempfile, os -from pathlib import Path - -def atomic_write(path: Path, content: str): - """原子写入:先写临时文件,再 rename""" - path.parent.mkdir(parents=True, exist_ok=True) - fd, tmp = tempfile.mkstemp(dir=path.parent, prefix=f"{path.stem}-", suffix=".tmp") - with os.fdopen(fd, 'w') as f: - f.write(content) - Path(tmp).replace(path) # atomic on same filesystem - -class BlackboardLock: - """文件系统互斥锁""" - def __init__(self, lock_dir: Path): - self.lock_dir = lock_dir - - def acquire(self, resource: str, holder: str, timeout_ms=10000) -> bool: - lock_path = self.lock_dir / f"{resource.replace('/', '_')}.lock" - lock_path.parent.mkdir(parents=True, exist_ok=True) - start = time.monotonic() - while (time.monotonic() - start) * 1000 < timeout_ms: - try: - fd = os.open(str(lock_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) - os.write(fd, json.dumps({"holder": holder, "acquired_at": time.time()}).encode()) - os.close(fd) - return True - except FileExistsError: - # stale lock check (> 30s) - age = time.time() - lock_path.stat().st_mtime - if age > 30: - lock_path.unlink(missing_ok=True) - time.sleep(0.1) - return False - - def release(self, resource: str, holder: str): - lock_path = self.lock_dir / f"{resource.replace('/', '_')}.lock" - if lock_path.exists(): - data = json.loads(lock_path.read_text()) - if data.get("holder") == holder: - lock_path.unlink() -``` - ---- - -### 2.2 Control Unit(庞统 AI 指挥官) - -#### 2.2.1 四相循环 - -``` -Phase 1: 需求探索 -├── 苏格拉底对话,帮用户发现真实需求 -├── 歧义评分(0-1),高歧义时深入追问 -├── 输出:context.json(goal/intent/end_state/constraints) -├── 自评 confidence,>0.8 才进入 Phase 2 -└── 人的参与:🔴 高(全程对话) - -Phase 2: 动态规划 -├── 根据 context.json 生成 plan.json -├── AI 挑战:庞统自己审视计划的弱点 -├── 三方共识(可选):庞统+司马懿+用户审核 -├── Plan 审批:用户确认后才执行 -├── 人的参与:🟡 可选(简单任务可跳过审批) -└── 输出:plan.json(version 1) - -Phase 3: 自主执行 -├── 按 plan.json 调度 Agent -├── 每步执行: -│ ├── 选择 Agent(能力画像匹配) -│ ├── 注入任务上下文(Fidelity 按角色) -│ ├── Agent 写入黑板 -│ ├── 幻觉门控(验证产出存在) -│ ├── 异常检测(超时/质量低/Agent 崩溃) -│ └── 动态调整计划(如需) -├── /goal Ralph Loop:跨 turn 保持目标专注 -├── 人的参与:🟢 几乎不参与(可随时介入 steer) -└── 输出:artifacts/ + moments 流 - -Phase 4: 主动汇报 -├── AI 推送进展摘要(不等人查) -├── 验收:庞统自审 + 司马懿终审 -├── 经验沉淀:提取关键经验写入 experience/ -├── 人的参与:🔵 验收 -└── 输出:最终报告 + experience 条目 -``` - -#### 2.2.2 庞统的运行模式 - -**事件驱动 + 持久 session**: - -``` -庞统 session 始终在线(OpenClaw persistent session) - -触发方式: -1. 用户发消息 → 直接在 session 中处理 -2. Agent 写入黑板 → daemon 更新状态 → `openclaw agent` CLI 通知庞统 -3. Agent 完成/失败 → 写入 moments → wake 庞统 -4. 异常检测 → daemon 事件循环 → `openclaw agent` CLI 通知庞统 -5. 用户 steer(中途干预)→ 直接注入 session - -空闲时: -- 不消耗资源 -- 被 wake 事件唤醒后立即恢复上下文 -- 通过黑板恢复状态(不需要重载全部历史) -``` - -#### 2.2.3 信息路由(Fidelity 三档) - -```python -def route_information(target_agent: str, task_ctx: dict, moments: list) -> dict: - """根据目标 Agent 的角色,选择合适的信息保真度""" - role = get_agent_role(target_agent) - - if role == "control_unit": # 庞统自己 - return { - "fidelity": "full", - "context": task_ctx, - "moments": moments, # 全量事件 - "artifacts": all_artifacts, # 全部产出物 - "agent_states": all_agent_states # 所有Agent状态 - } - - elif role in get_collaborators(task_ctx): # 同任务协作伙伴 - return { - "fidelity": "summary", - "context": task_ctx, # 完整任务上下文 - "relevant_steps": filter_relevant(moments, target_agent), - "artifacts": get_dependent_artifacts(target_agent), - "summary": ai_summarize(moments) # AI 压缩摘要 - } - - else: # 外围 Agent - return { - "fidelity": "signal", - "action_required": get_pending_actions(target_agent), - "final_result": task_ctx.get("result"), - "notification": True - } -``` - -#### 2.2.4 Agent 选择算法 - -```python -def select_agent(step: dict, registry: dict) -> str: - """根据步骤需求和Agent能力画像选择最合适的Agent""" - required_caps = step.get("required_capabilities", infer_caps(step)) - - candidates = [] - for agent_id, profile in registry["agents"].items(): - # 能力匹配 - cap_overlap = len(set(required_caps) & set(profile["capabilities"])) - if cap_overlap == 0: - continue - - # 可用性检查 - if profile["performance"]["tasks_in_progress"] >= profile["max_parallel_tasks"]: - continue - - # 评分:能力匹配度 * 历史表现 * 当前空闲度 - score = ( - cap_overlap / len(required_caps) * 0.4 + # 能力匹配 - profile["performance"]["avg_confidence"] * 0.3 + # 历史表现 - (1 - profile["performance"]["tasks_in_progress"] / - profile["max_parallel_tasks"]) * 0.3 # 当前空闲度 - ) - candidates.append((agent_id, score)) - - if not candidates: - return None # 需要排队或调整计划 - - return max(candidates, key=lambda x: x[1])[0] -``` - ---- - -### 2.3 Agent 层 - -#### 2.3.1 Agent 工作流程 - -``` -1. 接收任务 - ├── 庞统通过 daemon API 创建任务步骤 - ├── 消息包含:step intent + end_state + constraints + 相关黑板内容 - └── 不包含:完整计划、其他Agent的详情(Fidelity 控制) - -2. 执行任务 - ├── 读取黑板中自己需要的上下文 - ├── 执行实际工作(编码/数据分析/风控检查等) - ├── 写入产出到 agents/{agent-id}/output/ - └── 追加 Moments 事件 - -3. 提交产出 - ├── propose 共享产出物到 artifacts/ - ├── 庞统 validate + commit - ├── 自评 [confidence: 0.X] - └── 元认知:confidence < 0.6 时推荐人工审核 - -4. 等待下一步 - ├── 庞统根据执行结果决定下一步 - └── Agent 进入空闲状态 -``` - -#### 2.3.2 Agent 行为注入 - -每个 Agent 的 SOUL.md / prompt 中注入**Boids 协作规则**: - -```markdown -## 协作规则(Boids 群体智能) -1. **Separation(不重复)**:开始工作前检查黑板,确认没有其他 Agent 在做相同的事 -2. **Alignment(风格一致)**:遵循团队的编码规范、产出格式、命名约定 -3. **Cohesion(主动共享)**:发现重要信息时主动写入黑板共享区域 -4. **Boundary(不越界)**:只在自己的工作区和共享区域操作,不修改其他 Agent 的产出 -``` - -以及**元认知自评**: - -```markdown -## 自评要求 -完成任务后,标注置信度: -- `[confidence: 0.X]` 其中 X 为 0-10 -- confidence < 0.6 时说明不确定之处并推荐人工审核 -- 遇到专业外的问题主动上报,不硬撑 -``` - -以及**Auftragstaktik 任务式指挥**: - -```markdown -## 任务执行方式 -你会收到:Intent(意图)、End State(终态)、Constraints(约束) -- 自主决定如何达成目标 -- 可以选择任何合理的方法 -- 但必须遵守所有 Constraints -- 遇到 Constraints 阻碍目标时,上报而不是绕过 -``` - ---- - -### 2.4 事件系统 - -#### 2.4.1 Moments 事件类型 - -```python -class MomentType(str, Enum): - # 任务生命周期 - TASK_CREATED = "task_created" - REQUIREMENT_CLARIFIED = "requirement_clarified" - TASK_COMPLETED = "task_completed" - TASK_FAILED = "task_failed" - - # 计划 - PLAN_GENERATED = "plan_generated" - PLAN_APPROVED = "plan_approved" - PLAN_ADJUSTED = "plan_adjusted" - - # Agent 调度 - AGENT_ASSIGNED = "agent_assigned" - AGENT_STARTED = "agent_started" - AGENT_COMPLETED = "agent_completed" - AGENT_FAILED = "agent_failed" - AGENT_BLOCKED = "agent_blocked" - - # 产出 - ARTIFACT_PRODUCED = "artifact_produced" - ARTIFACT_VALIDATED = "artifact_validated" - - # 决策 - DECISION_MADE = "decision_made" - CHALLENGE_RAISED = "challenge_raised" - CHALLENGE_VERDICT = "challenge_verdict" - - # 异常 - ANOMALY_DETECTED = "anomaly_detected" - TIMEOUT_WARNING = "timeout_warning" - - # 用户交互 - USER_STEER = "user_steer" - USER_APPROVED = "user_approved" - USER_REJECTED = "user_rejected" - - # 经验 - EXPERIENCE_CAPTURED = "experience_captured" -``` - -#### 2.4.2 事件驱动唤醒 - -```python -# 庞统的唤醒条件 -WAKE_CONDITIONS = { - # 事件驱动:Agent 回报触发下一步 - "blackboard_change": { - "trigger": "moments.jsonl 有新行", - "action": "wake pangtong session", - "context": "新增的 moments" - }, - # Agent 完成 - "agent_completed": { - "trigger": "agents/{id}/state.json status=completed", - "action": "wake pangtong", - "context": "agent_id + step_id" - }, - # 超时检测 - "step_timeout": { - "trigger": "step started_at + 30min < now", - "action": "wake pangtong with alert", - "context": "step_id + duration" - }, - # 用户消息 - "user_message": { - "trigger": "inbox/ 有新文件", - "action": "wake pangtong", - "context": "消息内容" - } -} -``` - ---- - -### 2.5 经验沉淀系统 - -#### 2.5.1 闭环学习 - -``` -DISCOVER(发现) -├── 任务执行过程中 Agent 发现好做法 -├── 异常处理中发现新模式 -└── 写入 blackboard/tasks/{id}/agents/{id}/discoveries.json - -DISTILL(蒸馏) -├── 任务完成后庞统自动提取关键转折点 -├── 从 decisions.jsonl + moments.jsonl 提炼经验 -├── 压缩为经验条目:{pattern, context, outcome, applicability} -└── 写入 blackboard/global/experience/{domain}.jsonl - -APPLY(应用) -├── 新任务开始时,庞统检索相关经验 -├── 按任务类型+标签匹配 -├── 注入 Agent prompt 作为参考 -└── 标记"来自经验 X" - -IMPROVE(改进) -├── 验证经验是否真的有效 -├── 无效经验标记 deprecated -├── 有效经验提升 confidence -└── 定期合并相似经验 -``` - -#### 2.5.2 经验数据结构 - -```json -{ - "id": "exp-001", - "pattern": "数据清洗应在策略编码前完成", - "context": "量化策略开发任务", - "outcome": "减少返工率 40%", - "applicability": ["backtest", "strategy_development"], - "source_task": "task-20260514-001", - "confidence": 0.85, - "times_applied": 3, - "times_validated": 2, - "created_at": "...", - "last_validated_at": "..." -} -``` - ---- - -### 2.6 监控与运维 - -#### 2.6.1 健康检查 - -```python -class HealthChecker: - """定期检查黑板和 Agent 健康状态""" - - checks = [ - # Agent 存活检测 - "agent_heartbeat", # 检查 state.json 更新时间 - "agent_zombie", # 运行超过 2 小时的 Agent - "agent_stale_lock", # 超过 30 秒的锁 - - # 任务健康 - "step_timeout", # 步骤超时 - "plan_stuck", # 计划卡住(所有 pending 步骤都有未完成的依赖) - "artifact_missing", # Agent 声称产出但文件不存在(幻觉门控) - - # 系统健康 - "blackboard_disk", # 磁盘空间 - "moment_flood", # 事件洪泛检测 - ] -``` - -#### 2.6.2 Token 成本治理 - -借鉴 Network-AI FederatedBudget + ClawTeam 成本追踪: - -```python -class TokenBudget: - """Token 预算管理""" - - def __init__(self): - self.global_ceiling = 500_000 # 每任务全局上限 - self.per_agent_ceiling = 100_000 # 每 Agent 上限 - self.spent = {} # agent_id → tokens used - - def check(self, agent_id: str, estimated: int) -> bool: - total = sum(self.spent.values()) - if total + estimated > self.global_ceiling: - return False # 全局预算不足 - if self.spent.get(agent_id, 0) + estimated > self.per_agent_ceiling: - return False # Agent 预算不足 - return True -``` - ---- - -## 3. 技术实现方案(v2.1 修订) - -> ⚠️ v2.1 关键修正: -> - ❌ 废弃 sessions_send(不稳定、timeout) -> - ❌ 废弃 sessions_spawn(sub-agent 大爆炸、session 文件堆积) -> - ❌ 废弃 cron wake(不稳定) -> - ✅ 采用自建 Daemon HTTP API + SQLite(v1.0 已验证可靠) -> - ✅ Agent 复用主 session,通过 Daemon API 回报 -> - ✅ 所有状态/流转/事件类型从配置文件加载,不硬编码 - -### 3.1 技术栈 - -| 层级 | 技术 | 说明 | -|------|------|------| -| 编排引擎 | **自建 Daemon** (FastAPI + uvicorn) | HTTP API + 事件循环,PM2 管理 | -| 数据存储 | **SQLite** (WAL mode) | 任务/计划/事件/Agent状态/经验 | -| 文件存储 | **文件系统** (artifacts) | 产出物(代码/数据/文档),git 可追踪 | -| Agent 运行时 | **OpenClaw Gateway** | Agent 的主 session 管理 | -| Agent 通信 | **Daemon HTTP API** | Agent 回报结果、查询黑板 | -| Agent 调度 | **`openclaw agent` CLI** | 发消息到 Agent 主 session(不创建 sub-agent) | -| 庞统通信 | **`openclaw agent` CLI** | Daemon → 庞统主 session 注入 systemEvent | -| 配置管理 | **YAML/JSON 配置文件** | 状态/流转/事件/模板全部配置化 | -| 文件锁 | **fcntl / O_EXCL** | 零依赖,跨进程安全 | -| 前端 | **OpenClaw Control Center** | 对话式入口(庞统主 session) | -| 经验检索 | **ripgrep + SQLite FTS** | 文本搜索 | -| 同步 | **sanguo_git_sync** | 已有的三端 Git 同步 | - -### 3.2 为什么不用 OpenClaw 原生调度? - -| 方案 | 问题 | 结论 | -|------|------|------| -| sessions_send | 不稳定,经常 timeout | ❌ 废弃 | -| cron wake | 各种问题,不可靠 | ❌ 废弃 | -| sessions_spawn | 每次创建新 session,文件堆积(庞统 296个/354MB),sub-agent 缺少 SOUL.md | ❌ 废弃 | -| **自建 Daemon HTTP API** | v1.0 已验证(FastAPI + SQLite + PM2),可靠 | ✅ 采用 | - -### 3.3 Agent 调度方式:主 session + HTTP 回报 - -**核心原则:不创建 sub-agent,复用 Agent 主 session。** - -``` -Daemon 需要调度张飞执行编码任务: - -1. Daemon 通过 `openclaw agent` CLI 发消息到 agent:zhangfei-dev:main - - 消息内容:step intent + end_state + constraints - - 张飞在自己的主 session 里收到消息 - -2. 张飞执行任务 - - 读取 daemon API 获取需要的上下文 - - 在自己的 workspace 里工作 - -3. 张飞通过 daemon HTTP API 回报结果 - - curl POST http://localhost:8080/api/step/{id}/complete - - body: { artifacts: [...], confidence: 0.9, summary: "..." } - - daemon 做幻觉门控(验证文件存在) - - daemon 更新 SQLite 状态 - - daemon 触发下一步 - -4. daemon 调 `openclaw agent` CLI 通知庞统进展 - - 庞统在主 session 收到 systemEvent - - 庞统决定下一步操作 -``` - -**为什么不用 sub-agent?** -- 每个 sub-agent 产生 3-5 个磁盘文件(.jsonl + .trajectory + .path) -- 庞统已有 296 个 session 文件 354MB,姜维 227 个 1.4GB -- sub-agent 没有 SOUL.md/IDENTITY.md,行为不够可控 -- cleanup: delete 只是从 UI 隐藏,文件仍然在磁盘上 - -**主 session 的上下文膨胀怎么办?** -- Agent 每完成一个任务步骤后,daemon 发 systemEvent 触发 reset -- OpenClaw 的 reset 会压缩历史,释放上下文空间 -- 或者:每 N 个步骤后自动 reset 一次 - -### 3.4 用户查看进展的流程 - -``` -用户: "任务进展如何?" - │ - ▼ -庞统主 session 收到消息 - │ - ▼ -庞统调用 Daemon API: - GET http://localhost:8080/api/task/{task_id}/status - │ - ▼ -Daemon 返回: - { - "task": { "title": "...", "state": "executing", "phase": 3 }, - "plan": { "steps": [...], "completed": 3, "total": 5 }, - "current_step": { - "agent": "zhangfei", - "status": "executing", - "started_at": "...", - "progress": "正在编码策略逻辑" - }, - "recent_moments": [ - { "type": "agent_completed", "agent": "zhaoyun", "summary": "数据获取完成" }, - { "type": "plan_adjusted", "reason": "发现数据质量问题" } - ], - "anomalies": [], - "token_budget": { "used": 120000, "total": 500000 } - } - │ - ▼ -庞统用 AI 生成人类可读的进展汇报,回复用户 -``` - -**关键设计:庞统是无状态的** -- 所有任务状态在 Daemon 的 SQLite 里 -- 庞统 session 不保存任务状态 -- 每次被问到进展,实时查询 Daemon API -- 这比 v1.0 好:v1.0 庞统需要在 session 里记住所有任务,上下文很快就爆了 - -### 3.5 配置化(零硬编码) - -``` -config/ -├── states.yaml # 任务状态定义 + 合法流转 -├── step-states.yaml # 步骤状态定义 + 合法流转 -├── events.yaml # 事件类型定义 -├── agent-registry.json # Agent 能力画像 -├── templates/ # 任务模板 -│ ├── backtest.yaml -│ ├── strategy-research.yaml -│ └── deployment.yaml -└── settings.yaml # 全局设置 -``` - -**states.yaml**: -```yaml -# 任务级状态定义(v2.0) -# 基于 v1.0 state_machine.py 的成熟经验,新增 AI native 场景 -# Daemon 启动时加载,代码里不允许出现硬编码的状态名 - -# ── 终态(不可变)── -terminal_states: [completed] - -# ── 任务状态 ── -states: - # === Phase 1: 需求探索 === - - name: exploring - description: "AI 与用户需求探索中" - phase: 1 - type: active - transitions_to: [planning, cancelled] - user_actions: [cancel, steer, takeover] - auto_triggers: - - trigger: confidence >= 0.8 - to: planning - description: "AI 判断需求已足够清晰" - - # === Phase 2: 动态规划 === - - name: planning - description: "AI 生成/调整执行计划" - phase: 2 - type: active - transitions_to: [executing, planning, paused, cancelled] - user_actions: [cancel, pause, steer, approve, reject] - auto_triggers: - - trigger: plan_approved - to: executing - description: "计划被批准(用户或 AI 自动)" - - trigger: plan_revise - to: planning # 自转换 - description: "challenge 驳回,修订计划" - - # === Phase 3: 自主执行 === - - name: executing - description: "Agent 自主执行中" - phase: 3 - type: active - transitions_to: [reviewing, paused, escalated, failed, executing, cancelled] - user_actions: [cancel, pause, steer, takeover, intervene] - auto_triggers: - - trigger: all_steps_done - to: reviewing - description: "所有步骤完成" - - trigger: critical_step_failed - to: escalated - description: "关键步骤失败,需人工" - - # === Phase 3.5: 执行中暂停 === - - name: paused - description: "用户暂停,等待恢复" - phase: 3 - type: active - transitions_to: [executing, planning, cancelled] - user_actions: [cancel, resume, replan] - auto_triggers: - - trigger: resume - to: executing - description: "用户恢复" - - trigger: goal_changed - to: planning - description: "用户改需求,重新规划" - - # === 人工介入 === - - name: escalated - description: "AI 主动升级,需要用户决策" - phase: 3 - type: active - transitions_to: [executing, planning, cancelled] - user_actions: [cancel, rollback, replan] - auto_triggers: - - trigger: user_decision - to: executing - description: "用户给出决策,继续执行" - - trigger: goal_changed - to: planning - description: "用户决定改需求" - - # === Phase 4: 验收 === - - name: reviewing - description: "AI 向用户汇报,等待验收" - phase: 4 - type: active - transitions_to: [completed, executing, cancelled] - user_actions: [accept, reject, cancel, steer] - auto_triggers: - - trigger: user_accept - to: completed - description: "用户验收通过" - - trigger: user_reject - to: executing - description: "用户不满意,继续执行" - - # === 终态 === - - name: completed - description: "任务完成" - phase: 4 - type: terminal - transitions_to: [] - - - name: cancelled - description: "已取消(可 resume 重新激活)" - phase: null - type: normal # 非终态,可恢复 - transitions_to: [executing] - user_actions: [resume] - - - name: failed - description: "任务失败(可 retry/escalate)" - phase: null - type: normal - transitions_to: [executing, escalated, cancelled] - user_actions: [retry, escalate, cancel] - -# ── 用户动作定义(AI native 扩展)── -user_actions: - cancel: - description: "取消任务" - available_from: "任何非终态" - pause: - description: "暂停,冻结所有执行" - available_from: [planning, executing] - resume: - description: "恢复执行" - available_from: [paused, cancelled] - steer: - description: "用户中途改方向(不改 goal,只调整执行策略)" - available_from: [exploring, planning, executing, reviewing] - ai_native: true # v2.0 新增:用户在对话中说“改成MACD”即触发 - takeover: - description: "用户接管某个步骤" - available_from: [executing, escalated] - ai_native: true # v2.0 新增:用户说“这部分我来” - intervene: - description: "用户主动干预(查看/修改产出物)" - available_from: [executing] - ai_native: true # v2.0 新增:用户随时可以介入 - approve: - description: "批准计划" - available_from: [planning] - reject: - description: "驳回计划" - available_from: [planning, reviewing] - replan: - description: "重新规划" - available_from: [paused, escalated] - ai_native: true - accept: - description: "验收通过" - available_from: [reviewing] - retry: - description: "失败后重试" - available_from: [failed] - escalate: - description: "升级到人工" - available_from: [failed] - rollback: - description: "回滚到执行态" - available_from: [escalated] -``` - -**step-states.yaml**: -```yaml -# 步骤级状态定义(v2.0) -# 基于 v1.0 NODE_STATES 成熟经验 - -step_states: - - name: pending - description: "待分配" - transitions_to: [assigned, cancelled] - - - name: assigned - description: "已分配 Agent" - transitions_to: [executing, cancelled] - - - name: executing - description: "Agent 执行中" - transitions_to: [completed, failed, blocked, reviewing, waiting_human, cancelled] - # reviewing: 执行完进入审查(v1.0 challenge 机制) - # waiting_human: Agent 请求人工确认 - - - name: reviewing - description: "产出审查中(challenge)" - transitions_to: [completed, pending, failed, escalated] - # v1.0 挑战循环:pass → completed, iterate → pending, fail → failed - - - name: blocked - description: "被阻塞" - transitions_to: [pending, failed, cancelled] - max_retries: 3 - - - name: waiting_human - description: "等待人工确认(Checkpoint)" - transitions_to: [executing, completed, cancelled] - # v2.0: AI 主动请求用户确认关键产出 - - - name: completed - description: "完成" - transitions_to: [] - type: terminal - - - name: failed - description: "失败(可重试)" - transitions_to: [pending, escalated, cancelled] - max_retries: 3 - - - name: escalated - description: "升级到人工" - transitions_to: [executing, pending, cancelled] - - - name: cancelled - description: "已取消" - transitions_to: [] - type: terminal - -# ── 步骤执行方式 ── -execution_modes: - - name: sub_agent - description: "创建 isolated sub-agent 执行(sessions_spawn)" - when: "复杂任务(编码、文档、调研)" - cleanup: "delete" - archive_transcript_to: "artifacts/task-{id}/steps/{step_id}/" - - - name: main_session - description: "在 Agent 主 session 中执行" - when: "简单任务(数据获取、文件操作)" - record_to: "artifacts/task-{id}/steps/{step_id}/transcript.jsonl" - - - name: human - description: "用户自己执行" - when: "用户说'这部分我来'" - trigger: "takeover" -``` - -**代码中禁止出现硬编码状态名**: -```python -# ❌ 禁止 -if task.state == "executing": - -# ✅ 正确 -EXECUTING = config.get_state("executing") -if task.state == EXECUTING: -``` - -### 3.6 核心代码模块 - -``` -sanguo_moziplus_v2/ -├── daemon/ # 守护进程 -│ ├── main.py # FastAPI 入口 -│ ├── api/ # HTTP API 路由 -│ │ ├── tasks.py # 任务 CRUD -│ │ ├── steps.py # 步骤 CRUD + 回报 -│ │ ├── board.py # 黑板查询 -│ │ ├── moments.py # 事件查询 -│ │ └── agents.py # Agent 状态/心跳 -│ ├── engine/ # 编排引擎 -│ │ ├── orchestrator.py # 编排主循环(事件驱动) -│ │ ├── planner.py # 动态规划 -│ │ ├── selector.py # Agent 选择 -│ │ ├── validator.py # 产出验证(幻觉门控) -│ │ ├── archiver.py # 执行历史归档 -│ │ └── experience.py # 经验沉淀引擎 -│ ├── cli_client.py # openclaw agent CLI 调用封装 -│ ├── db.py # SQLite 数据层 -│ ├── lock.py # 文件锁实现 -│ ├── health.py # 健康检查(daemon 内部定时) -│ ├── budget.py # Token 预算管理 -│ └── config_loader.py # YAML/JSON 配置加载 -├── config/ # 配置文件 -│ ├── states.yaml -│ ├── step-states.yaml -│ ├── events.yaml -│ ├── agent-registry.json -│ ├── templates/ -│ └── settings.yaml -├── artifacts/ # 产出物 + 执行历史(git 追踪) -│ └── task-{id}/ # 每个任务一个目录 -│ ├── context.json # 任务上下文 -│ ├── plan.json # 执行计划 -│ ├── steps/ # 各步骤产出 -│ │ ├── s1/ -│ │ │ ├── output.json # 步骤产出元数据 -│ │ │ ├── transcript.jsonl # 执行历史(从 OpenClaw 归档) -│ │ │ └── ... # 实际产出文件 -│ │ └── s2/ -│ ├── moments.jsonl # 关键事件流 -│ └── experience.md # 提取的经验教训 -├── skills/ # Skill 包(供 Agent 加载) -│ ├── task-bootstrap/ # Agent 任务引导 Skill -│ │ └── SKILL.md # Boids + 元认知 + Auftragstaktik -│ └── wiki-query/ # 复用已有 -├── docs/ -│ ├── design/ -│ └── research/ -├── scripts/ -│ ├── create-task.sh # CLI: 创建任务 -│ ├── status.sh # CLI: 查看状态 -│ └── bootstrap.sh # 初始化脚本 -└── README.md -``` - -### 3.6.1 执行历史归档机制 - -**目标**:每个步骤的完整执行过程都归档到任务目录,可追溯、可检索、可提炼经验。 - -``` -执行流程: - -1. Daemon 调度 Agent 执行步骤(sub-agent 或主 session) -2. Agent 执行完毕,回报结果 -3. Daemon archiver 做以下事情: - a. 读取 OpenClaw session transcript(.jsonl 文件) - b. 提取与该步骤相关的部分(按时间范围裁剪) - c. 写入 artifacts/task-{id}/steps/{step_id}/transcript.jsonl - d. 如果是 sub-agent,transcript 包含完整对话历史 - e. 如果是主 session,只裁剪该步骤执行期间的对话 -4. 经验沉淀时,读取 transcript.jsonl 做 AI 分析 -``` - -**transcript.jsonl 包含什么**: -- user 消息(用户的指令/反馈) -- assistant 消息(Agent 的回复/决策) -- tool 调用及结果(执行的命令、文件操作、API 调用) -- token 消耗统计 -- 错误和异常信息 - -**价值**: -- 🔍 可追溯:每个步骤的每个决策都有据可查 -- 📚 可学习:经验提取时能看完整上下文,不只是结论 -- 🐛 可复盘:坑和踩坑过程一目了然 -- 🧹 可清理:归档后 OpenClaw 原始 session 文件可以安全删除 - -### 3.6.2 Agent 调度混合方案 - -简单步骤用主 session,复杂步骤用 sub-agent: - -| 步骤类型 | 调度方式 | 原因 | -|---------|---------|------| -| 数据获取/文件操作 | 主 session + Gateway 发消息 | 简单快速,不需要隔离 | -| 编码/文档/调研 | sessions_spawn + cleanup=delete | 复杂任务需要隔离,防上下文污染 | -| 用户自己执行 | takeover,不调度 | 用户说"这部分我来" | - -sub-agent 完成后,archiver 自动把 transcript 归档到任务目录。 - -### 3.7 关键交互流程 - -#### 流程 1:完整任务生命周期 - -``` -用户: "帮我做一个均线策略回测" - │ - ▼ -庞统 Phase 1(需求探索) - ├── 苏格拉底对话 2-3 轮 - ├── 澄清:标的?周期?资金?评价指标? - ├── 写入 context.json,confidence=0.9 - └── 转入 Phase 2 - │ - ▼ -庞统 Phase 2(动态规划) - ├── 检索经验库 → 找到"数据清洗应先于策略编码" - ├── 生成 plan.json(5步) - ├── 用户确认(或跳过) - └── 转入 Phase 3 - │ - ▼ -庞统 Phase 3(自主执行) - │ - ├── Step s1: 数据获取 → 选择赵云(data_fetch 能力) - │ ├── `openclaw agent` CLI 调度赵云 - │ ├── 赵云执行,写入 output/hs300_daily.csv - │ ├── 赵云 propose → 庞统 validate → commit - │ ├── 幻觉门控:文件存在?大小合理? ✓ - │ └── confidence=0.95 ✓ - │ - ├── 发现:数据有缺失值(anomaly_detected) - │ └── 庞统动态添加 s1.5 数据清洗步骤 - │ - ├── Step s1.5: 数据清洗 → 选择赵云 - │ └── ... 执行并完成 - │ - ├── Step s2: 策略编码 → 选择张飞 - │ └── ... 执行并完成 - │ - ├── Step s3: 风控审查 → 选择关羽 - │ └── ... 执行并完成 - │ - ├── Step s4: 质量评审 → 选择司马懿 - │ ├── 司马懿发现问题 → challenge_raised - │ ├── 庞统裁决 → 要求张飞修正 - │ ├── 张飞修正 → 重新评审 - │ └── 司马懿通过 ✓ - │ - └── 转入 Phase 4 - │ - ▼ -庞统 Phase 4(主动汇报) - ├── 生成最终报告 - ├── 经验沉淀:提取 3 条经验写入 experience/ - ├── 向用户推送完成通知 - └── 用户验收 -``` - -#### 流程 2:异常处理 - -``` -场景:赵云执行超时 - -1. health scanner 检测到 zhaoyun state.json 30分钟未更新 -2. daemon 事件循环检测到超时,通知庞统 -3. 庞统: - a. 检查赵云 session 是否存活(sessions_list) - b. 存活 → `openclaw agent --agent zhaoyun` 询问进度 - c. 不存活 → 标记 s1 为 failed,重新分配给其他 Agent 或调整计划 - d. 记录 decision: "赵云超时,重新分配" -4. 追加 Moment: agent_failed + decision_made -``` - -#### 流程 3:用户中途干预 - -``` -场景:执行到 s2 时用户说"改成 MACD 策略" - -1. 用户消息注入庞统 session -2. 庞统: - a. 暂停当前执行(通知张飞停止) - b. 修改 context.json(goal 改为 MACD 策略) - c. 重新规划 plan.json(s2 需要修改) - d. 向用户确认修改方案 - e. 用户确认后继续执行 -3. 追加 Moment: user_steer + plan_adjusted -``` - -### 3.8 与 OpenClaw 的集成点(v2.3 修正) - -> ⚠️ v2.3 关键修正(司马懿第二轮评审反馈): -> - ❌ 废弃自建 Gateway WS Client(不稳定、需维护长连接) -> - ✅ 改用 `openclaw agent` CLI 命令(Gateway 原生、有超时和 fallback、零连接管理) -> - ✅ 全程推送式事件链路,零轮询零 cron - -| v2.0 功能 | 实现方式 | 说明 | -|-----------|---------|------| -| 庞统对话 | 庞统主 session (webchat) | 用户通过 Control Center 对话 | -| 庞统查进展 | curl GET daemon API | 实时查询 SQLite,AI 生成汇报 | -| 庞统操作任务 | curl POST daemon API | 创建/规划/启动/暂停/恢复任务 | -| **Daemon 调度 Agent** | **`openclaw agent` CLI** | daemon 调用 CLI 发消息到 Agent 主 session | -| Agent 调度(复杂) | sessions_spawn + cleanup=delete | 复杂步骤用 sub-agent 隔离 | -| Agent 回报 | curl POST daemon API | 完成/失败/进度上报 | -| Agent 查黑板 | curl GET daemon API | 查询任务上下文/计划/产出物 | -| **Daemon 通知庞统** | **`openclaw agent` CLI** | daemon 调用 CLI 注入消息到庞统 session | -| Agent 上下文管理 | Gateway reset | 定期 reset Agent 主 session 防止膨胀 | -| 知识检索 | wiki-query skill (Agent 侧) | Agent 需要时可调用 | -| 数据持久化 | SQLite (daemon) + 文件系统 (artifacts) | 状态在 SQLite,产出物在文件系统 | -| 执行历史归档 | daemon archiver | 归档后物理删除原始 session 文件 | - -#### 3.8.1 Agent 调度的具体实现 - -```bash -# Daemon 调度 Agent(简单步骤:主 session) -openclaw agent --agent zhangfei-dev \ - --message "执行步骤 s2: 编码均线策略逻辑。上下文见 http://localhost:8080/api/steps/s2" \ - --json - -# Daemon 通知庞统 -openclaw agent --agent pangtong-fujunshi \ - --message "[systemEvent] 步骤 s1 完成,zhaoyun 回报:数据获取成功" \ - --json -``` - -**并行调度**:daemon 用 `subprocess.Popen` 异步启动多个 `openclaw agent` 命令,poll 等待结果。 - -**事件传播链(全程推送,零轮询)**: -``` -Agent 完成任务 - → curl POST http://localhost:8080/api/steps/{id}/complete(回报 daemon) - → daemon 更新 SQLite + 验证产出 - → daemon 调 openclaw agent --agent pangtong-fujunshi(通知庞统) - → 庞统收到消息,决定下一步 - → daemon 调 openclaw agent --agent zhangfei-dev(调度下一个 Agent) -``` - -#### 3.8.2 庞统上下文恢复协议 - -庞统是"无任务状态"的(状态在 daemon SQLite),但需要"推理上下文"。 -庞统每次被 daemon 通知时,第一步总是 `GET /api/tasks/{id}/status` 重建认知。 - -#### 3.8.3 Agent 任务消息模板 - -Daemon 调度 Agent 时发送的标准消息格式: - -``` -[moziplus v2] 任务步骤分配 - -任务: {task_title} -步骤: {step_id} - {step_title} -目标: {step_intent} -预期产出: {step_end_state} - -上下文获取: - 任务状态: curl http://localhost:8080/api/tasks/{task_id} - 步骤详情: curl http://localhost:8080/api/steps/{step_id} - 前序产出: curl http://localhost:8080/api/steps/{prev_step_id}/output - -约束: - 产出物必须写到 artifacts/task-{task_id}/steps/{step_id}/ 目录 - 完成后回报: curl -X POST http://localhost:8080/api/steps/{step_id}/complete - 失败回报: curl -X POST http://localhost:8080/api/steps/{step_id}/fail -``` | - ---- - -### 3.9 共享意识空间物理结构 - -> "可预测骨架 + AI 动态填充" 的物理载体。 -> SQLite 存状态(查询快、事务安全),文件系统存内容(代码/数据/文档,git 可追踪)。 -> 同一信息只存在一个地方,不重复。 - -#### 3.9.1 SQLite 表结构 - -```sql --- 任务表 -CREATE TABLE tasks ( - id TEXT PRIMARY KEY, -- UUID - title TEXT NOT NULL, - goal TEXT NOT NULL, -- 用户原始需求 - state TEXT NOT NULL DEFAULT 'exploring', - phase INTEGER NOT NULL DEFAULT 1, -- 1=探索, 2=规划, 3=执行, 4=验收 - context_json TEXT, -- {goal, constraints, clarifications, confidence} - plan_json TEXT, -- {steps: [{id, title, intent, agent_hint, depends_on}]} - created_at TEXT NOT NULL, - updated_at TEXT NOT NULL, - created_by TEXT DEFAULT 'user', -- user | pangtong - token_budget_used INTEGER DEFAULT 0, - token_budget_limit INTEGER DEFAULT 500000 -); - --- 步骤表 -CREATE TABLE steps ( - id TEXT PRIMARY KEY, -- "{task_id}-s{seq}" - task_id TEXT NOT NULL REFERENCES tasks(id), - seq INTEGER NOT NULL, - title TEXT NOT NULL, - intent TEXT NOT NULL, -- 步骤目标(给 Agent 的指令) - end_state TEXT, -- Agent 应该达到的产出状态 - state TEXT NOT NULL DEFAULT 'pending', - assigned_agent TEXT, -- agent-id 或 null(待分配) - execution_mode TEXT DEFAULT 'sub_agent', -- sub_agent | main_session | human - depends_on TEXT, -- JSON array of step_ids - output_json TEXT, -- {artifacts: [...], confidence, summary} - started_at TEXT, - completed_at TEXT, - retry_count INTEGER DEFAULT 0, - max_retries INTEGER DEFAULT 3 -); - --- 事件流(Moments) -CREATE TABLE moments ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL REFERENCES tasks(id), - step_id TEXT, -- nullable(任务级事件没有 step_id) - type TEXT NOT NULL, -- 事件类型(从 events.yaml 加载) - agent TEXT, -- 触发 Agent - data_json TEXT NOT NULL, -- 事件详情 - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -CREATE INDEX idx_moments_task ON moments(task_id, created_at); - --- Agent 状态(心跳) -CREATE TABLE agent_status ( - agent_id TEXT PRIMARY KEY, - status TEXT NOT NULL DEFAULT 'idle', -- idle | busy | offline - current_task_id TEXT, - current_step_id TEXT, - last_heartbeat TEXT, - capabilities TEXT -- JSON array(从 agent-registry.json 加载) -); - --- AI 决策记录(可追溯) -CREATE TABLE decisions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL REFERENCES tasks(id), - step_id TEXT, - decision_type TEXT NOT NULL, -- agent_selected | plan_adjusted | anomaly_handled | escalated - reasoning TEXT NOT NULL, -- AI 的思考过程 - outcome TEXT NOT NULL, -- 决策结果 - model TEXT, -- 使用的模型 - token_cost INTEGER, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - --- 经验库 -CREATE TABLE experiences ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source_task_id TEXT NOT NULL, - source_step_id TEXT, - category TEXT NOT NULL, -- pattern | pitfall | best_practice | tool_usage - title TEXT NOT NULL, - content TEXT NOT NULL, -- Markdown 格式 - tags TEXT, -- JSON array - relevance_score REAL DEFAULT 0.5, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` - -#### 3.9.2 文件系统结构 - -``` -sanguo_moziplus_v2/ -└── artifacts/ - └── task-{uuid}/ - ├── context.json # 任务上下文(冗余 SQLite 的 context_json,供 Agent 直接 read) - │ # {goal, constraints, clarifications, confidence, phase_history} - │ - ├── plan.json # 执行计划(冗余 SQLite 的 plan_json,供 Agent 直接 read) - │ # {steps: [{id, title, intent, end_state, assigned_agent, depends_on, state}]} - │ - ├── steps/ # 各步骤产出物 - │ ├── s1/ - │ │ ├── output.json # {artifacts: [{path, type, size}], confidence, summary, duration_s} - │ │ ├── transcript.jsonl # 执行历史归档(从 OpenClaw session transcript 复制) - │ │ ├── hs300_daily.csv # ← 实际产出文件(Agent 直接写到这里) - │ │ └── data_quality.json # ← 数据质量报告 - │ ├── s2/ - │ │ ├── output.json - │ │ ├── transcript.jsonl - │ │ ├── strategy.py # ← 策略代码 - │ │ └── backtest_result.json - │ └── s3/ - │ └── ... - │ - ├── observations/ # Agent 主动观察与建议(v2.4 新增) - │ # Agent 执行中发现的异常、建议、洞察 - │ # 其他 Agent 和庞统可直接 read 获取 - │ ├── zhaoyun-001.md # "数据有缺失值,建议加清洗步骤" - │ ├── zhangfei-001.md # "建议用 MACD 替代均线" - │ └── ... - │ - ├── status.json # 全局状态摘要(v2.4 新增,daemon 实时维护) - │ # {phase, steps: {s1: {state, agent}, ...}, anomalies, observations} - │ # Agent 执行前第一步 read 此文件获取全局视野 - │ - ├── moments.jsonl # 原子事件流(冗余 SQLite moments 表,追加写入) - │ # 每行: {"type":"step_completed","ts":"...","agent":"zhaoyun","data":{...}} - │ - ├── decisions.jsonl # AI 决策记录(冗余 SQLite decisions 表) - │ # 每行: {"type":"agent_selected","ts":"...","reasoning":"...","outcome":"zhangfei"} - │ - └── experience.md # 任务完成后 AI 提取的经验(Markdown) -``` - -**为什么 SQLite 和文件都有?** -- **SQLite**:给 daemon 内部用(查询、事务、索引、复杂条件筛选) -- **文件系统**:给 Agent 用(`read` 工具直接读,Agent 不能访问 daemon 的 SQLite) -- 两者通过 daemon API 同步,SQLite 是权威源,文件是缓存/视图 - -#### 3.9.3 产出物门控(Gating)设计 - -> 参考 OpenAI Codex Cookbook 的 gating check:"Do not advance until required files are present" - -``` -步骤完成回报 POST /api/steps/{id}/complete - │ - ▼ -Daemon validator 做三层检查: - 1. 文件存在性检查 - - output_json 中声明的每个 artifact.path 都必须存在 - - ls -la 验证 - 2. 内容完整性检查 - - 文件大小 > 0(空文件不算) - - 文件大小在合理范围内(非异常值) - - 如果是 JSON/CSV,验证 schema/parsing - 3. 语义检查(可选,消耗 token) - - AI 检查产出物是否与 end_state 描述匹配 - - 只在 confidence < 0.7 时触发 - │ - ▼ -三层全过 → 同一事务内: - - UPDATE steps SET state='completed' - - INSERT INTO moments (type='step_completed') - - INSERT INTO moments (type='validation_passed') -任何一层失败 → 回报 validation_failed,步骤保持 executing -``` - ---- - -### 3.10 Daemon API 接口定义 - -> RESTful + JSON,端口 8080(沿用 v1.0)。 -> 所有请求/响应都记录到 moments 表。 - -#### 3.10.0 API 设计原则(调研审视) - -| 原则 | 来源 | 实现方式 | -|------|------|----------| -| **幂等性** | Wanman JSON-RPC | 所有写操作可安全重试,重复回报不产生副作用 | -| **标准错误码** | Wanman JSON-RPC | 统一错误格式 `{ok: false, error: {code, message}}` | -| **事务保护** | Edict Outbox | 关键写操作(步骤完成)原子化:产出验证 + 状态更新 + 事件记录同一事务 | -| **版本化** | Gstack | API 版本号前缀 `/api/v1/...`,未来升级不破坏兼容性 | -| **Agent 零SDK** | Wanman CLI | Agent 只需 curl,不需要任何 SDK | - -#### 3.10.1 任务生命周期 API - -``` -POST /api/tasks # 创建任务 -GET /api/tasks/{id} # 查询任务状态 -GET /api/tasks/{id}/status # 轻量状态查询(给庞统用) -PUT /api/tasks/{id}/context # 更新任务上下文 -POST /api/tasks/{id}/plan # 提交执行计划 -POST /api/tasks/{id}/start # 启动执行(Phase 3) -POST /api/tasks/{id}/pause # 暂停 -POST /api/tasks/{id}/resume # 恢复 -POST /api/tasks/{id}/cancel # 取消 -POST /api/tasks/{id}/replan # 重新规划(用户改需求或异常调整) -GET /api/tasks/{id}/moments # 查询事件流 -GET /api/tasks/{id}/decisions # 查询决策记录 -``` - -**POST /api/tasks** — 创建任务 -```json -// Request -{ - "title": "均线策略回测", - "goal": "对沪深300做双均线交叉策略回测", - "constraints": ["5年日线数据", "滑点0.2%"], - "metadata": {} // 可选 -} -// Response -{ - "ok": true, - "task_id": "t-uuid-001", - "state": "exploring" -} -``` - -**GET /api/tasks/{id}/status** — 轻量状态查询(庞统用,返回人类可读摘要) -```json -{ - "task": { "title": "均线策略回测", "state": "executing", "phase": 3 }, - "plan": { - "steps": [ - { "id": "s1", "title": "数据获取", "state": "completed", "agent": "zhaoyun" }, - { "id": "s2", "title": "策略编码", "state": "executing", "agent": "zhangfei" } - ], - "completed": 1, "total": 4 - }, - "current_step": { - "id": "s2", "agent": "zhangfei", "started_at": "...", - "progress": null // Agent 可选上报进度 - }, - "recent_moments": [ - { "type": "step_completed", "agent": "zhaoyun", "summary": "数据获取完成" } - ], - "anomalies": [], - "token_budget": { "used": 120000, "limit": 500000 } -} -``` - -#### 3.10.2 步骤执行 API - -``` -POST /api/steps/{id}/assign # 分配 Agent -POST /api/steps/{id}/start # 标记开始执行 -POST /api/steps/{id}/complete # Agent 回报完成 -POST /api/steps/{id}/fail # Agent 回报失败 -POST /api/steps/{id}/block # Agent 回报阻塞 -POST /api/steps/{id}/progress # Agent 上报进度(可选) -POST /api/steps/{id}/escalate # Agent 请求人工介入 -GET /api/steps/{id} # 查询步骤详情 -GET /api/steps/{id}/output # 查询步骤产出 -``` - -**POST /api/steps/{id}/complete** — Agent 回报完成 -```json -// Request -{ - "artifacts": [ - { "path": "artifacts/task-xxx/steps/s1/hs300_daily.csv", "type": "data", "size_bytes": 1048576 } - ], - "confidence": 0.95, - "summary": "获取沪深300 5年日线数据,共1218条记录,含缺失值处理报告" -} -// Response -{ - "ok": true, - "state": "completed", - "validation": { - "files_exist": true, - "sizes_reasonable": true, - "passed": true - } -} -``` - -**POST /api/steps/{id}/fail** — Agent 回报失败 -```json -// Request -{ - "error": "数据源返回403,权限不足", - "retry_suggested": true, - "partial_artifacts": [] -} -``` - -#### 3.10.3 查询 & 管理API - -``` -GET /api/agents # 所有 Agent 状态 -GET /api/agents/{id}/status # 单个 Agent 状态 -POST /api/agents/{id}/heartbeat # Agent 心跳 - -GET /api/experiences # 经验检索(给 AI 用) -POST /api/experiences # 写入经验 - -GET /api/health # Daemon 健康检查 -GET /api/stats # 统计信息(任务数、成功率等) -``` - -**GET /api/experiences** — 经验检索 -```json -// Query: ?tags=data_cleaning,backtest&limit=5 -{ - "experiences": [ - { - "id": 1, - "source_task": "t-uuid-001", - "category": "pitfall", - "title": "数据缺失值必须在策略编码前处理", - "content": "...", - "tags": ["data_cleaning", "backtest"], - "relevance_score": 0.92 - } - ] -} -``` - -#### 3.10.4 AI Native 扩展 API - -``` -POST /api/tasks/{id}/steer # 用户中途改方向(不改 goal) -POST /api/tasks/{id}/takeover/{step_id} # 用户接管某步骤 -POST /api/tasks/{id}/intervene # 用户主动干预 -POST /api/steps/{id}/review # 触发 challenge 审查 -POST /api/tasks/{id}/distill # 触发经验蒸馏 -``` - -**POST /api/tasks/{id}/steer** — 用户改方向 -```json -// Request -{ - "direction": "改用MACD策略", - "reason": "用户要求", - "affected_steps": ["s2"] // 哪些步骤受影响(可选,AI自动判断) -} -// Response -{ - "ok": true, - "action": "replanning", - "paused_steps": ["s2"], - "message": "正在重新规划受影响的步骤..." -} -``` - ---- - -### 3.11 经验沉淀引擎 - -> 每次任务完成后自动触发,提取可复用的经验存入经验库。 - -#### 3.11.1 沉淀流程 - -``` -任务完成 - │ - ▼ -Daemon 检测到任务 state = completed - │ - ▼ -Daemon 通过 Gateway 触发 AI session 做经验蒸馏 - - 输入: task context + plan + 所有 steps 的 transcript.jsonl + moments - - AI 分析: - 1. 哪些决策是好的?(pattern) - 2. 哪些坑踩了?(pitfall) - 3. 有什么可复用的做法?(best_practice) - 4. 工具使用技巧?(tool_usage) - - 输出: 3-5 条经验 - │ - ▼ -Daemon 写入 SQLite experiences 表 + 生成 experience.md - │ - ▼ -下次创建类似任务时,Daemon 自动检索相关经验注入 context -``` - -#### 3.11.2 经验检索时机 - -| 时机 | 触发者 | 检索内容 | -|------|--------|----------| -| Phase 2 规划 | Daemon | 检索与任务 goal 相关的经验,注入 plan 上下文 | -| Phase 3 Agent 选择 | Daemon | 检索与步骤类型相关的经验(如"这类任务哪个 Agent 做得好") | -| Agent 执行前 | Daemon | 注入与步骤相关的经验到 task 描述 | -| 异常处理 | Daemon/庞统 | 检索类似异常的历史处理方式 | - -#### 3.11.3 调研审视:经验沉淀引擎的改进方向 - -基于 wiki 知识管理体系 + Nuwa 五层蒸馏 + Corpus2Skill + A-MEM + MemAgents ICLR 2026 的调研: - -**改进 1:闭环四阶段(当前只有 DISTILL+APPLY)** - -``` -DISCOVER(发现问题): - - 异常即知识源:步骤失败、数据质量异常、Agent 超时都是潜在的待沉淀知识 - - 反向触发:Agent 主动发现好实践时建议"这个做法可以固化" - - 外部注入:用户丢链接/文章,评估适用性 - -DISTILL(蒸馏提取): - - 参考 Nuwa 五层:表象 → 模式 → 决策启发式 → 反模式 → 边界 - - 不是简单的"踩坑记录",而是提炼出可复用的决策规则 - - AI 分析 transcript.jsonl,不只是看结论,看完整决策过程 - -APPLY(应用): - - 规划时:检索与 goal 相关的经验注入 plan 上下文 - - 执行时:检索与步骤相关的经验注入 task 描述 - - 异常时:检索类似异常的历史处理方式 - -IMPROVE(持续改进): - - 新经验可能让旧经验过时(A-MEM 的 Zettelkasten 思路) - - 检测冲突:新经验与旧经验矛盾时,标注差异,等待下次应用时验证 - - 不删除旧经验,标记为 superseded + 关联新经验 -``` - -**改进 2:经验→Skill 转化(参考 Corpus2Skill)** - -高频使用的经验可以自动转化为 Agent Skill: - -``` -经验使用频率 >= 5次 - → 自动转化为 skills/auto-generated/{experience-name}/SKILL.md - → 下次同类任务直接注入 Skill,不需要检索经验库 - → 减少 token 消耗(Skill 注入比检索更精炼) -``` - -**改进 3:经验蒸馏的五层结构(参考 Nuwa)** - -```yaml -experience: - title: "数据清洗应先于策略编码" - layers: - - layer: surface # 表象:发生了什么 - content: "赵云获取数据后有缺失值,张飞直接编码导致回测结果异常" - - layer: pattern # 模式:这类问题的通用模式 - content: "数据质量和策略逻辑是两个独立关注点,应串行处理" - - layer: heuristic # 决策启发式:下次怎么做 - content: "IF 任务涉及数据分析 THEN 第一步必须是数据质量检查" - - layer: anti_pattern # 反模式:绝对不做的事 - content: "绝对不要假设数据是干净的,即使数据源声称已清洗" - - layer: boundary # 边界:什么时候不适用 - content: "如果是纯模拟数据回测,不需要数据清洗步骤" -``` - -### 3.12 中央协调 + Agent 自主(v2.4 核心设计修正) - -> **问题**:v2.3 设计在三轮评审后逐渐妥协了三个 AI native 核心目标: -> 1. Agent 自主协作 → 退化为中央调度 -> 2. 实时共享感知 → 退化为按需查询 -> 3. AI 持续参与 → 退化为完成时通知 -> -> **根因**:遇到技术难点时采纳降级方案,而非在约束下找实现目标的方法。 -> -> **v2.4 修正**:技术栈不变(Daemon + SQLite + `openclaw agent` CLI + 文件系统),变的是**设计理念**—— -> 从“中央控制”变为“中央协调 + Agent 自主”。 -> -> **调研来源**: -> - Edict 朝堂议政:TurnScheduler + phaseMask(Agent 声明在哪些阶段主动发言) -> - Ouroboros:后台意识循环 consciousness.py(任务间主动思考) -> - Network-AI:Blackboard + CRDT(Agent 读黑板、看到变化、自己决定行动) -> - Open Multi-Agent:sharedMemory + delegate_to_agent(Agent 可主动委派) -> - oh-my-claudecode:Team staged pipeline(team-plan → team-prd → team-exec → team-verify → team-fix) -> - Hermes Agent:kanban board + dispatcher(Agent 通过 kanban_* 工具集自主 claim task) -> - Claude Code:Grove(共享项目工作区)、Swarm/Team(hub-spoke 协调) -> - MCP + A2A:MCP 提供上下文共享,A2A 提供 Agent 间直接通信 -> - 学术:Multi-Agent Blackboard System (arXiv:2510.01285) 消除中央协调器先验知识需求 - -#### 3.12.1 三层自主模型 - -``` -┌──────────────────────────────────────────────────────┐ -│ 庞统(中央协调) │ -│ 定方向、定约束、异常干预、持续观察 │ -│ 不微观管理:不指定每一步怎么做 │ -└────────────┬─────────────────────┬───────────────────┘ - │ │ - ┌────────▼────────┐ ┌────────▼────────┐ - │ Agent 自主权 │ │ Agent 自主权 │ - │ 执行方式自主决定 │ │ 执行方式自主决定 │ - │ 可主动发现问题 │ │ 可主动发现问题 │ - │ 可主动建议 │ │ 可主动建议 │ - └────────┬────────┘ └────────┬────────┘ - │ │ - ┌────────▼─────────────────────▼────────┐ - │ 共享意识空间(文件系统) │ - │ status.json → 全局状态实时摘要 │ - │ observations/ → Agent 主动观察 │ - │ moments.jsonl → 原子事件流 │ - │ steps/ → 各步骤产出物 │ - └──────────────────────────────────────┘ -``` - -**与 v2.3 的核心区别**: - -| 维度 | v2.3(妥协版) | v2.4(AI native) | -|------|-------------|----------------| -| 调度模式 | daemon 全权调度 | 庞统定方向,Agent 自主执行 | -| 信息获取 | Agent curl 查 API | Agent 直接 read 共享目录文件 | -| AI 参与 | 步骤完成后通知 | 持续推送执行摘要,庞统主动干预 | -| Agent 主动性 | 无 | observations 目录 + 主动建议机制 | -| 协作方式 | 庞统串行分配 | Agent 可观察其他 Agent 产出并主动调整 | - -#### 3.12.2 Agent 自主执行协议 - -Agent 被调度后,不只是“执行指令”,而是执行一套自主行为协议: - -``` -Agent 收到任务消息后: - -1. 【感知】read artifacts/task-{id}/status.json - → 了解全局:当前 phase、各步骤状态、异常列表 - -2. 【感知】read artifacts/task-{id}/observations/ - → 了解其他 Agent 的观察和建议 - → 特别是前序步骤 Agent 的发现 - -3. 【感知】read artifacts/task-{id}/steps/{前序步骤}/output.json - → 获取前序步骤的具体产出 - -4. 【执行】基于全局信息自主决定执行方式 - → 不需要庞统指定“用什么工具、什么方法” - → 根据任务意图 + 全局上下文自行判断 - -5. 【主动观察】如果执行中发现异常/有价值的发现: - → write artifacts/task-{id}/observations/{agent}-{seq}.md - → 内容:发现的问题、建议的调整、重要的洞察 - → 格式自由(Markdown),由庞统或其他 Agent 读取 - -6. 【回报】完成/失败后回报 daemon API - → POST /api/steps/{id}/complete 或 /fail -``` - -**关键设计**:步骤 1-3(感知)是**强制的**,写入 Agent 的 task-bootstrap Skill。 -这确保每个 Agent 执行前都有全局视野,而不是盲人摸象。 - -#### 3.12.3 Agent 任务消息模板(v2.4 增强) - -调度 Agent 时,消息内容不仅是“执行 X”,还包含: - -```markdown -## 任务:{step_title} - -### 意图 -{step_intent} → 目标状态:{end_state} - -### 全局上下文 -- 任务:{task_title} -- Phase:{current_phase} -- 前序步骤:{completed_steps_summary} -- **请先感知全局**:read artifacts/task-{id}/status.json 和 observations/ - -### 你的自主权 -- 执行方式由你决定(工具、方法、顺序) -- 发现异常请写入 observations/ -- 遇到专业外的问题主动回报,不硬撑 - -### 约束 -- 产出物必须写到 artifacts/task-{id}/steps/{step_id}/ -- 超时 {timeout_minutes} 分钟自动标记 blocked - -### 完成后 -POST /api/steps/{step_id}/complete -body: {artifacts: [...], confidence: 0-1, summary: "...", observations: [...]} -``` - -#### 3.12.4 庞统持续意识机制 - -庞统不是被动的审批节点,而是持续观察的指挥官。 - -**实现方式**:daemon 通过 `openclaw agent` CLI 定期推送执行摘要给庞统。 - -``` -触发时机(不是 cron,是事件驱动): - -1. 步骤完成事件 - → daemon 立即通知庞统 - → 庞统判断:正常→无干预 / 异常→主动发指令 - -2. Agent 写入 observation - → daemon 检测到新文件(inotify/fswatch) - → 推送给庞统:"赵云发现数据异常" - → 庞统决定:加步骤 / 调整方案 / 通知用户 - -3. 定期心跳(每 N 分钟,可配置) - → daemon 推送当前全局摘要 - → 庞统判断是否有异常趋势(如某步骤超时、Agent 连续失败) - → 正常→回"继续" / 异常→主动干预 - -4. AI 质量评估点(Phase 2/3 交界处) - → 庞统审查当前执行计划 vs 实际进展 - → 主动调整步骤、重新分配 Agent、升级问题 -``` - -**Token 消耗控制**(通过质量分级): -- `critical` 任务:每个事件都推送 -- `standard` 任务:步骤完成 + observation 时推送 -- `exploratory` 任务:仅步骤完成时推送 - -#### 3.12.5 文件系统 = 实时共享感知 - -**核心洞察**:文件系统天然就是“实时共享”的——一个 Agent write 了文件,另一个 Agent 立刻可以 read。 -不需要 WebSocket、不需要推送、不需要轮询 API。 - -关键在于让 Agent **知道去读什么**。这就是 `status.json` + `observations/` 的作用: - -- `status.json`:daemon 在每次状态变化后立即更新,Agent read 即可获得全局视野 -- `observations/`:Agent 发现问题时主动写入,其他 Agent read 即可感知 -- `steps/`:前序步骤的产出物就在文件系统里,不需要通过 API 传输 - -**为什么不用 WebSocket/实时推送?** -因为 Agent 的执行是 `openclaw agent` CLI 调用——每次调用是一个独立进程。 -Agent 不需要“订阅”事件流,而是在被调用时通过 read 文件获取全部所需上下文。 -这比实时推送更简单、更可靠(无连接断开问题)。 - -#### 3.12.6 Agent 主动协作场景 - -``` -场景 1:赵云发现数据异常 - → 赵云写入 observations/zhaoyun-001.md - → daemon 检测到 → 通知庞统 - → 庞统判断:需要加清洗步骤 - → 庞统调整计划 → daemon 更新 status.json + plan.json - → 张飞被调度时 read status.json,看到新增的清洗步骤 - → 张飞 read observations/zhaoyun-001.md,了解数据异常详情 - → 张飞自主决定:在策略编码前加入数据清洗逻辑 - -场景 2:张飞主动建议 - → 张飞执行策略编码时,发现更好的技术指标 - → 张飞写入 observations/zhangfei-001.md - → daemon 通知庞统 - → 庞统判断:值得尝试,修改计划允许探索 - → 关羽审核时 read observations/,看到张飞的建议,针对性审查 - -场景 3:关羽主动拦截 - → 关羽审核代码时发现风险 - → 关羽写入 observations/guanyu-001.md(标记为 blocking) - → daemon 立即通知庞统 - → 庞统决定:暂停后续步骤,要求张飞修改 - → 不需要等步骤“完成”才发现问题 -``` - -#### 3.12.7 实现注意事项(司马懿第四轮评审反馈,v2.4 补充) - -> 司马懿判定:v2.4 设计方向正确,可进入实现。以下为实现时必须注意的 5 个细节。 - -**1. Agent 自主的真实边界** - -Agent 在执行开始时获取全局快照(read status.json + observations/),**执行过程中不感知变化**。 -并行执行场景下,如果需要运行时感知,由庞统通过 steer 触发。 -不在文件监控或实时推送上过度承诺。 - -**2. Observation 通知机制** - -不依赖 inotify/fswatch(macOS 可靠性存疑,SMB 挂载不工作)。 -改为:**Agent 写入 observation 后主动 POST `/api/observations` 通知 daemon**,daemon 再通知庞统。 -更简单、更可靠、不依赖文件系统事件。 - -**3. daemon vs 庞统的角色透明化** - -不要把 daemon 的确定性决策包装成"庞统定方向"。 - -| 决策类型 | 执行者 | 性质 | -|---------|-------|------| -| Agent 选择算法 | daemon (selector.py) | 确定性 | -| 状态流转规则 | daemon (orchestrator.py) | 确定性 | -| 超时检测 | daemon (watchdog.py) | 确定性 | -| 产出验证 | daemon (validator.py) | 确定性 + 可选 AI | -| Phase 2 规划 | **庞统 AI** | AI 决策 | -| Phase 3 异常裁决 | **庞统 AI** | AI 决策 | -| 计划调整审批 | **庞统 AI** | AI 决策 | -| 用户意图一致性检查 | **庞统 AI** | AI 决策 | - -**4. 文件系统可靠性细节** - -- `status.json` 更新必须使用 `atomic_write`(tmp → rename),复用 Section 2.1.4 的实现 -- observations 文件命名改为 `{agent}-{timestamp}-{uuid短}.md`,避免命名冲突 -- artifacts 目录在本地文件系统(Mac mini SSD),不写在 SMB 挂载上 - -**5. Observation 写入标准与频率限制** - -task-bootstrap Skill 中必须明确写入标准: -``` -observation 写入条件(必须满足至少一条): -- 发现了可能影响后续步骤的问题 -- 有能提升最终产出质量的建议 -- 遇到了专业外需要上报的问题 -禁止写入:进度报告、正常状态确认、无关细节 -``` - -频率限制:每个步骤每个 Agent 最多 3 个 observation,超过由 daemon 丢弃并记录 warning。 - -**6. 庞统上下文管理** - -每次被触发时,不把历史推理过程带入上下文,只带入当前状态快照。 -类似"每次被唤醒都是全新视角"——避免庞统 session 的上下文膨胀。 - -**7. B1 贯穿性:用户意图一致性检查** - -当用户在执行中途改方向时(steer),庞统不只重新规划,还应先评估影响并帮用户决策: -- "改成周线意味着数据量减少到 1/5,统计显著性可能不够。你确定吗?" -- "增加止损条件会导致策略复杂度上升,回测时间可能翻倍。要继续吗?" - -这是 PRD B1(AI 帮用户想清楚要什么)的全程贯穿,不限于 Phase 1。 -在庞统的 prompt 中加入"用户意图一致性检查"规则。 - -#### 3.12.8 AI native Skill 体系(v2.6 修订) - -> **v1.0 的 Skill 问题**:把命令操作手册写到 Skill 里——"数据获取步骤:1. 连接数据库 2. 执行 SQL 3. 保存 CSV"。 -> 这不是 AI native,这是给 AI 写操作流程。AI 自己会决定怎么操作。 -> -> **v2.0 AI native Skill**:不是告诉 AI 怎么做,而是设定边界——做什么是好的、做什么是错的、遇到什么情况该怎么想。 -> AI 自己决定具体步骤。 - -**三层 Skill 模型**: - -| 层级 | 名称 | 内容 | 类比 | -|------|------|------|------| -| L1 | **Principles**(原则) | 做事的底线和方向 | 宪法 | -| L2 | **Patterns**(模式) | 遇到什么情况应该怎么想 | 判例法 | -| L3 | **Anti-patterns**(反模式) | 绝对不能做的事 | 刑法 | - -**具体 Skill 内容示例**(`data-acquisition/SKILL.md`): - -```markdown -# 数据获取 Skill(赵云) - -## L1 原则 -- 数据获取后必须先检查质量再回报 -- 任何数据源声称已清洗都不能信任 -- 产出物必须包含数据质量报告 - -## L2 模式 -- 如果发现缺失值 < 5%:前值填充,记录在 observation -- 如果发现缺失值 > 5%:标记异常区间,在 observation 中建议后续步骤如何处理 -- 如果发现数据时间范围与任务要求不符:立即回报,不自行裁剪 -- 如果数据源有多个版本:选择最完整的,在 observation 中说明选择理由 - -## L3 反模式 -- 绝不能假设数据是干净的 -- 绝不能默默修复数据问题而不在 observation 中记录 -- 绝不能返回未经检查的数据 -- 绝不能跳过数据质量报告直接标记步骤完成 -``` - -**与 v1.0 的核心区别**: -- v1.0:"连接 jqdatasdk,执行 get_price(),保存到 CSV"(操作手册) -- v2.0:"获取后必须检查质量,缺失值要记录"(行为准则) -- AI 自己决定用什么工具、什么 API、什么格式 - -**v2.0 预设 Skill 目录**: - -``` -skills/ -├── task-bootstrap/SKILL.md # Agent 启动协议(感知→执行→观察) -├── task-report/SKILL.md # Agent 完成报告协议 -├── quality-gate/SKILL.md # 产出物自检协议 -├── orchestration-strategy/SKILL.md # 庞统调度策略(五原则 + 防降级) -├── data-acquisition/SKILL.md # 数据获取行为准则(赵云) -├── strategy-coding/SKILL.md # 策略编码行为准则(张飞) -├── risk-review/SKILL.md # 风控审核行为准则(关羽) -├── infra-management/SKILL.md # 基础设施管理行为准则(姜维) -└── experience-distill/SKILL.md # 经验蒸馏行为准则 -``` - -**Skill 格式**:Markdown(LLM 理解最好、token 最省、自由度最高)。 -每个 Skill 包含完整的 L1 + L2 + L3 三层。 - -**Skill 加载方式**: -- 每个 Agent 的 SOUL.md 中引用自己的专属 Skill -- task-bootstrap 和 quality-gate 通过任务消息模板注入(所有 Agent 通用) -- orchestration-strategy 通过庞统的 SOUL.md 注入 -- 经验蒸馏完成后,相关经验自动注入到后续任务的上下文中(半自动 Skill 更新) - -#### 3.12.9 庞统调度策略 Skill(v2.5 新增) - -> 综合 Microsoft Azure Checker Pattern、Google RouterAgent、oh-my-claudecode Team pipeline、AWS Strands ReWOO。 - -**调度五原则**: - -| # | 原则 | 说明 | 业界来源 | -|---|------|------|---------| -| 1 | **Capability Matching** | 根据 Agent 能力画像匹配任务,不是轮询分配 | Google RouterAgent | -| 2 | **Dependency-Aware Scheduling** | 先调度无依赖步骤(可并行),有依赖的等前置完成 | AWS Strands ReWOO | -| 3 | **Iterative Refinement** | 执行→验证→如有问题→第二轮修正→再验证(设上限) | Microsoft Azure Checker Pattern | -| 4 | **Quality-Gated Advancement** | 当前步骤通过门控后才调度下一步 | oh-my-claudecode verify→fix loop | -| 5 | **Escalation Over Failure** | 失败不放弃:重试→换 Agent→升级到用户 | Hermes per-task retry | - -**Iterative Refinement 具体流程**: - -``` -庞统调度张飞 → 张飞产出 → 庞统/司马懿审核 - → 通过:调度下一步 - → 未通过:调度张飞第二轮(带上审核意见 + 相关 observation) - → 张飞 read observations/ → 自主调整 → 再提交 - → 通过:调度下一步 - → 未通过 + 达到迭代上限(3次):升级到用户 -``` - -**并行步骤的处理**: -并行步骤间不需要实时感知。庞统在两个并行步骤都完成后, -检查 observation 是否有关联。如果有关联,调度受影响的 Agent 做第二轮。 - -#### 3.12.10 Observation 设计方案(v2.5 完善) - -**Observation 生命周期**: - -``` -1. Agent 执行中发现问题 - → 判断是否符合写入标准(影响后续步骤/提升质量/专业外问题) - → 不符合标准就不写(禁止进度报告、正常确认、无关细节) - -2. 写入 observation - → write artifacts/task-{id}/observations/{agent}-{timestamp}-{uuid短}.md - → 唯一强制:第一行必须是 [SEVERITY: blocking/warning/info] - → 内容格式完全自由(不固定 JSON 或 Markdown) - → POST /api/observations 通知 daemon - -3. daemon 收到通知 - → blocking:立即通知庞统 - → warning/info:步骤完成时一起通知 - -4. 庞统处理 - → 评估影响 → 决策(忽略/调整计划/通知后续 Agent/调度第二轮) - -5. 后续 Agent 感知 - → 被调度时 read observations/ → 自主决定是否调整 -``` - -**为什么内容格式不固定**: -固定 JSON 或 Markdown 格式会束缚 AI 的表达能力。 -Agent 根据发现的情况自由选择最合适的表达方式。 -唯一强制 severity 标记是因为 daemon 需要它来决策通知级别。 - -**频率限制**:每个步骤每个 Agent 最多 3 个 observation。 - -**observation 的 pav 执行记录**(v2.5 新增): -Agent 执行过程中的关键决策点用 severity=audit 记录: -- 为什么选这个方法 -- 做了什么取舍 -- 发现了什么 -audit 级别不通知庞统,只记录在文件系统供事后审查。 - -#### 3.12.11 Agent 调度方式(v2.6 终版确认) - -**实验结论**(2026-05-14 两轮 spawn 实验): -- `sessions_spawn` 配置 `allowAgents: ["*"]` 后可以指定其他 agentId -- 跨 Agent spawn 读写文件、结论回传、cleanup:delete 全部正常 -- **spawn 是 v2.0 调度将军的首选方式** - -**Gateway 配置**(已应用): -```json -// ~/.openclaw/openclaw.json → agents.list[id=pangtong-fujunshi] -{ "subagents": { "allowAgents": ["*"] } } -``` - -**v2.0 调度方式(按优先级排序)**: - -| 优先级 | 方式 | 用途 | 特点 | -|--------|------|------|------| -| **首选** | `sessions_spawn(agentId="xxx", cleanup="delete")` | 调度将军执行步骤 | 不走 Gateway、并行、自动清理、结论回传 | -| Fallback | `openclaw agent --agent xxx --local --json` | spawn 不可用时 | `--local` 本地运行,自动 fallback | -| 内部 | `sessions_spawn()` (无 agentId) | 庞统内部复杂分析 | 子 session 继承上下文 | - -**spawn 的关键优势**(vs `openclaw agent` CLI): -- 不依赖 Gateway(Gateway 挂了也能调度) -- 庞统可以通过 `subagents list` 实时监控子 session 状态 -- 完成后自动回传结果,不需要轮询 -- 支持嵌套调度(`maxSpawnDepth: 2`) -- 支持并行(`maxConcurrent: 8`) - -#### 3.12.12 庞统上下文管理(v2.6 新增) - -> **核心问题**:庞统在一个 session 里处理多个任务时,上下文会膨胀。如果“每次唤醒全新视角”, -> 之前的推理过程丢失,决策质量下降。如果保留全部上下文,token 爆炸。 -> **必须找到平衡**。 - -**调研来源**: -- **get-shit-done**:Wave Execution——每个 plan 用独立新鲜上下文,避免 context rot -- **Claude Code**:session linked by parent_session_id + auxiliary-LLM compression -- **学术:Episodic Memory**(arXiv:2502.06975):episodic memory 是长期 Agent 的缺失拼图 -- **学术:Memory as Action**(arXiv:2509.25250):自主上下文策展 - -**方案:三层上下文架构** - -``` -┌───────────────────────────────────────────┐ -│ L1: 永久记忆(跨任务保留) │ -│ MEMORY.md + 经验库 + Skill 体系 │ -│ 每次启动自动注入,不受上下文窗口限制 │ -└───────────────────────────────────────────┘ - -┌───────────────────────────────────────────┐ -│ L2: 任务工作记忆(单任务内保留) │ -│ decisions.jsonl + moments.jsonl + status.json │ -│ 存文件系统,庞统被唤醒时 read 获取 │ -│ → 这就是“上下文恢复协议”的实质 │ -└───────────────────────────────────────────┘ - -┌───────────────────────────────────────────┐ -│ L3: 推理上下文(每次唤醒重建) │ -│ 当前推理过程,对话历史中的近期部分 │ -│ 通过 OpenClaw compaction 自动管理 │ -└───────────────────────────────────────────┘ -``` - -**具体机制**: - -1. **庞统被唤醒时**: - - read `status.json`(当前全局状态) - - read `decisions.jsonl`(之前做过的决策及理由) - - read `moments.jsonl`(最近 10 条事件) - - 这三步重建 L2 工作记忆 - -2. **庞统做决策时**: - - 关键决策写入 `decisions.jsonl`(不只在对话历史里) - - 格式:`{decision, reason, alternatives_rejected, timestamp}` - - 下次被唤醒时能看到自己之前为什么做这个决策 - -3. **防止“忘了”的兜底**: - - 每个 observation 写入时,daemon 同时记录到 `moments.jsonl` - - 庞统被唤醒时看到的 moments 包含所有 Agent 的 observation 摘要 - - 即使庞统“忘了”对话中的推理,通过文件系统能找回来 - -4. **并行步骤完成后的识别**: - - 两个并行步骤都完成后,daemon 生成一个“并行步骤关联分析” - - 检查两个步骤的 observation 是否有关联 - - 如果有关联,写入 moments 并通知庞统 - - 庞统不需要“记住”并行步骤的关系——文件系统替他记着 - -**与之前“全新视角”方案的区别**: -- 之前:每次唤醒清空一切,只看 status.json -- 现在:每次唤醒有 L1(永久)+ L2(文件系统工作记忆)+ L3(对话上下文) -- L2 是关键创新——决策理由和 observation 摘要存在文件里,不依赖对话历史 - -#### 3.12.13 防降级机制(v2.6 新增) - -> **核心问题**:调度策略本身如何防止在执行过程中逐渐妥协? -> 庞统如何保证不为了“完成任务”而降级目标? - -**四个防降级机制**: - -**机制 1:反合理化表(Anti-Rationalization Table)** - -来源:Agent Skills 生命周期实践 - -写入 orchestration-strategy Skill 的 L3(反模式)层: - -``` -庞统自检清单——每个调度决策前必须过: -❌ “这个步骤简化一下” → 问:PRD 目标是否被覆盖? -❌ “时间不够跳过验证” → 没有验证的产出不算完成 -❌ “这个Agent做不到就算了” → 换 Agent 或拆步骤 -❌ “先用简单方案后续优化” → v2.0 就是完整实现 -❌ “用户应该不在意” → 你的判断 ≠ 用户需求 -``` - -**机制 2:目标锚定(Goal Anchoring)** - -每个任务的原始 goal 和 PRD 需求写入 `context.json`,不可修改。 -庞统每次做调度决策时必须对照 goal: -- 决策是否更接近 goal? -- 如果偏离 goal,必须升级到用户,不能自行降级 - -**机制 3:范围缩减检测(Scope Reduction Detection)** - -来源:get-shit-done 实践 - -planner.py 在 Phase 2 生成计划时,每个步骤必须标注覆盖哪些需求。 -daemon 在执行完所有步骤后,反向检查:每个需求是否被至少一个步骤覆盖。 -如果有遗漏,不允许进入 Phase 4,必须 replan。 - -**机制 4:验证前不完成(Verification-Before-Completion)** - -来源:superpowers 实践 - -Agent 声称完成时,validator.py 检查: -1. 产出物文件存在且非空 -2. 产出物与 end_state 描述匹配(AI 语义检查) -3. 没有未处理的 blocking observation -任何一项不通过,步骤状态不变为 completed。 - ---- - -## 4. 已决策(全部) - -| # | 决策 | 结论 | 理由 | -|---|------|------|------| -| 1 | 黑板载体 | **SQLite + 文件系统** | SQLite 存状态(查询快、事务安全),文件系统存产出物(git 可追踪) | -| 2 | 庞统运行方式 | **主 session + daemon API** | 庞统在正常 session 里与用户对话,通过 HTTP API 调度 daemon | -| 3 | Agent 调度 | **`openclaw agent` CLI(简单)+ sessions_spawn(复杂)** | CLI 是 Gateway 原生能力,有超时和 fallback;复杂步骤用 sub-agent 隔离 | -| 4 | 事件触发 | **daemon 内部事件循环** | Agent 回报时触发下一步,不用 cron | -| 5 | 配置化 | **YAML/JSON 配置文件** | 状态/流转/事件/模板全部配置化,代码零硬编码 | -| 6 | Agent 团队 | 复用三国角色 | 角色映射清晰 | -| 7 | v1.0 共存 | 独立 session,并行运行 | v1.0 和 v2.0 互不干扰 | -| 8 | 前端 | 先纯对话(Control Center) | 后续加可视化 | -| 9 | 状态机 | 继承 v1.0 + AI native 扩展 | steer/takeover/intervene/replan 等 | -| 10 | 执行历史 | sub-agent transcript 归档到任务目录 | 可追溯、可学习、可复盘 | -| 11 | Daemon 通知庞统 | `openclaw agent` CLI | 步骤完成/异常/需要裁决时 | -| 12 | 经验沉淀 | 任务完成后自动触发 AI 蒸馏 | + 规划/执行/异常时自动检索 | -| 13 | 实现方式 | **一次性完整实现** | 不做最小集迭代,避免做着做着偏离 | -| 14 | 治理原则 | **质量优先,成本和工期为质量让步,但有硬性上限** | 见 4.1 治理框架 | -| 15 | Agent 调度机制 | **`openclaw agent` CLI** | 司马懿评审:废弃自建 WS Client,改用 CLI | -| 16 | 数据存储 | **SQLite 为唯一权威,文件系统为索引缓存** | 司马懿评审:消除双写一致性问题 | -| 17 | 经验蒸馏 | **先两层(surface+heuristic),验证后扩展** | 司马懿评审:五层过重,YAGNI | -| 18 | 实施策略 | **分批集成(设计一次做完,实现分批验证)** | 司马懿评审:骨架→智能→高级 | -| 19 | Agent 协作模式 | **中央协调 + Agent 自主**(非纯中央调度) | v2.4 修正:庞统定方向约束,Agent 自主执行+主动观察 | -| 20 | 信息共享方式 | **文件系统直读**(非 API 查询) | v2.4 修正:status.json + observations/ 目录,Agent 直接 read | -| 21 | AI 参与方式 | **事件驱动持续参与**(非完成时通知) | v2.4 修正:步骤完成/observation/心跳/质量评估点四类触发 | -| 22 | Agent 主动性 | **observations 目录 + 自主行为协议** | v2.4 新增:Agent 可主动写观察、建议、异常,其他 Agent 可感知 | -| 23 | Skill 体系 | **三层模型(Principles + Patterns + Anti-patterns)** | v2.5 新增:综合 oh-my-claudecode/Hermes/Nuwa/Agent Skills | -| 24 | 调度策略 | **五原则(Capability/Dependency/Iterative/QualityGate/Escalation)** | v2.5 新增:综合 Azure/Google/OMC/AWS | -| 25 | Observation 格式 | **自由格式 + 强制 severity 标记** | v2.5 确认:不固定 JSON/MD,唯一强制 [SEVERITY: xxx] | -| 26 | Agent 调度方式 | **双模式(openclaw agent CLI + sessions_spawn)** | v2.5 确认:CLI 调度将军,spawn 庞统内部分析 | -| 27 | 并行感知方案 | **多轮迭代(非实时感知)** | v2.5 确认:庞统判断是否需第二轮,不依赖运行时感知 | -| 28 | 信息隔离预留 | **agent-registry.json 中 visibility 字段** | v2.6 预留:v2.0 全部 full,v2.1+ 按需开启 | -| 29 | Agent 调度首选 | **sessions_spawn(allowAgents配置已生效)** | v2.6 确认:实验验证跨 Agent spawn 可用 | -| 30 | 庞统上下文管理 | **三层架构(永久记忆+文件工作记忆+对话上下文)** | v2.6 新增:防止“全新视角”丢信息 | -| 31 | 防降级机制 | **四个机制(反合理化+目标锚定+范围缩减+验证前不完成)** | v2.6 新增:确保调度不降级 | -| 32 | Skill 本质 | **行为准则(L1原则+L2模式+L3反模式),非操作手册** | v2.6 明确:告诉 AI 做什么是对的,不告诉怎么做 | -| 33 | 评审模式 | **单任务单评审+汇总后再评审,交叉评审按需** | v2.6 确认 | - -### 4.1 质量/成本/工期治理框架 - -> 核心原则:**质量是第一优先级,成本和工期可以为质量妥协,但不能无限妥协。** -> -> 参考:Wanman TokenBudget(per-agent ceiling + 全局 ceiling)、Network-AI FederatedBudget(双层预算)、Network-AI QualityGate(两层门控:规则+AI)。 - -#### 三层预算体系 - -每个任务创建时分配三重预算,任何一重耗尽都触发干预: - -```yaml -# config/settings.yaml 中的默认预算 -budget_defaults: - token: - per_task_ceiling: 500000 # 单任务总 token 上限 - per_step_ceiling: 100000 # 单步骤 token 上限 - per_agent_ceiling: 200000 # 单 Agent token 上限 - time: - per_task_ceiling: 3600 # 单任务总时间上限(秒) - per_step_ceiling: 600 # 单步骤时间上限(秒) - depth: - max_plan_adjustments: 5 # 计划调整次数上限 - max_retries_per_step: 3 # 单步骤重试次数上限 - max_challenge_rounds: 3 # 挑战循环次数上限 -``` - -**预算耗尽时的行为**: - -| 预算类型 | 耗尽行为 | 谁干预 | -|---------|---------|--------| -| step token 耗尽 | 步骤标记 failed,触发异常处理 | Daemon 自动 | -| step 时间耗尽 | 步骤标记 failed,检查 Agent 存活 | Daemon 自动 | -| task token 耗尽 | 任务暂停,通知庞统 + 用户 | Daemon → 庞统 → 用户 | -| task 时间耗尽 | 任务暂停,通知庞统 + 用户 | Daemon → 庞统 → 用户 | -| 计划调整次数耗尽 | 停止调整,用当前最佳计划继续 | Daemon 自动 | -| 重试次数耗尽 | 步骤标记 failed,升级到庞统 | Daemon → 庞统 | - -#### 质量门控(Quality Gate) - -参考 Network-AI 两层门控: - -``` -Agent 提交产出物 - │ - ▼ -Layer 1: 规则验证(确定性,零成本,毫秒级) - ├── 文件存在性检查 - ├── 文件非空检查 - ├── Schema 校验(JSON/CSV 格式正确) - ├── Placeholder 检测(TODO/FIXME/...) - └── 基础完整性(字段非空、数值范围合理) - │ - ├─ 通过 → 进入 Layer 2 - └─ 失败 → reject(退回 Agent 修复) - │ - ▼ -Layer 2: AI 审查(有成本,秒级,仅当 confidence < 阈值时触发) - ├── 产出物是否与 end_state 描述匹配 - ├── 代码质量(变量命名、异常处理、逻辑正确性) - └── 幻觉检测(声称的结论是否有数据支撑) - │ - ├─ approve → 完成 - ├─ quarantine → 隔离待人工审查(通知庞统 + 用户) - └─ reject → 退回 Agent 修复 -``` - -**成本控制**:Layer 2 不是每次都跑。默认只在以下情况触发: -- Agent 回报 confidence < 0.7 -- 步骤是关键步骤(用户明确指定要审查的) -- 连续多个步骤 Layer 1 都一次通过(抽样审查,防止 Agent 学会绕规则) - -#### 质量提升的分级投入 - -不是所有任务都需要极致质量。根据任务重要性分级投入: - -```yaml -# config/settings.yaml -quality_levels: - critical: # 资金安全、实盘交易 - layer2_threshold: 0.8 - max_retries: 5 - always_ai_review: true - human_approval_required: true - token_budget_multiplier: 2.0 - - standard: # 策略回测、数据分析 - layer2_threshold: 0.7 - max_retries: 3 - always_ai_review: false - human_approval_required: false - token_budget_multiplier: 1.0 - - exploratory: # 探索性研究、快速验证 - layer2_threshold: 0.5 - max_retries: 1 - always_ai_review: false - human_approval_required: false - token_budget_multiplier: 0.5 -``` - -任务创建时,AI 根据内容自动判定 quality_level,用户也可以手动指定。 - -#### 成本回收机制 - -高质量的产出如果被后续任务复用,相当于摊薄了成本: - -- **经验沉淀**:高质量任务的产出自动蒸馏为经验,下次同类任务减少 token 消耗 -- **Skill 转化**:高频经验固化为 Skill,直接注入减少检索成本 -- **模板复用**:高质量的计划结构可以保存为模板,下次直接使用 - -#### 工期控制:何时干预 - -| 场景 | 判断标准 | 干预方式 | -|------|---------|----------| -| 明显死循环 | 同一步骤 retry > max_retries | 强制终止 + 升级 | -| 计划反复调整 | plan_adjustments > max | 锁定当前计划继续 | -| Agent 超时无响应 | step 执行时间 > per_step_ceiling | 检查存活 → 重试或换 Agent | -| 质量长期不达标 | 连续 3 个步骤 AI 审查 reject | 暂停任务 + 通知用户 | -| 成本超预期 | task token > 80% ceiling | 通知庞统评估是否值得继续 | -| 接近 deadline | task 时间 > 80% ceiling | AI 判断:是否降级质量等级以保工期 | - -#### AI 决策结构化 - -每个 AI 决策必须记录完整的推理过程(司马懿评审意见)。 - -```sql --- decisions 表结构化扩展 -CREATE TABLE decisions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - step_id TEXT, - decision_type TEXT NOT NULL, -- agent_selected / plan_adjusted / anomaly_classified / quality_review - - -- 结构化推理(司马懿要求:输入、选项、选择、原因、置信度) - input_summary TEXT NOT NULL, -- AI 看到了什么 - options_considered TEXT NOT NULL, -- 考虑了哪些选项(JSON array) - selected TEXT NOT NULL, -- 选了什么 - reason TEXT NOT NULL, -- 为什么选这个 - confidence REAL NOT NULL, -- 置信度 0.0-1.0 - - model TEXT, - token_cost INTEGER, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` - -#### 经验生命周期 - -经验不是写进去就永远有效: - -```sql --- experiences 表扩展 -ALTER TABLE experiences ADD COLUMN lifecycle TEXT DEFAULT 'draft'; -- draft / verified / superseded / expired -ALTER TABLE experiences ADD COLUMN verified_at TEXT; -ALTER TABLE experiences ADD COLUMN verified_by TEXT; -- ai / human -ALTER TABLE experiences ADD COLUMN expires_at TEXT; -- TTL,过期自动降权 -ALTER TABLE experiences ADD COLUMN superseded_by INTEGER REFERENCES experiences(id); -``` - -| 生命周期 | 说明 | 检索权重 | -|---------|------|---------| -| draft | AI 自动蒸馏,未验证 | 0.5x | -| verified | 经过实际任务验证(引用 >= 2 次) | 1.0x | -| superseded | 被更好的经验替代 | 0.1x | -| expired | TTL 过期,自动降权 | 0.0x(不检索) | - -#### 异常处理策略表 - -异常分类用确定性逻辑,处理策略用预设决策表(司马懿评审意见): - -```yaml -# config/exceptions.yaml -exceptions: - - type: agent_timeout - detection: "step execution time > per_step_ceiling" - strategy: - - action: check_agent_alive - - action: retry - max: 3 - backoff: exponential - - action: reassign_agent # 换一个 Agent - - action: escalate_to_human - - - type: validation_failed - detection: "quality gate Layer 1 failed" - strategy: - - action: reject_to_agent - max: 3 - - action: escalate_to_human - - - type: ai_judgment_conflict - detection: "two AI decisions contradict" - strategy: - - action: escalate_to_human # AI 冲突必须人工裁决 - - - type: resource_deadlock - detection: "circular dependency detected" - strategy: - - action: break_cycle # 随机释放一个等待 - - action: reassign_agent - - action: escalate_to_human - - - type: budget_exceeded - detection: "any budget ceiling reached" - strategy: - - action: pause_task - - action: notify_pangtong - - action: await_human_decision - - - type: unknown - detection: "unclassified exception" - strategy: - - action: ai_classify # 只有未知异常才让 AI 分类 - - action: apply_strategy # 应用 AI 选择的策略 - - action: escalate_to_human # 兜底 -``` - ---- - -## 5. 实现清单 - -> 用户决策:一次性完整实现,不做最小集迭代。 - -``` -核心基础设施: - [x] Daemon HTTP API (FastAPI + uvicorn + SQLite WAL) - [x] config/ 配置化体系(states.yaml / step-states.yaml / events.yaml / agent-registry.json) - [x] SQLite 表结构(tasks / steps / moments / agent_status / decisions / experiences) - [x] artifacts/ 文件系统结构 - [x] `openclaw agent` CLI 封装(daemon → Agent session 通信) - [x] daemon 启动时补偿归档(检查未归档的 transcript) - [x] observations/ 目录(Agent 主动观察与建议) - [x] status.json 实时全局摘要(daemon 每次事件后更新) - [x] Agent 自主行为协议(task-bootstrap Skill 中注入感知+执行+观察三段式) - [x] 庞统持续意识机制(事件驱动通知 + 定期心跳) - -四相循环: - [x] Phase 1 需求探索(庞统苏格拉底对话) - [x] Phase 2 动态规划(AI 生成 plan + 用户审批) - [x] Phase 3 自主执行(daemon 事件循环 + Agent 调度) - [x] Phase 4 主动汇报(AI 生成报告 + 用户验收) - -Agent 管理: - [x] agent-registry.json 能力画像 - [x] Agent 选择算法(基于能力匹配 + 历史表现) - [x] 主 session 调度(简单步骤) - [x] sub-agent 调度(复杂步骤 + cleanup=delete) - [x] 执行历史归档(transcript → artifacts) - -智能特性: - [x] 幻觉门控(产出验证) - [x] 挑战循环(reviewing → challenge pass/iterate/fail) - [x] 动态计划调整(异常时 replan) - [x] AI 决策记录(可追溯) - [x] 经验沉淀引擎(任务完成后自动蒸馏) - [x] 经验检索(规划/执行/异常时自动注入) - -人工介入: - [x] steer(用户改方向) - [x] takeover(用户接管步骤) - [x] intervene(用户主动干预) - [x] escalated(AI 主动升级) - [x] waiting_human(Agent 请求确认) - -监控 & 运维: - [x] 健康检查(daemon 内部定时 + API) - [x] Token 预算管理 - [x] Agent 心跳 - [x] 与 v1.0 并行运行验证 -``` - ---- - -## 附录:术语表 - -| 术语 | 含义 | -|------|------| -| Blackboard | 共享意识空间,所有 Agent 的唯一信息共享中枢 | -| Control Unit | AI 指挥官(庞统),负责动态规划、Agent 选择、异常处理 | -| Moment | 原子事件,任务执行过程中的最小信息单元 | -| Fidelity | 信息保真度,控制不同 Agent 看到多少信息 | -| propose→validate→commit | 三阶段原子写入,防止并发竞态 | -| Boids | 群体智能规则,让 Agent 自行涌现协作行为 | -| Auftragstaktik | 任务式指挥,只给目标不给步骤 | -| 幻觉门控 | 验证 Agent 产出是否真实存在 | -| Observations | Agent 主动观察与建议,写入共享目录供其他 Agent 和庞统感知 | -| Status.json | 全局状态摘要,daemon 实时维护,Agent 执行前必读 | -| 自主行为协议 | Agent 三段式行为:感知(read 全局) → 执行(自主决定) → 观察(write observations) | -| Ralph Loop | 持久目标跨 turn 保持机制 | - ---- - -## 附录 B:调研参考来源 - -### 共享意识空间物理结构 -- **OpenAI Codex Cookbook** — PM Agent 写 REQUIREMENTS.md / AGENT_TASKS.md,产出物 gating check -- **Wanman Agent Matrix** — `artifact.put` CLI,Agent 通过 bash 与系统交互,per-agent workspace 隔离 -- **Wiki 知识管理体系** — 四层金字塔 L0-L3 + 7 种知识产物类型 + DISCOVER→DISTILL→APPLY→IMPROVE 闭环 - -### Daemon API 设计 -- **Edict (三省六部)** — Transactional Outbox Pattern + Redis Streams 消费者组 + 死信队列 -- **Wanman** — 30+ JSON-RPC 方法 + 标准错误码体系 + Agent 零SDK CLI 模式 -- **Gstack** — 长存活守护进程模型 + 状态文件持久化 + 版本自动重启 -- **Multica** — 任务生命周期状态机 + 分层事件总线(进程内同步 + WebSocket 实时推送) - -### 经验沉淀引擎 -- **Nuwa Skill (女娲)** — 五层蒸馏框架(表象→心智模型→决策启发式→反模式→诚实边界) -- **Corpus2Skill** (arXiv 2604.14572) — 离线蒸馏为可导航的层级 Skill 目录 -- **A-MEM** (arXiv 2502.12110) — Zettelkasten 式记忆,新记忆触发已有记忆的更新 -- **MemAgents** (ICLR 2026 Workshop) — 单次学习 + 上下文感知检索 + 整合为可泛化知识 -- **Hermes** — skill_manage 使用中发现过时立即修复 - -### 中央协调 + Agent 自主(v2.4 新增) -- **Edict 朝堂议政** — TurnScheduler + phaseMask(Agent 声明在哪些阶段主动发言),MessageBus 消息总线 -- **Ouroboros (joi-lab)** — 后台意识循环 consciousness.py,任务间主动思考,宪法驱动 -- **Network-AI** — Blackboard + CRDT,Agent 读黑板、看到变化、自己决定行动 -- **Open Multi-Agent** — sharedMemory + delegate_to_agent,Agent 可主动委派给其他 Agent -- **oh-my-claudecode** — Team staged pipeline(team-plan→team-prd→team-exec→team-verify→team-fix),hub-spoke 协调 -- **Hermes Agent** — Durable SQLite-backed kanban board,Agent 通过 kanban_* 工具集自主 claim task,dispatcher 驱动 -- **Claude Code** — Grove 共享项目工作区、Swarm/Team hub-spoke 协调、sidechain transcript 隔离 -- **Claude Code Sub-Agent Collective** — task-orchestrator 路由枢纽 + 30+ 专业化 Agent + hook TDD 强制 -- **agent-kanban (saltbo)** — Agent-first task board,Mission control for AI workforce -- **AgentsMesh** — PTY sandbox + git worktree 隔离 + ticket-pod binding + 内置 Kanban -- **Operator (untra)** — kanban-shaped git-versioned 软件开发,Agent 自主认领工单 -- **MCP + A2A 协议** — MCP 提供上下文共享,A2A 提供 Agent 间直接通信(2026 H2 成熟) -- **Multi-Agent Blackboard System (arXiv:2510.01285)** — 消除中央协调器对 Agent 先验知识的需求 -- **awesome-agent-orchestrators** — 2026 年活跃的 Agent 编排器汇总(nullclaw, agentsmesh 等) diff --git a/docs/design/archive-2.0/spawner-monitor-design.md b/docs/design/archive-2.0/spawner-monitor-design.md index 34c29f9..d73364f 100644 --- a/docs/design/archive-2.0/spawner-monitor-design.md +++ b/docs/design/archive-2.0/spawner-monitor-design.md @@ -410,95 +410,25 @@ await self.spawner.spawn_full_agent( 存储在 `task_attempts.metadata` JSON 中。 -### counter v2.1:per (agent, session) 粒度(2026-05-26) - -#### 问题 - -v2.0 的 `max_per_agent=1` 是 per agent 粒度,导致: -- 张飞正在跑一个 task(sub session,use_main_session=False) -- 这时又来一个新 task 需要张飞 spawn 新 sub session -- counter 拦住 → AgentBusyError -- 但 sub session 之间是独立的(不同 session_id,不同 lock),不应该互相限制 - -#### 新设计:三层控制 - -| 层级 | 配置 | 默认 | 作用 | -|------|------|------|------| -| per session key | `max_per_session` | 1 | 同一 (agent, session) 不能并发 spawn | -| per agent | `max_concurrent_sessions` | 3 | 同一 agent 最多同时跑 N 个不同 session | -| global | `max_global_agents` | 5 | 全局总并发上限 | - -**counter key**:`f"{agent_id}:{session_id}"` -- main session → `zhangfei:main` -- sub session → `zhangfei:abc-123` -- 不同 key 互不影响,同一 key 最多 1 个 - -**session_id 分配前移**: - -```python -# 新顺序(spawn_full_agent 内部) -1. 分配 session_id(main → "main", reuse → 传入值, new → uuid4) -2. session state 检查(main 时才检查) -3. counter.can_acquire(agent_id, session_id) # 检查三层 -4. counter.acquire(agent_id, session_id) # 占用 key -5. spawn -``` - -#### cooldown - -cooldown 保持 per agent 粒度(API 429 限制的是 agent 的 API key,不是 session)。 -冷却期间该 agent 的所有 spawn 都被拒绝。 - -#### 超限行为 - -- `can_acquire` 返回 False → `AgentBusyError` -- 任务/mail 保持 pending,等下个 tick(30s)重试 -- 不会丢失 - -#### release 调用点 - -所有 release 必须带 session_id: - -| 调用点 | 当前 | 改后 | -|--------|------|------| -| wrapped_on_complete 闭包 | `release(aid)` | `release(aid, session_id)` | -| spawn 失败 fallback(行 441) | `release(agent_id)` | `release(agent_id, session_id)` | -| _do_retry 续杯 | `release(agent_id)` | `release(agent_id, old_session_id)` | - -wrapped_on_complete 闭包需要捕获外层 session_id 变量。 - -#### 配置变更 - -```yaml -# config/default.yaml -max_global_agents: 5 # 不变 -max_per_session: 1 # 新增,替代 max_per_agent -max_concurrent_sessions: 3 # 新增,per agent 总并发 -# max_per_agent: 已删除 -``` - -### counter 生命周期(v2.1:per session 级) +### counter 生命周期(v2.0:调用级) ``` -spawn_full_agent 内部 +spawn_full_agent 内部 acquire │ - ├─ 1. 分配 session_id - ├─ 2. session state 检查(main 时) - ├─ 3. can_acquire(agent_id, session_id) → 检查三层 - ├─ 4. acquire(agent_id, session_id) → 占用 key - ├─ 5. spawn 进程 - │ - ├─ 进程退出 → wrapped_on_complete(aid, session_id) → release(aid, session_id) + ├─ 进程退出 → wrapped_on_complete → counter.release() │ ├─ A1/A4 完成 → 结束 - │ ├─ A2/A3 timeout → _do_retry: release(aid, old_sid) → spawn_full_agent(acquire 新的) + │ ├─ A2/A3 timeout → _do_retry 手动 release → spawn_full_agent 重新 acquire │ └─ A7-A12 → release → 等 ticker │ ├─ monitor timeout(B)→ counter 不 release(进程还在跑) - │ └─ B1 假死 → 手动 release(aid, session_id) + 复活 + │ └─ B1 假死 → 手动 release + 复活 │ - └─ 进程崩溃/PM2 重启 → ticker _check_timeouts 检测 → 手动 release(aid, session_id) + └─ 进程崩溃/PM2 重启 → ticker _check_timeouts 检测 → 手动 release ``` +counter 生命周期 = 调用级:spawn 时 acquire,进程退出时 release。 +只有情况 B(进程不退出)counter 保持占用。 + wrapped_on_complete 保证 release(try/finally),即使业务回调异常也不泄漏。 ## 9. escalate 消息格式 @@ -558,3 +488,287 @@ Session: {session_key} | B2 compact 卡住 | 主 session 长对话触发 compact | 等待或 escalate | | B3 进程不退出 | 模拟长时间无输出 | 等 3 轮后 escalate | | 续杯上限 | 设 max_retries=1,任务永远完不成 | 第 2 次续杯后 failed | + +--- + +## 12. v2.8.1 补丁:Crash 健壮性(2026-05-31) + +> 作者:庞统 | 触发:R1 E2E 中 simayi 连续 crash 3 次无熔断 + +### 12.1 问题发现 + +R1 E2E 测试中,simayi-challenger 在 review 阶段连续 crash 3 次(exit=1,空 stdout): + +``` +18:32:20 Spawned (broadcast), session=None, pid=13844 + status=done, recent_compact=False ← compact 检测通过(实际 Gateway 在做 compact) + Gateway 加载 10MB main session → 触发 auto-compaction → 耗时 ~3 分钟 +18:35:47 Crash (exit=1), task_status=review + → on_complete: "review agent crashed, NOT marking done" +18:35:48 PM2 restart → ticker 重启 → re-dispatch review to simayi + recent_routing 未拦截(crash 时 routing_decision 未持久化) +18:36:51 Crash again (exit=1) +...(共 3 次 crash) +``` + +**根因**:main session 历史 10MB,Gateway 触发 compact,compact 耗时导致进程异常退出。 + +### 12.2 暴露的三个问题 + +#### P1:Compact 检测失效 + +现有检测逻辑: +```python +for cp in main_session.get("compactionCheckpoints", []): + if (now_ms - cp.get("createdAt", 0)) < 300_000: + result["recent_compact"] = True +``` + +**失效原因**: +- Gateway 的 compact 不写入 `compactionCheckpoints`(只在 session reset 时写入,且只保留 3 条) +- 实际 `compactionCount` 已达 68 次,但 checkpoints 最新一条是 10 天前 +- 因此 `recent_compact` 始终为 False + +#### P2:current_agent 不回退 + +review dispatch 时设置 `current_agent = simayi-challenger`,crash 后不回退。 +导致 `exclude_current=True` 时,simayi 被排除,但无其他 review agent 可用 → 任务卡住。 + +#### P3:Crash 无分级防护 + +- crash 后无 cooldown(cooldown 只在 api_error/429 时设置) +- review 状态无超时回收(`_check_timeouts` 只查 claimed/working) +- 无 crash 计数上限,同一 task 可无限重试 +- v2.7.2 设计文档曾讨论 crash_count 熔断,结论是"❌ 不采纳,崩溃可能是任务问题" + → 部分正确,但缺乏更精细的分级处理 + +### 12.3 修复方案 + +#### Fix-1:Compact 检测改用 session jsonl 末尾扫描 + +**原理**:Gateway 完成一次 compact 后,会在 session jsonl 文件末尾追加一条 `type=compaction` 记录。 + +```python +def _check_recent_compaction(agent_id: str, window_seconds: int = 300) -> bool: + """读 session jsonl 末尾,检查是否有 window_seconds 内的 compaction 记录""" + sessions_path = Path.home() / ".openclaw" / "agents" / agent_id / "sessions" / "sessions.json" + if not sessions_path.exists(): + return False + try: + with open(sessions_path) as f: + sessions = json.load(f) + main = sessions.get(f"agent:{agent_id}:main", {}) + session_file = main.get("sessionFile", "") + if not session_file or not Path(session_file).exists(): + return False + # 读末尾 50 行,查找最近的 compaction + from datetime import datetime, timezone + now = datetime.now(timezone.utc) + with open(session_file, "rb") as sf: + # seek to last 50KB + sf.seek(0, 2) + size = sf.tell() + sf.seek(max(0, size - 51200)) + tail = sf.read().decode("utf-8", errors="replace") + for line in reversed(tail.splitlines()): + if not line.strip(): + continue + try: + obj = json.loads(line) + except: + continue + if obj.get("type") == "compaction": + ts = obj.get("timestamp", "") + if ts: + ct = datetime.fromisoformat(ts.replace("Z", "+00:00")) + if (now - ct).total_seconds() < window_seconds: + return True + return False + except Exception: + return False +``` + +**改动范围**:`spawner.py` `_check_session_state` 方法,替换 `compactionCheckpoints` 检测逻辑。 + +**同时保留** `_compact_waits` 的 monitor compact 等待逻辑不变。 + +**边界处理**(review WARN 补充): +- jsonl 不存在 → 返回 False(无 compact = 安全) +- json.loads 失败 → 跳过该行(compact 未完成 = 不判定为 compacting) +- 只在 agent 非空闲时(status 不是 done/idle)才扫描,减少不必要 I/O +- 检测到 compacting 后同时设 cooldown(和 Fix-3a 共用),避免 ticker 反复 dispatch → AgentBusyError 循环 + +#### Fix-2:current_agent 回退 + +**原理**:crash 后在 `_task_on_complete` 中回退 `current_agent` 到 `assignee`。 + +```python +# dispatcher.py _task_on_complete 中 +if _is_review: + if outcome in ("completed", "session_revived"): + _dispatcher._mark_task_status(_task_db, _task_id, "done") + else: + logger.warning("Task %s: review agent %s (%s), NOT marking done", _task_id, aid, outcome) + # Fix-2: crash 后回退 current_agent,避免 exclude_current 卡死 + conn = get_connection(_task_db) + try: + conn.execute( + "UPDATE tasks SET current_agent = " + "(SELECT assignee FROM tasks WHERE id=?) " + "WHERE id=? AND current_agent=?", + (_task_id, _task_id, aid) + ) + conn.commit() + finally: + conn.close() +``` + +**改动范围**:`dispatcher.py` `_task_on_complete` 回调,~6 行。 + +**精确化条件**(review WARN 回应): +```python +ROLLBACK_OUTCOMES = {"crashed", "compact_failed", "process_crash", "session_stuck", "compact_hanging"} +``` +只对明确的异常 outcome 回退,skipped/cancelled 不回退。 + +**assignee 排除问题**:回退后 assignee 可能仍在 exclude 列表。但 exclude_current 的逻辑是在 `_dispatch_reviews` 中基于 `task.current_agent` 构建,回退后 current_agent=assignee(原始执行者),exclude 会排除的是 review agent(已 crash 的那个),不是 assignee。所以无冲突。 + +#### Fix-3:Crash 分级防护 + +**设计原则**:不是"禁用 agent"(v2.7.2 已否决),而是"给 agent 恢复时间 + 限制单 task 重试次数"。 + +##### 3a. Crash 后设 cooldown + +```python +# spawner.py _handle_exit else 分支(crash 走这里) +if outcome in ("crashed", "compact_failed", "process_crash", "agent_error"): + if self.counter: + self.counter.set_cooldown(agent_id, seconds=300) # 5 分钟冷却 + logger.info("Crash cooldown set for %s: 300s", agent_id) +``` + +**改动范围**:`spawner.py` `_handle_exit` else 分支,~4 行。 + +**理由**:crash 后 agent 的 session 可能处于异常状态(timeout/compacting), +5 分钟冷却给 Gateway/agent 恢复时间。如果 compact 正在进行,5 分钟后一般已完成。 + +**优化**(review INFO 采纳):cooldown 固定 5 分钟足够,不引入轮询复杂度。 +compact 场景通过 Fix-1 的 compact 检测 + AgentBusyError 覆盖,cooldown 是额外保险。 + +##### 3b. Review 超时回收 + +在 `_check_timeouts` 中增加 review 状态的超时检查: + +```python +# ticker.py _check_timeouts 末尾增加 +# review 超时 → 推回 pending(让 ticker 重新 dispatch review) +review_tasks = queries.tasks_by_status("review") +for task in review_tasks: + # 检查是否有活跃的 review agent(current_agent 对应的进程是否存活) + current = task.current_agent # 需要确认 task model 有此字段 + if current: + session_info = self.spawner.get_session_by_agent(current) if self.spawner else None + pid = session_info.get("pid") if session_info else None + if pid and self._is_pid_alive(pid): + continue # review agent 还在跑,不回收 + # 无活跃进程 → 检查超时 + updated = task.updated_at + if updated: + elapsed = (now - datetime.fromisoformat(updated)).total_seconds() / 60.0 + if elapsed > self.review_timeout_minutes: # 默认 15 分钟 + conn = get_connection(db_path) + try: + self._transition_status( + conn, task.id, "pending", + agent="daemon", + detail={"reason": "review_timeout", "elapsed_minutes": round(elapsed, 1)}, + ) + # 回退 current_agent + conn.execute( + "UPDATE tasks SET current_agent = NULL WHERE id=?", (task.id,) + ) + conn.commit() + logger.warning("Review timeout: %s (%.1fm), pushed back to pending", + task.id, elapsed) + finally: + conn.close() +``` + +**改动范围**:`ticker.py` `_check_timeouts`,~25 行。 + +**配置**:新增 `review_timeout_minutes` 参数,默认 15 分钟。 + +**"无活跃进程"定义**(review WARN 回应):双层判断: +1. `spawner.get_session_by_agent(current_agent)` → 检查内存中是否有 running session +2. 如果有 → 检查 PID 存活 → PID 活着则不回收 +3. 如果无内存记录 → 无活跃进程 → 可回收 + +**推回原子性**(review WARN 回应):推回前先 recheck 当前状态, +如果已是 pending/working/done/failed 则不推回(可能被其他操作改变了)。 + +**Fix-3b/3c 交互**(review INFO 回应):超时推回 pending → ticker 重新 dispatch → +如果又 crash,这个 crash 会通过 `_record_attempt` 记录到 task_attempts, +自然被 `_check_crash_limit` 计数。无需特殊处理。 + +##### 3c. Crash 计数 + 上限 escalate + +利用现有 `task_attempts` 表,在 dispatch 前查该 task 的最近 crash 次数: + +```python +# dispatcher.py dispatch() 入口增加 +def _check_crash_limit(self, task_id: str, db_path: Path, limit: int = 3) -> bool: + """检查 task 最近 30 分钟内的 crash 次数是否超限""" + try: + conn = get_connection(db_path) + try: + row = conn.execute( + "SELECT COUNT(*) as cnt FROM task_attempts " + "WHERE task_id=? AND outcome='crashed' " + "AND created_at > datetime('now', '-30 minutes')", + (task_id,) + ).fetchone() + return (row["cnt"] if row else 0) >= limit + finally: + conn.close() + except Exception: + return False +``` + +在 `_dispatch_reviews` 中调用: +```python +if self._check_crash_limit(task.id, db_path, limit=3): + # 超限 → 标 failed + escalate + conn = get_connection(db_path) + try: + self._transition_status( + conn, task.id, "failed", + agent="daemon", + detail={"reason": "review_crash_limit", "task_id": task.id}, + ) + finally: + conn.close() + logger.error("Task %s: 3 crashes in 30min, marking failed", task.id) + continue +``` + +**改动范围**:`dispatcher.py` 新增 ~15 行方法 + `_dispatch_reviews` 调用点 ~8 行。 + +### 12.4 改动总览 + +| 文件 | 改动 | 行数 | +|------|------|------| +| `spawner.py` | Fix-1 compact 检测替换 + Fix-3a crash cooldown | ~30 行 | +| `dispatcher.py` | Fix-2 current_agent 回退 + Fix-3c crash 计数 | ~25 行 | +| `ticker.py` | Fix-3b review 超时回收 | ~25 行 | +| **总计** | | **~80 行** | + +### 12.5 测试计划 + +| 用例 | 模拟方式 | 验证 | +|------|---------|------| +| Compact 检测 | 手动在 session jsonl 末尾写 compaction 记录 | recent_compact=True → AgentBusyError | +| current_agent 回退 | review agent crash → 检查 DB current_agent = assignee | ✅ 回退 | +| Crash cooldown | review agent crash → 检查 counter.is_cooling_down() | ✅ 5 分钟冷却 | +| Review 超时 | review 状态 15 分钟无活跃进程 → 检查推回 pending | ✅ 回收 | +| Crash 计数上限 | 同一 task 连续 crash 3 次 → 检查标 failed | ✅ escalate | +| E2E 回归 | 重新跑 v3.0 E2E 测试 | 全流程通过 | diff --git a/docs/design/archive-2.0/test-plan-v2.6.md b/docs/design/archive-2.0/test-plan-v2.6.md index 076dc30..7e7cb26 100644 --- a/docs/design/archive-2.0/test-plan-v2.6.md +++ b/docs/design/archive-2.0/test-plan-v2.6.md @@ -290,74 +290,16 @@ def mock_spawner(): | F3 | ✅ | ✅ 11 | ✅ | ✅ 11/11 | ✅ 通过 | | F4 | ✅ | ✅ 14 | ✅ | ✅ 14/14 | ✅ 通过 | | F5 | ✅ | ✅ 23 | ✅ | ✅ 23/23 | ✅ 通过(附 1 条意见) | -| F6 | ✅ 本文档 | ✅ 17 | ✅ | ✅ 通过 | ✅ | -| F7 | ✅ 本文档 | ✅ 5 | ✅ | ✅ 通过 | ✅ | -| F8 | ✅ 本文档 | ✅ 4 | ✅ | ✅ 通过 | ✅ | -| F9 | ✅ 本文档 | ✅ 16 | ✅ | ✅ 通过 | ✅ | -| F10 | ✅ 本文档 | ✅ 13 | ✅ | ✅ 通过 | ✅ | -| F11 | ✅ 本文档 | ✅ 10 | ✅ | ✅ 通过 | ✅ | -| F12 | ✅ 本文档 | ✅ 13 | ✅ | ✅ 通过 | ✅ | +| F6 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | +| F7 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | +| F8 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | +| F9 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | +| F10 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | +| F11 | ✅ 本文档 | ⬜ 本文档 | ⬜ | — | — | +| F12 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | | F13 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | | F14 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F15 | ✅ 本文档 | ✅ 4 | ✅ | ✅ 通过 | ✅ | +| F15 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | | F16 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F17 | ✅ 本文档 | ✅ 6 | ✅ | ✅ 通过 | ✅ | +| F17 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | | F18 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | - ---- - -## 八、v2.8 新增测试(2026-06-01 更新) - -> 覆盖 #07 Spawner Acquire-First + Prompt v3.0 + _check_timeouts 统一 - -### F6-ext: Ticker _check_timeouts 增强(新增 4 个测试) - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T-ext1 | crash_limit 统一检查(working) | ✅ | executor crash 3 次/30min → _check_timeouts 标 failed | -| T-ext2 | crash_limit 统一检查(review) | ✅ | reviewer crash 3 次/30min → _check_timeouts 标 failed | -| T-ext3 | updated_at fallback | ✅ | mail auto-working 无 started_at → updated_at fallback → 超时回收 | -| T-ext4 | process_dead 对 review 状态 | ✅ | review agent 进程死 → 保持 review(不推 pending) | - -### F9-ext: Spawner Acquire-First(新增 6 个测试) - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T-ext1 | Phase 0 Pre-acquire revive | ✅ | timeout/failed → revive → acquire 成功 | -| T-ext2 | Phase 0 假死检测 | ✅ | status=running + lock PID 死 → revive | -| T-ext3 | Phase 1 Counter acquire 互斥 | ✅ | 并发 spawn → 第二个 AgentBusyError(counter_blocked) | -| T-ext4 | Phase 2 Session check 锁保护 | ✅ | acquire 后 → session locked → release counter → AgentBusyError(session_locked) | -| T-ext5 | Phase 2 多 blockers 收集 | ✅ | locked + compact → detail.blockers 列表 | -| T-ext6 | Phase 3 on_checks_passed 异常回滚 | ✅ | 回调异常 → counter 自动 release | - -### F9-ext2: Compact Hanging + Retry(新增 3 个测试) - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T-ext1 | compact_hanging release counter | ✅ | compact 超限 → 不标 failed → release counter → 任务保持 working | -| T-ext2 | compact_hanging ticker 重新 dispatch | P1 | compact_hanging → ticker 超时 → pending → 重新 dispatch | -| T-ext3 | retry 遇 busy 释放 counter | ✅ | _do_retry 遇 AgentBusyError → release counter → 任务保持 working | - -### F9-ext3: AgentBusyError 分类(新增 3 个测试) - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T-ext1 | counter_blocked | ✅ | counter acquire 失败 → reason=counter_blocked | -| T-ext2 | session blockers | ✅ | session locked/running/compacting → reason + detail.blockers | -| T-ext3 | Dispatcher 错误区分 | ✅ | AgentBusyError → 路由决策记录具体原因 | - -### 设计文档引用 - -- `docs/design/07-spawner-acquire-first.md`:#07.1 Acquire-First、#07.2 Executor/Review 统一、#07.3 Startup Recovery -- `docs/design/03-prompt-evolution.md`:Prompt v3.0 任务式指挥 -- `docs/design/06-pm2-crash-recovery.md`:PM2 crash 恢复设计(启动恢复机制,待实施) - -### E2E 测试文件映射 - -| 文件 | 覆盖范围 | 状态 | -|------|---------|------| -| `tests/test_e2e_v27.py` | E1-E10 API + 状态机 + 真实 Agent | ✅ 已有 | -| `tests/test_e2e_v31.py` | v3.1 E2E 集成 | ✅ 已有 | -| `tests/test_e2e_four_phase.py` | #01 四相循环 E2E | ✅ 已有 | -| `tests/test_four_phase_unit.py` | #01 四相循环单元测试 | ✅ 已有 | -| E11-E15 新增用例 | 待添加到 test_spawner.py / test_ticker.py / test_dispatcher.py | ⬜ 本轮更新 | diff --git a/docs/design/deployment-scripts.md b/docs/design/deployment-scripts.md deleted file mode 100644 index 8c439e0..0000000 --- a/docs/design/deployment-scripts.md +++ /dev/null @@ -1,292 +0,0 @@ -# moziplus v2 运维脚本设计 - -**版本**: 3.0.0 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-22 -**基于**: deployment-v2.6.md + deployment-v2.6-guide.md(更新版) - ---- - -## 1. 概述 - -moziplus v2 提供一套运维脚本,覆盖部署、状态查看、数据管理、备份和卸载的全生命周期。 - -### 脚本清单 - -| 脚本 | 用途 | 风险等级 | -|------|------|---------| -| `deploy.sh` | 部署/升级 | 低(不覆盖 data/config) | -| `status.sh` | 运行状态查看 | 无(只读) | -| `build-frontend.sh` | 构建前端 | 无 | -| `backup.sh` | 备份数据到 NAS | 无 | -| `reset-data.sh` | 清空运行数据 | 高(删数据) | -| `uninstall.sh` | 完全卸载 | 高(删目录) | - -### 目录约定 - -``` -开发目录: ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/ ← Git 仓库 -安装目录: ~/.sanguo_projects/sanguo_moziplus_v2/ ← PM2 运行 -脚本位置: 开发目录/scripts/ -``` - ---- - -## 2. 环境要求 - -| 依赖 | 版本 | 用途 | -|------|------|------| -| Python | ≥ 3.9 | 后端运行 | -| Node.js | ≥ 20 | 前端构建 | -| PM2 | ≥ 5.0 | 进程管理 | -| rsync | 任意 | 代码同步 | -| curl | 任意 | 健康检查 | -| sqlite3 | 任意 | 数据查询(status -v) | -| NAS SMB | — | 备份目标(backup.sh) | - ---- - -## 3. 场景手册 - -### 场景 1:首次部署 - -**触发**:新机器上第一次安装 moziplus v2 - -```bash -# 从开发目录部署 -cd ~/.openclaw/sanguo_projects/sanguo_moziplus_v2 -bash scripts/deploy.sh - -# 自定义安装目录 -bash scripts/deploy.sh --target=/path/to/install - -# 跳过前端构建(已有 dist/) -bash scripts/deploy.sh --skip-build -``` - -**deploy.sh 执行流程**: -1. 前置检查(Python/PM2/源码目录) -2. 前端构建(npm install → npm run build) -3. 创建安装目录 -4. rsync 同步代码(排除 data/logs/inbox/config/docs/tests) -5. 确保目录结构(data/logs/inbox/config) -6. 首次创建默认配置(不覆盖已有) -7. PM2 start(首次) -8. 前端 + 后端健康检查 -9. 输出结果 - -**排除项**(不部署到生产): -- `docs/` — 设计文档只存开发目录 -- `tests/` — 测试代码不需要在生产环境 -- `scripts/` — 脚本从开发目录执行 -- `config/` — 不覆盖用户配置 -- `data/` `logs/` `inbox/` — 运行时数据 -- `frontend/src/` `node_modules/` — 只需要 dist/ - -### 场景 2:日常升级(代码更新后) - -**触发**:开发目录有代码变更,需要更新生产环境 - -```bash -# 标准升级(自动构建前端 + 重启) -cd ~/.openclaw/sanguo_projects/sanguo_moziplus_v2 -bash scripts/deploy.sh - -# 仅后端改动,跳过前端构建 -bash scripts/deploy.sh --skip-build -``` - -**deploy.sh 自动检测**: -- 安装目录已存在 → PM2 restart(而非 start) -- 不覆盖 `config/` 和 `data/` -- 健康检查确认服务正常 - -### 场景 3:查看运行状态 - -```bash -# 简要状态 -bash scripts/status.sh - -# 详细状态(项目列表、任务数、磁盘占用) -bash scripts/status.sh -v -``` - -**输出示例**: -``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - moziplus v2 — Status -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Version: v3.0.0 - Install: ~/.sanguo_projects/sanguo_moziplus_v2 ✅ - PM2: online ✅ - PID: 19591 - Restarts: 18 - Memory: 49.6MB - Health: running ✅ - Ticks: 106 - Projects: 26 - Data size: 23M -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -``` - -### 场景 4:前端独立构建 - -**触发**:只改了前端代码,不需要完整部署 - -```bash -bash scripts/build-frontend.sh -# 产物在 src/frontend/dist/ -# 然后用 deploy.sh --skip-build 同步 -``` - -### 场景 5:数据备份 - -```bash -# 在线备份(不停服务,SQLite 可能有不一致快照,日常可接受) -bash scripts/backup.sh - -# 一致性备份(停服务) -bash scripts/backup.sh --stop - -# 指定 NAS 路径 -bash scripts/backup.sh --nas=/Volumes/stock -``` - -**备份策略**: -- 目标:NAS `/Volumes/stock/moziplus-v2-backups/backup-/` -- 内容:data/ + config/ + metadata.json -- 自动保留最近 10 个备份 -- metadata.json 记录时间、版本、是否停服务 - -### 场景 6:数据重置 - -**触发**:测试数据污染、需要干净环境 - -```bash -# 预览(不加 --confirm 只显示会做什么) -bash scripts/reset-data.sh - -# 清空所有数据 -bash scripts/reset-data.sh --confirm - -# 只清指定项目 -bash scripts/reset-data.sh --project=demo --confirm -bash scripts/reset-data.sh --project=demo,e2e-test --confirm -``` - -**安全保护**: -- 必须传 `--confirm`,否则只预览不执行 -- 停服务 → 清数据 → 重启 -- trap EXIT 确保异常时也恢复服务 -- 保留 config/ 配置 - -### 场景 7:完全卸载 - -**触发**:不再使用 moziplus v2 - -```bash -# 预览 -bash scripts/uninstall.sh - -# 完全删除(含数据) -bash scripts/uninstall.sh --confirm - -# 保留数据 -bash scripts/uninstall.sh --confirm --keep-data -``` - -**安全保护**: -- 必须传 `--confirm` -- `--keep-data` 将 data/ 移动到 `~/moziplus-v2-data-backup-/` -- PM2 delete + save - ---- - -## 4. 典型工作流 - -### 日常开发 → 部署循环 - -``` -开发目录修改代码 - ↓ -git commit(sanguo-git-sync 自动推送到 gitee) - ↓ -bash scripts/deploy.sh - ↓ -bash scripts/status.sh(确认正常) - ↓ -继续开发 或 测试验证 -``` - -### 版本发布 - -``` -1. 开发目录完成功能 + 测试 -2. 司马评审通过 -3. 更新 pyproject.toml 版本号 -4. git tag + git push -5. bash scripts/deploy.sh -6. bash scripts/backup.sh --stop(发布前备份) -7. bash scripts/status.sh -v(确认) -``` - -### 故障恢复 - -``` -1. bash scripts/status.sh(确认问题) -2. pm2 logs sanguo-moziplus-v2(查看日志) -3. 如需回滚数据:从 NAS 恢复备份 -4. 如需回滚代码:git revert → bash scripts/deploy.sh -5. 如 v2 不可用:pm2 stop sanguo-moziplus-v2,切回 v1(8082) -``` - ---- - -## 5. 脚本参数速查 - -### deploy.sh -| 参数 | 默认值 | 说明 | -|------|--------|------| -| `--source=DIR` | 开发目录 | 源码目录 | -| `--target=DIR` | `~/.sanguo_projects/sanguo_moziplus_v2` | 安装目标 | -| `--skip-build` | false | 跳过前端构建 | - -### status.sh -| 参数 | 说明 | -|------|------| -| `-v, --verbose` | 显示项目详情、配置信息 | - -### build-frontend.sh -无参数。自动检测 package-lock 变化并安装依赖。 - -### backup.sh -| 参数 | 默认值 | 说明 | -|------|--------|------| -| `--target=DIR` | 安装目录 | 数据源 | -| `--nas=DIR` | `/Volumes/stock` | NAS 挂载点 | -| `--stop` | false | 备份前停服务 | - -### reset-data.sh -| 参数 | 默认值 | 说明 | -|------|--------|------| -| `--target=DIR` | 安装目录 | 目标 | -| `--project=NAME` | 全部 | 指定项目(逗号分隔) | -| `--confirm` | false | **必须传** | - -### uninstall.sh -| 参数 | 默认值 | 说明 | -|------|--------|------| -| `--target=DIR` | 安装目录 | 目标 | -| `--keep-data` | false | 保留数据到备份目录 | -| `--confirm` | false | **必须传** | - ---- - -## 6. 与旧部署文档的关系 - -本文档替代以下旧文档: -- `deployment-v2.6.md` §6 部署流程 → 改用 `deploy.sh` -- `deployment-v2.6-guide.md` 全文 → 改用脚本 + 本文档场景说明 -- `deployment-v2.6.md` §13 运维场景手册 → 改用 `status.sh` + `backup.sh` + `reset-data.sh` - -旧文档保留作为架构参考,不删除。 diff --git a/docs/design/deployment-v2.6-guide.md b/docs/design/deployment-v2.6-guide.md deleted file mode 100644 index 6e237ff..0000000 --- a/docs/design/deployment-v2.6-guide.md +++ /dev/null @@ -1,171 +0,0 @@ -# v2.6 部署指南 - -**版本**: 2.6.0 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-17 - ---- - -## 架构概览 - -``` -安装目录: ~/.sanguo_projects/sanguo_moziplus_v2/ -├── config/default.yaml # 全局配置 -├── data/ # 运行时数据(项目黑板DB) -├── src/ # Python 源码 -│ ├── main.py # FastAPI 入口 -│ ├── frontend/dist/ # 前端构建产物 -│ ├── api/ # API 路由 -│ ├── blackboard/ # 黑板核心 -│ └── daemon/ # Daemon 模块 -├── tests/ # 单元测试 -├── pyproject.toml # Python 项目配置 -├── ecosystem.config.cjs # PM2 配置 -└── requirements.txt # Python 依赖 - -开发目录: ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/ -└── (同结构,git 仓库) -``` - -## 路径解析策略 - -所有路径通过以下优先级解析: - -1. **环境变量** `BLACKBOARD_ROOT` → 项目数据根目录(最高优先级) -2. **配置文件** `config/default.yaml` 的 `data_root` → 项目数据根目录 -3. **相对路径默认** → `{安装目录}/data/`(`main.py` 用 `__file__` 相对定位) - -### 关键路径 - -| 用途 | 解析方式 | 默认值 | -|------|---------|--------| -| 项目数据 | `BLACKBOARD_ROOT` 环境变量 或 `config.data_root` | `{src/../data/}` | -| 配置文件 | `__file__` 相对定位 | `{src/../config/default.yaml}` | -| 前端静态文件 | `__file__` 相对定位 | `{src/frontend/dist/}` | -| Inbox JSONL | 相对于项目数据目录 | `{项目}/inbox/daemon.jsonl` | -| 日志 | PM2 管理 | stdout | - -## 部署步骤 - -### 1. 同步代码 - -```bash -# 从开发目录同步到安装目录 -rsync -av --exclude='__pycache__' --exclude='.pytest_cache' \ - ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/ \ - ~/.sanguo_projects/sanguo_moziplus_v2/ -``` - -### 2. 安装依赖 - -```bash -cd ~/.sanguo_projects/sanguo_moziplus_v2 -pip3 install -r requirements.txt -``` - -### 3. 配置 - -编辑 `config/default.yaml`,按需调整: - -```yaml -# 数据根目录(可选,不设则用 {安装目录}/data/) -data_root: "~/.sanguo_projects/sanguo_moziplus_v2/data" - -daemon: - tick_interval: 30 - max_global_agents: 5 -``` - -### 4. 前端构建(如果从开发目录 rsync 了 dist/ 可跳过) - -```bash -cd ~/.sanguo_projects/sanguo_moziplus_v2/src/frontend -npm install -npm run build -# 产物在 src/frontend/dist/ -``` - -### 5. PM2 启动 - -```bash -cd ~/.sanguo_projects/sanguo_moziplus_v2 -pm2 start ecosystem.config.cjs -``` - -### 6. 验证 - -```bash -# API -curl http://localhost:8080/api/daemon/status - -# Swagger 文档 -open http://localhost:8080/docs - -# 前端 Dashboard -open http://localhost:8083 -# 或(如果 FastAPI 托管静态文件) -open http://localhost:8080/ -``` - -## PM2 配置 - -`ecosystem.config.cjs`: - -```javascript -module.exports = { - apps: [{ - name: "sanguo-moziplus-v2", - script: "src/main.py", - interpreter: "python3", - cwd: "/Users/chufeng/.sanguo_projects/sanguo_moziplus_v2", - env: { - BLACKBOARD_ROOT: "/Users/chufeng/.sanguo_projects/sanguo_moziplus_v2/data", - }, - max_memory_restart: "500M", - log_date_format: "YYYY-MM-DD HH:mm:ss", - }] -}; -``` - -## NAS/Docker 部署(姜维负责) - -- 数据目录挂载到 NAS `/Volumes/stock/moziplus-v2/` -- Docker 容器运行 FastAPI + 前端 -- 定时 `sqlite3 backup` 黑板 DB 到 NAS - -## 开发 → 部署 同步流程 - -``` -开发目录修改 → git commit → rsync 到安装目录 → pm2 restart → 验证 - ↑ ↓ - └────────────── 回滚(git revert)←─────────────┘ -``` - -## 测试 - -### 单元测试 - -```bash -cd ~/.sanguo_projects/sanguo_moziplus_v2 -python3 -m pytest tests/ -q -``` - -### 端到端测试(司马懿执行) - -```bash -# 1. 创建项目 -python3 src/cli/admin.py create demo "Demo Project" --agents agent1,agent2 - -# 2. 创建任务 -python3 src/cli/blackboard.py create demo --title "Test Task" --type coding - -# 3. 查看任务 -python3 src/cli/blackboard.py read demo - -# 4. API 测试 -curl http://localhost:8080/api/projects -curl http://localhost:8080/api/daemon/status - -# 5. 前端访问 -open http://localhost:8083 -``` diff --git a/docs/design/deployment-v2.6.md b/docs/design/deployment-v2.6.md deleted file mode 100644 index 7d4267e..0000000 --- a/docs/design/deployment-v2.6.md +++ /dev/null @@ -1,493 +0,0 @@ -# v2.6 部署方案设计 - -**版本**: v2.6.2-deploy -**基于**: technical-design-v2.6.md v2.6.2 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-16 - ---- - -## 1. 部署环境 - -| 项目 | 规格 | -|------|------| -| 主机 | 楚锋的 Mac mini (Apple M4, ARM64, 16GB RAM, macOS) | -| Python | 3.11(moziplus venv) | -| Node.js | v22.22.1 | -| PM2 | 6.0.14 | -| OpenClaw Gateway | 127.0.0.1:18789 | -| NAS | 192.168.2.154(SMB /Volumes/stock/) | -| Windows-Test-Node | 192.168.2.33 | - -### 目录规划 - -| 用途 | 路径 | 说明 | -|------|------|------| -| 开发目录 | `~/.openclaw/sanguo_projects/sanguo_moziplus_v2/` | Git 管理 | -| 安装目录 | `~/.sanguo_projects/sanguo_moziplus_v2/` | PM2 运行 | -| Git 远程 | gitee (origin) + gitea (NAS 192.168.2.154) | 双远程 | - ---- - -## 2. 部署架构 - -``` -PM2 (进程管理器) -├── sanguo-moziplus-v2 (port 8083) ← 🆕 v2 主进程(API + Daemon ticker + 前端) -├── sanguo-moziplus (port 8082) ← v1 保留(v2 验证后下线) -├── sanguo-git-sync ← 不变 -├── sanguo-mail-* (8个) ← v2 验证后逐步下线 -└── sanguo-mozi ← 不变 - -进程内部结构: -sanguo-moziplus-v2 -├── uvicorn (FastAPI) — asyncio event loop -│ ├── API 路由(黑板/Daemon/SSE/项目) -│ ├── 静态文件服务(frontend/dist/) -│ └── SSE 推送端点(/api/events) -├── asyncio background task: Daemon ticker(30s) -├── asyncio background task: Inbox watcher(1s 轮询) -├── Agent spawn: asyncio.create_subprocess_exec(异步非阻塞) -└── ActiveAgentCounter(asyncio.Semaphore) -``` - -**纯 asyncio 单线程。** Daemon ticker、API、SSE、Agent spawn 全部在同一个 asyncio event loop 中,无同步阻塞调用。Full Agent spawn 用 `asyncio.create_subprocess_exec`(不 await 完成),Subagent 用 Gateway 内部 API。不新增 PM2 进程。 - ---- - -## 3. 多项目数据布局 - -``` -~/.sanguo_projects/sanguo_moziplus_v2/ -├── projects/ -│ ├── _registry.yaml ← 全局注册表(YAML,人可读,Git 版本管理) -│ ├── sanguo_quant_live/ -│ │ ├── blackboard.db ← per-project 黑板 -│ │ ├── config/ -│ │ │ └── project.yaml ← per-project 配置覆盖 -│ │ ├── artifacts/ ← per-project 产出物 -│ │ │ └── {task-id}/ -│ │ │ ├── outputs/ -│ │ │ ├── reviews/ -│ │ │ ├── archive/ -│ │ │ └── data/ -│ │ └── experiences/ ← per-project 经验库 -│ │ └── skills/ ← per-project Skill 覆盖/扩展 -│ ├── sanguo_vnpy/ -│ │ └── ... -│ └── _archived/ ← 归档项目(只读) -│ └── old_project/ -│ -├── inbox/ -│ └── daemon.jsonl ← Inbox 事件推送 -│ -├── skills/ ← 全局 Skill 库 -│ ├── builtins/ ← 系统内置 Skill -│ │ ├── blackboard_operations/ -│ │ ├── code_review/ -│ │ └── data_validation/ -│ └── custom/ ← 蒸馏产出/用户创建 Skill -│ └── {skill_id}/ -│ ├── SKILL.md ← 四要素 -│ ├── template.md ← 可选详细模板 -│ └── meta.yaml ← 版本/来源/tag -│ -├── config/ -│ ├── default.yaml ← 全局默认配置 -│ ├── guardrails.yaml -│ ├── template_components.yaml -│ └── review_protocols/ -│ ├── plan_review.yaml -│ ├── output_review.yaml -│ ├── analysis_review.yaml -│ └── final_review.yaml -│ -├── prompt_templates/ ← L2 模板(全局共享) -├── schemas/ ← Schema 校验(全局共享) -└── src/ ← 代码 -``` - -**物理隔离**:每个项目的 blackboard.db 完全独立。一个项目数据库损坏不影响其他项目。 - ---- - -## 4. PM2 配置 - -```javascript -// ecosystem.config.cjs -module.exports = { - apps: [{ - name: 'sanguo-moziplus-v2', - script: '~/.sanguo_projects/sanguo_moziplus_v2/.venv/bin/uvicorn', - args: 'src.main:app --host 0.0.0.0 --port 8083', - cwd: '~/.sanguo_projects/sanguo_moziplus_v2', - interpreter: 'none', - autorestart: true, - max_restarts: 10, - restart_delay: 3000, - watch: false, - env: { - PYTHONPATH: '~/.sanguo_projects/sanguo_moziplus_v2', - BLACKBOARD_ROOT: '~/.sanguo_projects/sanguo_moziplus_v2/projects', - INBOX_PATH: '~/.sanguo_projects/sanguo_moziplus_v2/inbox/daemon.jsonl', - LOG_LEVEL: 'INFO', - }, - out_file: '~/.sanguo_projects/sanguo_moziplus_v2/logs/daemon.out.log', - error_file: '~/.sanguo_projects/sanguo_moziplus_v2/logs/daemon.err.log', - }] -} -``` - ---- - -## 5. 配置体系 - -### 5.1 全局配置(default.yaml) - -```yaml -daemon: - tick_interval: 30 # Tick 间隔(秒) - task_timeout: 600 # 单任务超时(秒,10分钟) - max_global_agents: 5 # 全局最大并发 Agent - max_per_agent: 1 # 每 Agent 最大并发(默认 sequential) - zombie_threshold: 20 # 连续 N tick 无变更 → 告警 - -inbox: - path: "inbox/daemon.jsonl" - watch_interval: 1 # Inbox 轮询间隔(秒) - max_size_bytes: 1048576 # 1MB 触发 truncate - -review: - default_max_rounds: 3 - debate_max_rounds: 5 - confidence_threshold: 0.7 # 低于此值升级庞统 - -experience: - distill_threshold: 5 # 同 tag 积累 N 条触发二级蒸馏 - expire_days: 30 # 30天无引用 → deprecated - -logging: - level: INFO - max_file_size: 10MB -``` - -### 5.2 Per-project 配置覆盖 - -```yaml -# projects/{project_id}/config/project.yaml -project: - name: "量化实战项目" - description: "sanguo_quant_live" - agents: - - pangtong-fujunshi - - simayi-challenger - - zhangfei-dev - - guanyu-dev - - zhaoyun-data - - jiangwei-infra - max_global_agents: 3 # 覆盖全局默认 - context: - nas_path: "/Volumes/stock/" - backtest_service: "http://192.168.2.154:8088" - frameworks: - - "vnpy" - - "sanguo_vnpy" -``` - ---- - -## 6. 部署流程 - -### 6.1 首次部署 - -```bash -# 1. 创建安装目录 -mkdir -p ~/.sanguo_projects/sanguo_moziplus_v2/{projects,inbox,logs,config} - -# 2. 同步代码(从开发目录) -rsync -av --exclude='.venv' --exclude='node_modules' --exclude='.git' \ - ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/ \ - ~/.sanguo_projects/sanguo_moziplus_v2/ - -# 3. 安装 Python 依赖 -cd ~/.sanguo_projects/sanguo_moziplus_v2 -python3 -m venv .venv -.venv/bin/pip install fastapi uvicorn pyyaml - -# 4. 构建前端 -cd src/frontend && npm install && npm run build && cd ../.. - -# 5. 启动 PM2 -pm2 start ecosystem.config.cjs - -# 6. 验证 -curl http://127.0.0.1:8083/api/daemon/status -curl http://127.0.0.1:8083/ # 前端页面 -``` - -### 6.2 日常更新 - -```bash -# 1. 同步代码 -rsync -av --exclude='.venv' --exclude='node_modules' --exclude='.git' \ - ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/ \ - ~/.sanguo_projects/sanguo_moziplus_v2/ - -# 2. 如果前端有变更 -cd ~/.sanguo_projects/sanguo_moziplus_v2/src/frontend && npm run build && cd ../.. - -# 3. 重启 -pm2 restart sanguo-moziplus-v2 - -# 4. 验证 -curl http://127.0.0.1:8083/api/daemon/status -``` - -### 6.3 创建项目 - -```bash -# 通过 CLI 创建 -python3 src/cli/admin.py project create \ - --name "量化实战" \ - --id "sanguo_quant_live" \ - --agents "pangtong-fujunshi,simayi-challenger,zhangfei-dev,guanyu-dev,zhaoyun-data,jiangwei-infra" - -# 自动创建 projects/sanguo_quant_live/ 目录和 blackboard.db -``` - ---- - -## 7. 回滚方案 - -| 场景 | 回滚方式 | -|------|---------| -| v2 启动失败 | `pm2 stop sanguo-moziplus-v2`,v1 (8082) 不受影响 | -| Daemon tick 异常 | API `POST /api/daemon/stop` 停 ticker 不停服务 | -| 单项目数据库损坏 | 删除该项目的 `blackboard.db`,重启自动重建 | -| 全局不可用 | v1 仍在 8082 运行,切换回 v1 | -| 前端构建失败 | 回退 `dist/` 目录到上一版 Git commit | - -**v1 和 v2 完全独立**。v2 出问题直接停掉,v1 继续服务。 - ---- - -## 8. 监控 - -### 8.1 健康端点 - -``` -GET /api/daemon/status -{ - "ticker_running": true, - "last_tick": "2026-05-16T23:00:00", - "tick_interval_seconds": 30, - "projects": { - "sanguo_quant_live": { - "active_agents": 2, - "tasks_summary": {"pending": 3, "working": 1, "review": 1, "done": 15}, - "db_size_bytes": 45056 - } - }, - "inbox_pending": 0, - "active_sessions": 2 -} -``` - -### 8.2 逻辑健康自检 - -Daemon 每 tick 检查:连续 N tick(默认 20,即 10 分钟)无任何任务状态变更 → 写 observation 告警 + 通知用户。 - -PM2 只能检测进程崩溃,逻辑死循环/卡死需要应用层检测。 - -### 8.3 日志 - -```bash -# Tick 日志 -pm2 logs sanguo-moziplus-v2 --lines 100 | grep "Tick" - -# Agent spawn 日志 -pm2 logs sanguo-moziplus-v2 --lines 100 | grep "Spawning" - -# 审查日志 -pm2 logs sanguo-moziplus-v2 --lines 100 | grep "review\|rebuttal" -``` - -### 8.4 SSE 推送监控 - -前端通过 `/api/events` SSE 接收实时事件。推送 4 级: - -| 级别 | 图标 | 场景 | -|------|------|------| -| 🔴 紧急 | 任务失败/系统异常 | 立即推 | -| 🟡 需关注 | 任务超时/审查不通过 | 实时推 | -| 🟢 日常 | 任务完成/日报 | 批量推 | -| 🔵 可选 | Agent 活动日志 | 用户订阅 | - ---- - -## 9. 安全 - -| 项目 | 措施 | -|------|------| -| SQLite 文件权限 | `blackboard.db` owner-only 读写(0600) | -| API 认证 | 内网环境,暂不认证;后续可加 API Key | -| CLI 限制 | blackboard.py 只能本地 exec,不暴露网络 | -| Agent 权限 | SOUL.md 约束 + claim assignee 检查 | -| 项目隔离 | per-project SQLite 物理隔离,一个 WHERE 漏掉也不会跨项目 | -| Session 清理 | fcntl 文件锁保护 sessions.json 编辑 | -| 归档安全 | 项目归档前必须无 working 状态任务 | - ---- - -## 10. 性能预估 - -| 指标 | 预估值 | 理由 | -|------|--------|------| -| Tick 耗时 | <50ms | 单项目 <10 张表几十条记录 | -| Agent spawn 延迟 | 2-5s | openclaw agent 冷启动 | -| 并发 Agent 数 | ≤5(全局) | ActiveAgentCounter 控制 | -| 项目数上限 | ~20 | SQLite 单文件无压力,PM2 不新增进程 | -| Inbox 文件大小 | <200KB | 每秒 truncate,崩溃积压 ~200KB/1000条 | -| blackboard.db 大小 | <10MB/项目 | 每任务 ~50-100KB,含索引和 WAL overhead | -| 前端首屏加载 | <2s | Vite 构建优化 + 本地网络 | - ---- - -## 11. v1 → v2 迁移计划 - -``` -Phase 1: v2 后端验证(v1 不动) - ├── v2 部署到 8083 - ├── CLI 验证全流程 - └── E2E 测试通过 - -Phase 2: v2 前端验证 - ├── 前端构建部署 - ├── Dashboard 功能验证 - └── SSE 推送验证 - -Phase 3: 并行运行 - ├── v1 继续服务(8082) - ├── v2 接受新任务(8083) - └── 对比 v1/v2 行为一致性 - -Phase 4: v1 下线 - ├── pm2 stop sanguo-moziplus - ├── pm2 delete sanguo-moziplus - ├── v2 可选接管 8082 - └── v1 代码和数据库保留只读(历史归档) - -Phase 5: Mail 逐步下线(远期目标,与黑板评论长期共存) - ├── v2 上线后 Mail 和黑板评论并行 - ├── 所有 Agent 的 Skill 更新支持黑板评论后逐步关闭 Mail poller - └── 最终只保留 Mail 作为外部通知通道 -``` - ---- - -## 12. 依赖梳理 - -### 12.1 运行时依赖 - -| 依赖 | 版本要求 | 用途 | 安装方式 | -|------|---------|------|----------| -| Python | ≥ 3.9(推荐 3.11) | Daemon + FastAPI | macOS 系统自带 3.9.6 / venv 用 3.11 | -| Node.js | ≥ 20 | 前端构建 | nvm 管理 | -| PM2 | ≥ 5.0(当前 6.0.14) | 进程管理 | `npm i -g pm2` | -| FastAPI | latest | HTTP API | `pip install fastapi` | -| uvicorn | latest | ASGI server | `pip install uvicorn` | -| PyYAML | latest | YAML 配置读写 | `pip install pyyaml` | -| OpenClaw CLI | ≥ 2026.5.7 | Agent spawn / sessions_spawn | 已安装 | -| Git | ≥ 2.x | 版本管理 | 系统自带 | - -### 12.2 构建时依赖 - -| 依赖 | 用途 | 说明 | -|------|------|------| -| npm / pnpm | 前端构建 | React + Vite + TypeScript | -| rsync | 开发→安装同步 | macOS 自带 | - -### 12.3 外部服务依赖 - -| 服务 | 地址 | 用途 | 容错 | -|------|------|------|------| -| OpenClaw Gateway | 127.0.0.1:18789 | Agent spawn / sessions_spawn | v2 启动时检查,不可用则 Daemon 只运行 tick 不调度 Agent | -| NAS SMB | 192.168.2.154 | 数据存储(跨项目桥接) | 挂载失败时 per-project 功能不受影响 | -| Gitee | gitee.com | Git 远程 | 网络不可用时本地正常运行 | -| Gitea | 192.168.2.154:3000 | Git 备份远程 | 网络不可用时本地正常运行 | - ---- - -## 13. 运维场景手册 - -### 13.1 日常运维 - -| 场景 | 操作 | 频率 | -|------|------|------| -| 查看系统状态 | `curl http://127.0.0.1:8083/api/daemon/status` | 按需 | -| 查看日志 | `pm2 logs sanguo-moziplus-v2 --lines 50` | 按需 | -| 重启服务 | `pm2 restart sanguo-moziplus-v2` | 更新后 | -| 查看活跃 Agent | `curl http://127.0.0.1:8083/api/daemon/sessions` | 按需 | - -### 13.2 数据备份 - -| 数据 | 备份方式 | 频率 | -|------|---------|------| -| blackboard.db | `sqlite3 backup` 到 NAS | 每小时 cron | -| _registry.yaml | Git 管理 | CLI 操作时自动 commit | -| skills/ 目录 | Git 管理 | Skill 变更时 commit | -| 前端 dist/ | Git 管理 | 构建后 commit | -| artifacts/ | 不备份(可重建) | — | - -**备份策略**:Git 只管文本文件(YAML/Markdown/JSON/代码/前端 dist)。`blackboard.db`、`artifacts/`、`experiences/` 加到 `.gitignore`,用独立备份策略: -- `blackboard.db`:每小时 cron 执行 `sqlite3 {db} ".backup '{backup_path}'"` 到 NAS。WAL 模式下直接 git add `.db` 文件会拿到不一致快照(db 和 -wal/-shm 不匹配),必须用 SQLite 官方 backup 命令 -- `_registry.yaml` + `config/` + `skills/` + `dist/`:Git 管理,推送到 gitee + gitea 双远程 - -### 13.3 磁盘满处理 - -```bash -# 1. 检查磁盘占用 -du -sh ~/.sanguo_projects/sanguo_moziplus_v2/projects/*/ - -# 2. 归档不活跃项目 -python3 src/cli/admin.py project archive --id old_project - -# 3. 清理归档项目 artifacts -rm -rf ~/.sanguo_projects/sanguo_moziplus_v2/projects/_archived/old_project/artifacts/ - -# 4. 清理旧日志 -pm2 flush sanguo-moziplus-v2 -``` - -### 13.4 告警升级 - -| 级别 | 触发条件 | 处理方式 | -|------|---------|----------| -| 自动恢复 | 进程崩溃 | PM2 auto restart | -| 应用告警 | 逻辑死循环 | observation 写黑板 + SSE 推送 | -| 人工介入 | DB 损坏 / 逻辑异常 | Dashboard 告警 + 用户介入 | -| 回滚 | v2 不可用 | `pm2 stop sanguo-moziplus-v2`,切回 v1(8082) | - ---- - -## 14. 交付检查清单 - -P1 部署前逐项验证: - -- [ ] 项目创建、git init、远程配置完成 -- [ ] PM2 配置正确,`pm2 start` 成功 -- [ ] 全局注册表 `_registry.yaml` 可读写,项目列表正确 -- [ ] CLI `admin.py project create` 创建项目成功 -- [ ] per-project `blackboard.db` 自动创建且 schema 正确(含 reviews/experiences/comments.comment_type) -- [ ] CLI `blackboard.py read/claim/output/comment/decide/observe/create/review` 全部可用 -- [ ] Daemon tick 30s 循环正常运行 -- [ ] Inbox JSONL watcher 正常消费 + truncate -- [ ] Agent spawn 成功(asyncio.create_subprocess_exec 非阻塞 + sessions_spawn) -- [ ] Session 完成后自动存档 -- [ ] ActiveAgentCounter 并发控制生效 -- [ ] API 端点全部可用(/api/projects/ /api/daemon/ /api/events) -- [ ] SSE 推送正常工作 -- [ ] 前端构建产物可访问(8083 端口) -- [ ] 健康端点返回正确状态 -- [ ] 逻辑健康自检(连续 20 tick 无变更→告警) -- [ ] v1 (8082) 不受 v2 (8083) 影响 diff --git a/docs/design/development-plan-v2.6.md b/docs/design/development-plan-v2.6.md deleted file mode 100644 index 2966efc..0000000 --- a/docs/design/development-plan-v2.6.md +++ /dev/null @@ -1,327 +0,0 @@ -# v2.6 开发计划 - -**版本**: v2.6.0-plan -**基于**: technical-design-v2.6.md v2.6.3 + deployment-v2.6.md v2.6.3 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-17 -**状态**: 执行中 - ---- - -## 开发模式 - -- **分工**: 庞统编码(F1-F18 全部),司马懿测试+评审 -- **流程**: 写完一个功能 → 单元测试 → 发司马懿评审 → 通过 → 下一个 -- **参考实践**: superpowers(verify-before-complete)、oh-my-claudecode(Execution+Enhancement+Guarantee 三层)、hermes(僵尸检测+幻觉门控) -- **原则**: 每个功能一次做完,不分 phase;不确定的先记录待确认区,按 AI native + 最优实践方向先执行 - ---- - -## 开发顺序(按依赖拓扑) - -``` -Layer 0: F1 骨架 → F2 黑板 -Layer 1: F3 多项目 → F4 CLI → F5 API -Layer 2: F6 Ticker → F7 Inbox → F8 健康检查 -Layer 3: F9 调度器 → F10 Counter → F11 Bootstrap -Layer 4: F12 审查流水线 → F13 Guardrail → F14 反驳权 -Layer 5: F15 经验沉淀 → F16 Skill -Layer 6: F17 SSE+Hook → F18 前端 -``` - -### F1 项目骨架 + 配置体系 - -| 项目 | 内容 | -|------|------| -| **依赖** | 无 | -| **估计** | 1h | -| **产出** | `src/main.py`(FastAPI 空壳)+ `config/default.yaml` + `pyproject.toml` + `ecosystem.config.cjs` + `.gitignore` | -| **完成标准** | `pm2 start` 成功,`/api/daemon/status` 返回 200,`/docs` 自动文档可访问 | -| **测试** | `test_main.py`: 启动、健康端点、CORS | - -### F2 黑板核心(DB + CRUD) - -| 项目 | 内容 | -|------|------| -| **依赖** | F1 | -| **估计** | 3h | -| **产出** | `src/blackboard/{db,models,operations,queries}.py` + `schemas/*.json` | -| **完成标准** | 全部 9 表 CRUD 可用,WAL + busy_timeout + BEGIN IMMEDIATE,schema 校验通过 | -| **测试** | `test_blackboard.py`: create_task / claim / output / comment / decide / observe / review / events 写入 + 状态机转换 + 并发写入 | - -### F3 多项目管理 - -| 项目 | 内容 | -|------|------| -| **依赖** | F1, F2 | -| **估计** | 2h | -| **产出** | `src/blackboard/registry.py` + `src/cli/admin.py`(project create/delete/list) | -| **完成标准** | CLI 创建项目 → per-project DB 自动创建 → `_registry.yaml` 更新 → list 显示正确 | -| **测试** | `test_registry.py`: create/delete/list + YAML 读写 + 并发注册 | - -### F4 CLI 工具 - -| 项目 | 内容 | -|------|------| -| **依赖** | F1, F2 | -| **估计** | 2h | -| **产出** | `src/cli/blackboard.py`(8 个子命令) | -| **完成标准** | read / claim / output / comment / decide / observe / create / review 全部可用 | -| **测试** | `test_cli.py`: 每个子命令 happy path + 参数校验 + 错误处理 | - -### F5 API 层 - -| 项目 | 内容 | -|------|------| -| **依赖** | F1, F2 | -| **估计** | 3h | -| **产出** | `src/api/{blackboard,daemon,project,sse}_routes.py` | -| **完成标准** | 全部 API 端点 curl 可用,Swagger 文档完整,SSE 端点可连接 | -| **测试** | `test_api.py`: 每个端点 CRUD + 状态码 + 参数校验 | - -### F6 Daemon Ticker - -| 项目 | 内容 | -|------|------| -| **依赖** | F2, F3, F5 | -| **估计** | 3h | -| **产出** | `src/daemon/ticker.py` | -| **完成标准** | 30s 循环运行,scan_tasks + 依赖推进 + events 写入正常,asyncio background task 与 FastAPI 共存 | -| **测试** | `test_ticker.py`: tick 循环 + 状态扫描 + 依赖推进 + 多项目轮询 | - -### F7 Inbox JSONL Watcher - -| 项目 | 内容 | -|------|------| -| **依赖** | F6 | -| **估计** | 1.5h | -| **产出** | `src/daemon/inbox.py` | -| **完成标准** | Agent 写 JSONL → 1s 内 Daemon 收到并处理 + truncate | -| **测试** | `test_inbox.py`: 写入+消费+truncate + 并发写入 + 损坏文件恢复 | - -### F8 健康检查 - -| 项目 | 内容 | -|------|------| -| **依赖** | F6 | -| **估计** | 1h | -| **产出** | `src/daemon/health.py` | -| **完成标准** | 连续 20 tick 无变更 → observation 告警写入 | -| **测试** | `test_health.py`: 正常/僵尸/恢复 场景 | - -### F9 Agent 调度器 - -| 项目 | 内容 | -|------|------| -| **依赖** | F6, F7 | -| **估计** | 3h | -| **产出** | `src/daemon/{dispatcher,spawner}.py` | -| **完成标准** | 三级调度(Daemon/Full/Sub)可用,`asyncio.create_subprocess_exec` spawn 不阻塞 event loop | -| **测试** | `test_dispatcher.py`: 三级决策树 + `test_spawner.py`: spawn + session 注册 + 超时 | - -### F10 ActiveAgentCounter - -| 项目 | 内容 | -|------|------| -| **依赖** | F9 | -| **估计** | 1h | -| **产出** | `src/daemon/counter.py` | -| **完成标准** | max_global=5 控制,per-agent 串行,acquire/release 正确 | -| **测试** | `test_counter.py`: 全局上限 + per-agent 串行 + 并发竞争 + release 恢复 | - -### F11 Bootstrap 拼装 - -| 项目 | 内容 | -|------|------| -| **依赖** | F1, F2 | -| **估计** | 2h | -| **产出** | `src/daemon/bootstrap.py` + `prompt_templates/*.md` + `config/template_components.yaml` | -| **完成标准** | `build_bootstrap()` 按 role 拼装 L0-L3 四层,token 占用 < 4096 | -| **测试** | `test_bootstrap.py`: 各 role 拼装 + token 估算 + 缺失组件降级 | - -### F12 审查流水线 - -| 项目 | 内容 | -|------|------| -| **依赖** | F9, F10, F11 | -| **估计** | 3h | -| **产出** | `src/daemon/review_flow.py` + `config/review_protocols/*.yaml` | -| **完成标准** | 4 级分级审查(high/standard/low/research)端到端可用,confidence 计算 + 升级逻辑正确 | -| **测试** | `test_review_flow.py`: 4 级分流 + confidence 阈值 + 升级庞统 | - -### F13 Guardrail 系统 - -| 项目 | 内容 | -|------|------| -| **依赖** | F9, F11 | -| **估计** | 2h | -| **产出** | `src/daemon/guardrail.py` + `config/guardrails.yaml` | -| **完成标准** | L1 assert 自动执行 + L2 subagent 二次确认,配置驱动 | -| **测试** | `test_guardrail.py`: L1 通过/失败 + L2 触发 + 配置加载 + 误杀恢复 | - -### F14 反驳权流程 - -| 项目 | 内容 | -|------|------| -| **依赖** | F12 | -| **估计** | 2h | -| **产出** | 集成在 `review_flow.py` + `prompt_templates/rebuttal.md` | -| **完成标准** | 审查不通过 → 反驳协商 → max_rounds → 庞统裁决 | -| **测试** | `test_rebuttal.py`: 反驳触发 + 轮次计数 + 超轮次升级 + 结果写入 | - -### F15 经验沉淀 - -| 项目 | 内容 | -|------|------| -| **依赖** | F6, F2 | -| **估计** | 2h | -| **产出** | `src/daemon/experience.py` | -| **完成标准** | 任务完成 → 一级蒸馏 → experiences 表写入 → 二级蒸馏触发 → Skill 草稿生成 | -| **测试** | `test_experience.py`: 一级蒸馏 + tag 匹配 + 二级触发 + 过期清理 | - -### F16 Skill 体系 - -| 项目 | 内容 | -|------|------| -| **依赖** | F15 | -| **估计** | 1.5h | -| **产出** | `src/daemon/skill_loader.py` + Skill 目录结构 | -| **完成标准** | Skill 四要素(name/description/triggers/actions)加载 + per-project 覆盖 + 生命周期管理 | -| **测试** | `test_skill_loader.py`: 加载 + 验证 + 覆盖 + 版本管理 | - -### F17 SSE 推送 + Hook 系统 - -| 项目 | 内容 | -|------|------| -| **依赖** | F5, F6 | -| **估计** | 2h | -| **产出** | `src/api/sse_routes.py`(增强)+ `src/daemon/notifier.py` + Hook 触发点 | -| **完成标准** | SSE 4 级推送正常,降级轮询兜底,3 个 Hook 触发点工作 | -| **测试** | `test_sse.py`: 连接+推送+断线重连 + `test_hooks.py`: 触发+过滤 | - -### F18 前端 Dashboard - -| 项目 | 内容 | -|------|------| -| **依赖** | F5, F17 | -| **估计** | 8h | -| **产出** | `src/frontend/` 全部 5 页面 + 组件 | -| **完成标准** | TaskBoard + GlobalMonitor + ArtifactVault + SystemConfig + AIBriefing 全部可用,SSE 实时更新,项目切换器工作 | -| **测试** | `test_frontend.py`(E2E): 页面渲染 + API 集成 + SSE 更新 + 项目切换 | - ---- - -## 测试策略 - -### 单元测试(每个功能自带) - -| 测试文件 | 覆盖模块 | 优先级 | -|----------|---------|--------| -| `test_main.py` | main.py 启动 + 配置 | P0 | -| `test_blackboard.py` | 黑板 CRUD + 并发 + WAL | P0 | -| `test_registry.py` | 多项目管理 | P0 | -| `test_cli.py` | CLI 8 子命令 | P0 | -| `test_api.py` | API 全端点 | P0 | -| `test_ticker.py` | Daemon tick 循环 | P0 | -| `test_inbox.py` | Inbox JSONL | P1 | -| `test_health.py` | 健康检查 | P1 | -| `test_dispatcher.py` | Agent 调度决策 | P0 | -| `test_spawner.py` | Agent spawn | P0 | -| `test_counter.py` | ActiveAgentCounter | P0 | -| `test_bootstrap.py` | Bootstrap 拼装 | P1 | -| `test_review_flow.py` | 审查流水线 | P0 | -| `test_guardrail.py` | Guardrail | P0 | -| `test_rebuttal.py` | 反驳权 | P0 | -| `test_experience.py` | 经验沉淀 | P1 | -| `test_skill_loader.py` | Skill 加载 | P1 | -| `test_sse.py` | SSE 推送 | P1 | -| `test_hooks.py` | Hook 系统 | P1 | -| `test_frontend.py` | 前端 E2E | P1 | - -### 集成测试(F14 完成后) - -端到端链路:用户提需求 → 庞统规划 → 任务写入 → Agent 执行 → Guardrail → 审查 → 反驳 → 完成 → 经验沉淀 - -### 司马懿职责 - -- 每个 F 编码完成后评审代码 + 审查测试覆盖 -- F14 完成后执行集成测试 -- F18 完成后执行 E2E 验收 -- 发现问题写 Mail → 庞统修复 → 复审 - ---- - -## 进度追踪 - -| 序号 | 功能 | 状态 | 测试 | 评审 | -|------|------|------|------|------| -| F1 | 项目骨架+配置 | ✅ 完成 | ✅ 10/10 | ✅ 通过 | -| F2 | 黑板核心 | ✅ 完成 | ✅ 39/39 | ✅ 通过 | -| F3 | 多项目管理 | ✅ 完成 | ✅ 11/11 | ✅ 通过 | -| F4 | CLI 工具 | ✅ 完成 | ✅ 14/14 | ✅ 通过 | -| F5 | API 层 | ✅ 完成 | ✅ 23/23 | ✅ 通过 | -| F6 | Daemon Ticker | ✅ 完成 | ✅ 18/18 | ✅ 通过 | -| F7 | Inbox JSONL | ✅ 完成 | ✅ 16/16 | ✅ #277 | -| F8 | 健康检查 | ✅ 完成 | ✅ 16/16 | ✅ #277 | -| F9 | Agent 调度器 | ✅ 完成 | ✅ 29/29 | ✅ #278 | -| F10 | Counter | ✅ 完成 | ✅ 14/14 | ✅ #278 | -| F11 | Bootstrap | ✅ 完成 | ✅ 22/22 | ✅ #278 | -| F12 | 审查流水线 | ✅ 完成 | ✅ 20/20 | ✅ #278 | -| F13 | Guardrail | ✅ 完成 | (含F12) | ✅ #278 | -| F14 | 反驳权 | ✅ 完成 | (含F12) | ✅ #278 | -| F15 | 经验沉淀 | ✅ 完成 | ✅ 22/22 | ✅ #278 | -| F16 | Skill 体系 | ✅ 完成 | ✅ 24/24 | ✅ #278 | -| F17 | SSE+Hook | ✅ 完成 | ✅ 24/24 | ✅ #278 | -| F18 | 前端 Dashboard | ✅ 完成 | ✅ build | ✅ #279 | -| --- | **v2.8 状态增强 + M3 Checkpoint** | | | | -| v2.8 | 状态增强(paused/escalated/waiting_human + 归档 + 前端改造) | ✅ 设计完成 | ✅ 已完成 | ✅ #278-#280 | -| M3 | Checkpoint 机制 + Artifact 成果物面板 | ✅ 设计完成 | ✅ 已完成 | ✅ #280 | -| --- | **Agent 编排集成** | | | | -| F19 | Ticker 调度集成 | ✅ 设计完成 | ✅ 已完成 | ✅ 评审通过 | -| F20 | Spawner prompt 模板 | ✅ 设计完成 | ✅ 已完成 | ✅ 评审通过 | -| F21 | 审查流水线集成 | ✅ 设计完成 | ✅ 已完成 | ✅ 评审通过 | -| F22 | 前端 API 对接 | ✅ 设计完成 | ✅ 已完成 | ✅ 评审通过 | -| --- | **v3.0 集成 + 安全红线** | | | | -| G1 | HealthChecker → Ticker | ✅ 已完成 | ✅ 33/33 | ✅ #313 | -| G2 | BootstrapBuilder → Spawner | ✅ 已完成 | ✅ 测试通过 | ✅ #313 | -| G3 | InboxWatcher → Ticker | ✅ 已完成 | ✅ 33/33 | ✅ #313 | -| G4 | ExperienceDistiller → Ticker | ✅ 已完成 | ✅ 33/33 | ✅ #313 | -| G5 | 安全红线 Guardrails | ✅ 已完成 | ✅ 测试通过 | ✅ #316 | -| G6 | MorningPanel + ArtifactPanel 前端集成 | ✅ 已完成 | ✅ build | ✅ #313 | - ---- - -## 待确认区 - -开发过程中遇到的设计决策,按 AI native + 最优实践方向先执行,记录于此,用户明早 review。 - -#### 2026-05-17 SQLite 3.51.0 CHECK 约束解析 bug - -**发现**: SQLite 3.51.0 不允许 CHECK 约束作为列定义列表的中间项(带尾随逗号)。 -```sql --- ❌ 报错:near "foo": syntax error -CREATE TABLE t (status TEXT, CHECK (status IN ('a','b')), foo TEXT) --- ✅ 正常:inline CHECK -CREATE TABLE t (status TEXT CHECK (status IN ('a','b')), foo TEXT) -``` -**处理**: 所有 CHECK 约束改为内联方式(附在字段定义上)。 -**影响**: 无功能影响,只是 SQL 写法不同。 - -#### 2026-05-17 BUG-1 修复:/api/daemon/status 端点重复注册 - -**发现**: 司马懿评审 F1-F5 时发现 main.py 和 daemon_routes.py 都注册了 `/api/daemon/status`,导致 Swagger 文档 Duplicate Operation ID warning。 -**修复**: 删掉 main.py 中的定义,统一由 daemon_routes.py 提供。同时重构 main.py 将占位 ticker 替换为真实 Ticker 类。 -**影响**: daemon_routes.py 新增 tick_count 字段,manual_tick 端点对接真实 Ticker。 - ---- - -## 参考实践来源 - -| 实践 | 来源项目 | 应用点 | -|------|---------|--------| -| verify-before-complete | superpowers (clawgator) | 每个 F 编码完必须测试通过才算 done | -| Execution+Enhancement+Guarantee | oh-my-claudecode | 三层质量保证:编码→增强→保障 | -| 僵尸检测+reclaim | hermes-agent | health.py 逻辑死循环检测 | -| 幻觉门控 | hermes-agent | Guardrail 产出物验证 | -| YAML frontmatter + preamble-tier | gstack | Skill 加载格式 | -| Wave Execution(新鲜上下文) | get-shit-done | Bootstrap 拼装不累积旧上下文 | diff --git a/docs/design/frontend-principles.md b/docs/design/frontend-principles.md deleted file mode 100644 index 5497ec7..0000000 --- a/docs/design/frontend-principles.md +++ /dev/null @@ -1,40 +0,0 @@ -# moziplus v2 前端设计原则 - -**创建**: 2026-05-15 -**状态**: 用户确认 - -## 核心原则 - -> **UI 样式和布局参考 v1.0,具体功能设计按 v2.6。** - -| 维度 | 参考谁 | 说明 | -|------|--------|------| -| **样式(CSS/主题/配色)** | v1.0 朝堂古风设计系统 | 深蓝底 + mc-card + m-section + CSS 变量体系 | -| **布局(页面结构/组件位置)** | v1.0 Dashboard | 侧边栏导航 + 主内容区 + 弹窗详情 | -| **组件风格** | v1.0 TaskModal / EdictBoard | 卡片/管道/状态徽章等视觉元素 | -| **功能设计** | v2.6 课题7+9 | 5页精简、推送分级、评论线程、DAG拓扑、AI Briefing | -| **交互逻辑** | v2.6 课题7+9 | 四种交互模式、超时兜底、乐观锁 | - -## 参考文件 - -- **v1.0 前端源码**: `~/.sanguo_projects/sanguo_moziplus/dashboard/` -- **v1.0 设计文档**: `~/.sanguo_projects/sanguo_moziplus/docs/design/dashboard-frontend-design.md` -- **v2.6 课题7+9方案**: `~/.openclaw/sanguo_projects/sanguo_moziplus_v2/docs/design/topic7-interaction-dashboard-proposal.md` -- **Edict 组件参考**: `~/.openclaw/knowledge_base/edict/` - -## 页面对照(v1.0 10Tab → v2.6 5页) - -| v2.6 页面 | v1.0 来源 | 功能变更 | -|-----------|----------|---------| -| 任务看板 | 任务看板 + 编排调度 | 合并,加 DAG 拓扑视图 | -| 全局监控 | 将军总览 + 会话监控 | 合并,加心跳可视化 | -| 产出档案 | 奏折阁 | 保留,加产出物预览 | -| 系统配置 | 模型配置 + 技能配置 | 合并为一个配置页 | -| AI 简报 | 无(新增) | v2.6 新增,AI 自动生成 | - -## 技术栈(延续 v1.0) - -- React 18 + Vite + TypeScript -- Zustand 状态管理 -- Tailwind CSS -- 轮询(Phase 2) → WebSocket(Phase 3) diff --git a/docs/design/frontend-redesign-v2.6.md b/docs/design/frontend-redesign-v2.6.md deleted file mode 100644 index 7f1d0b2..0000000 --- a/docs/design/frontend-redesign-v2.6.md +++ /dev/null @@ -1,213 +0,0 @@ -# v2.6 前端任务管理改造方案 - -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-17 -**状态**: 待确认 - ---- - -## 一、从 v2.0 定位出发 - -### v2.0 的核心定位(PRD v2.0) - -> **用户提一个方向,AI 帮他梳理成清晰目标,然后自主组织 Agent 团队执行,全程持续指挥、自主协调、主动汇报,人只在关键决策点介入。** - -四条信念: -1. AI 帮人想清楚要什么 -2. 编排层是 AI 指挥官,不是确定性代码 -3. Agent 之间共享意识,不传递消息 -4. 人退到系统的边缘 - -### Dashboard 在 v2.0 中的角色 - -**不是"管理控制台",而是"AI 工作可视化窗口"**。 - -| 传统 Dashboard | AI Native Dashboard | -|---|---| -| 用户主动操作(点按钮、填表单) | AI 主动展示(推送状态、产出、决策请求) | -| 用户监控一切 | AI 只在关键点拉人,其余自主 | -| 表格+表单为主 | 时间线+卡片+对话为主 | -| 用户是驾驶员 | 用户是乘客,偶尔导航 | - -参考的优秀实践: -- **Devin 模式**:全自主执行,Dashboard 是观察窗口 -- **Claude Code App**:多 session 并排 + diff review -- **Linear**:精准通知,不噪音,状态流转极简 -- **Edict**:MiniPipe 管道可视化 + 心跳 + 操作按钮 -- **Hermes Kanban**:任务卡片 + 结果区 + 评论线程 + 事件流 + Nudge 按钮 - ---- - -## 二、当前后端能力盘点 - -### 已有(v2.6 F1-F18 实现) - -| 能力 | API | 数据表 | -|------|-----|--------| -| 多项目管理 | GET/POST /api/projects | _registry.yaml | -| 任务 CRUD | GET/POST /api/projects/{pid}/tasks | tasks | -| 任务认领 | POST /tasks/{id}/claim | tasks.claimed_at | -| 状态流转 | POST /tasks/{id}/status | tasks.status + events | -| 评论/Handoff | GET/POST /tasks/{id}/comments | comments | -| 产出写入 | GET/POST /tasks/{id}/outputs | outputs | -| 决策记录 | POST /tasks/{id}/decisions | decisions | -| 观察/吹哨 | POST /tasks/{id}/observations | observations | -| 评审 | GET/POST /tasks/{id}/reviews | reviews | -| 事件流 | GET /tasks/{id}/events(缺) | events | -| SSE 推送 | GET /api/events | SSEBroker | -| Daemon 状态 | GET /api/daemon/status | 内存 | -| 经验沉淀 | experiences.jsonl + 读取API | experiences | -| Agent 注册 | agents 表 | agents | - -### 缺失但设计中有 - -| 能力 | 设计位置 | 说明 | -|------|---------|------| -| **任务详情聚合 API** | §9.1 `GET /tasks/{id}` 返回关联数据 | 前端需要一次性拿全部关联数据 | -| **任务事件查询 API** | §9.1 `GET /tasks/{id}/events` | Flow Log 时间线 | -| **任务关联经验 API** | §9.1 | 关联经验展示 | -| **Checkpoint 机制** | 课题7 §6 + §11 前端架构 | 三种检查点(验证/决策/执行),后端无实现 | -| **通知推送对接** | §9.3 SSE + §7 异常通知 | SSEBroker 存在,但 ticker 未推送事件 | -| **AI Briefing** | 课题7 §8 + §3.5 | 日报/周报自动生成,完全未实现 | -| **/api/daemon/sessions** | §9.2 | 活跃 Agent session 列表 | - ---- - -## 三、改造方案 - -### 设计原则 - -1. **从 AI Native 出发,不是从后端有什么出发** -2. **前后端联动**——前端需要的数据,后端必须提供;后端已有的数据,前端必须展示 -3. **缺的功能标记为 TODO**,不隐藏也不糊弄 -4. **参考 Edict + Hermes + Claude Code 的优秀实践** - -### 页面设计(5页 + 通知中心) - -#### P1. 任务看板(核心页面) - -**参考**: Edict EdictBoard + Hermes Kanban - -**卡片设计**(v2.0 简化版,无 DAG 节点概念): - -``` -┌─────────────────────────────────────────────┐ -│ task-013 │ -│ 动量因子策略回测 │ -│ │ -│ 🔄 working 👤 agent1 📊 coding │ -│ ⏱ 12min前创建 ⚠️ standard │ -│ │ -│ ┌─ 状态管线 ──────────────────────────────┐ │ -│ │ ●pending → ●claimed → ◉working → ○done │ │ -│ └──────────────────────────────────────────┘ │ -│ │ -│ [⏸ 暂停] [🚫 取消] [📋 详情] │ -└─────────────────────────────────────────────┘ -``` - -数据来源:tasks 表全部字段,映射清晰。 - -#### P2. 任务详情面板(Modal) - -**参考**: Edict TaskModal + Claude Code diff review + Hermes result section - -**8 个区域**(按优先级排列): - -| # | 区域 | 数据来源 | 交互 | 状态 | -|---|------|---------|------|------| -| 1 | **基本信息** | tasks 表 | 只读 | ✅ 后端有 | -| 2 | **状态管线** | tasks.status + VALID_TRANSITIONS | 按钮(基于守卫) | ✅ 后端有 | -| 3 | **产出物** | outputs 表 | 预览/下载 | ✅ 后端有 | -| 4 | **评审记录** | reviews 表 | 只读展示 | ✅ 后端有 | -| 5 | **事件时间线** | events 表 | 时间线展示 | ⚠️ 需补 API | -| 6 | **评论/决策** | comments + decisions 表 | 只读展示 | ✅ 后端有 | -| 7 | **关联经验** | experiences 表 | 只读展示 | ⚠️ 需补 API | -| 8 | **Checkpoint** | ❌ 后端未实现 | 占位("Checkpoint 功能开发中") | ❌ TODO | - -#### P3. 全局监控 - -保留 v1.0 的 MonitorPanel 适配 v2.6 API。新增 Agent 状态展示。 - -#### P4. 系统配置 - -保留 v1.0 的 SettingsPanel + 项目管理。适配 v2.6 的项目 API。 - -#### P5. AI Briefing - -**参考**: v2.0 设计 §8(日报/周报格式) - -当前状态:后端未实现。前端做占位页面,显示"Briefing 功能开发中"。 - -#### P6. 通知中心(Header 铃铛) - -**参考**: v2.0 设计 §3.4 + Linear 通知 - -当前状态:SSE broker 存在但未对接。前端做铃铛 UI + 占位。 - -### 后端改动清单 - -| 优先级 | 改动 | 说明 | 工作量 | -|--------|------|------|--------| -| **P0** | `GET /tasks/{id}?expand=all` | 任务详情聚合(一次返回 comments+outputs+reviews+events+decisions) | 30min | -| **P0** | `GET /tasks/{id}/events` | 事件时间线查询 | 15min | -| **P1** | `GET /tasks/{id}/experiences` | 关联经验查询 | 15min | -| **P1** | ticker 事件推送 SSE | ticker 状态变更 → SSE push | 1h | -| **P2** | Checkpoint 数据模型 + API | 新表 + 交互 API | 设计+编码 3-4h | -| **P2** | AI Briefing 生成 | 聚合数据 + 模板 | 设计+编码 2-3h | - -### 前端改动清单 - -| 优先级 | 改动 | 说明 | 工作量 | -|--------|------|------|--------| -| **P0** | EdictBoard 重写 | v2.0 任务卡片 + 状态管线 + 操作按钮 | 2h | -| **P0** | TaskModal 重写 | 8 区域详情面板 | 3h | -| **P1** | api.ts 补新 API | 对接聚合 API + events + experiences | 30min | -| **P1** | 通知中心占位 | 铃铛 + 下拉面板 | 30min | -| **P1** | AI Briefing 占位 | 页面框架 + "开发中" | 15min | - -### 执行顺序 - -``` -Phase 1: 后端补 API(P0 的 3 个接口) ~1h -Phase 2: 前端核心重写(EdictBoard + TaskModal) ~5h -Phase 3: 前端补完(通知+Briefing占位) ~1h -Phase 4: 联调 + 部署 + 端到端验证 ~1h -总计: ~8h -``` - -### Checkpoint 和 AI Briefing 的处理 - -**v2.8/M3 更新**: Checkpoint 已完成设计(`docs/design/v2.8-state-enhancement.md`),作为 M3 专项开发。 -- 三种 Checkpoint 类型(验证/决策/执行)+ Artifact 成果物面板 -- 依赖 v2.8 新增的 `waiting_human` 状态 -- 前端已有 CheckpointPanel.tsx(350行)和 ArtifactPanel.tsx(185行),扩展对接新 API - -AI Briefing 仍未实现,保持占位。 - -### v2.8 前端易用性改造 - -详见 `docs/design/v2.8-state-enhancement.md` §三,核心改动: - -1. **筛选栏两行布局**(仿 v1.0): - - 第 1 行:项目下拉 + 活跃/归档/全部 切换 + 一键归档 - - 第 2 行:11 个状态筛选 + 搜索框 - -2. **卡片快捷按钮**(最多 3 个):每个状态有专属操作按钮,直接调 API + toast 反馈 - -3. **项目下拉移入筛选栏**:支持"全部任务"跨项目聚合 - -4. **新建军令 title 可选**:后端自动生成(description 前 30 字 + "…") - ---- - -## 四、与优秀实践的对齐 - -| 实践 | 我们借鉴了什么 | -|------|--------------| -| **Edict MiniPipe** | 任务卡片上的状态管线可视化(pending→claimed→working→done) | -| **Hermes Kanban** | 卡片 + 结果区 + 评论线程 + 事件流的组合布局 | -| **Claude Code** | TaskModal 的多 Tab 切换(产出/评审/事件),产出 diff 风格展示 | -| **Linear** | 通知铃铛 + 精准推送(不噪音);状态流转按钮守卫 | -| **Devin** | AI 自主执行的观察窗口理念——Dashboard 不需要密集操作 | -| **OpenClaw Control Center** | WebChat 入口 + Dashboard 入口的双入口对等理念 | diff --git a/docs/design/mail-fix-design.md b/docs/design/mail-fix-design.md deleted file mode 100644 index 09329c7..0000000 --- a/docs/design/mail-fix-design.md +++ /dev/null @@ -1,225 +0,0 @@ -# Mail 修复方案设计文档 - -> 版本:v1.0 | 日期:2026-05-22 | 作者:庞统 | 状态:评审中 - -## 1. 背景与问题 - -moziplus v2 的飞鸽传书(Mail)功能当前存在多个 bug 和设计缺陷,导致 v2 Mail 基本不可用。 - -### 已确认的 Bug - -| 编号 | 问题 | 严重程度 | -|------|------|---------| -| M-BUG-1 | ticker 不扫 `_mail` 项目,邮件永远 pending | 🔴 阻塞 | -| M-BUG-2 | `send_mail` 对 inform 类型直接 `status=done`,无投递确认 | 🔴 阻塞 | -| M-BUG-3 | Tab badge 取值错误(用 sseEvents 而非 mail API) | 🟡 体验 | -| M-BUG-4 | `is_read` / `mark_executed` 语义与 Agent 系统不匹配 | 🟡 设计 | -| M-BUG-5 | MailPanel 无发送入口 | 🟡 功能缺失 | - -### 调查发现 - -1. **v1 投递卡死根因**:`isAgentBusy()` 进程死后仍返回 true + Gateway 永远存活(PID 有效)→ webchat 占用 main session lock → poller 永远认为忙碌 -2. **v2 不存在此问题**:spawner 用 `--session-id uuid4()` 创建新 session,不走 main session lock -3. **v2 Mail 和 v1 Sanguo Mail 是两套独立系统**:v2 Mail 存在 `_mail/blackboard.db`,v1 Sanguo Mail 用文件系统 inbox - -## 2. 设计原则 - -1. **Mail = 通信层,Task = 任务层**:Mail 不加 depends_on,复杂工作用 Task -2. **done = Agent 处理完返回**,不是收到即 done -3. **统一用 done 标记完成**,废弃 `is_read` / `mark_executed` -4. **inform 也不直接 done**:统一从 pending 开始,由 Agent 处理后写 done -5. **conversation_id 只做前端聚合**,不加后端依赖机制 - -## 3. Mail vs Task 约束 - -| 场景 | 用 Mail | 用 Task | -|------|---------|---------| -| 通知/同步信息 | ✅ | ❌ | -| 简单请求(< 10 分钟) | ✅ | ❌ | -| 问问题 | ✅ | ❌ | -| 多步骤/有依赖/需追踪 | ❌ | ✅ | -| 需要产出物追踪 | ❌ | ✅ | -| 不确定复杂度 | 先 Mail → 发现复杂 → 转 Task | — | - -Mail 转 Task 的约束: -- 当前 Task 创建入口只有 Dashboard 和庞统的 Control UI -- Agent 不会调 v2 `/api/mail` 发邮件(不知道 API 存在) -- Agent 不会主动创建 Task(只能通过 mozi CLI) -- **结论:Mail 转 Task 需要庞统或用户手动操作,不自动转换** - -## 4. 修复方案 - -### P0-1:ticker 加 `_mail` 虚拟项目扫描 - -**问题**:`_mail` 不在 registry 中,ticker 只扫 registry + `_general`,不扫 `_mail`。 - -**修复**:在 ticker 的 tick 循环中,和 `_general` 一样加 `_mail` 虚拟项目处理。 - -**文件**:`src/daemon/ticker.py` - -**改动**: -```python -# _tick_all_projects 中,_general 之后加 _mail - -# 虚拟项目 _mail:飞鸽传书 -mail_db = Path(self.registry.root) / "_mail" / "blackboard.db" -if mail_db.exists() and "_mail" not in active_project_ids: - try: - pr = await self._tick_project("_mail", { - "id": "_mail", "name": "飞鸽传书", - "status": "active", "source": "virtual", - }) - results["projects"]["_mail"] = pr - except Exception: - logger.exception("Tick %d _mail error", tick_num) - results["projects"]["_mail"] = {"error": str(e)} -``` - -**预估**:~10 行 - -### P0-2:send_mail 统一 pending - -**问题**:`send_mail` 对 `type=inform` 直接设 `status=done`,无投递确认。 - -**修复**:inform 和 text 统一从 `pending` 开始,由 Agent 处理后写 done。 - -**文件**:`src/api/mail_routes.py` - -**改动**: -```python -# 之前: -status="done" if body.get("type") == "inform" else "pending", - -# 之后: -status="pending", -``` - -**预估**:1 行 - -**注意**:inform 类型邮件的 Agent spawn prompt 需要引导 Agent 快速确认(不需要复杂执行),在 spawner 的 spawn message 中根据 task_type 区分。 - -### P1-1:Tab badge + mailUnread 计算逻辑修正 - -**问题**: -- Tab badge 用 `sseEvents`,不是 mail API 的实际未读数 -- `store.ts` 的 `mailUnread` 按 `!m.is_read` 计算,但废弃 is_read 后语义不对 -- failed 的邮件 is_read 仍为 false,会被误计为 unread - -**修复**: - -**文件 1**:`src/frontend/src/store.ts`(mailUnread 计算逻辑) -```typescript -// 之前: -mailUnread: mails.filter(m => !m.is_read).length, - -// 之后:按 status 计算,unread = 需要处理的邮件数 -mailUnread: mails.filter(m => !['done', 'failed', 'cancelled'].includes(m.status)).length, -``` - -**文件 2**:`src/frontend/src/App.tsx`(Tab badge 取值) -```typescript -// 之前: -const unread = ((useStore.getState().sseEvents || []) as any[]) - .filter((n: any) => !n.read).length; - -// 之后: -const unread = useStore.getState().mailUnread || 0; -``` - -**预估**:2 行 - -### P1-2:废弃 is_read / mark_executed,统一用 done - -**问题**: -- `is_read` 在 Agent 系统里无意义(Agent 不需要"已读") -- `mark_executed` 是给人手动标记的,但语义模糊 -- Agent 通过 Task 状态机写 done 后,`is_read` 仍是 false,前端还显示"未读" - -**修复**: - -**后端**:`src/api/mail_routes.py` -- 删除 `mark_read` 和 `mark_executed` 端点(或标记 deprecated) -- `list_mail` 返回时,`status=done` 的邮件 `is_read` 强制为 true -- 不改数据库字段(保持向后兼容),只在 API 层面调整 - -**前端**:`src/frontend/src/components/MailPanel.tsx` -- 去掉"标记已读"和"标记已执行"按钮 -- 改为展示邮件状态:`pending`(未处理)/ `claimed/working`(处理中)/ `done`(已处理)/ `failed`(失败) -- 未处理邮件 = `status != done && status != failed` - -**文件**:`mail_routes.py` + `MailPanel.tsx` - -**预估**:~30 行 - -### P1-3:前端加人工重试按钮(failed → pending) - -**问题**:投递失败的邮件没有重试入口。 - -**修复**:MailPanel 中对 `failed` 状态的邮件显示"重试"按钮。 - -**后端**:`src/api/mail_routes.py` 新增端点或复用 status 更新 -```python -# PATCH /api/mail/{task_id}/status -# {"status": "pending"} → 重置为 pending,ticker 重新调度 -``` - -**前端**:`MailPanel.tsx` 加重试按钮 - -**预估**:~20 行 - -### P2-1:投递失败自动 escalate - -**问题**:Agent 多次失败后无人知道。 - -**修复**:ticker 检测 `_mail` 任务的 `retry_count >= 3` → escalate 给庞统。 - -**文件**:`src/daemon/ticker.py`(_check_timeouts 中已有 escalate 逻辑,_mail 自然享受) - -**预估**:0 行(现有逻辑已覆盖) - -## 5. 邮件状态机 - -``` -pending → claimed → working → done - │ │ - └→ failed ─┘→ pending(人工重试) -``` - -- `pending`:邮件已创建,等待调度 -- `claimed`:ticker 调度,分配给 Agent -- `working`:Agent 正在处理 -- `done`:Agent 处理完成 -- `failed`:处理失败,可人工重试 - -邮件复用 Task 的状态机和状态转换逻辑,不需要独立的状态机。 - -## 6. 不做的事 - -| 项目 | 原因 | -|------|------| -| Mail 加 depends_on | 当前不需要,复杂依赖用 Task | -| Mail Tab 加发送入口 | P2,当前通过 API 发送足够 | -| inform 走简化流程(跳过 working/review) | 先统一走 Task 状态机,确认能跑通再优化 | -| conversation_id 后端依赖 | 纯前端聚合字段,不加后端逻辑 | -| 投递到主 session | Mail 投递到主 Agent session(use_main_session=True),通过 Gateway queue 排队;spawn session 执行 Task 与主 session session lane 隔离,互不干扰 | - -## 7. 改动范围 - -| 文件 | 改动 | 预估行数 | -|------|------|---------| -| `src/daemon/ticker.py` | `_tick_all_projects` 加 `_mail` 虚拟项目 | ~10 行 | -| `src/api/mail_routes.py` | send_mail 统一 pending + 废弃 mark_read/mark_executed + 重试端点 | ~30 行 | -| `src/frontend/src/App.tsx` | Tab badge 改用 mailUnread | ~1 行 | -| `src/frontend/src/components/MailPanel.tsx` | 废弃已读/已执行按钮 + 状态展示 + 重试按钮 | ~40 行 | - -总计约 80 行,4 个文件。 - -## 8. 测试计划 - -| 用例 | 验证方式 | 预期 | -|------|---------|------| -| Mail 投递到 Agent | POST /api/mail → ticker 调度 → Agent spawn → done | 邮件状态 pending → done | -| Tab badge 数字 | 发 3 封邮件,2 封 done | badge 显示 1 | -| inform 类型不直接 done | POST /api/mail {type: inform} | 初始 status=pending | -| 重试按钮 | 邮件 failed → 点重试 → status 回 pending → ticker 重新调度 | 重新执行 | -| escalate | 邮件连续 3 次失败 | 自动 escalate 给庞统 | diff --git a/docs/design/polling-ux-proposal.md b/docs/design/polling-ux-proposal.md deleted file mode 100644 index 61b64e1..0000000 --- a/docs/design/polling-ux-proposal.md +++ /dev/null @@ -1,103 +0,0 @@ -# 轮询 UX 优化方案 - -> 日期:2026-05-20 -> 作者:庞统 -> 状态:待评审 -> 问题:5 秒全局轮询导致输入框失焦,用户无法在搜索框、新建对话框等输入文字 - -## 一、现状 - -### 当前轮询机制 -- `setInterval` 每 1 秒减 countdown,countdown 归零时 `loadAll()` -- `loadAll()` 每 5 秒执行:`loadLive()` → `loadProjects()` → `loadV2Tasks()` → 可选 `loadAgentConfig()` -- 右上角显示 `⟳ 5s` 倒计时 - -### 问题分析 -1. **输入失焦**:`set({ v2tasks: ... })` 触发 EdictBoard 重渲染,所有受控输入框失焦 -2. **请求浪费**:用户无操作时仍每 5 秒全量刷新 -3. **全部任务模式**:聚合 N 个项目的请求,5 秒一次压力大 -4. **视觉干扰**:倒计时数字不停跳动,分散注意力 - -## 二、方案:智能轮询 + 输入保护 - -### 2.1 输入焦点保护(核心修复) - -**原理**:轮询前检测是否有输入焦点,有则延迟刷新。 - -```typescript -// 判断是否有活跃输入 -function hasActiveInput(): boolean { - const el = document.activeElement; - if (!el) return false; - const tag = (el as HTMLElement).tagName; - return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' - || (el as HTMLElement).isContentEditable; -} -``` - -**轮询逻辑调整**: -- 有输入焦点 → 跳过本次 `loadV2Tasks()`,只刷新轻量数据(ticker) -- 输入结束 2 秒后补刷一次任务列表(用 `blur` 事件 + debounce) -- 倒计时继续走,不暂停计时器本身 - -### 2.2 轮询间隔分级 - -| 场景 | 间隔 | 说明 | -|------|------|------| -| 活跃操作后 | 10 秒 | 用户刚点击/切换 tab | -| 空闲状态 | 30 秒 | 无操作,静默刷新 | -| 全部任务模式 | 30 秒 | 聚合请求多,降低频率 | - -**实现**:记录上次用户操作时间戳,动态调整间隔。 - -### 2.3 视觉优化 - -| 改动 | 说明 | -|------|------| -| 去掉秒级倒计时 | 改为状态指示器:`●` 绿色=正常 `●` 黄色=刷新中 `●` 红色=连接失败 | -| 手动刷新按钮 | 点击立即刷新,重置计时器 | -| 刷新时卡片闪烁 | 用 CSS transition 平滑过渡,不要整块替换 | - -### 2.4 增量刷新(可选优化) - -当前每次轮询全量替换 `v2tasks` 数组。优化为: -- 比对新旧数据,只有状态变化时才更新对应任务 -- 减少不必要的重渲染 - -```typescript -// 简单增量合并 -const merged = newTasks.map(nt => { - const old = oldTasks.find(ot => ot.id === nt.id); - return (old && JSON.stringify(old) === JSON.stringify(nt)) ? old : nt; -}); -``` - -## 三、改动范围 - -| 文件 | 改动 | 行数估计 | -|------|------|---------| -| `store.ts` | 轮询逻辑 + hasActiveInput + 分级间隔 + 增量合并 | ~50 行 | -| `App.tsx` | 倒计时 UI → 状态指示器 + 手动刷新按钮 | ~20 行 | -| `EdictBoard.tsx` | 搜索框添加 onFocus/onBlur 事件(可选) | ~5 行 | - -**总计**:~75 行改动,不涉及后端。 - -## 四、不做的事 - -1. ❌ 不改 WebSocket(当前 HTTP 轮询够用,未来迁移成本不高) -2. ❌ 不改后端 API(轮询是前端优化) -3. ❌ 不做 Service Worker 缓存(过度设计) - -## 五、实施优先级 - -1. **P0**:输入焦点保护(解决失焦问题) -2. **P1**:轮询间隔 10→30 秒分级(减少请求) -3. **P2**:视觉优化(倒计时→状态指示器) -4. **P3**:增量刷新(性能优化,当前数据量不需要) - -## 六、验收标准 - -1. 在搜索框、新建项目对话框输入时,5 秒内不失焦 -2. 空闲 30 秒后自动刷新任务列表 -3. 手动刷新按钮可用 -4. 右上角不再有跳动数字 diff --git a/docs/design/spawner-monitor-design.md b/docs/design/spawner-monitor-design.md deleted file mode 100644 index d73364f..0000000 --- a/docs/design/spawner-monitor-design.md +++ /dev/null @@ -1,774 +0,0 @@ -# Spawner Monitor 设计文档 - -> 版本:v2.0 | 日期:2026-05-26 | 作者:庞统 | 状态:v2.0 大幅更新(P0 stdout 修复 + spawn 前检查 + counter 调用级 + 假死复活术) - -## 1. 背景与问题 - -当前 `_monitor_process` 只看进程退出码,不读 stdout/stderr,不检查 session 状态,无法区分超时原因。且超时后直接 `proc.kill()`,可能丢失 Agent 执行进度。 - -`openclaw agent` 命令的实际行为: -- Gateway 有内置 timeout(默认 600s),到时间后 Gateway 会中断 Agent,进程自行退出 -- 中断后上下文保留在 session 里,用同一 session-id 再次调用可继续("续杯"机制) -- 执行过程中可能触发 auto-compaction(上下文压缩),compact 完成后自动 retrying prompt -- compact 期间进程不退出,只是执行时间变长 -- 极端情况下进程可能卡住不退出(LLM 卡死、Gateway 异常等) - -## 2. 核心设计原则 - -1. **每次 agent 调用都是独占的**:openclaw 无论成功失败都会返回,最差情况 timeout 返回。谁占用谁持有,进程退出就 release -2. **counter 生命周期是调用级**:spawn 时 acquire,进程退出就 release。不是任务级 -3. **spawn 前检查所有可用信号**:counter + 冷却期 + session state(lock/processing/compact),避免注定失败的 spawn -4. **不主动 kill 进程**:进程可能还在正常执行,kill 会丢失所有进度 -5. **续杯只有 Gateway timeout 才触发**:lock/compact/api_error 等不续杯,等 ticker -6. **escalate 不自动 kill**:超过重试上限后标记 failed + escalate,是否 kill 留给用户决定 - -## 3. Spawn 前检查(拦截无效 spawn) - -spawn_full_agent 启动进程前,依次检查: - -| # | 场景 | 检测方法 | 检测到后方案 | -|---|------|---------|-------------| -| L1 | moziplus 内部并发 | counter.can_acquire() | AgentBusyError → 等 ticker | -| L2 | API 429 冷却期 | counter.is_cooling_down() | AgentBusyError → 等 ticker | -| L3a | main session 被外部占用 | _check_session_state → lock_pid_alive | AgentBusyError → 等 ticker | -| L3b | main session 正在执行 | _check_session_state → status=processing | AgentBusyError → 等 ticker | -| L3c | main session 正在 compact | _check_session_state → recent_compact | AgentBusyError → 等 ticker | - -L1+L3 互补:counter 防 moziplus 内部并发,session state 防外部占用(webchat/Control UI/cron)。 -所有检查失败统一走 AgentBusyError → 任务保持 working → ticker 30 秒后重新调度。 - -## 4. 参数配置 - -```yaml -daemon: - gateway_timeout: 600 # 传给 openclaw agent --timeout 的值 - agent_timeout: 630 # _monitor_process 的等待时间(比 gateway_timeout 长 30s) - max_retries: 3 # 续杯上限(情况 A) - max_monitor_timeouts: 3 # monitor timeout 上限(情况 B) -``` - -## 5. 情况 A:进程在 monitor_timeout 内退出 - -``` -0s 600s 630s -├──────────────────────┤──────────────────┤ -│ Agent 执行中 │ Gateway timeout │ monitor timeout -│ │ 进程退出 │ (情况 B 才到这里) -``` - -进程退出后,读 stdout JSON(`openclaw agent --json` 输出)+ 查任务 DB 状态。 - -### JSON 输出格式 - -`openclaw agent --json` 输出到 stdout 的 JSON 结构: - -```json -{ - "status": "ok", - "summary": "completed", - "result": { - "payloads": [{ "text": "...", "mediaUrl": null }], - "meta": { - "durationMs": 5673, - "executionTrace": { - "runner": "gateway", - "fallbackUsed": false, - "fallbackReason": null - }, - "aborted": false - } - } -} -``` - -### 可用字段 - -| 字段 | 路径 | 取值范围 | 说明 | -|------|------|---------|------| -| `status` | `data.status` | `"ok"` / `"error"` / `"timeout"` | CLI 执行结果 | -| `summary` | `data.summary` | `"completed"` / 错误信息字符串 | 辅助判断 | -| `fallbackUsed` | `data.result.meta.executionTrace.fallbackUsed` | `true` / `false` | 是否 fallback | -| `fallbackReason` | `data.result.meta.executionTrace.fallbackReason` | `"gateway_timeout"` 等 | fallback 原因 | -| `payloads` | `data.result.payloads` | `[{text, mediaUrl}]` 或空数组 | Agent 回复内容 | - -### 分类原则 - -- **优先用 `status`**:`status` 是 Gateway 官方提供的执行结果,比推断准确 -- **不解析 `meta` 的其他字段**:agentMeta、systemPromptReport 等是 OpenClaw 内部信息 -- **stdout 为空 = 进程异常终止**:`openclaw agent` 正常退出一定会输出 JSON - -### A0:stdout 为空(进程异常终止) - -> 注意:A0 在判定顺序上位于 A4 之后。exit=0 + stdout 为空 + task_status=done/review -> 会被 A4/A1 兜住,不会走到 A0。 - -``` -现象: - - 进程退出,exit_code ≠ 0 - - stdout 完全为空(没有 JSON 输出) - - status 解析为 None - -原因:进程被异常终止(被 kill、崩溃等),没有走到 writeRuntimeJson - -处理: - - 记录 outcome = "process_crash" - - counter.release()(wrapped_on_complete 可能没被调用 → ticker T1 兜底) - - 不续杯 - - 任务保持 working → ticker T1 检测 PID 死 → release counter + 推回 pending - - 等 ticker 重新 dispatch -``` - -### A0b:stdout 为空但 exit=0 - -``` -现象: - - 进程退出,exit_code = 0 - - stdout 完全为空(没有 JSON 输出) - - status 解析为 None - -原因:openclaw agent --json 在某些情况下不输出 JSON(已知行为) - -处理: - - 查任务 DB 状态 - - done/review → outcome = "completed"(正常完成) - - 其他 → outcome = "agent_error"(不续杯,等 ticker) -``` - -### A1:status="ok" + summary="completed" + fallbackUsed=false - -``` -现象: - - stdout JSON status = "ok" - - summary = "completed" - - executionTrace.fallbackUsed = false - - 任务 DB status = done 或 review - -原因:Agent 正常完成 - -处理: - - counter.release()(由 wrapped_on_complete 保证) - - 记录 outcome = "completed" - - 无需其他操作 -``` - -### A2/A3:status="timeout" - -``` -现象: - - stdout JSON status = "timeout" - -原因:Gateway timeout,Agent 被中断 - -处理: - - counter.release() - - 续杯次数 +1 - - 超过上限(3) → ❌ failed + escalate - - 未超限 → 🔄 通过 spawn_full_agent 续杯 - - 续杯 message:提示 Agent 检查历史继续未完成工作 -``` - -### A4:status="ok" + 任务 DB status=failed - -``` -现象: - - stdout JSON status = "ok" - - 但任务 DB status = failed - -原因:Agent 自己判断无法完成,主动标了 failed - -处理: - - counter.release() - - 记录 outcome = "agent_failed" - - 尊重 Agent 的判断,不续杯 -``` - -### A5/A6:status="ok" + fallbackUsed=true - -``` -现象: - - stdout JSON status = "ok" - - executionTrace.fallbackUsed = true - - executionTrace.runner = "embedded" - -原因:Gateway 端超时/错误,CLI fallback 到本地 embedded 执行 - -处理: - - 查任务 DB 状态 - - done/review → release counter → 结束(fallback 成功完成了) - - working/claimed → release counter → 标 failed + escalate - - 记录 outcome = "fallback_timeout",附带 warning -``` - -### A7-A12:status="error" - -``` -现象: - - stdout JSON status = "error" - - summary 含错误信息 - -原因:各类错误(认证/连接/API/compact/lock/未知) - -处理: - - counter.release() - - 不续杯 - - 记录 outcome = "api_error" / "gateway_unreachable" / "auth_failed" 等 - - 等 ticker 重新调度 -``` - -### A 兜底:status 未知值 - -``` -现象: - - stdout 有 JSON 但 status 不是 ok/error/timeout - -原因:未预期的状态值 - -处理: - - counter.release() - - 不续杯 - - 记录 outcome = "unknown_status" - - 等 ticker 重新调度 -``` -## 6. 情况 B:monitor_timeout 到了进程还没退出 - -``` -0s 600s 630s -├──────────────────────┤──────────────────┤ -│ Agent 执行中 │ Gateway timeout │ monitor timeout 触发 -│ │ (可能没触发) │ 进程还没退出 -``` - -### B1:lock PID 已死 + sessions.json status=running - -``` -现象: - - monitor_timeout 触发,进程没退出 - - lock 文件中的 PID 已不存在(os.kill(pid, 0) 抛 ProcessLookupError) - - sessions.json 中 status = "running" - -原因:Gateway 异常退出/崩溃,没有清理 lock 和 session 状态 - 子进程可能已经变成孤儿进程 - -处理(v2.0:假死复活术): - 1. 尝试复活: - a. 修改 sessions.json,把对应 session 的 status 从 running 改为 idle - b. release counter - c. ticker 下次 dispatch 时重新投递任务给 agent - 2. 如果同一任务连续假死 ≥ 2 次: - - ❌ failed + escalate - - 记录 outcome = "session_stuck" - - escalate 消息中包含:PID、session key、诊断信息、假死次数 - 3. 不 kill(让用户决定) -``` - -### B2:lock PID 存活 + sessions.json status=running + stderr 有 compact 关键字 - -``` -现象: - - monitor_timeout 触发,进程没退出 - - lock PID 仍然存活 - - sessions.json status = "running" - - 已读的 stderr 含 "compaction" / "context-overflow" - -原因:compact 正在进行中或 compact 后 retrying prompt 仍在执行 - compact 本身可能耗时很长(最长记录 15 分钟) - -处理: - - monitor_timeout_count +1 - - 未超限(< 3) → 不 release counter → 再启动一轮 _monitor_process 继续等 - - 超限(≥ 3,累计 31.5 分钟) → ❌ failed + escalate → counter.release(),不 kill - - 记录 outcome = "compact_hanging" -``` - -### B3:lock PID 存活 + sessions.json status=running + 无 compact 关键字 - -``` -现象: - - monitor_timeout 触发,进程没退出 - - lock PID 存活 - - sessions.json status = "running" - - 无 compact 相关关键字 - -原因(两种可能): - a) LLM 推理极慢/卡死(无输出) - b) 长任务正在执行(Agent 有在输出,但整体时间超过预期) - -处理: - - monitor_timeout_count +1 - - 未超限(< 3) → 不 release counter → 再启动一轮 _monitor_process 继续等 - - 超限(≥ 3) → ❌ failed + escalate → counter.release(),不 kill - - 记录 outcome = "process_hanging" - -区别 a 和 b: - - 无法在 monitor 层面精确区分 - - escalate 消息中列出两种可能,让用户判断 -``` - -### B4:lock PID 存活 + sessions.json status≠running(如 idle) - -``` -现象: - - monitor_timeout 触发,进程没退出 - - lock PID 存活 - - sessions.json status = "idle" 或其他非 running 状态 - -原因:session 状态已被其他操作改变(如 /reset、daily reset), - 但子进程还在运行(可能是 Gateway 正在清理或延迟退出) - -处理: - - 再等 60s(给 Gateway 清理时间) - - 如果进程仍未退出 → 按 B3 处理 - - 记录 outcome = "session_state_mismatch" -``` - -## 7. 续杯机制 - -### 续杯触发条件 - -进程退出 + 任务 API 状态不是终态(done/failed/cancelled)。 - -### Session 策略 - -| 任务类型 | Session 策略 | 说明 | -|---------|-------------|------| -| `_mail` 项目 | **主 Agent session**(不带 `--session-id`) | Mail 投递到主 session | -| 普通任务 | 新 session(`--session-id uuid4`) | 未来可动态选择主/sub | - -实现:`spawn_full_agent(use_main_session=True)` → 不传 `--session-id`,dispatcher 根据 `project_id == "_mail"` 判断。 - -⚠️ **已知问题**:Mail 用 main session 和 webchat/Control UI 共享同一 session。当 webchat 占用时,spawn 会等 session lock → timeout。通过 spawn 前 L3 session state 检查提前拦截。 - -### 续杯 message - -```python -RETRY_PROMPT = """你收到一个续杯提醒。你的任务在执行过程中被中断了。 - -## 任务信息 - -- 项目: {project_id} -- 任务ID: {task_id} -- 标题: {title} -- 续杯次数: 第 {retry_count} 次(上限 {max_retries} 次) - -请检查 session 历史中你之前做了什么,然后继续未完成的工作。 - -## 操作指令 - -### 查看任务当前状态 -```bash -curl http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}?expand=all -``` - -### 如果已经完成,标记 review -```bash -curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status \ - -H 'Content-Type: application/json' \ - -d '{{"status": "review", "agent": "{agent_id}"}}' -``` - -### 写入产出(如果之前没写) -```bash -curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/outputs \ - -H 'Content-Type: application/json' \ - -d '{{"agent": "{agent_id}", "type": "<类型>", "title": "<标题>", "content": "<内容>", "summary": "<摘要>"}}' -``` - -### 如果无法解决,标记失败 -```bash -curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status \ - -H 'Content-Type: application/json' \ - -d '{{"status": "failed", "agent": "{agent_id}", "detail": "<失败原因>"}}' -``` - -{fallback_hint}""" -``` - -### 续杯 spawn - -```python -# 续杯时复用 session_id -session_id = task.detail.get("retry_session_id") or original_session_id - -await self.spawner.spawn_full_agent( - agent_id=agent_id, - message=RETRY_PROMPT.format(...), - session_id=session_id, # 复用! - task_id=task.id, - on_complete=on_complete, -) -``` - -## 8. 计数器设计 - -| 计数器 | 用途 | 上限 | 超限处理 | -|--------|------|------|---------| -| `retry_count` | 续杯次数(A2/A3/A10/A12) | 3 | failed + escalate | -| `connect_retry_count` | 连接失败次数(A8) | 3 | failed + escalate | -| `api_retry_count` | API 错误次数(A9) | 3 | failed + escalate | -| `lock_retry_count` | Lock 冲突次数(A11) | 3 | ticker 下个 tick 重试 | -| `monitor_timeout_count` | monitor timeout 次数(B2/B3) | 3 | failed + escalate | - -存储在 `task_attempts.metadata` JSON 中。 - -### counter 生命周期(v2.0:调用级) - -``` -spawn_full_agent 内部 acquire - │ - ├─ 进程退出 → wrapped_on_complete → counter.release() - │ ├─ A1/A4 完成 → 结束 - │ ├─ A2/A3 timeout → _do_retry 手动 release → spawn_full_agent 重新 acquire - │ └─ A7-A12 → release → 等 ticker - │ - ├─ monitor timeout(B)→ counter 不 release(进程还在跑) - │ └─ B1 假死 → 手动 release + 复活 - │ - └─ 进程崩溃/PM2 重启 → ticker _check_timeouts 检测 → 手动 release -``` - -counter 生命周期 = 调用级:spawn 时 acquire,进程退出时 release。 -只有情况 B(进程不退出)counter 保持占用。 - -wrapped_on_complete 保证 release(try/finally),即使业务回调异常也不泄漏。 - -## 9. escalate 消息格式 - -``` -⚠️ Agent {agent_id} 任务 {task_id} 执行异常 - -类型: {outcome} -累计时间: {elapsed} -PID: {pid}({存活/已死}) -Session: {session_key} -续杯次数: {retry_count}/{max_retries} - -诊断信息: -- sessions.json status: {status} -- lock PID: {lock_pid} -- 最后 stderr: {stderr_tail} -- compaction checkpoints: {recent_checkpoints} - -建议操作: -1. 查看日志: pm2 logs sanguo-moziplus-v2 -2. 检查 session: openclaw sessions --agent {agent_id} --json -3. 继续执行: 如果任务在正常执行,手动 reset 状态 -4. 终止进程: kill {pid}(强制终止,会丢失进度) - -请决定如何处理。 -``` - -## 10. 改动范围 - -| 文件 | 改动 | 预估行数 | -|------|------|---------| -| `src/daemon/spawner.py` | `_monitor_process` 重写(情况 A/B 全部分支) | ~150 行 | -| `src/daemon/spawner.py` | `spawn_full_agent` 加 `--timeout` + session_id 复用 | ~15 行 | -| `src/daemon/spawner.py` | 新增辅助方法:`_get_task_status`、`_classify_exit`、`_read_sessions_json`、`_check_lock_pid` | ~80 行 | -| `src/daemon/spawner.py` | 新增 `RETRY_PROMPT` 模板 | ~20 行 | -| `src/daemon/ticker.py` | `_check_timeouts`:暂时性失败(A8/A9/A11)不改状态,等 ticker 自然重试 | ~15 行 | -| `config/guardrails.yaml` | 无需改动 | — | -| `config/default.yaml` | 新增 `gateway_timeout`、`max_retries`、`max_monitor_timeouts` | ~3 行 | - -注:`task_attempts` 表已有 `metadata` 列(TEXT 类型),无需改 db.py/models.py。 - -总计约 280 行,3 个文件。 - -## 11. 测试计划 - -| 用例 | 模拟方式 | 验证 | -|------|---------|------| -| A1 正常完成 | E2E 已有 | 任务 done | -| A2 Gateway timeout + 续杯 | 手动设 gateway_timeout=60s,任务需要 90s | 续杯 1 次后完成 | -| A3 连 working 都没写 | 模拟 Agent 第一步就超时 | 续杯后从步骤 1 开始 | -| A4 Agent 自己 failed | Agent 输出 `status: failed` | 不续杯 | -| A5 fallback 成功 | 模拟 Gateway timeout(很难模拟) | 查任务状态决定 | -| A7 认证失败 | 改错 token | 不续杯,escalate | -| A8 Gateway 不可达 | 停 Gateway | 重试 3 次后 escalate | -| B1 假死 | kill Gateway 但保留子进程 | escalate | -| B2 compact 卡住 | 主 session 长对话触发 compact | 等待或 escalate | -| B3 进程不退出 | 模拟长时间无输出 | 等 3 轮后 escalate | -| 续杯上限 | 设 max_retries=1,任务永远完不成 | 第 2 次续杯后 failed | - ---- - -## 12. v2.8.1 补丁:Crash 健壮性(2026-05-31) - -> 作者:庞统 | 触发:R1 E2E 中 simayi 连续 crash 3 次无熔断 - -### 12.1 问题发现 - -R1 E2E 测试中,simayi-challenger 在 review 阶段连续 crash 3 次(exit=1,空 stdout): - -``` -18:32:20 Spawned (broadcast), session=None, pid=13844 - status=done, recent_compact=False ← compact 检测通过(实际 Gateway 在做 compact) - Gateway 加载 10MB main session → 触发 auto-compaction → 耗时 ~3 分钟 -18:35:47 Crash (exit=1), task_status=review - → on_complete: "review agent crashed, NOT marking done" -18:35:48 PM2 restart → ticker 重启 → re-dispatch review to simayi - recent_routing 未拦截(crash 时 routing_decision 未持久化) -18:36:51 Crash again (exit=1) -...(共 3 次 crash) -``` - -**根因**:main session 历史 10MB,Gateway 触发 compact,compact 耗时导致进程异常退出。 - -### 12.2 暴露的三个问题 - -#### P1:Compact 检测失效 - -现有检测逻辑: -```python -for cp in main_session.get("compactionCheckpoints", []): - if (now_ms - cp.get("createdAt", 0)) < 300_000: - result["recent_compact"] = True -``` - -**失效原因**: -- Gateway 的 compact 不写入 `compactionCheckpoints`(只在 session reset 时写入,且只保留 3 条) -- 实际 `compactionCount` 已达 68 次,但 checkpoints 最新一条是 10 天前 -- 因此 `recent_compact` 始终为 False - -#### P2:current_agent 不回退 - -review dispatch 时设置 `current_agent = simayi-challenger`,crash 后不回退。 -导致 `exclude_current=True` 时,simayi 被排除,但无其他 review agent 可用 → 任务卡住。 - -#### P3:Crash 无分级防护 - -- crash 后无 cooldown(cooldown 只在 api_error/429 时设置) -- review 状态无超时回收(`_check_timeouts` 只查 claimed/working) -- 无 crash 计数上限,同一 task 可无限重试 -- v2.7.2 设计文档曾讨论 crash_count 熔断,结论是"❌ 不采纳,崩溃可能是任务问题" - → 部分正确,但缺乏更精细的分级处理 - -### 12.3 修复方案 - -#### Fix-1:Compact 检测改用 session jsonl 末尾扫描 - -**原理**:Gateway 完成一次 compact 后,会在 session jsonl 文件末尾追加一条 `type=compaction` 记录。 - -```python -def _check_recent_compaction(agent_id: str, window_seconds: int = 300) -> bool: - """读 session jsonl 末尾,检查是否有 window_seconds 内的 compaction 记录""" - sessions_path = Path.home() / ".openclaw" / "agents" / agent_id / "sessions" / "sessions.json" - if not sessions_path.exists(): - return False - try: - with open(sessions_path) as f: - sessions = json.load(f) - main = sessions.get(f"agent:{agent_id}:main", {}) - session_file = main.get("sessionFile", "") - if not session_file or not Path(session_file).exists(): - return False - # 读末尾 50 行,查找最近的 compaction - from datetime import datetime, timezone - now = datetime.now(timezone.utc) - with open(session_file, "rb") as sf: - # seek to last 50KB - sf.seek(0, 2) - size = sf.tell() - sf.seek(max(0, size - 51200)) - tail = sf.read().decode("utf-8", errors="replace") - for line in reversed(tail.splitlines()): - if not line.strip(): - continue - try: - obj = json.loads(line) - except: - continue - if obj.get("type") == "compaction": - ts = obj.get("timestamp", "") - if ts: - ct = datetime.fromisoformat(ts.replace("Z", "+00:00")) - if (now - ct).total_seconds() < window_seconds: - return True - return False - except Exception: - return False -``` - -**改动范围**:`spawner.py` `_check_session_state` 方法,替换 `compactionCheckpoints` 检测逻辑。 - -**同时保留** `_compact_waits` 的 monitor compact 等待逻辑不变。 - -**边界处理**(review WARN 补充): -- jsonl 不存在 → 返回 False(无 compact = 安全) -- json.loads 失败 → 跳过该行(compact 未完成 = 不判定为 compacting) -- 只在 agent 非空闲时(status 不是 done/idle)才扫描,减少不必要 I/O -- 检测到 compacting 后同时设 cooldown(和 Fix-3a 共用),避免 ticker 反复 dispatch → AgentBusyError 循环 - -#### Fix-2:current_agent 回退 - -**原理**:crash 后在 `_task_on_complete` 中回退 `current_agent` 到 `assignee`。 - -```python -# dispatcher.py _task_on_complete 中 -if _is_review: - if outcome in ("completed", "session_revived"): - _dispatcher._mark_task_status(_task_db, _task_id, "done") - else: - logger.warning("Task %s: review agent %s (%s), NOT marking done", _task_id, aid, outcome) - # Fix-2: crash 后回退 current_agent,避免 exclude_current 卡死 - conn = get_connection(_task_db) - try: - conn.execute( - "UPDATE tasks SET current_agent = " - "(SELECT assignee FROM tasks WHERE id=?) " - "WHERE id=? AND current_agent=?", - (_task_id, _task_id, aid) - ) - conn.commit() - finally: - conn.close() -``` - -**改动范围**:`dispatcher.py` `_task_on_complete` 回调,~6 行。 - -**精确化条件**(review WARN 回应): -```python -ROLLBACK_OUTCOMES = {"crashed", "compact_failed", "process_crash", "session_stuck", "compact_hanging"} -``` -只对明确的异常 outcome 回退,skipped/cancelled 不回退。 - -**assignee 排除问题**:回退后 assignee 可能仍在 exclude 列表。但 exclude_current 的逻辑是在 `_dispatch_reviews` 中基于 `task.current_agent` 构建,回退后 current_agent=assignee(原始执行者),exclude 会排除的是 review agent(已 crash 的那个),不是 assignee。所以无冲突。 - -#### Fix-3:Crash 分级防护 - -**设计原则**:不是"禁用 agent"(v2.7.2 已否决),而是"给 agent 恢复时间 + 限制单 task 重试次数"。 - -##### 3a. Crash 后设 cooldown - -```python -# spawner.py _handle_exit else 分支(crash 走这里) -if outcome in ("crashed", "compact_failed", "process_crash", "agent_error"): - if self.counter: - self.counter.set_cooldown(agent_id, seconds=300) # 5 分钟冷却 - logger.info("Crash cooldown set for %s: 300s", agent_id) -``` - -**改动范围**:`spawner.py` `_handle_exit` else 分支,~4 行。 - -**理由**:crash 后 agent 的 session 可能处于异常状态(timeout/compacting), -5 分钟冷却给 Gateway/agent 恢复时间。如果 compact 正在进行,5 分钟后一般已完成。 - -**优化**(review INFO 采纳):cooldown 固定 5 分钟足够,不引入轮询复杂度。 -compact 场景通过 Fix-1 的 compact 检测 + AgentBusyError 覆盖,cooldown 是额外保险。 - -##### 3b. Review 超时回收 - -在 `_check_timeouts` 中增加 review 状态的超时检查: - -```python -# ticker.py _check_timeouts 末尾增加 -# review 超时 → 推回 pending(让 ticker 重新 dispatch review) -review_tasks = queries.tasks_by_status("review") -for task in review_tasks: - # 检查是否有活跃的 review agent(current_agent 对应的进程是否存活) - current = task.current_agent # 需要确认 task model 有此字段 - if current: - session_info = self.spawner.get_session_by_agent(current) if self.spawner else None - pid = session_info.get("pid") if session_info else None - if pid and self._is_pid_alive(pid): - continue # review agent 还在跑,不回收 - # 无活跃进程 → 检查超时 - updated = task.updated_at - if updated: - elapsed = (now - datetime.fromisoformat(updated)).total_seconds() / 60.0 - if elapsed > self.review_timeout_minutes: # 默认 15 分钟 - conn = get_connection(db_path) - try: - self._transition_status( - conn, task.id, "pending", - agent="daemon", - detail={"reason": "review_timeout", "elapsed_minutes": round(elapsed, 1)}, - ) - # 回退 current_agent - conn.execute( - "UPDATE tasks SET current_agent = NULL WHERE id=?", (task.id,) - ) - conn.commit() - logger.warning("Review timeout: %s (%.1fm), pushed back to pending", - task.id, elapsed) - finally: - conn.close() -``` - -**改动范围**:`ticker.py` `_check_timeouts`,~25 行。 - -**配置**:新增 `review_timeout_minutes` 参数,默认 15 分钟。 - -**"无活跃进程"定义**(review WARN 回应):双层判断: -1. `spawner.get_session_by_agent(current_agent)` → 检查内存中是否有 running session -2. 如果有 → 检查 PID 存活 → PID 活着则不回收 -3. 如果无内存记录 → 无活跃进程 → 可回收 - -**推回原子性**(review WARN 回应):推回前先 recheck 当前状态, -如果已是 pending/working/done/failed 则不推回(可能被其他操作改变了)。 - -**Fix-3b/3c 交互**(review INFO 回应):超时推回 pending → ticker 重新 dispatch → -如果又 crash,这个 crash 会通过 `_record_attempt` 记录到 task_attempts, -自然被 `_check_crash_limit` 计数。无需特殊处理。 - -##### 3c. Crash 计数 + 上限 escalate - -利用现有 `task_attempts` 表,在 dispatch 前查该 task 的最近 crash 次数: - -```python -# dispatcher.py dispatch() 入口增加 -def _check_crash_limit(self, task_id: str, db_path: Path, limit: int = 3) -> bool: - """检查 task 最近 30 分钟内的 crash 次数是否超限""" - try: - conn = get_connection(db_path) - try: - row = conn.execute( - "SELECT COUNT(*) as cnt FROM task_attempts " - "WHERE task_id=? AND outcome='crashed' " - "AND created_at > datetime('now', '-30 minutes')", - (task_id,) - ).fetchone() - return (row["cnt"] if row else 0) >= limit - finally: - conn.close() - except Exception: - return False -``` - -在 `_dispatch_reviews` 中调用: -```python -if self._check_crash_limit(task.id, db_path, limit=3): - # 超限 → 标 failed + escalate - conn = get_connection(db_path) - try: - self._transition_status( - conn, task.id, "failed", - agent="daemon", - detail={"reason": "review_crash_limit", "task_id": task.id}, - ) - finally: - conn.close() - logger.error("Task %s: 3 crashes in 30min, marking failed", task.id) - continue -``` - -**改动范围**:`dispatcher.py` 新增 ~15 行方法 + `_dispatch_reviews` 调用点 ~8 行。 - -### 12.4 改动总览 - -| 文件 | 改动 | 行数 | -|------|------|------| -| `spawner.py` | Fix-1 compact 检测替换 + Fix-3a crash cooldown | ~30 行 | -| `dispatcher.py` | Fix-2 current_agent 回退 + Fix-3c crash 计数 | ~25 行 | -| `ticker.py` | Fix-3b review 超时回收 | ~25 行 | -| **总计** | | **~80 行** | - -### 12.5 测试计划 - -| 用例 | 模拟方式 | 验证 | -|------|---------|------| -| Compact 检测 | 手动在 session jsonl 末尾写 compaction 记录 | recent_compact=True → AgentBusyError | -| current_agent 回退 | review agent crash → 检查 DB current_agent = assignee | ✅ 回退 | -| Crash cooldown | review agent crash → 检查 counter.is_cooling_down() | ✅ 5 分钟冷却 | -| Review 超时 | review 状态 15 分钟无活跃进程 → 检查推回 pending | ✅ 回收 | -| Crash 计数上限 | 同一 task 连续 crash 3 次 → 检查标 failed | ✅ escalate | -| E2E 回归 | 重新跑 v3.0 E2E 测试 | 全流程通过 | diff --git a/docs/design/technical-design-v2.6.md b/docs/design/technical-design-v2.6.md deleted file mode 100644 index e5ca997..0000000 --- a/docs/design/technical-design-v2.6.md +++ /dev/null @@ -1,998 +0,0 @@ -# v2.6 技术方案设计 - -**版本**: v2.6.2-tech -**基于**: architecture-v2.6.md (v2.6.11) -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-16 - ---- - -## 1. 总体技术架构 - -### 1.1 全局架构图 - -``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ 用户层 │ -│ ┌──────────────────┐ ┌──────────────────┐ │ -│ │ Agent 对话入口 │ │ Dashboard (8083) │ │ -│ │ (OpenClaw Chat) │ │ React + Vite │ │ -│ └────────┬─────────┘ └────────┬─────────┘ │ -│ │ │ │ -│ 自然语言对话 REST API + SSE │ -│ │ │ │ -└───────────┼───────────────────────┼────────────────────────────────────────┘ - ▼ ▼ -┌───────────────────────────────┐ ┌──────────────────────────────────────┐ -│ Agent 执行环境(独立进程) │ │ v2 主进程(PM2: sanguo-moziplus-v2) │ -│ │ │ asyncio event loop │ -│ ┌─────────────────────────┐ │ │ │ -│ │ L0 铁律 (OpenClaw Hook) │ │ │ ┌─────────────────────────────────┐ │ -│ │ L1 角色 (SOUL/IDENTITY) │ │ │ │ API 层 (FastAPI + uvicorn) │ │ -│ │ L2 引擎注入 (bootstrap) │ │ │ │ ┌──────┐ ┌──────┐ ┌────────┐ │ │ -│ │ L3 被动参考 (Skill) │ │ │ │ │黑板API│ │项目API│ │SSE推送 │ │ │ -│ └─────────────────────────┘ │ │ │ └──┬───┘ └──┬───┘ └───┬────┘ │ │ -│ │ │ └─────┼────────┼─────────┼───────┘ │ -│ Agent → 黑板 的交互方式: │ │ │ │ │ │ -│ ① CLI (blackboard.py) ───────┼──┼──→ 直接操作黑板 DB │ -│ ② Inbox JSONL ──────────────┼──┼──→ 秒级事件推送给 Daemon │ -│ │ │ │ -│ │ │ ┌─────────────────────────────────┐ │ -│ │ │ │ Daemon 引擎层(同一 event loop)│ │ -│ │ │ │ │ │ -│ │ │ │ ticker (30s scan) │ │ -│ │ │ │ ┌──────────┐ ┌──────────────┐ │ │ -│ │ │ │ │审查流水线 │ │ 依赖推进 │ │ │ -│ │ │ │ │(4级分级) │ │ 反驳权/蒸馏 │ │ │ -│ │ │ │ └──────────┘ └──────────────┘ │ │ -│ │ │ │ ┌──────────┐ ┌──────────────┐ │ │ -│ │ │ │ │Agent调度器│ │Bootstrap拼装 │ │ │ -│ ← asyncio.create_subprocess │ │ │ └────┬─────┘ └──────────────┘ │ │ -│ _exec (spawn 不等返回) │←─┼──┼───────┘ │ │ -│ │ │ │ ┌──────────────────────────────┐│ │ -│ │ │ │ │ ActiveAgentCounter (并发控制) ││ │ -│ │ │ │ └──────────────────────────────┘│ │ -│ │ │ └─────────────────────────────────┘ │ -│ │ │ │ -│ │ │ ┌─────────────────────────────────┐ │ -│ │ │ │ 黑板数据层 (per-project SQLite) │ │ -│ │ │ │ ┌─────┐ ┌──────┐ ┌──────┐ │ │ -│ │ │ │ │tasks│ │outputs│ │reviews│ │ │ -│ │ │ │ └─────┘ └──────┘ └──────┘ │ │ -│ │ │ │ ┌──────────┐ ┌───────────┐ │ │ -│ │ │ │ │comments │ │experiences│ │ │ -│ │ │ │ │decisions │ │events │ │ │ -│ │ │ │ │observations│ │ │ │ -│ │ │ │ └──────────┘ └───────────┘ │ │ -│ │ │ └─────────────────────────────────┘ │ -└───────────────────────────────┘ └──────────────────────────────────────┘ - -┌───────────────────────────────────────────────────────────────────────────┐ -│ 配置 & 知识层(文件系统) │ -│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ -│ │_registry │ │guardrails │ │review_ │ │prompt_ │ │ -│ │.yaml │ │.yaml │ │protocols/ │ │templates/ │ │ -│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ -│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ -│ │schemas/ │ │skills/ │ │project.yaml│ (per-project 覆盖) │ -│ │(JSON) │ │(builtin+ │ │ │ │ -│ │ │ │ custom) │ │ │ │ -│ └────────────┘ └────────────┘ └────────────┘ │ -└───────────────────────────────────────────────────────────────────────────┘ -``` - -### 1.2 多用户并发架构 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 多用户并发入口 │ -│ │ -│ 用户A ──→ OpenClaw Chat ──→ 庞统 Agent ──→ 项目X 任务A1, A2 │ -│ 用户B ──→ OpenClaw Chat ──→ 庞统 Agent ──→ 项目Y 任务B1 │ -│ 用户C ──→ Dashboard ─────→ REST API ────→ 项目X 任务C1 │ -│ │ -│ 所有用户的请求汇聚到同一个 Daemon ticker(30s 扫描全部项目) │ -│ 并发由 ActiveAgentCounter 控制(max_global=5, max_per_agent=1) │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -**多用户设计要点**: - -| 维度 | 机制 | 说明 | -|------|------|------| -| 项目隔离 | per-project blackboard.db | 不同项目的任务、产出、评论完全隔离 | -| 并发控制 | ActiveAgentCounter | 全局 max_global=5 同时最多 5 个 Agent,per-agent max=1 串行执行 | -| 资源竞争 | Agent 池排队 | 张飞正在执行任务A,任务B 排队等张飞释放 | -| tick 调度 | 30s 扫全部项目 | 每轮 tick 按 project_id 轮询,每项目独立 tick | -| 事件驱动 | 共享 Inbox JSONL | 任何项目的 Agent 完成都秒级触发 Daemon | -| 前端隔离 | URL path `/api/projects/{pid}/` | Dashboard 切换项目,SSE 按 pid 过滤 | - -**当前规模定位**:单机部署(Mac mini M4, 16GB),3-5 个活跃项目,同时 ≤5 个 Agent 并发。超出此规模需要架构演进(见 §15.3)。 - -### 1.3 核心数据流 - -``` - ┌──────────┐ - │ 用户需求 │ - └────┬─────┘ - │ - 庞统规划+拆解 - │ - ┌────▼─────┐ - │ 任务写入 │ - │ 黑板 │ - └────┬─────┘ - │ - ┌──────────┼──────────┐ - ▼ ▼ ▼ - pending in_review blocked - │ │ │ - │ │ ┌────┘ - │ │ ▼ - 调度Agent 审查流水线 依赖满足 - (受Counter (分级处理) │ - 控制)│ │ → pending - │ │ - ▼ │ - ┌──────────────┐ │ - │ Agent 执行 │ │ - │ (独立进程) │ │ - └───┬──────┬───┘ │ - │ │ │ - 写产出 写评论 │ - │ │ │ - ▼ │ │ - Guardrail │ │ - 检查 ───────┤ │ - │ │ │ - ┌────┘ │ │ - ▼ │ │ - 通过 → 触发审查 │ │ - 失败 → 回 working │ │ - │ │ │ - │ ┌─────┘ │ - │ ▼ │ - │ 通过 → done ───────┤──→ 一级蒸馏 → 经验注入 - │ │ │ - │ 不通过 → 反驳权 │ - │ │ ↕ 协商 │ - │ │ max_rounds │ - │ │ ↓ 超轮次 │ - │ 庞统裁决 │ - │ │ │ - │ ├→ done/revision│ - │ └───────────────┘ - │ - └──→ 完成后检查:下游 blocked 任务是否可解锁 -``` - -**关键循环路径**(非直线流程): -1. **Guardrail 失败循环**:产出写完 → Guardrail 检查 → 失败 → 回 working → Agent 重新执行 -2. **审查-反驳循环**:审查不通过 → 反驳权协商(≤ max_rounds 轮)→ 超轮次庞统裁决 -3. **依赖解锁循环**:任务完成 → 检查下游 blocked 任务 → 依赖满足 → 下游变 pending → 调度 -4. **经验闭环**:任务完成 → 一级蒸馏 → 经验注入 → 下次 build_bootstrap 复用 - -### 1.4 交互路径详解 - -| 路径 | 方式 | 说明 | -|------|------|------| -| 用户 → Agent | OpenClaw Chat | 自然语言对话,Agent 自带身份和工具 | -| 用户 → Dashboard | 浏览器访问 8083 | REST API 查黑板、SSE 收推送 | -| Agent → 黑板 | CLI `blackboard.py` | read/claim/output/comment/decide/observe/create/review | -| Agent → Daemon | 写 Inbox JSONL | 秒级事件推送,触发 Daemon 提前 tick | -| Agent → Agent | 黑板评论 | comments 表(handoff/rebuttal/debate),替代 Mail | -| Daemon → Agent | `asyncio.create_subprocess_exec` | spawn 不等返回,下次 tick 检查产出 | -| Daemon → Dashboard | SSE `/api/events` | 任务状态变更、审查结果、通知推送 | -| Dashboard → Daemon | REST API | 查状态、手动 tick、项目管理 | - -### 1.5 组件职责矩阵 - -| 层 | 组件 | 职责 | 技术 | -|----|------|------|------| -| **用户层** | Agent 对话 | 自然语言交互入口 | OpenClaw Chat | -| | Dashboard | 可视化监控/配置/Checkpoint | React + Vite (8083) | -| **API 层** | FastAPI | REST API + SSE + 静态文件 | uvicorn | -| **引擎层** | Daemon ticker | 30s 状态扫描 + 事件驱动 | asyncio background task | -| | 调度器 | 三级调度 (Daemon/Full/Sub) | DISPATCH_RULES 配置表 | -| | 审查流水线 | 分级审查 + 反驳权 + 辩论 | review_flow.py | -| | Bootstrap 拼装 | 四层上下文 L0-L3 | prompt_templates + YAML | -| | Inbox watcher | JSONL 秒级事件推送 | asyncio 1s 轮询 | -| | 健康自检 | 逻辑死循环检测 | 连续 N tick 无变更告警 | -| **Agent 层** | Full Agent | 有身份角色的任务执行 | openclaw agent CLI | -| | Subagent | 无身份一次性检查 | sessions_spawn API | -| **数据层** | 黑板 DB | 任务/评论/产出/审查/经验 | SQLite per-project | -| | Inbox | 跨进程事件队列 | JSONL + truncate | -| | _registry.yaml | 项目注册表 | YAML (人可读) | -| **配置层** | guardrails.yaml | L1/L2 检查规则 | YAML | -| | review_protocols/ | 审查协议 | YAML | -| | skills/ | 三层载体 (Memory/Skill/Rule) | SKILL.md + meta.yaml | -| | project.yaml | per-project 配置覆盖 | YAML | - ---- - -## 2. 技术栈与选型权衡 - -### 2.1 技术栈总览 - -| 组件 | 选型 | 版本 | -|------|------|------| -| Daemon 进程管理 | PM2 | 6.0.14 | -| Daemon 框架 | Python + asyncio | 3.9+ | -| 黑板存储 | SQLite (per-project) | 3.51+ | -| HTTP API | FastAPI + uvicorn | 独立安装 | -| Agent 调度 | openclaw CLI / sessions_spawn | 2026.5.7 | -| 前端 | React + Vite + TypeScript | 复用 v1.0 | -| 实时推送 | SSE | FastAPI 内置 | -| 配置 | YAML (PyYAML) | — | - -### 2.2 选型权衡分析 - -**每个关键技术选型都遵循"不加重型新依赖"原则,基于已有环境。** - -| 决策 | 选择 | 备选 | 选择理由 | 备选缺陷 | -|------|------|------|---------|---------| -| HTTP 框架 | **FastAPI** | Starlette / Litestar / Flask | 2025-2026 AI Agent 编排系统事实标准(Agno AgentOS、OpenAI Agents SDK 均用 FastAPI);内置 OpenAPI + SSE + async;团队已有 v1 经验 | Starlette 需自己拼 Auth/SSE/DI;Litestar 太新生态小;Flask 不原生 async | -| 黑板存储 | **SQLite** | PostgreSQL / JSON 文件 | per-project 物理隔离(单项目故障不影响其他);零运维(无 DB server);WAL 模式足够应对单机并发;团队已有 WAL+busy_timeout 经验 | PostgreSQL 需额外 DB server 运维,对单机场景过重;JSON 文件无事务、无并发保护 | -| 实时推送 | **SSE** | WebSocket / 轮询 | 单向推送足够(Daemon→Dashboard);FastAPI 原生支持;自动重连;降级轮询简单 | WebSocket 双向能力多余,增加复杂度;纯轮询延迟高 | -| 并发模型 | **纯 asyncio 单线程** | threading / multiprocessing | 与 FastAPI event loop 共存;Agent spawn 用 create_subprocess_exec 非阻塞;无锁竞争问题 | threading 引入 GIL + 锁复杂度;multiprocessing 进程间通信代价大 | -| 配置格式 | **YAML** | TOML / JSON / Python | guardrails/review_protocols 需要人可读可编辑;YAML 支持 multiline string(prompt 模板);紧急运维可直接编辑 | TOML multiline 不便;JSON 无注释;Python 有安全风险 | -| 进程管理 | **PM2** | systemd / supervisord | 已有 10 个 PM2 进程运行;log management + auto restart + 开机自启;ecosystem.config.cjs 统一管理 | systemd 需 root 权限;supisord 不如 PM2 对 Node.js 友好 | -| Agent spawn | **asyncio.create_subprocess_exec** | subprocess.Popen / sessions_spawn | Full Agent 需要独立进程+独立 session;async 版本不阻塞 event loop;下次 tick 检查产出而非等待返回 | Popen 同步阻塞 event loop;sessions_spawn 仅适用无身份 Subagent | - ---- - -## 3. 项目结构 - -``` -sanguo_moziplus_v2/ # 项目根目录 -├── src/ -│ ├── main.py # FastAPI 入口 + Daemon ticker 启动 -│ │ -│ ├── blackboard/ # 黑板核心模块 -│ │ ├── __init__.py -│ │ ├── db.py # SQLite 连接管理(per-project) -│ │ ├── models.py # 数据模型 -│ │ ├── operations.py # 写操作(task/comment/output/decision/observation/review) -│ │ └── queries.py # 读操作(L1/L2/L3 分层) -│ │ -│ ├── daemon/ # Daemon 核心模块 -│ │ ├── __init__.py -│ │ ├── ticker.py # Tick 循环主逻辑(30s) -│ │ ├── dispatcher.py # Agent 调度判据(Full/Subagent/Daemon 三级) -│ │ ├── spawner.py # Agent spawn + session 管理 -│ │ ├── inbox.py # Inbox JSONL watcher + truncate -│ │ ├── guardrail.py # L1 assert 执行 + L2 subagent 触发 -│ │ ├── review_flow.py # 审查流水线(单审/反驳权/辩论) -│ │ ├── experience.py # 一级蒸馏触发 + 经验注入 -│ │ ├── bootstrap.py # build_bootstrap() L1/L2/L3 消息拼装 -│ │ ├── health.py # 健康检查 + 逻辑死循环检测 -│ │ ├── counter.py # ActiveAgentCounter 异步计数器 -│ │ └── notifier.py # @mention 解析 + Inbox 事件注入 -│ │ -│ ├── api/ # HTTP API -│ │ ├── blackboard_routes.py # 黑板 CRUD API -│ │ ├── daemon_routes.py # Daemon 控制 API -│ │ ├── sse_routes.py # SSE 推送端点 -│ │ └── project_routes.py # 多项目管理 API -│ │ -│ ├── cli/ # CLI 工具 -│ │ ├── blackboard.py # Agent 黑板操作 CLI -│ │ └── admin.py # 管理员控制 CLI -│ │ -│ └── frontend/ # 前端(React + Vite) -│ ├── src/ -│ │ ├── pages/ # 5 页面 -│ │ │ ├── TaskBoard.tsx # 任务看板(核心) -│ │ │ ├── GlobalMonitor.tsx # 全局监控 -│ │ │ ├── ArtifactVault.tsx # 产出档案 -│ │ │ ├── SystemConfig.tsx # 系统配置 -│ │ │ └── AIBriefing.tsx # AI Briefing -│ │ ├── components/ -│ │ │ ├── CheckpointPanel.tsx # Checkpoint 交互 -│ │ │ ├── NotificationCenter.tsx # 推送通知中心 -│ │ │ └── ProjectSwitcher.tsx # 项目切换器 -│ │ ├── store.ts # Zustand 状态管理 -│ │ └── api.ts # API 层 -│ └── dist/ # 构建产物(8083 直接服务) -│ -├── config/ -│ ├── default.yaml # 全局默认配置 -│ ├── guardrails.yaml # Guardrail 规则(per task_type) -│ ├── template_components.yaml # 模板组件库(7组件 + custom) -│ └── review_protocols/ # 审查协议 -│ ├── plan_review.yaml -│ ├── output_review.yaml -│ ├── analysis_review.yaml -│ └── final_review.yaml -│ -├── prompt_templates/ # L2 引擎注入模板 -│ ├── executor.md # 执行者操作规范 -│ ├── reviewer.md # 审查者操作规范 -│ ├── planner.md # 庞统规划操作规范 -│ ├── adjudicator.md # 庞统裁决操作规范 -│ ├── rebuttal.md # 反驳权操作规范 -│ └── plan_checker.md # Plan Checker 验证规范 -│ -├── schemas/ # L1 Schema 校验 -│ ├── handoff.schema.json -│ ├── output.schema.json -│ ├── decide.schema.json -│ └── observe.schema.json -│ -├── projects/ # 多项目目录(课题11) -│ └── {project_id}/ -│ ├── blackboard.db # per-project SQLite -│ ├── config/ # per-project 配置覆盖 -│ │ └── project.yaml -│ ├── artifacts/ # per-project 产出物 -│ ├── experiences/ # per-project 经验库 -│ └── skills/ # per-project Skill 覆盖/扩展 -│ -├── inbox/ # Inbox JSONL -│ └── daemon.jsonl # 跨进程事件推送 -│ -├── skills/ # 全局 Skill 库 -│ ├── builtins/ # 系统内置 Skill -│ │ ├── blackboard_operations/ -│ │ ├── code_review/ -│ │ └── data_validation/ -│ └── custom/ # 蒸馏产出/用户创建 -│ └── {skill_id}/ -│ ├── SKILL.md # 四要素 -│ ├── template.md # 可选详细模板 -│ └── meta.yaml # 版本/来源/tag -│ -├── tests/ -│ ├── unit/ -│ └── e2e/ -│ -├── pyproject.toml -├── ecosystem.config.cjs # PM2 配置 -└── docs/ # 设计文档 -``` - ---- - -## 4. 数据库设计 - -### 4.1 数据库架构(per-project) - -每个项目独立 SQLite 数据库,物理隔离。路径:`projects/{project_id}/blackboard.db`。 - -全局注册表:`projects/_registry.yaml`(YAML 文件,人可读可 Git 版本管理,紧急运维可直接编辑)。tick 热路径用内存 dict 缓存,_registry.yaml 只在 CLI 操作时读写(非热路径)。 - -### 4.2 表结构 - -完整 Schema 见 `architecture-v2.6.md §3.2`,此处列出核心表: - -| 表 | 用途 | 来源课题 | -|----|------|---------| -| `tasks` | 任务主表(状态机) | 课题1 | -| `comments` | 评论/Handoff/辩论论点(含 comment_type) | 课题2/3 | -| `outputs` | 产出物(含 attempt_number) | 课题2 | -| `decisions` | 决策记录 | 课题1 | -| `observations` | 观察/吹哨(severity 分级) | 课题1 | -| `events` | 事件流(审计追踪) | 课题2 | -| `reviews` | 评审结果(含 confidence/rebuttal_status/debate_round) | 课题3 | -| `experiences` | 经验沉淀(含 tags) | 课题6 | -| `experience_tags` | 经验标签关联表 | 课题6 | -| `checkpoints` | Checkpoint 人工确认(v2.8/M3) | M3 | - -### 4.3 关键约束 - -- WAL 模式 + `busy_timeout=5000` + `PRAGMA foreign_keys=ON` -- 所有写操作走 `BEGIN IMMEDIATE` 串行化 -- comments 表 `comment_type` CHECK 约束区分 8 种类型 - ---- - -## 5. Daemon 核心架构 - -### 5.1 Tick 循环(30s) - -```python -async def tick(project_id: str): - conn = get_connection(project_id) - try: - # 1. 逻辑健康自检(连续 N tick 无变更 → 告警) - health_check(conn) - - # 2. 处理 Inbox JSONL(即时事件) - process_inbox(conn) - - # 3. 扫描黑板状态 - tasks = scan_tasks(conn) - - # 4. 审查流水线处理 - review_flow(conn, tasks) - - # 5. 依赖推进(已完成任务解锁下游) - advance_dependencies(conn, tasks) - - # 6. 反驳权进度检测(催促通知) - check_rebuttal_progress(conn) - - # 7. Agent 调度 - dispatch_pending(conn, tasks) - - # 8. 一级蒸馏触发 - check_distillation(conn) - - finally: - conn.close() -``` - -**Tick + Inbox 双层事件架构**: -- Tick 30s 兜底扫描(Pull) -- Inbox JSONL 秒级推送加速(Push) -- Inbox 文件用 truncate(清空不删除),避免并发写入时文件不存在 - -### 5.2 Agent 调度判据(三级决策树) - -详见 `topic3-challenge-review-proposal.md §5.4`。 - -```python -def dispatch(task, action_type, project_config): - registered_agents = project_config.get("agents", []) - - # Level 1: 纯机械检查 → Daemon 直接执行 - if action_type in ("L1_guardrail", "format_check", "file_exists_check"): - return execute_locally(task) - - # Level 2: 有名字的角色 → Full Agent (asyncio.create_subprocess_exec) - if task.assignee in registered_agents: - if action_type == "adjudication": - return spawn_full_agent(task.assignee, new_session=True) - return spawn_full_agent(task.assignee) - - # Level 3: 无名字的一次性任务 → Subagent (sessions_spawn) - if DISPATCH_RULES.get(action_type) == "subagent": - return spawn_subagent(task_description=action_type) - - # Level 4: 未知 action_type → 庞统裁决 - return spawn_full_agent("pangtong-fujunshi", new_session=True) -``` - -**spawn 方式**: -- Full Agent:`asyncio.create_subprocess_exec`(异步非阻塞),不 await 完成,下次 tick 检查产出 -- Subagent:`sessions_spawn`(Gateway API),等返回 - -### 5.3 线程模型:纯 asyncio 单线程 - -整个 Daemon 运行在单个 asyncio event loop 中(与 FastAPI 共享)。 - -**关键设计决策**:Full Agent spawn 不用 `subprocess.Popen`(同步,会短暂阻塞 event loop),改用 `asyncio.create_subprocess_exec`(异步非阻塞)。这样 API 请求处理、SSE 推送、Daemon tick 三者互不阻塞。 - -```python -async def spawn_full_agent(agent_id: str, message: str, new_session: bool = False) -> str: - """异步非阻塞 spawn,全程 asyncio""" - session_id = str(uuid.uuid4()) - cmd = ["openclaw", "agent", "--agent", agent_id, - "--session-id", session_id, "--message", message, "--json"] - - proc = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - # 不 await proc.wait(),让子进程独立运行 - # 下次 tick 通过产出物检查完成状态 - _register_session(agent_id, session_id, proc.pid) - return session_id -``` - -**Subagent spawn** 通过 OpenClaw Gateway 内部 API(`sessions_spawn`),天然异步。 - -### 5.4 ActiveAgentCounter(课题11) - -纯 asyncio 实现(`asyncio.Semaphore`),与线程模型一致: - """异步计数器,控制并发""" - def __init__(self, max_global=5, max_per_agent=1): - self._global = asyncio.Semaphore(max_global) - self._per_agent = {} # agent_id → Semaphore(max_per_agent) - - async def acquire(self, agent_id: str) -> bool: - if self._global.locked(): - return False - agent_sem = self._per_agent.get(agent_id) - if agent_sem and agent_sem.locked(): - return False - await self._global.acquire() - if agent_id not in self._per_agent: - self._per_agent[agent_id] = asyncio.Semaphore(self._max_per_agent) - await self._per_agent[agent_id].acquire() - return True - - def release(self, agent_id: str): - self._per_agent[agent_id].release() - self._global.release() -``` - -### 5.5 build_bootstrap() 四层上下文拼装 - -详见 `topic4-decomposition-skill-proposal.md D4-7`。 - -``` -L0 铁律层(~500 tokens) → Hook 注入,不占 bootstrap -L1 角色层(~2000 tokens) → SOUL.md / IDENTITY.md(Agent 自带) -L2 引擎注入层(~1500 tokens)→ prompt_templates 按 role 拼装 - ① 操作规范(executor.md / reviewer.md / planner.md) - ② 项目背景(project_context.yaml) - ③ 任务上下文(黑板数据) - ④ 前序信息(depends_on 产出摘要) - ⑤ Guardrail 规则(guardrails.yaml,仅执行者) - ⑥ 审查协议(review_protocols/,仅审查者) - ⑦ 经验注入(experiences 表按 tag 匹配) -L3 被动参考层(按需加载)→ Skill description 四要素 -``` - -L2 按角色精确注入:执行者注入⑤⑥,审查者注入⑥,庞统注入⑥。 - ---- - -## 6. 审查流水线 - -### 6.1 分级审查 - -| 风险等级 | 流水线 | 方案审查 | 产出审查 | 模式 | max_rounds | -|---------|--------|---------|---------|------|-----------| -| **high** | 三阶段 | ✅ 对抗辩论 | ✅ 对抗辩论 + Guardrail | debate | 5 | -| **standard** | 二阶段 | ✅ 单审 | ✅ 单审 + Guardrail | single_reviewer | 3 | -| **low** | 一阶段 | ❌ | ⚡ Guardrail 自动 | auto | 0 | -| **research** | 一阶段 | ❌ | ✅ 庞统确认 | single_reviewer | 2 | - -### 6.2 Guardrail 执行 - -```yaml -# config/guardrails.yaml 示例 -task_types: - coding: - output_review: - required: true - layers: - L1: # Daemon 直接执行 - - assert: "len(glob('tests/test_*.py')) > 0" - message: "缺少测试文件" - - assert: "import subprocess; subprocess.run(['python', '-m', 'py_compile', output_path], check=True)" - message: "Python 语法检查失败" - L2: # Subagent 执行 - prompt: "检查代码是否遵循项目规范,重点关注:命名、异常处理、资源释放" - deploy: - output_review: - required: true - layers: - L1: - - assert: "os.path.exists('deploy_log.md')" - message: "缺少部署日志" - data: - output_review: - required: false # low 风险,Guardrail 自动 -``` - -### 6.3 反驳权流控 - -审查者 verdict=needs_revision(有 critical/major)→ spawn 原执行者反驳 → 协商轮次 ≤ max_rounds → 不设超时,有催促通知 → 超轮次升级庞统。 - -详见 `topic3-challenge-review-proposal.md §5.3`。 - -### 6.4 评审详情 Schema - -评审结果写入 `{task_id}/reviews/{review_id}.json`,包含: -- `issues[]`(status/severity/category/location+context/suggestion) -- `evidence[]`(file_content/command_output) -- `positives[]`、`meta` -- category 含:correctness/security/performance/style/scope_deviation/**architecture/robustness** - -详见 `topic3-challenge-review-proposal.md §5.2`。 - ---- - -## 7. 经验沉淀 - -### 7.1 两级蒸馏 - -| 级别 | 触发时机 | 产出 | 存储 | -|------|---------|------|------| -| 一级(实时) | 任务完成后 | 经验条目(key findings/lessons/tags) | experiences 表 | -| 二级(周期) | 同 tag 积累 N 条 | Skill 草稿(draft→active→deprecated) | skills/ 目录 | - -### 7.2 经验注入 - -build_bootstrap() 按 tag 检索 experiences 表,格式化后注入 L2 上下文。 - -详见 `topic6-experience-loop-proposal.md`。 - ---- - -## 8. Skill 体系 - -### 8.1 Skill 三层载体 - -| 载体 | 自由度 | 生命周期 | 产出者 | 存储 | -|------|--------|---------|--------|------| -| **Memory** | 高(自然语言) | 短期(30天过期) | Agent 自动沉淀 | experiences 表 | -| **Skill** | 中(模板+约束) | 中期(版本管理) | 二级蒸馏 / 人工编写 | skills/ 目录 | -| **Rule** | 低(脚本/断言) | 长期(代码级稳定) | 人工提炼 | guardrails.yaml | - -信息压缩方向:Memory → Skill → Rule(自由度递减,可靠性递增)。 - -### 8.2 Skill 目录结构 - -``` -sanguo_moziplus_v2/ -├── skills/ ← 全局 Skill 库 -│ ├── builtins/ ← 系统内置 Skill -│ │ ├── blackboard_operations/ ← 黑板操作规范 -│ │ ├── code_review/ ← 代码审查标准 -│ │ └── data_validation/ ← 数据验证 -│ └── custom/ ← 用户/蒸馏产出 Skill -│ └── {skill_id}/n │ ├── SKILL.md ← 四要素:description/triggers/actions/constraints - │ ├── template.md ← 可选:详细模板 - │ └── meta.yaml ← 版本、来源、tag - │ -├── projects/{pid}/ - │ └── skills/ ← per-project Skill 覆盖/扩展 - │ └── {skill_id}/ - │ └── project_override.md -``` - -### 8.3 Skill 四要素(SKILL.md 格式) - -```markdown -# {skill_name} - -## Description -一句话描述能力。 - -## Triggers -自动触发条件(任务类型/关键词/产出类型)。 - -## Actions -执行步骤和产出要求。 - -## Constraints -硬性约束(必须满足/禁止做)。 -``` - -### 8.4 Skill 生命周期 - -``` -draft(蒸馏产出/人工创建) - → active(验证通过,build_bootstrap 可引用) - → deprecated(30天无引用/被更好的替代) - → deleted(清理) -``` - -- **draft → active**:二级蒸馏自动标记,或人工在 Dashboard 审批 -- **active → deprecated**:`expire_days` 无引用自动降级 -- **active → deprecated → deleted**:有新版本替代时 - -### 8.5 Skill 在 Daemon 中的角色 - -```python -# daemon/bootstrap.py 中 Skill 加载逻辑 - -def load_skills(task, role, project_config) -> list[str]: - """L3 被动参考层:按需加载 Skill description""" - skills = [] - - # 1. 按 task_type 匹配 triggers - builtin_skills = scan_skills("skills/builtins/", task.task_type) - - # 2. per-project 覆盖 - project_skills = scan_skills(f"projects/{task.project_id}/skills/", task.task_type) - - # 3. 经验匹配(experiences 表按 tag 检索,注入 L2) - # → 这部分在 build_bootstrap() 的 L2 ⑦ 中处理 - - # 4. 只返回 description(四要素摘要),不加载完整 SKILL.md - return [s.description for s in builtin_skills + project_skills if s.status == 'active'] -``` - -**四层加载**(来源:课题4 D4-6): - -| 层 | 加载时机 | 内容 | 占用 | -|----|---------|------|------| -| Agent 固化 | OpenClaw agent 配置 | SOUL.md/IDENTITY.md/TOOLS.md | L1(Agent 自带) | -| OpenClaw 注册 | agent 启动时 | OpenClaw skills/ 目录 | L3(按需) | -| moziplus 注入 | build_bootstrap() | prompt_templates + guardrails + review_protocols | L2(精确注入) | -| 按需检索 | Agent 执行中主动查询 | Skill SKILL.md 全文 | L3(Agent 自己读) | - -### 8.6 template_components.yaml - -模板组件库,build_bootstrap() L2 拼装时引用: - -```yaml -components: - - id: executor_ops - file: prompt_templates/executor.md - inject_to: [executor] - - - id: reviewer_ops - file: prompt_templates/reviewer.md - inject_to: [reviewer] - - - id: planner_ops - file: prompt_templates/planner.md - inject_to: [planner] - - - id: guardrails - file: config/guardrails.yaml - inject_to: [executor] - condition: task_type in guardrails - - - id: review_protocol - file: config/review_protocols/{task_type}_review.yaml - inject_to: [reviewer, planner] - condition: review required - - - id: experience_injection - source: experiences_table - inject_to: [executor, reviewer] - condition: matching_tags > 0 - - - id: custom - file: null # per-project 自定义 - inject_to: [] -``` - ---- - -## 9. API 设计 - -### 9.1 黑板 API - -| 方法 | 路径 | 说明 | -|------|------|------| -| GET | `/api/projects/{pid}/tasks` | 任务列表(支持过滤) | -| GET | `/api/projects/{pid}/tasks/{id}` | 任务详情 | -| POST | `/api/projects/{pid}/tasks` | 创建任务 | -| POST | `/api/projects/{pid}/tasks/{id}/claim` | 认领 | -| PATCH | `/api/projects/{pid}/tasks/{id}/status` | 更新状态 | -| POST | `/api/projects/{pid}/tasks/{id}/comments` | 添加评论 | -| POST | `/api/projects/{pid}/tasks/{id}/outputs` | 写入产出 | -| POST | `/api/projects/{pid}/tasks/{id}/decisions` | 记录决策 | -| POST | `/api/projects/{pid}/tasks/{id}/observations` | 添加观察 | -| POST | `/api/projects/{pid}/tasks/{id}/reviews` | 提交评审 | -| POST | `/api/projects/{pid}/tasks/{id}/archive` | 归档/取消归档(v2.8) | -| POST | `/api/projects/{pid}/tasks/archive-done` | 一键归档(v2.8) | -| GET | `/api/projects/{pid}/tasks/{id}/checkpoints` | Checkpoint 列表(M3) | -| POST | `/api/projects/{pid}/tasks/{id}/checkpoints` | 创建 Checkpoint(M3) | -| POST | `/api/projects/{pid}/tasks/{id}/checkpoints/{cid}/approve` | Checkpoint 通过(M3) | -| POST | `/api/projects/{pid}/tasks/{id}/checkpoints/{cid}/reject` | Checkpoint 驳回(M3) | - -### 9.2 Daemon 控制 API - -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | `/api/daemon/tick` | 手动触发 tick | -| GET | `/api/daemon/status` | Daemon 状态 | -| GET | `/api/daemon/sessions` | 活跃 session | - -### 9.3 SSE 推送 - -``` -GET /api/events?project={pid} -→ SSE 流,事件类型: - task_created / task_status_changed / review_submitted / rebuttal_added - notification(4级:🔴🟡🟢🔵) -``` - -### 9.4 多项目 API - -| 方法 | 路径 | 说明 | -|------|------|------| -| GET | `/api/projects` | 项目列表 | -| POST | `/api/projects` | 创建项目 | -| GET | `/api/projects/{pid}` | 项目详情 | -| DELETE | `/api/projects/{pid}` | 归档项目(安全流程) | - ---- - -## 10. Hook 系统 - -双重 Hook 架构: - -| Hook | 所属 | 触发时机 | 用途 | -|------|------|---------|------| -| `agent_turn_prepare` | OpenClaw Plugin | Agent 接收消息前 | 注入铁律(L0) | -| `agent_turn_complete` | OpenClaw Plugin | Agent 完成后 | 触发 lint/test(工具链集成点) | -| `pre_task_claim` | moziplus HookRegistry | Agent claim 前 | 权限检查 | -| `post_output_write` | moziplus HookRegistry | 产出写入后 | 触发 Guardrail 检查 | -| `post_review_submit` | moziplus HookRegistry | 评审提交后 | 触发反驳权流程 | - ---- - -## 11. 前端架构 - -### 11.1 技术选型 - -复用 v1.0 前端框架(React + Vite + TypeScript),重设计页面结构。 - -### 11.2 页面结构(5页) - -| 页面 | 内容 | 来源 | -|------|------|------| -| **任务看板** | 任务卡片、Checkpoint 交互、状态流转 | 课题7+9 | -| **全局监控** | Agent 状态、系统健康、资源使用 | 课题9 | -| **产出档案** | 产出物预览/下载、评审详情 | 课题9 | -| **系统配置** | 项目管理、Agent 配置、Guardrail 编辑 | 课题9 | -| **AI Briefing** | 日报/周报自动生成 | 课题9 | - -### 11.3 实时推送 - -SSE 端点 `/api/events`,前端 EventSource 监听。4 级推送(🔴🟡🟢🔵)。降级:SSE 不可用时 30s 轮询。 - -### 11.4 构建部署 - -```bash -cd src/frontend && npm run build -# 产物在 src/frontend/dist/ -# FastAPI 直接托管静态文件(port 8083) -``` - -详见 `topic9-dashboard-design.md`(课题9 Dashboard 前后端完整设计方案)。 - ---- - -## 12. 测试策略 - -### 12.1 单元测试 - -| 测试文件 | 覆盖 | -|---------|------| -| test_blackboard.py | operations 全函数(claim CAS、状态机、comment_type、review CRUD) | -| test_daemon_tick.py | tick 流程(Inbox 处理、依赖推进、反驳权进度检测) | -| test_dispatcher.py | 三级调度判据 + fallback | -| test_guardrail.py | L1 assert 执行 + L2 prompt 触发 | -| test_review_flow.py | 审查流水线(分级审查、反驳权、升级) | -| test_experience.py | 一级蒸馏 + tag 检索 | -| test_bootstrap.py | 四层上下文拼装(按 role 精确注入) | -| test_counter.py | ActiveAgentCounter 并发控制 | - -### 12.2 集成测试 - -- 创建任务 → 调度执行 → Guardrail → 审查 → 反驳 → 完成 闭环 -- 多项目并发:两个项目同时 tick,互不影响 -- 竞态测试:两个 Agent 同时 claim + 同时写 inbox - -### 12.3 测试数据库 - -per-project 测试用 `:memory:` SQLite。全局注册表测试用临时文件。 - ---- - -## 13. 实现顺序 - -| Phase | 内容 | 估计时间 | -|-------|------|---------| -| **P1** | 黑板 + Daemon + CLI + API + 多项目 | ~20-25h(含调试 buffer) | -| **P2** | 审查流水线 + Guardrail + 反驳权 + build_bootstrap | ~12h | -| **P3** | 前端重设计 + SSE 推送 + AI Briefing | ~10h | -| **P4** | 经验沉淀 + Skill 进化 + 工具链集成 | ~8h | - ---- - -## 14. 容量估算 - -### 14.1 存储容量 - -| 组件 | 单项目估算 | 说明 | -|------|-----------|------| -| blackboard.db | 2-10 MB / 100 任务 | tasks + comments(5-20行/任务) + outputs + reviews + events(5-10行/任务) + experiences,含索引和 WAL overhead | -| artifacts/ | 10-50 MB / 100 任务 | 代码文件、分析报告、数据文件 | -| experiences/ | < 1 MB | 经验条目轻量,文本为主 | -| Inbox JSONL | < 100 KB | 持续 truncate,不累积 | -| _registry.yaml | < 10 KB | 项目元信息,轻量 | - -**总磁盘预估(10 项目,1000 任务)**:~1-2 GB,完全在 Mac mini 容量范围内。 - -### 14.2 内存占用 - -| 组件 | 占用估算 | 说明 | -|------|---------|------| -| Daemon + FastAPI | 50-100 MB | Python 进程基础 + asyncio event loop | -| 每个 Agent spawn | 100-300 MB | openclaw agent CLI 进程,执行期间临时占用 | -| SQLite 连接池 | < 10 MB | per-project 连接,用完释放 | -| 前端 dist 静态服务 | < 5 MB | 内存映射 | - -**峰值内存(max_global=5,5 个 Agent 同时执行)**:~1-1.5 GB,16GB Mac mini 无压力。 - -### 14.3 并发能力 - -| 维度 | 设计上限 | 瓶颈分析 | -|------|---------|----------| -| 同时活跃项目 | 无硬限(10+) | tick 30s 扫全部项目,每项目 tick 耗时 < 100ms,10 项目 < 1s | -| 并发 Agent | 5 (max_global) | ActiveAgentCounter 控制,超出排队 | -| SSE 连接 | 10-20 | 单机场景足够,FastAPI SSE 无硬限 | -| Inbox 吞吐 | 200+ 写/秒 | truncate 模式,实测无丢失 | -| API QPS | 100+ | FastAPI 单进程足够,无 LB 瓶颈 | - -### 14.4 性能约束 - -| 场景 | 延迟 | 说明 | -|------|------|------| -| 用户提需求 → 任务写入黑板 | < 5s | Agent 对话 + CLI 写入 | -| 任务 pending → Agent 开始执行 | 30s 内 | 最坏等一轮 tick;Inbox 触发可秒级 | -| Agent 完成 → 下游任务 pending | 30s 内 | tick 扫描依赖推进 | -| SSE 事件推送延迟 | < 1s | event loop 内直接推送 | -| 审查 → 反驳权启动 | 30s 内 | tick 扫描 review 状态 | - ---- - -## 15. 失败模式与可靠性 - -### 15.1 进程级故障 - -| 故障 | 检测 | 恢复 | -|------|------|------| -| Daemon 进程崩溃 | PM2 auto restart | PM2 5s 内自动拉起,tick 恢复扫描 | -| Agent spawn 子进程僵死 | health.py 僵尸检测 | 连续 20 tick 无变更 → observation 告警 + reclaim 释放 semaphore | -| Agent spawn 失败(CLI 错误) | spawner 异常捕获 | 标记任务 failed,写入 events 表,不阻塞其他任务 | -| FastAPI 进程异常 | PM2 restart | 与 Daemon 同进程,PM2 统一管理 | - -### 15.2 数据级故障 - -| 故障 | 检测 | 恢复 | -|------|------|------| -| SQLite 写入冲突 | busy_timeout=5s 等待 + WAL 模式 | WAL 允许并发读写;超时后 raise,tick 下次重试 | -| SQLite DB 损坏 | 连接时 PRAGMA integrity_check | per-project 隔离,单 DB 损坏不影响其他项目;从最新 Git 备份恢复 | -| Inbox JSONL 损坏 | JSON 解析异常 | truncate 清空重置,Agent 重发事件 | -| _registry.yaml 损坏 | YAML 解析异常 | Git 版本管理,`git checkout` 恢复 | - -### 15.3 逻辑级故障 - -| 故障 | 检测 | 恢复 | -|------|------|------| -| Agent 无限循环 | 连续 20 tick 无状态变更 | observation 告警 + 通知用户 + reclaim | -| 反驳权无限协商 | max_rounds 兜底 | 超轮次自动升级庞统裁决 | -| 任务卡在 blocked | tick 依赖推进检查 | 每轮 tick 重算依赖,不依赖缓存 | -| Guardrail 误杀 | L1 assert 可能误判 | L2 subagent 二次确认;用户可在 Dashboard 手动 override | - -### 15.4 恢复优先级 - -``` -P0: Daemon 进程 → PM2 auto restart -P1: 数据完整性 → SQLite WAL + Git 备份 -P2: 任务进度 → tick 全量扫描自动恢复 -P3: 经验数据 → experiences 表 + skills/ 目录 Git 版本管理 -``` - ---- - -## 16. 演进路径 - -### 16.1 已知限制与扩展点 - -| 当前限制 | 触发条件 | 演进方向 | -|---------|---------|--------| -| SQLite 单机 | 项目 > 20 或 DB > 100MB | 迁移 PostgreSQL(per-project schema),代码层只改 db.py | -| max_global=5 | Agent 并发不够 | 调整 ActiveAgentCounter 参数;引入优先级队列 | -| 单机部署 | 需要多节点 | Agent spawn 改为远程调度(Windows-Test-Node 已可远程执行) | -| SSE 单进程 | SSE 连接 > 50 | 引入 Redis pub/sub + 多 worker | -| 无用户认证 | 多用户协作需求 | 引入 API token + per-project 权限(project.yaml 扩展) | - -### 16.2 接口抽象层(为演进预留) - -关键接口已做抽象,替换底层不影响上层逻辑: - -| 接口 | 当前实现 | 替换时影响范围 | -|------|---------|--------------| -| `blackboard.db.py` | SQLite | 只改 db.py,operations/queries 不变 | -| `daemon.spawner.py` | asyncio.create_subprocess_exec | 只改 spawner.py,dispatcher 不变 | -| `daemon.counter.py` | asyncio.Semaphore | 只改 counter.py,dispatcher 不变 | -| `api.sse_routes.py` | FastAPI SSE | 只改 sse_routes.py,前端不变 | - ---- - -## 17. 风险 - -| 风险 | 概率 | 影响 | 缓解 | -|------|------|------|------| -| per-project SQLite 并发 tick | 中 | 中 | WAL + busy_timeout + per-project asyncio.Lock | -| Agent spawn 子进程回收 | 中 | 中 | health.py 僵尸检测 + reclaim | -| Inbox JSONL 并发写入 | 低 | 低 | truncate(不删除),实测 200 并发 0 丢失 | -| 前端 v1→v2 迁移 | 低 | 低 | v1(8082) 和 v2(8083) 独立运行,渐进替换 | -| Daemon 逻辑死循环 | 低 | 中 | 连续 20 tick 无变更 → observation 告警 + 通知用户 | -| 反驳权无限协商 | 低 | 低 | max_rounds 兜底 + 超轮次升级庞统 | -| 上下文窗口膨胀 | 中 | 中 | L0-L3 四层分级 + 产出写入黑板不累积 | -| 经验注入噪声 | 低 | 低 | 只注入 matching tag 的经验;N 条阈值可控 | -| Guardrail 误杀 | 低 | 中 | L1+L2 双层验证;用户可 Dashboard override | diff --git a/docs/design/test-plan-v2.6.md b/docs/design/test-plan-v2.6.md deleted file mode 100644 index 7e7cb26..0000000 --- a/docs/design/test-plan-v2.6.md +++ /dev/null @@ -1,305 +0,0 @@ -# v2.6 测试计划 - -**版本**: v2.6.0-test-plan -**作者**: 司马懿(质量总监)🗡️ -**日期**: 2026-05-17 -**状态**: F1-F5 已评审,F6-F18 待编码 - ---- - -## 一、测试基础设施 - -### 1.1 当前配置 - -- **框架**: pytest(已有 `tests/` 目录,97 个用例全部通过) -- **Fixture**: `tmp_path`(SQLite 临时数据库)、`TestClient`(FastAPI) -- **Mock 策略**: 目前无 mock,全部用真实 SQLite 内存/临时文件 -- **运行方式**: `python3 -m pytest tests/ -q` - -### 1.2 测试分层 - -| 层级 | 覆盖范围 | 何时执行 | -|------|---------|---------| -| **单元测试** | 每个 F 的内部逻辑 | 每写完一个 F | -| **集成测试** | F1-F14 端到端链路 | F14 完成后 | -| **E2E 验收** | 完整系统含前端 | F18 完成后 | - -### 1.3 测试规范 - -- 每个 F 必须有对应的 `test_*.py`,测试文件与模块同名 -- 测试类按功能分组:`class TestTaskCRUD`、`class TestTransitions` 等 -- P0 测试必须有,P1 测试建议有但不强阻塞 -- 测试必须可重复运行(不依赖外部状态、不依赖时序) -- 新增测试必须在 `tests/` 目录下,不放在 `src/` 内 - ---- - -## 二、F1-F5 单元测试评审(已完成) - -### 现有覆盖情况 - -| F | 测试文件 | 用例数 | P0 覆盖 | 评审 | -|---|---------|--------|---------|------| -| F1 骨架 | `test_main.py` | 10/10 | ✅ 启动、健康端点、配置、Swagger | 通过 | -| F2 黑板 | `test_blackboard.py` | 39/39 | ✅ CRUD、状态机、并发、WAL、事件 | 通过(附 2 条意见) | -| F3 多项目 | `test_registry.py` | 11/11 | ✅ 创建/删除/归档/持久化/黑板的联动 | 通过 | -| F4 CLI | `test_cli.py` | 14/14 | ✅ 8 子命令 + admin + 错误处理 | 通过 | -| F5 API | `test_api.py` | 23/23 | ✅ 全端点 CRUD + 状态码 + SSE 连接 | 通过(附 1 条意见) | - -**总计**: 97/97 通过,质量合格。 - -### 评审意见(F1-F5) - -#### BUG-1 [P1] `/api/daemon/status` 端点重复注册 -- **位置**: `main.py` 第 99-111 行直接定义了 `daemon_status` 端点,同时 `daemon_routes.py` 也定义了同名路由 -- **现象**: pytest 输出 `Duplicate Operation ID daemon_status` warning -- **修复**: 删除 `main.py` 中的 `/api/daemon/status` 端点定义,统一由 `daemon_routes.py` 提供 -- **影响**: 不影响功能,但 Swagger 文档会混乱 - -#### OBS-1 [P2] `operations.py` 的 `_conn()` 每次操作都新建连接 -- **位置**: `operations.py` 全文,每次方法调用 `self._conn()` → `sqlite3.connect()` -- **现状**: 单次操作用完即关,连接不池化 -- **评估**: v2.6 阶段可接受(SQLite WAL + busy_timeout 足够),但如果后续 F6 ticker 30s 轮询 + 多项目并发,建议 F6 阶段引入连接池或 `per-thread` 连接复用 -- **行动**: 不阻塞当前进度,F6 编码时再评估性能 - -#### OBS-2 [P2] `queries.py` 的 `blocked_tasks_with_deps()` 和 `pending_dispatchable()` 存在 SQL 注入风险 -- **位置**: `queries.py` 第 52 行和第 71 行,使用 `f-string` 拼接 SQL -- **现象**: `f"SELECT ... WHERE id IN ({','.join('?' * len(deps))})"` — 虽然 `deps` 来自数据库的 `depends_on` 字段(不是用户输入),且用了参数化占位符,但 `f-string` 拼接 SQL 是坏习惯 -- **评估**: 当前实际安全(因为值都来自参数化 `?`),但风格不好 -- **行动**: 不阻塞,但后续统一改为纯参数化写法 - ---- - -## 三、F6-F18 测试计划(逐模块) - -### F6 Daemon Ticker — `test_ticker.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | tick 循环正常运行 | ✅ | 30s 间隔(测试中加速),不阻塞 event loop | -| T2 | scan_tasks 检测 pending | ✅ | pending → 触发调度 | -| T3 | 依赖推进 | ✅ | blocked → pending(依赖 done 后) | -| T4 | events 写入 | ✅ | 每次 tick 写 `daemon_tick` 事件 | -| T5 | 多项目轮询 | ✅ | tick 遍历所有 active 项目 | -| T6 | tick 异常不中断 | P1 | 单次 tick 抛异常 → 下次 tick 继续 | -| T7 | 手动 tick 端点 | P1 | POST /api/daemon/tick 触发即时 tick | - -### F7 Inbox JSONL Watcher — `test_inbox.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 写入+消费 | ✅ | Agent 写 JSONL → 1s 内 Daemon 读取并处理 | -| T2 | truncate | ✅ | 消费后清空文件 | -| T3 | 并发写入 | ✅ | 多 Agent 同时写不同行 | -| T4 | 损坏行恢复 | P1 | 非法 JSON 行跳过不崩溃 | -| T5 | 空文件处理 | P1 | 空文件不触发处理 | - -### F8 健康检查 — `test_health.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 正常场景 | ✅ | 有变更 → 无告警 | -| T2 | 僵尸检测 | ✅ | 连续 N tick 无变更 → observation 写入 | -| T3 | 恢复场景 | ✅ | 僵尸后有变更 → 告警解除 | -| T4 | 多项目独立检测 | P1 | 项目 A 僵尸不影响项目 B | - -### F9 Agent 调度器 — `test_dispatcher.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 三级决策树 | ✅ | Daemon task → Full task → Sub task 正确分流 | -| T2 | 调度不阻塞 | ✅ | spawn 用 `asyncio.create_subprocess_exec` | -| T3 | 队列满拒绝 | ✅ | active agent 数达上限 → 不调度 | -| T4 | 任务优先级排序 | P1 | 高优先级先调度 | - -### F9 Agent Spawner — `test_spawner.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | spawn 成功 | ✅ | 子进程启动 + session 注册 | -| T2 | 超时处理 | ✅ | 超时后 kill + task_attempt 记录 | -| T3 | spawn 失败 | ✅ | 命令不存在 → task_attempt 记录 spawn_failed | -| T4 | session 清理 | P1 | 子进程结束后 session 归档 | - -### F10 ActiveAgentCounter — `test_counter.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 全局上限 | ✅ | max_global=5 时第 6 个 acquire 失败 | -| T2 | per-agent 串行 | ✅ | 同一 agent 第二个 acquire 排队 | -| T3 | release 恢复 | ✅ | release 后等待中的 acquire 成功 | -| T4 | 并发竞争 | P1 | 多 agent 同时 acquire 不超限 | - -### F11 Bootstrap 拼装 — `test_bootstrap.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 各 role 拼装 | ✅ | 不同 role 得到不同 L0-L3 四层 | -| T2 | token 估算 | ✅ | 拼装结果 < 4096 tokens | -| T3 | 缺失组件降级 | P1 | 某层文件不存在 → 跳过不崩溃 | -| T4 | 模板变量替换 | P1 | `{{task_id}}` 等占位符正确替换 | - -### F12 审查流水线 — `test_review_flow.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 4 级分流 | ✅ | high/standard/low/research 正确路由 | -| T2 | confidence 计算 | ✅ | 分值在 0-1 区间,阈值逻辑正确 | -| T3 | 升级到庞统 | ✅ | confidence < 阈值 → 升级 | -| T4 | 审查通过 → done | ✅ | verdict=approved → 状态变 done | -| T5 | 审查拒绝 → 重新执行 | P1 | verdict=rejected → 回到 working | - -### F13 Guardrail — `test_guardrail.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | L1 assert 通过 | ✅ | 合规产出 → 放行 | -| T2 | L1 assert 失败 | ✅ | 不合规产出 → 拦截 | -| T3 | L2 subagent 触发 | ✅ | 高风险 → 二次确认 | -| T4 | 配置加载 | P1 | `guardrails.yaml` 正确解析 | -| T5 | 误杀恢复 | P1 | L2 确认通过 → 放行 | - -### F14 反驳权 — `test_rebuttal.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 反驳触发 | ✅ | review rejected → 反驳协商开始 | -| T2 | 轮次计数 | ✅ | round 递增正确 | -| T3 | 超轮次升级 | ✅ | 超过 max_rounds → 庞统裁决 | -| T4 | 结果写入 | ✅ | 最终裁决写入 reviews 表 | - -### F15 经验沉淀 — `test_experience.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 一级蒸馏 | ✅ | 任务完成 → experiences 表写入 | -| T2 | tag 匹配 | ✅ | 相似 tag 的经验可被查询到 | -| T3 | 二级触发 | P1 | N 条同类经验 → Skill 草稿生成 | -| T4 | 过期清理 | P1 | deprecated 经验不被查询 | - -### F16 Skill 体系 — `test_skill_loader.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 加载 + 验证 | ✅ | 四要素完整才加载 | -| T2 | per-project 覆盖 | ✅ | 项目级 Skill 覆盖全局 Skill | -| T3 | 缺要素拒绝 | P1 | 缺少 name/description/triggers/actions → 拒绝 | -| T4 | 版本管理 | P1 | 同名 Skill 版本号正确比较 | - -### F17 SSE + Hook — `test_sse.py` / `test_hooks.py` - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | SSE 连接 | ✅ | 客户端连接 → 收到初始事件 | -| T2 | SSE 4 级推送 | ✅ | task/agent/daemon/system 四级 | -| T3 | 断线重连 | P1 | 断线后重连 → 收到后续事件 | -| T4 | 降级轮询 | P1 | SSE 不可用 → 轮询兜底 | -| T5 | Hook 触发 | ✅ | 3 个触发点正确执行 | -| T6 | Hook 过滤 | P1 | 条件不满足 → 不触发 | - -### F18 前端 Dashboard — `test_frontend.py`(E2E) - -| # | 测试用例 | P0 | 验证点 | -|---|---------|-----|-------| -| T1 | 页面渲染 | ✅ | 5 个页面均可加载 | -| T2 | API 集成 | ✅ | 页面操作 → API 调用正确 | -| T3 | SSE 实时更新 | ✅ | 后端事件 → 前端 UI 更新 | -| T4 | 项目切换 | ✅ | 切换项目 → 数据刷新 | - ---- - -## 四、集成测试计划(F14 完成后) - -### 端到端链路测试 - -``` -用户提需求 → 庞统规划 → 任务写入黑板 → Agent 执行 → -Guardrail 检查 → 审查流水线 → 反驳协商 → 完成 → 经验沉淀 -``` - -| # | 场景 | 关键验证 | -|---|------|---------| -| I1 | 正常流程(happy path) | 全链路无阻断,最终 done | -| I2 | 依赖链 | A→B→C,A done 后 B 自动 pending,B done 后 C 自动 pending | -| I3 | 审查拒绝 → 反驳 → 通过 | review rejected → rebuttal → verdict 翻转 | -| I4 | 审查拒绝 → 反驳 → 超轮次 → 庞统裁决 | max_rounds 后升级 | -| I5 | Guardrail 拦截 → L2 确认 → 放行 | L1 fail → L2 confirm → proceed | -| I6 | Agent 僵尸检测 | 无 heartbeat → observation → reclaim | -| I7 | 多项目并行 | 项目 A/B 同时运行,互不干扰 | -| I8 | 失败重试 | failed → pending(retry_count +1)→ 再次执行 | - -### 集成测试基础设施 - -```python -# conftest.py 需要的 fixture -@pytest.fixture -def full_stack(tmp_path): - """完整系统:FastAPI + Ticker + Dispatcher + Spawner(mock)""" - ... - -@pytest.fixture -def mock_spawner(): - """Mock spawner,模拟 Agent 执行""" - ... -``` - ---- - -## 五、E2E 验收场景(F18 完成后) - -| # | 场景 | 验收标准 | -|---|------|---------| -| E1 | 创建项目 → 提交任务 → 查看任务板 | Dashboard 实时更新 | -| E2 | 全局监控页 | 所有项目状态汇总、活跃 Agent 数、最近事件 | -| E3 | 产物仓库页 | 任务产出物可浏览、可下载 | -| E4 | 系统配置页 | 运行时配置可查看、Skill 列表可管理 | -| E5 | AI 简报页 | 项目级 AI 总结可生成 | -| E6 | SSE 实时 | 所有页面操作后 SSE 实时反映 | - ---- - -## 六、测试通过标准 - -### 每个 F 的单元测试通过标准 - -1. **全部 P0 用例通过**(硬性要求) -2. **P1 用例通过率 ≥ 80%**(可协商) -3. **无 crash / 无未捕获异常** -4. **测试可重复运行**(跑 3 次结果一致) - -### 集成测试通过标准 - -1. **I1-I8 全部场景通过** -2. **无死锁、无资源泄漏** -3. **并发测试无数据竞争** - -### E2E 验收标准 - -1. **E1-E6 全部场景通过** -2. **前端无 console error** -3. **SSE 延迟 < 2s** - ---- - -## 七、进度追踪 - -| F | 测试计划 | 用例设计 | 编码 | 执行 | 评审 | -|---|---------|---------|------|------|------| -| F1 | ✅ | ✅ 10 | ✅ | ✅ 10/10 | ✅ 通过 | -| F2 | ✅ | ✅ 39 | ✅ | ✅ 39/39 | ✅ 通过(附 2 条意见) | -| F3 | ✅ | ✅ 11 | ✅ | ✅ 11/11 | ✅ 通过 | -| F4 | ✅ | ✅ 14 | ✅ | ✅ 14/14 | ✅ 通过 | -| F5 | ✅ | ✅ 23 | ✅ | ✅ 23/23 | ✅ 通过(附 1 条意见) | -| F6 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F7 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F8 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F9 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F10 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F11 | ✅ 本文档 | ⬜ 本文档 | ⬜ | — | — | -| F12 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F13 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F14 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F15 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F16 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F17 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | -| F18 | ✅ 本文档 | ⬜ 待设计 | ⬜ | — | — | diff --git a/docs/design/toolchain-proposal.md b/docs/design/toolchain-proposal.md deleted file mode 100644 index cb188a7..0000000 --- a/docs/design/toolchain-proposal.md +++ /dev/null @@ -1,77 +0,0 @@ -# 工具链课题:DevOps 工具链集成 - -> **状态**: 信息收集阶段,未正式立项设计 -> **作者**: 庞统(副军师)🐦 -> **日期**: 2026-05-16 -> **定位**: 所有工具链能力最终要集成到 moziplus v2.0 中,不是独立系统 - ---- - -## 1. 背景 - -moziplus v2.0 编排的不只是 AI Agent 的任务流转,还包括完整的开发生命周期。Agent 执行编码任务后,lint/test/build/deploy 应该自动串联;Agent 发现 bug 后应该能自动创建 Issue 追踪。工具链是 v2.0 从"任务流转工具"升级为"AI Native DevOps 平台"的关键拼图。 - -## 2. 需求来源 - -| 来源 | 内容 | 状态 | -|------|------|------| -| PRD C10 | lint/test/build 自动触发,按需调用,结果驱动流转 | ❌ 未设计 | -| 评审 D10 | 工具链自动化 Phase 规划 | ❌ 未覆盖 | -| 评审 D13 | 需求→设计→编码→测试→部署→运维 全生命周期阶段映射 | ❌ 未覆盖 | -| 评审 F8 | lint/test/build/deploy 自动化 | ❌ 未覆盖 | -| MEMORY.md | gitee Issue 管理机制:选 repo、定流程、谁维护、标签体系 | ❌ 未做 | -| T4-9 | Skill 测试框架(结构测试+干跑测试+场景测试) | ➡️ 移入本课题 | - -## 3. 工具链分类(初步) - -| 分类 | 具体内容 | 当前状态 | 备注 | -|------|---------|---------|------| -| **代码管理** | Git 仓库(gitee + gitea)、分支策略、PR 流程 | ✅ 有基础 | gitee Issue 管理机制待建 | -| **问题管理** | Issue/Bug/需求追踪、标签体系 | ❌ 无 | 与黑板 tasks 表的关系待定 | -| **CI/CD** | lint/test/build 自动触发、结果驱动流转 | ❌ 无 | PRD C10 | -| **测试工具** | 单元测试、集成测试、Skill 测试框架 | ❌ 无 | T4-9 移入 | -| **开发环境** | Mac mini 本地开发环境 | ✅ 有 | 多 Agent 并行改代码隔离待设计 | -| **测试/Staging 环境** | 独立验证环境 | ❌ 无 | 待定 | -| **沙箱环境** | Agent 执行命令时的安全隔离 | ⚠️ 部分 | AgentsMesh PTY sandbox 参考 | -| **运维工具** | PM2 进程管理、Daemon 健康自检、日志 | ⚠️ 部分 | Daemon 健康自检已在 architecture-v2.6 | - -## 4. 调研参考 - -| 项目 | 关键实践 | 可借鉴点 | -|------|---------|---------| -| AgentsMesh | PTY sandbox + git worktree 隔离 + ticket-pod binding | Agent 执行隔离、代码并行修改 | -| Aider | verification_commands(lint/test 自动验证) | Guardrail 验证脚本层已在 architecture-v2.6 | -| nuwa-skill | 5 阶段蒸馏 + 质量验证 | Skill 测试框架参考 | -| Claude Code | 98.4% Infrastructure,上下文管理为主 | 工具链服务于 Agent,不是 Agent 服务于工具链 | - -## 5. 待讨论的关键问题 - -1. **CI/CD 走什么**:本地跑(Mac mini)?接 GitHub Actions?接 gitee CI?还是 moziplus Daemon 自己调度? -2. **测试环境怎么搭**:和开发环境同一台机器?NAS Docker?Windows-Test-Node(192.168.2.33)? -3. **沙箱到什么程度**:Agent 执行命令时的安全隔离?文件系统级隔离(worktree)?还是进程级隔离? -4. **Issue 管理用 gitee 还是自己做**:黑板 tasks 表已能追踪任务,还需要外部 Issue 系统吗?两者怎么打通? -5. **与 moziplus v2.0 集成方式**:工具链是 v2.0 内置能力还是外部集成?Daemon 怎么触发 lint/test/build?Guardrail 和工具链的关系? -6. **全生命周期阶段映射**:需求→设计→编码→测试→部署→运维,每个阶段对应哪些工具链能力?Daemon 怎么感知当前阶段? -7. **结果驱动流转**:lint/test/build 结果怎么写回黑板?怎么影响任务状态?(如 test 失败 → 自动标记 needs_revision) - -## 6. 与 v2.0 架构的集成点 - -``` -moziplus v2.0 Daemon - │ - ├── Guardrail(课题3)──→ 工具链触发点 - │ ├── L1 机械检查 ──→ 文件存在/格式校验(Daemon 直接执行) - │ ├── L2 AI 检查 ──→ lint/test 自动触发 + 结果评估 - │ └── L3 Agent 审查 ──→ 代码审查 + Issue 创建 - │ - ├── Hook(双重 Hook)──→ 工具链触发时机 - │ ├── agent_turn_prepare ──→ 注入工具链上下文 - │ └── agent_turn_complete ──→ 触发 lint/test/build - │ - ├── reviews 表 ──→ 工具链结果存储 - │ └── review_type: "guardrail" ──→ lint/test/build 结果 - │ - └── Dashboard(课题9)──→ 工具链可视化 - ├── 任务看板 ──→ CI/CD 状态展示 - └── 系统配置 ──→ 工具链配置管理 -``` diff --git a/docs/design/topic11-multi-project-proposal.md b/docs/design/topic11-multi-project-proposal.md deleted file mode 100644 index d291568..0000000 --- a/docs/design/topic11-multi-project-proposal.md +++ /dev/null @@ -1,685 +0,0 @@ -# 课题11 设计方案:用户级多项目支持 - -> **日期**: 2026-05-16 -> **作者**: 庞统(副军师)🐦 -> **状态**: v2(并发调度模型重设计,待评审) -> **前置**: 课题1-4、课题6 已完成设计 -> **变更**: v2 新增 §5.4 并发调度模型(per-project 线程 + 全局资源信号量),替代原串行 tick - ---- - -## 1. 核心问题 - -用户同时有多个工作域(量化策略A + 平台开发 + 数据研究),需要项目级隔离——不同项目的任务、配置、产出互不干扰,但共享同一套 Agent 团队和 Daemon 基础设施。 - -## 2. 需要隔离什么 - -| 隔离项 | 原因 | 隔离方式 | -|--------|------|---------| -| 黑板数据(tasks/comments/outputs/decisions/observations/events/agents/task_attempts/reviews/experiences/experience_tags) | 不同项目的任务不能混在一起 | 独立 SQLite 文件 | -| 配置(guardrails.yaml / prompt_templates / project_context.yaml) | 不同项目可能有不同的审查规则、上下文 | 项目级 config/ 目录,覆盖全局默认 | -| 产出文件 | 不同项目的代码/数据物理隔离 | 项目级 outputs/ 目录 | -| Agent session | 同一 Agent 参与不同项目时上下文不串 | OpenClaw `--session-id` 已有隔离 | -| Daemon 连接 | 不同数据库连接不能混淆 | 连接池 project_id → Connection 映射 | - -**不需要隔离的**: -- Agent 注册表(agents.yaml)—— 同一套 Agent 团队服务所有项目 -- Daemon 进程 —— 单进程管理所有项目 -- Schema 定义 —— 所有项目共享同一套表结构 -- 全局 prompt_templates —— 项目级覆盖,不是替换 - -## 3. 方案选择 - -### 3.1 三个方案对比 - -| 方案 | 做法 | 优点 | 缺点 | -|------|------|------|------| -| **A. 多实例** | 每个项目独立 Daemon + 独立 SQLite + 独立端口 | 完全隔离、互不影响 | 资源开销翻倍、管理复杂、Agent 重复注册 | -| **B. 单实例多命名空间** | 一个 SQLite,所有表加 `project_id` | 零额外资源 | 每个查询带 WHERE、单文件性能上限、删除项目危险 | -| **C. 单 Daemon 多数据库** | 一个 Daemon,每个项目一个 SQLite 文件 | 物理隔离数据、共享 Daemon | Daemon 需管理多连接 | - -### 3.2 选择方案 C - -理由: -1. **方案 A 不适合**——Mac mini 资源有限,每多一个项目就多一套 Daemon + PM2 进程 + 端口。6 个 Agent 跑在 OpenClaw 上已经固定开销,不需要重复。 -2. **方案 B 不够安全**——`WHERE project_id = ?` 容易漏,SQLite 单文件多项目并发有 WAL 锁瓶颈,删除项目 = 跨所有表 DELETE。 -3. **方案 C 是最优点**——数据物理隔离(每个 `.db` 文件独立),但共享 Daemon 进程和 Agent 注册表。Daemon 切换项目只是切换 SQLite 连接,无额外资源开销。 - -**优秀实践验证**: -- Wanman:Per-Agent Worktree + $HOME 严格隔离 → 验证"物理隔离比逻辑隔离可靠" -- ClawTeam:Git Worktree 隔离 + fcntl 文件锁 → 验证"共享进程 + 独立存储"模式可行 -- Cline:Kanban + Worktree → 验证"多任务并行 + 物理隔离"是主流 -- Hermes:单 Dispatcher + 单 SQLite → **Hermes 是单项目设计**,我们没有"多项目用单数据库"的先例 - -## 4. 目录结构 - -``` -~/.sanguo_projects/moziplus_v2/ -├── daemon.py # 单 Daemon 进程 -├── daemon.yaml # Daemon 全局配置(端口、tick 间隔等) -├── projects/ -│ ├── _registry.yaml # 项目注册表(所有项目的元数据) -│ ├── quant-momentum/ # 项目 1 -│ │ ├── blackboard.db # 独立 SQLite -│ │ ├── config/ -│ │ │ ├── project.yaml # 项目元信息(名称、描述、创建时间) -│ │ │ ├── guardrails.yaml # 项目级审查规则(覆盖全局默认) -│ │ │ ├── project_context.yaml # 项目背景知识(注入 L2) -│ │ │ └── prompt_overrides/ # 可选:覆盖默认 prompt 模板 -│ │ └── outputs/ # 项目产出目录 -│ ├── quant-pairs/ # 项目 2 -│ │ ├── blackboard.db -│ │ ├── config/ -│ │ └── outputs/ -│ └── moziplus-dev/ # 项目 3(自身开发) -│ ├── blackboard.db -│ ├── config/ -│ └── outputs/ -└── shared/ - ├── prompt_templates/ # 全局默认模板 - ├── schemas/ # 全局 Schema - └── agents.yaml # 全局 Agent 注册表 -``` - -### 4.1 项目注册表(_registry.yaml) - -```yaml -# projects/_registry.yaml -default_project: quant-momentum -projects: - quant-momentum: - display_name: "动量因子策略" - description: "基于动量因子的量化策略研发" - created_at: "2026-05-16T10:00:00Z" - status: active # active / archived - agents: [zhangfei-dev, zhaoyun-data, guanyu-dev] # 该项目可用的 Agent - quant-pairs: - display_name: "配对交易策略" - description: "统计套利配对交易研究" - created_at: "2026-05-16T10:00:00Z" - status: active - agents: [zhangfei-dev, zhaoyun-data] - moziplus-dev: - display_name: "平台开发" - description: "moziplus 自身开发" - created_at: "2026-05-16T10:00:00Z" - status: active - agents: [zhangfei-dev, simayi-challenger] -``` - -## 5. Daemon 变更 - -### 5.1 多连接池 - -```python -# daemon 内部 -class ProjectManager: - def __init__(self, projects_dir: Path): - self.projects_dir = projects_dir - self._connections: dict[str, sqlite3.Connection] = {} - self._configs: dict[str, ProjectConfig] = {} - self._load_registry() - - def get_connection(self, project_id: str) -> sqlite3.Connection: - if project_id not in self._connections: - db_path = self.projects_dir / project_id / "blackboard.db" - self._connections[project_id] = sqlite_connect(db_path) - return self._connections[project_id] - - def get_config(self, project_id: str) -> ProjectConfig: - if project_id not in self._configs: - config_path = self.projects_dir / project_id / "config" / "project.yaml" - self._configs[project_id] = ProjectConfig.load(config_path) - return self._configs[project_id] - - def load_guardrails(self, project_id: str) -> dict: - """项目级 guardrails.yaml 覆盖全局默认""" - global_guardrails = load_yaml("shared/guardrails.yaml") - project_guardrails_path = self.projects_dir / project_id / "config" / "guardrails.yaml" - if project_guardrails_path.exists(): - project_guardrails = load_yaml(project_guardrails_path) - return deep_merge(global_guardrails, project_guardrails) # 项目级覆盖 - return global_guardrails -``` - -### 5.2 ~~Tick 逻辑变更~~(已废弃,见 §5.4) - -> **原设计**:Daemon 主循环串行遍历所有项目 tick。每个项目 tick 完再 tick 下一个。 -> **问题**:所有项目/任务一起排队,项目 A 的长任务阻塞项目 B。 -> **新设计**:见 §5.4 per-project 并发调度。 - -### 5.3 Daemon 逻辑健康自检 + 线程存活监控(v2 扩展) - -```python -# §14 风险缓解:连续 N tick 无状态变更则告警 -STALE_TICK_THRESHOLD = 20 - -class DaemonHealth: - def __init__(self, project_id: str): - self.project_id = project_id - self._idle_ticks = 0 - - def record_idle(self): - self._idle_ticks += 1 - - def record_change(self): - self._idle_ticks = 0 - - def is_stale(self) -> bool: - return self._idle_ticks >= STALE_TICK_THRESHOLD -``` - -**线程存活监控**(见 §5.4.4 `Daemon._check_slot_health()`): -- Daemon 主线程每 60s 检查所有 ProjectSlot 线程是否存活 -- 线程死亡 → 记录日志 + 自动重启 -- 连续重启 3 次失败 → 告警(通过 Sanguo Mail 通知用户) - -**计数器超时兜底**: -- 如果 Agent 完成回调丢失(进程被杀、网络断),`ActiveAgentCounter` 不会归零 -- `_check_working_tasks()` 中,working 任务超过 `task_timeout`(默认 10 分钟)视为完成 -- 视为完成时主动 `decrement()`,防止计数器泄漏 - -### 5.5 Daemon 崩溃恢复(v2 新增) - -**设计原则**:状态全在 SQLite,Daemon 无状态。重启 = 重新加载所有项目 + 所有任务状态,继续执行。 - -**恢复流程(保守策略)**: - -``` -PM2 检测 Daemon 挂了 → 重启 Daemon - │ - ├── 读取 _registry.yaml → 恢复项目列表 - ├── 遍历每个 active 项目 → 打开 SQLite 连接 - ├── 扫描所有 working 任务 → 标记为 failed(原因: "Daemon restart, agent process lost") - ├── 启动 ProjectSlot 线程 - └── 后续 pending 任务正常分配 -``` - -**为什么不重新执行**: -1. Daemon 崩溃是不正常事件,Agent 子进程状态不可预测 -2. output.json 可能写了一半,重新执行比恢复更安全 -3. 用户手动 retry 比自动重新执行更可控 -4. task_attempts 表记录完整,不丢信息 - -**关键设计**: -1. **SQLite 是真相来源**——所有任务状态、产出记录都在 `.db` 文件里,Daemon 内存无状态 -2. **SQLite WAL 保护数据完整性**——崩溃时未提交的事务自动回滚 -3. **ActiveAgentCounter / DaemonHealth 重启后归零**——不需要持久化 -4. **task_attempts 的 attempt_index 递增**——retry 不覆盖历史 - -**不需要额外存储**: -- 不需要 checkpoint 文件——SQLite 就是 checkpoint -- 不需要 recovery log——task_attempts 表已经记录所有尝试 -- 不需要 recovery log——`task_attempts` 表已记录每次尝试 -- 不需要状态快照——每次 tick 从 SQLite 实时读取 - -**唯一注意**:`ActiveAgentCounter` 重启后从零开始——但 `_tick()` 会重新扫描 working 任务并重新计数,所以没问题。 - -### 5.4 并发调度模型(v2 新增) - -#### 5.4.1 问题 - -原设计中 Daemon 主循环串行 tick 所有项目: - -``` -Tick → Project A(30s)→ Project B(等A完成)→ Project C(等B完成) -``` - -问题: -1. **项目间互相阻塞**——Project A 有一个长任务在执行,Project B 的独立任务必须等 -2. **响应延迟**——3 个项目 tick 一次可能要 90s+,Project C 要等 60s 才被检查 -3. **不符合业界实践**——调研 7 个项目(Hermes/open-multi-agent/Wanman/Google ADK/Microsoft AutoGen/AgentScope/GSD),没有一个用全局串行排队 - -#### 5.4.2 业界并发模型调研 - -| 项目 | 并发模型 | 核心机制 | -|------|---------|---------| -| **open-multi-agent** | AgentPool + Semaphore | 全局 `maxConcurrency=5`,per-agent 互斥锁,`Promise.allSettled` 并行执行独立任务 | -| **Wanman** | per-agent 进程 | 每个 Agent 独立进程+独立 runLoop,Supervisor 通过消息总线协调 | -| **Google ADK** | asyncio.TaskGroup | `ParallelAgent` 用 `TaskGroup` 并行执行子 Agent | -| **Microsoft AutoGen** | Pregel Superstep | 每个 superstep 内所有激活 Executor 并行执行 | -| **Hermes** | 单线程 tick + flock | **单项目设计**,tick 内只有几个 cron job,不需要并发 | - -**关键发现**:open-multi-agent 的 `AgentPool + Semaphore + per-agent Lock` 是最成熟、最可借鉴的模型。 - -#### 5.4.3 设计:per-project 线程 + 全局资源信号量 - -``` -Daemon 主进程(轻量路由器 + 资源管控) -│ -├── 全局 LLM Semaphore(max_concurrent=3) -├── per-agent Lock(张飞不能同时在两个项目里跑) -│ -├── ProjectSlot A(独立线程) -│ └── 自己的 SQLite 连接 -│ └── 自己的 tick 循环(30s) -│ └── spawn Agent 时:acquire agent_lock → acquire llm_semaphore -│ -├── ProjectSlot B(独立线程) -│ └── (同上) -│ -└── ProjectSlot C(独立线程) - └── (同上) -``` - -**三层资源控制**: - -| 层级 | 控制对象 | 机制 | 原因 | -|------|---------|------|------| -| L1: 项目隔离 | SQLite 连接 | per-project 独立连接 | 数据物理隔离,无竞争 | -| L2: Agent 互斥 | 同一 Agent 不能并行 | `threading.Lock` per-agent | Agent session 不是线程安全的,张飞同一时刻只能服务一个任务 | -| L3: 全局资源 | LLM API 调用并发 | `threading.Semaphore(max_concurrent)` | 防止 API 限流、控制成本 | - -#### 5.4.4 核心代码 - -```python -import threading -import time -from pathlib import Path - - -class ActiveAgentCounter: - """线程安全的 Agent 活跃任务计数器。""" - - def __init__(self, max_global: int = 5): - self._counts: dict[str, int] = {} # agent_id → 活跃任务数 - self._total = 0 # 全局活跃总数 - self._max_global = max_global - self._lock = threading.Lock() - - def can_acquire(self, agent_id: str, max_per_agent: int = 1) -> bool: - """检查是否可以分配(非阻塞)。""" - with self._lock: - if self._total >= self._max_global: - return False - return self._counts.get(agent_id, 0) < max_per_agent - - def increment(self, agent_id: str): - with self._lock: - self._counts[agent_id] = self._counts.get(agent_id, 0) + 1 - self._total += 1 - - def decrement(self, agent_id: str): - with self._lock: - if self._counts.get(agent_id, 0) > 0: - self._counts[agent_id] -= 1 - self._total -= 1 - - -class Daemon: - """单进程 Daemon,per-project 线程并发。""" - - def __init__(self, config: DaemonConfig): - self.config = config - self.agent_counter = ActiveAgentCounter(max_global=config.max_global_active) # 默认 5 - self.slots: dict[str, ProjectSlot] = {} - self._slot_threads: dict[str, threading.Thread] = {} # 线程存活监控 - self._shutdown = threading.Event() - - def start(self): - """启动所有 active 项目的独立线程。""" - registry = load_registry() - for project_id, meta in registry["projects"].items(): - if meta["status"] != "active": - continue - self._start_slot(project_id, meta) - - # 主线程:监控 + 等待 shutdown - while not self._shutdown.is_set(): - self._check_slot_health() - self._shutdown.wait(60) # 每 60s 检查一次线程存活 - - def _start_slot(self, project_id: str, meta: dict): - slot = ProjectSlot( - project_id=project_id, - config=meta, - agent_counter=self.agent_counter, - tick_interval=self.config.tick_interval, - shutdown_event=self._shutdown, - ) - self.slots[project_id] = slot - t = threading.Thread(target=slot.run_loop, name=f"project-{project_id}", daemon=True) - t.start() - self._slot_threads[project_id] = t - - def _check_slot_health(self): - """检查所有 ProjectSlot 线程是否存活,死亡则重启。""" - for project_id, thread in list(self._slot_threads.items()): - if not thread.is_alive(): - logger.warning(f"ProjectSlot {project_id} thread died, restarting...") - meta = self.slots[project_id].config - self._start_slot(project_id, meta) - - def shutdown(self): - self._shutdown.set() - - -class ProjectSlot: - """单项目的独立 tick 循环。""" - - def __init__(self, project_id, config, agent_counter, - tick_interval=30, shutdown_event=None): - self.project_id = project_id - self.conn = sqlite_connect(Path(f"projects/{project_id}/blackboard.db")) - self.config = config - self.agent_counter = agent_counter # 共享:全局 Agent 活跃计数器 - self.tick_interval = tick_interval - self.shutdown = shutdown_event or threading.Event() - self.health = DaemonHealth(project_id) - - def run_loop(self): - """独立线程的主循环。""" - while not self.shutdown.is_set(): - try: - self._tick() - except Exception as e: - logger.error(f"[{self.project_id}] tick failed: {e}") - self.shutdown.wait(self.tick_interval) - - def _tick(self): - """单次 tick:找 pending 任务,尝试分配。""" - # 先检查已完成的 Agent,释放计数器 - completed = self._check_working_tasks() # 返回已完成的 agent_id 列表 - for agent_id in completed: - self.agent_counter.decrement(agent_id) - - pending = find_pending(self.conn) - if not pending: - self.health.record_idle() - return - - for task in pending: - agent_id = task["assignee"] - max_per_agent = self.config.get("max_active_per_agent", 1) - - # 检查全局 + per-agent 并发上限 - if not self.agent_counter.can_acquire(agent_id, max_per_agent): - logger.info(f"[{self.project_id}] {agent_id} at capacity, skip task {task['id']}") - continue - - # sequential 模式:检查该 Agent 在本项目是否有 working 任务 - if self.config.get("agent_parallelism") != "parallel": - if has_working_task(self.conn, agent_id): - continue - - # 生成 session_id(§5.4.6 命名规则) - session_id = self._get_session_id(agent_id, task['id']) - - # spawn Agent(异步,不阻塞) - spawn_agent( - project_id=self.project_id, - task=task, - session_id=session_id, - ) - self.agent_counter.increment(agent_id) # +1 - self.health.record_change() - - def _get_session_id(self, agent_id: str, task_id: str) -> str: - """§5.4.6 session 命名规则。""" - if self.config.get("agent_parallelism") != "parallel": - # sequential:同一项目同一 Agent 复用 session(保持上下文连续性) - return f"agent:{agent_id}:project:{self.project_id}" - else: - # parallel:每个任务独立 session - return f"agent:{agent_id}:project:{self.project_id}:task:{task_id}" -``` - -#### 5.4.5 资源控制模型(v2 修订:异步计数器替代同步信号量) - -**原设计问题**:spawn_agent() 不是瞬时操作——Agent 执行涉及 LLM API 调用(可能多轮工具调用),如果在 spawn 完成后才释放锁/信号量,并发退化回串行;如果在 spawn 启动后立即释放,信号量没有真正限流。 - -**修订方案**(采纳司马懿评审建议): - -| 资源 | 原方案 | 修订方案 | 原因 | -|------|--------|---------|------| -| Agent 互斥 | `threading.Lock` | 移除。改为 per-project session 命名 + `_check_working_tasks()` | 同一 Agent 可用不同 session-id 安全服务不同项目 | -| LLM 并发 | `threading.Semaphore` | `ActiveAgentCounter`(异步计数器) | spawn 是异步的,同步信号量无法精确限流 | -| 项目隔离 | per-project SQLite | 不变 | | - -**新时序**: - -``` -ProjectSlot._tick() - │ - ├── 检查 ActiveAgentCounter[agent_id] < max_active? - │ └── 否 → 跳过,下个 tick 再检查 - ├── 检查 agent_parallelism == "sequential" 且该 Agent 有 working 任务? - │ └── 是 → 跳过 - ├── spawn_agent(project_id, task) - │ ├── session_id = "agent:{agent_id}:project:{project_id}:task:{task_id}" - │ └── ActiveAgentCounter.increment(agent_id) # +1 - │ - └── Agent 完成回调(下次 tick 检测到 output 或 webhook) - └── ActiveAgentCounter.decrement(agent_id) # -1 -``` - -**关键变化**: -1. 不再使用 `threading.Lock` 和 `threading.Semaphore`——它们是同步原语,不适合异步 spawn 场景 -2. 改用 `ActiveAgentCounter`(线程安全计数器),spawn 时 +1,Agent 完成回调时 -1 -3. `_tick()` 分配前检查计数器,超过阈值就跳过 -4. Agent session 按 `agent:{agent_id}:project:{project_id}:task:{task_id}` 命名,项目+任务级天然隔离 - -#### 5.4.6 Agent 并行策略 + Session 隔离 - -**并行策略配置**: - -```yaml -# _registry.yaml 中可配置 -projects: - quant-momentum: - agent_parallelism: sequential # 同一 Agent 同一时刻只跑一个任务(默认) - max_active_per_agent: 1 # sequential 的显式写法 - quant-pairs: - agent_parallelism: parallel # 同一 Agent 可同时跑多个任务 - max_active_per_agent: 2 # 最多 2 个并行 -``` - -**Session 命名规则**: - -``` -格式:agent:{agent_id}:project:{project_id}:task:{task_id} -示例:agent:zhangfei-dev:project:quant-momentum:task:task-001 -``` - -- 每个任务独立 session,任务间上下文不串 -- 同一 Agent 在不同项目用不同 session,项目间上下文不串 -- `sequential` 模式:同一项目同一 Agent 只有一个活跃 session(新的任务复用或新开) -- `parallel` 模式:每个任务独立 session - -**sequential 模式下的 session 复用**: - -```python -def _get_session_id(self, agent_id: str, task_id: str) -> str: - project_config = self.config - if project_config.get("agent_parallelism") != "parallel": - # sequential:同一项目同一 Agent 复用 session(保持上下文连续性) - return f"agent:{agent_id}:project:{self.project_id}" - else: - # parallel:每个任务独立 session - return f"agent:{agent_id}:project:{self.project_id}:task:{task_id}" -``` - -#### 5.4.7 并发安全保证 - -| 并发场景 | 风险 | 保护机制 | -|---------|------|---------| -| 两个项目同时写同一个 SQLite | 数据损坏 | 每个项目独立 `.db` 文件,不存在此场景 | -| 两个项目同时分配同一个 Agent | Agent 资源争抢 | `ActiveAgentCounter` + `max_active_per_agent` 限制 | -| LLM API 并发超限 | API 限流/超限 | `ActiveAgentCounter` 全局计数,`_tick()` 分配前检查 | -| ProjectSlot 线程异常退出 | 项目 tick 停止 | try/except 包裹 + Daemon 监控线程存活(§5.4.8) | -| Daemon 主进程崩溃 | 所有项目停止 | PM2 自动重启 + SQLite WAL 保护数据完整性 | -| Agent 完成回调丢失 | 计数器不归零 | 超时兜底:working 任务超过 `task_timeout` 视为完成,计数器 -1 | -| _registry.yaml 并发写入 | 数据损坏 | _registry.yaml 只在 CLI 操作时读写(非 tick 热路径),tick 状态用内存 dict | - -### 6.1 项目管理命令 - -```bash -# 创建项目 -python3 blackboard.py project create --name quant-momentum --display-name "动量因子策略" --agents zhangfei-dev,zhaoyun-data,guanyu-dev - -# 列出项目 -python3 blackboard.py project list - -# 切换默认项目 -python3 blackboard.py project default quant-momentum - -# 归档项目(不删除数据,只停 tick) -python3 blackboard.py project archive quant-pairs - -# 删除项目(删除数据,需确认) -python3 blackboard.py project delete quant-pairs --confirm -``` - -### 6.2 所有操作指定项目 - -```bash -# 方式1:命令行参数 -python3 blackboard.py read --project quant-momentum --task task-001 - -# 方式2:环境变量(设置后所有命令默认用此项目) -export MOZIPLUS_PROJECT=quant-momentum -python3 blackboard.py read --task task-001 - -# 方式3:默认项目(_registry.yaml 中 default_project) -# 不指定 --project 也不设环境变量时,使用 default_project -python3 blackboard.py read --task task-001 -``` - -### 6.3 Agent 使用的项目解析优先级 - -``` -1. --project 参数(显式指定) -2. MOZIPLUS_PROJECT 环境变量 -3. _registry.yaml 中的 default_project -4. 如果只有一个 active 项目,自动使用 -5. 都没有 → 报错"请指定项目" -``` - -## 7. L2 上下文注入变更 - -### 7.1 Agent spawn 时注入项目上下文 - -L2 prompt_template 三段式注入增加项目段: - -``` -═══ 项目上下文 ═══ -项目: quant-momentum(动量因子策略) -背景: -可用 Agent: 张飞(编码)、赵云(数据)、关羽(风控) -═══ 任务上下文 ═══ -(原有内容不变) -``` - -### 7.2 project_context.yaml 示例 - -```yaml -# projects/quant-momentum/config/project_context.yaml -description: "基于动量因子的量化策略研发" -domain: "量化交易" -data_sources: - - "NAS /Volumes/stock/ A股日线数据" - - "NAS /Volumes/stock/minute_kline/ 分钟线数据" -code_repo: "~/.openclaw/sanguo_projects/sanguo_quant_live/" -key_constraints: - - "所有策略必须通过回测验证才能上实盘" - - "止损逻辑必须经过关羽风控审查" -``` - -## 8. 跨项目协作 - -### 8.1 默认禁止跨项目 - -Agent 不能跨项目读写黑板。这是安全边界——不同项目的数据、配置、产出互不干扰。 - -### 8.2 跨项目数据共享 - -如果项目 A 需要项目 B 的产出(如"moziplus-dev 需要赵云的数据"),通过文件系统共享: - -```bash -# 项目 A 中,Agent 把产出写到 NAS 共享路径 -# 项目 B 中,Agent 从 NAS 共享路径读取 -``` - -不需要特殊的跨项目协议——**NAS 路径就是跨项目的桥梁**,和当前团队的工作方式一致。 - -### 8.3 跨项目任务请求(可选扩展) - -如果未来需要 Agent 主动发起跨项目请求: - -``` -项目 A 黑板 → 创建 cross_project_request 类型任务 -→ Daemon 检测到 → 在项目 B 黑板创建对应任务 -→ 项目 B Agent 完成 → 产出写入 NAS -→ Daemon 检测项目 B 完成 → 更新项目 A 任务状态 -``` - -当前不实现,预留设计空间。 - -### 8.4 项目归档/删除时正在运行的任务(v2 新增) - -**归档(archive)**: -1. 检查是否有 working 状态的任务 -2. 有 → 将这些任务标记为 cancelled,等待 Agent 完成回调(超时兜底 5 分钟) -3. 无 → 立即停止该项目的 ProjectSlot 线程 -4. 将 `_registry.yaml` 中状态改为 `archived` - -**删除(delete)**: -1. 必须先 archive -2. 必须无 working 任务(归档时已处理) -3. `--confirm` 确认 -4. `rm -rf projects/{project_id}/` -5. 从 `_registry.yaml` 移除条目 - -**禁止直接删除 active 项目**——必须先归档。 - -## 9. 与其他课题的关系 - -| 课题 | 关系 | 说明 | -|------|------|------| -| 课题1(三层执行) | 无冲突 | Agent spawn 时多传一个 project_id,L2 注入多加项目上下文 | -| 课题2(事件驱动) | 微调 | Inbox JSONL 增加 project_id 字段,Daemon 路由到正确连接 | -| 课题3(挑战/评审) | 项目级配置 | guardrails.yaml 项目级覆盖 | -| 课题4(拆解+上下文) | 项目级配置 | project_context.yaml 注入 L2 | -| 课题6(经验沉淀) | 项目级经验 | 每个项目独立的 experiences 表,经验不跨项目污染 | -| 课题7+9(交互+Dashboard) | 多项目视图 | Dashboard 需要项目切换/多项目概览 | -| Worktree 隔离 | 正交 | Worktree 解决"同项目内多 Agent 并行改代码",课题11 解决"不同项目数据隔离" | - -## 10. 黑板 Schema 变更 - -**不增加 project_id 字段**——每个项目有独立数据库,表结构不变。 - -唯一新增:`_registry.yaml` 项目注册表。 - -## 11. 开发清单 - -| # | 任务 | 依赖 | -|---|------|------| -| 1 | 项目目录结构 + _registry.yaml + project.yaml Schema | 无 | -| 2 | ActiveAgentCounter(线程安全计数器 + 全局/per-agent 双重限制) | 无 | -| 3 | ProjectSlot(独立线程 tick + SQLite 独立连接 + spawn 前检查计数器) | 1, 2 | -| 4 | Daemon 主循环(启动状态恢复 + ProjectSlot 线程监控/重启) | 3 | -| 4a | 启动时幽灵 working 任务处理(扫描+标记 failed) | 4 | -| 5 | CLI project create/list/default/archive/delete 命令 | 1 | -| 6 | CLI 所有操作增加 --project 参数 + 优先级解析 | 1, 3 | -| 7 | L2 prompt_template 注入 project_context + session_id 命名规则 | 3, 6 | -| 8 | Daemon 逻辑健康自检(按项目追踪 + 计数器超时兜底) | 4 | -| 9 | 项目归档/删除安全流程(working 任务处理) | 4, 5 | -| 10 | Dashboard 项目切换 + 多项目概览 | 课题9 | - ---- - -## 附录:方案 B 的详细反驳 - -有人可能觉得方案 B(加 project_id 字段)更简单。但实际上: - -1. **安全面**:方案 B 靠 `WHERE project_id = ?` 逻辑隔离。一个漏掉的 WHERE = 数据泄漏。方案 C 靠物理文件隔离,漏不掉。 - -2. **性能面**:方案 B 所有项目共享一个 SQLite 文件。10 个项目各 10 个任务 = 100 个任务在一个 `.db` 里。WAL 写入是串行的,多项目并发 tick 会互相等待。方案 C 每个项目独立文件,互不影响。 - -3. **运维面**:方案 B 删除项目 = `DELETE FROM tasks WHERE project_id = ?` + 8 张表都要删。方案 C = `rm -rf projects/xxx/`。备份/恢复同理。 - -4. **配置面**:方案 B 的 guardrails.yaml 要设计"全局默认 + 项目覆盖"的合并逻辑。方案 C 每个项目独立 config/ 目录,天然隔离,覆盖逻辑更清晰。 - -5. **经验沉淀面**:方案 B 的经验混在一个 experiences 表里。如果项目 A 的"pytest 参数经验"污染到项目 B(B 可能不做 Python),反而有害。方案 C 每个项目独立经验库。 - -**唯一方案 B 更优的场景**:跨项目统计分析("所有项目的平均完成时间")。但这个需求可以后期通过注册表元数据实现,不需要把所有数据放一个库里。 diff --git a/docs/design/topic3-challenge-review-proposal.md b/docs/design/topic3-challenge-review-proposal.md deleted file mode 100644 index 2c5f0bd..0000000 --- a/docs/design/topic3-challenge-review-proposal.md +++ /dev/null @@ -1,906 +0,0 @@ -# 课题3 设计方案:挑战/评审体系 + 质量门控结构化 - -**作者**: 庞统士元 -**日期**: 2026-05-15 -**状态**: 方案待确认 -**前置**: 课题1(AI决策者参与)、课题2(事件驱动+上下文传递)已结题 - ---- - -## 1. 设计目标 - -课题1已定义了"谁来判断"(双轨制:Daemon系统保护 + Coordinator AI决策)和"判断什么"(must_haves三件套、幻觉门控、Scope Guard)。课题2定义了"怎么触发判断"(Tick+Inbox事件驱动、Handoff Comment无缝接手)。 - -**课题3要解决的问题是:判断的结果怎么记录、怎么协商、怎么流转。** - -具体来说: -1. 评审结果怎么结构化存储(不是散落在 comments 里的自然语言) -2. 挑战/评审的协商流程怎么设计(几轮、升级条件、对抗模式) -3. 评审体系和现有状态机(pending→working→review→done)怎么对齐 - -## 2. v1.0 三层体系的问题 - -| 问题 | v1.0 做法 | 根因 | -|------|----------|------| -| PAV自律形同虚设 | Plan→Act→Verify 靠 Skill 文本约束 | Skill 是"菜单"不是"触发器",Agent 可忽略 | -| 每个节点都审过重 | 司马懿审所有节点 | 没有分级,简单任务和复杂任务同一个审查标准 | -| 方案阶段没审查 | 只审产出不审方案 | 编码前的设计方案没有独立审查 | -| 评审结果不结构化 | 司马懿写自然语言评论 | 无法机器判断"通过还是没通过",只能人读 | -| 司马懿角色重叠 | 节点审一次 + 终审一次 | 重复劳动 | - -## 3. 设计决策 - -### D3-1:评审体系由三层改为"分级 + 流水线" - -> **参考实践**: -> - **superpowers**:串行双审(spec reviewer → code quality reviewer),先审方案再审代码 -> - **TradingAgents**:多空对抗辩论(Bull vs Bear → Research Manager 裁决) -> - **OpenAI Agent SDK**:Guardrail 是 tripwire(检测到违规立即中断,不是建议) -> - **Hermes v0.13**:Comment 的 metadata JSON 承载结构化数据(changed_files / tests_run / diff_path) -> - **v1.0 PRD**:review_result JSON 结构(verdict + issues + round + max_rounds) -> - **GitHub PR Review**:APPROVE / REQUEST_CHANGES / COMMENT 三种明确 verdict - -**核心思路**:不搞三层并行,而是每个任务按风险等级走不同的审查流水线。 - -| 任务风险等级 | 流水线 | 方案审查 | 产出审查 | 审查模式 | 最大轮次 | -|------------|--------|---------|---------|---------|---------| -| **high**(部署/策略/资金) | 三阶段 | ✅ 独立审查 | ✅ 独立审查 + Guardrail | 对抗辩论 | 5 | -| **standard**(编码/设计) | 二阶段 | ✅ 方案过挑战 | ✅ 产出过挑战 | 单审 | 3 | -| **low**(文档/格式化/搬运) | 一阶段 | ❌ 跳过 | ⚡ Guardrail 自动检查 | 自动 | 0 | -| **research**(调研/分析) | 一阶段 | ❌ 跳过 | ✅ 庞统确认方向 | 单审 | 2 | - -**和课题1的分级审查矩阵对齐**:课题1的 §9.5 已定义了四级风险等级和审查深度,课题3 补充的是每个等级内部的具体流程。 - -### D3-2:评审三阶段流水线(high/standard 任务) - -> **参考实践**: -> - **superpowers**:implementer → spec reviewer(必须通过) → code quality reviewer -> - **TradingAgents**:多空辩论 + Manager 裁决,用 `add_conditional_edges` 实现 -> - **OpenAI Agent SDK**:OutputGuardrail 是并行运行的轻量 AI Agent,不是 if-else 规则 - -**阶段 1:方案审查(Plan Review)**——只对 high/standard 任务 - -``` -执行者提交方案(scope_declaration)到黑板 decisions 表 - ↓ -挑战者审查方案 - ├── high:对抗辩论(正方 vs 反方 → 庞统裁决) - └── standard:单审(指定挑战者,默认司马懿) - ↓ -通过 → 进入执行阶段 -未通过 → 协商修改(最多 max_rounds 轮) - └── 超轮次 → 升级用户 -``` - -方案审查和课题1的 Scope Guard 关系: -- Scope Guard(L2 sub 异步检查)是吹哨人——发现偏离写 observation -- 方案审查(L3 Agent 正式审查)是正式评审——通过/打回有 verdict -- 两者互补:Scope Guard 是过程中的软检查,方案审查是阶段门 - -**阶段 2:执行 + Guardrail 自动检查**——所有任务 - -``` -执行者按方案执行 - ↓ -每次写 output 时,Daemon 自动触发 Guardrail - ├── L1 机械检查(文件存在、JSON 格式、字段非空)→ 不通过直接打回 - ├── L2 轻量 AI 检查(Schema 校验、artifacts 路径验证)→ 不通过写 observation - └── L3 安全红线(tripwire:如直接操作生产环境)→ 立即中断 - ↓ -Guardrail 通过 → 进入产出审查 -Guardrail 不通过 → 自动打回(tripwire 直接 block) -``` - -**阶段 3:产出审查(Output Review)**——high/standard 任务 - -``` -执行者提交产出到黑板 outputs 表 - ↓ -挑战者审查产出(质量/完整性/与方案一致性) - ↓ -评审结果结构化记录到 reviews 表 - ↓ -通过 → status: review → done -未通过 → status: review → working(打回重做) - └── 协商在 comments 表讨论 - └── 超轮次 → 升级用户 -``` - -### D3-3:评审结果结构化存储(reviews 表) - -> **参考实践**: -> - **Hermes**:Comment 的 metadata JSON 承载结构化数据 -> - **SWE-agent**:Trajectory 线性追加,不修改历史 -> - **v1.0 PRD**:review_result JSON(verdict + issues + round) -> - **GitHub PR Review**:APPROVE / REQUEST_CHANGES / COMMENT 三种 verdict -> - **Control Center**:artifacts 按 stage 组织,支持阶段回退 - -**设计原则**: -1. **追加写入不修改历史**(SWE-agent 模式)——每轮评审是新记录 -2. **黑板索引 + 文件详情**(课题2 原则)——黑板存摘要,文件存详情 -3. **结构化 verdict**(GitHub PR Review 模式)——枚举值,不是自然语言 -4. **关联产出物**(Control Center 模式)——review 关联到具体 output - -```sql --- ===== 评审结果表 ===== -CREATE TABLE IF NOT EXISTS reviews ( - id TEXT PRIMARY KEY, -- rev-001 - task_id TEXT NOT NULL, - output_id TEXT, -- 关联产出物(outputs 表) - 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); -``` - -**评审详情文件**(detail_path 指向): - -```json -{ - "review_id": "rev-001", - "task_id": "task-001", - "output_id": "out-001", - "reviewer": "simayi-challenger", - "review_type": "output_review", - "verdict": "needs_revision", - "confidence": 0.65, - "round": 1, - "max_rounds": 3, - "summary": "代码逻辑正确但缺少错误处理和单元测试", - "issues": [ - { - "id": "iss-001", - "severity": "critical", - "category": "correctness", - "location": "src/trading.py:42", - "description": "未处理网络超时异常", - "suggestion": "添加 try-except 包装,设置 30s 超时" - } - ], - "positives": ["代码结构清晰,职责分离做得好"], - "metadata": { - "files_reviewed": ["src/trading.py", "src/utils.py"], - "total_lines_reviewed": 150 - } -} -``` - -### D3-4:Guardrail 检查结果也入 reviews 表 - -> **参考实践**: -> - **OpenAI Agent SDK**:Guardrail 的 tripwire_triggered 是结构化输出 -> - **Hermes 幻觉门控**:系统验证产出是否真实存在 - -Guardrail 自动检查结果存入 reviews 表(`reviewer='system'`, `review_type='guardrail'`): - -```json -{ - "review_id": "rev-auto-001", - "reviewer": "system", - "review_type": "guardrail", - "verdict": "rejected", - "summary": "Guardrail 检查失败:产出文件不存在", - "issues": [ - { - "severity": "blocking", - "description": "产出文件不存在: output.md" - } - ] -} -``` - -**和课题1的 Guardrail 吹哨人机制对齐**: -- L1 机械检查失败 → 直接入 reviews 表(verdict=rejected)+ 打回 -- L2 轻量 AI 检查发现问题 → 写 observation(课题1设计)+ 入 reviews 表(verdict=needs_revision) -- L3 tripwire → 直接入 reviews 表(verdict=rejected)+ block 任务 - -### D3-5:对抗辩论模式(high 风险任务) - -> **参考实践**: -> - **TradingAgents**:Bull Researcher vs Bear Researcher → Research Manager 裁决 -> - **MetaGPT**:Engineer Agent 隐式 supervisor,按 SOP 产出 -> - **oh-my-claudecode Critic**:Critic 被禁止 Write/Edit 工具,只能读和评论 - -**high 风险任务的方案审查和产出审查都走对抗模式**: - -``` -方案审查(对抗模式): - 正方(执行者):写 scope_declaration,论证方案可行性 - 反方(挑战者池中的一人):找方案漏洞、风险、遗漏 - ↓ - 庞统裁决:综合双方观点,做最终判断 - ├── 认可正方 → 方案通过 - ├── 认可反方 → 方案打回 - └── 综合意见 → 要求修改后重新辩论 -``` - -**挑战者池**:不是固定司马懿一个人,而是按任务类型选择: -- 编码任务 → 司马懿 -- 风控任务 → 关羽 -- 数据任务 → 赵云(数据质量视角) -- 部署任务 → 姜维 - -庞统在黑板上创建任务时指定 reviewer(`assigned_by` 字段已有)。 - -### D3-6:协商流程与状态机对齐 - -> **参考实践**: -> - **superpowers**:implementer 报告四种状态(task_completed / task_completed_with_notes / task_blocked / task_failed) -> - **Hermes**:block with reason 前缀约定(review-required: / blocked-by:) -> - **v1.0 PRD**:三轮协商上限,超轮次升级用户 - -**现有状态机**:`pending → claimed → working → review → done` - -**和评审流水线的对齐**: - -``` -pending → claimed → working - ↓ - [方案审查](high/standard) - ├── approved → 继续 working - └── needs_revision → 继续 working(修改方案) - └── 超轮次 → 升级用户 - ↓ - [执行 + Guardrail] - ├── guardrail rejected → back to working(自动打回) - └── guardrail passed → 继续 - ↓ - 写 output → status: review - ↓ - [产出审查](high/standard) - ├── approved → status: done - ├── needs_revision → status: working(协商在 comments) - └── rejected → status: working(打回重做) - └── 超轮次 → 升级用户 - -low 风险任务:working → [Guardrail 自动检查] → done(跳过 review 状态) -research 任务:working → [庞统确认] → review → done -``` - -**新状态机扩展**(在现有 8 状态基础上,不增加新状态): - -| 状态转换 | 触发条件 | 对应阶段 | -|---------|---------|---------| -| 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 → blocked | 超轮次升级 | 阶段 3 | - -### D3-7:comments 表和 reviews 表的职责分离 - -| 表 | 职责 | 内容类型 | verdict | -|---|------|---------|---------| -| **comments** | 讨论、@mention、协商过程 | 自然语言 | ❌ 无 | -| **reviews** | 正式评审结论 | 结构化 JSON | ✅ 必须有 | - -评审协商过程("这里有个问题,建议这样改")写在 comments。 -评审结论("通过" / "不通过,原因见 issues")写在 reviews。 - -两者关联:reviews.detail_path 指向的完整报告可以引用 comments 中的讨论。 - -### D3-8:声明式 Guardrail 配置(YAML) - -> **参考实践**: -> - **OpenAI Agent SDK**:`@output_guardrail` 装饰器声明检查函数 -> - **v1.0 M4 Guard**:entry/exit guard + skill 化检查逻辑 -> - **SonarQube / CodeClimate**:rule_id 关联可查规则文档 - -```yaml -# 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 -``` - -## 4. 评审记录与 Handoff Comment 的关系 - -> **对齐课题2设计** - -Handoff Comment(课题2)是 Agent 结束时写的交接信息。评审记录(课题3)是挑战者写的审查结论。 - -两者在黑板上的关系: - -``` -Agent A 完成产出 - → 写 Handoff Comment(comments 表,type=handoff) - → 写 output(outputs 表) - → 通知 Daemon(inbox JSONL) - ↓ -Daemon 触发 Guardrail 自动检查 - → 结果写入 reviews 表(reviewer='system', review_type='guardrail') - → 通过 → 进入产出审查 - → 不通过 → 打回 - ↓ -Daemon spawn 挑战者 - → 挑战者读黑板:L1 含 Agent A 的 Handoff Comment + output summary - → 挑战者做审查 - → 写 review(reviews 表,verdict + summary + detail_path) - → 写 comment(comments 表,协商讨论) - → 通过 → status: done - → 不通过 → status: working + Agent A 被 spawn 重做 -``` - -下一个 Agent(如果需要)的 L1 消息中包含: -1. Agent A 的 Handoff Comment(最近3条评论之一) -2. 挑战者的 review summary(reviews 表最新一条) -3. 协商过程的 comments(L2 按需读取) - -### D3-9:审查协议注册表(Review Protocol Registry) - -> **参考实践**: -> - **superpowers**:三个独立 prompt 文件——`implementer-prompt.md`、`spec-reviewer-prompt.md`、`code-quality-reviewer-prompt.md`,每个角色有专属模板,不是笼统的"请审查" -> - **oh-my-claudecode Critic**:Investigation Protocol 分 Phase 执行(预判→验证→多视角→缺口分析→自审),不同 artifact type 自动切换视角(代码→安全/新人/运维,方案→执行者/利益相关者/怀疑论者) -> - **superpowers spec-reviewer**:prompt 注入对抗性指令——"DO NOT trust the report. Read the actual code.""Compare to requirements line by line." - -**问题**:审查者不知道自己该审什么,容易陷入局部审查(编码规范、编译通过),漏掉本质问题(需求一致性、语义正确性)。或者被挑战后一律照改,不加思考。 - -**根因**:审查指令是笼统的"请审查",审查者靠"自觉"决定审什么。Skill 是软引导,可看可不看。 - -**方案**:审查协议是代码注入的,不是靠 Agent 自己去找的。Daemon spawn 审查者时,根据任务类型 + 审查类型动态加载协议模板,注入到 bootstrap 消息中。 - -``` -review_protocols/ -├── plan_review.yaml # 方案审查协议 -├── output_review.yaml # 产出审查协议 -├── guardrail_l2.yaml # L2 轻量AI检查协议 -└── analysis_review.yaml # 分析/调研审查协议 -``` - -每个协议文件定义四个维度: - -**维度1:审查维度(审什么)** - -```yaml -# review_protocols/output_review.yaml(示例片段) -dimensions: - - id: requirement_traceability - description: "每个 must_have truth 是否被覆盖" - weight: critical - method: "逐条比对 truths → 产出代码/文件" - - - id: scope_alignment - description: "产出是否与 scope_declaration 一致" - weight: critical - method: "对比 decisions.scope_declaration vs 实际产出" - - - id: correctness - description: "逻辑是否正确" - weight: major - method: "追踪执行路径,验证关键逻辑" - - - id: completeness - description: "是否有遗漏" - weight: major - method: "缺口分析:什么缺失了?什么假设未被验证?" - - - id: constraint_compliance - description: "是否遵守约束" - weight: major - method: "逐条检查 tasks.constraints" -``` - -**维度2:审查方法(怎么审)**——参考 oh-my-claudecode Critic 的 Investigation Protocol - -```yaml -investigation_protocol: - phases: - - name: pre_commitment - instruction: > - 先不读产出,凭领域经验预测3-5个最可能出问题的点。 - 写下预测。然后逐个验证。这迫使主动搜索而非被动阅读。 - - - name: verification - instruction: > - 读实际产出(不是报告),逐条验证每个 truth。 - 提取所有文件引用、函数名、API 调用,逐一对照源码验证。 - 模拟执行每个步骤,不只读文字描述。 - - - name: multi_perspective - instruction: > - 从不同角色看这份产出: - - 安全视角:什么信任边界被跨越?什么输入没校验? - - 新人视角:不熟悉代码的人能理解吗?什么上下文被假设但没说明? - - 运维视角:规模化后怎样?依赖失败时怎样?爆炸半径多大? - - - name: gap_analysis - instruction: > - 不只看"什么有问题",还看"什么缺失了"。 - 问:什么会破坏这个?什么边界情况没处理?什么假设可能是错的? - - - name: self_audit - instruction: > - 给自己的每个 finding 打 confidence(HIGH/MEDIUM/LOW)。 - LOW confidence → 降级为 Open Question。 - "作者能立刻反驳吗?"如果能 → 降级。 - "这是真正的缺陷还是风格偏好?"如果是偏好 → 降级。 -``` - -不同审查类型用不同的 multi_perspective 视角集(参考 Critic 的代码视角 vs 方案视角分离): - -| 审查类型 | 多视角集合 | -|---------|----------| -| `output_review`(代码产出) | 安全 / 新人 / 运维 | -| `plan_review`(方案) | 执行者 / 利益相关者 / 怀疑论者 | -| `analysis_review`(调研分析) | 领域专家 / 实践者 / 反方辩手 | -| `guardrail_l2`(轻量检查) | 无多视角(单一维度快速检查) | - -**维度3:对抗性指令(防止走过场)**——参考 superpowers spec-reviewer 的"DO NOT trust"指令 - -```yaml -adversarial_instructions: - - "DO NOT trust the implementer's report. Read the actual code/files." - - "If something looks correct on the surface, verify it works in context." - - "Not just what's wrong — also check what's MISSING." - - "Accept findings only with evidence (file:line or specific quote). No evidence = opinion." - - "If you find 1 CRITICAL or 3+ MAJOR issues, escalate to adversarial mode: actively hunt for more problems, challenge every design decision, expand scope to adjacent code." - - "Also check for EXTRA/UNNEEDED work that wasn't requested." -``` - -**维度4:输出格式** - -```yaml -output_schema: "schemas/review-output.schema.json" -verdict_options: ["approved", "rejected", "needs_revision"] - -required_fields: - - verdict - - summary - - issues[] (each with severity, description, evidence) - - confidence (0.0-1.0) -``` - -**Daemon 拼接逻辑**: - -```python -def build_reviewer_bootstrap(task, review_type): - # 1. 加载协议模板 - protocol = load_protocol(f"review_protocols/{review_type}.yaml") - - # 2. 注入任务上下文( truths, constraints, scope_declaration) - protocol.inject_context(task) - - # 3. 拼接成 L1 bootstrap 消息 - # 审查者收到的消息 = 角色定义 + 审查协议 + 任务上下文 + 必读材料 - return format_reviewer_bootstrap(protocol, task) -``` - -**防止"一律照改"——反驳权(Rebuttal Phase)**: - -审查不是单向的。但不是每次都触发反驳——有跳过条件: - -**跳过条件**(不需 spawn 反驳): -- 审查者 verdict=approved → 直接 done,跳过 rebuttal -- 审查者 verdict=needs_revision,但 issues 全是 minor severity → 执行者自然在 comments 接受并修改,不 spawn 反驳 - -**触发条件**(spawn 反驳): -- 审查者 verdict=needs_revision,且 issues 中有 critical 或 major severity -- 审查者 verdict=rejected - -``` -审查者提交 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 -``` - -### D3-10:OpenClaw 集成——Full Agent vs Subagent vs Daemon 直接执行 - -> **参考实践**: -> - **superpowers**:Implementer / Spec Reviewer / Code Quality Reviewer 都是 Task tool dispatch 的 subagent,但各自有独立的 prompt 模板和模型选择 -> - **oh-my-claudecode**:Critic 被禁止 Write/Edit 工具(disallowedTools),是角色隔离而非进程隔离 -> - **open-multi-agent**:TaskQueue 维护 agent pool,scheduler 按 capability-match 分配任务 - -**问题**:什么任务需要完整的 Agent 身份(SOUL/IDENTITY/MEMORY),什么任务只需要无身份的 AI Worker? - -**判据:五个问题** - -| 问题 | 走 Full Agent | 走 Subagent | Daemon 直接执行 | -|------|-------------|-----------|--------------| -| 需要独立身份/人格吗? | ✅ 司马懿的"质量守门人" | ❌ | ❌ | -| 需要 Agent 专属工具吗? | ✅ 关羽的风控工具 | ❌ 通用 exec/read 就够 | 不需要 AI | -| 任务复杂度 | 编码、审查、调研、决策 | 单一检查、快速评估 | 格式校验、文件存在检查 | -| 需要写黑板吗? | ✅ 写 review/output/decision | ❌ 只返回 pass/fail | 只改状态 | -| 需要多轮交互吗? | ✅ 协商、反驳、辩论 | ❌ 一次性的 | 一次性的 | - -**对应到我们的场景**: - -| 场景 | 走什么 | OpenClaw API | 理由 | -|------|--------|-------------|------| -| 张飞编码 | Full Agent | `openclaw agent --agent zhangfei-dev` | 需要身份、编码工具 | -| 司马懿产出审查 | Full Agent | `openclaw agent --agent simayi-challenger` | 需要质量守门人角色、多轮协商 | -| 执行者反驳审查 | Full Agent(原 Agent) | `openclaw agent --agent <原执行者>` | 需要原执行者的身份和上下文 | -| 庞统任务规划 | Full Agent | `openclaw agent --agent pangtong-fujunshi` | 需要副军师角色、决策能力 | -| 庞统冲突裁决 | Full Agent(隔离 session) | `openclaw agent --agent pangtong-fujunshi --session-id ` | 避免主 session 上下文膨胀 | -| 赵云数据下载 | Full Agent | `openclaw agent --agent zhaoyun-data` | 需要数据工具、NAS 操作 | -| 姜维部署 | Full Agent | `openclaw agent --agent jiangwei-infra` | 需要 Docker/PM2 工具 | -| L2 Guardrail AI 检查 | Subagent | `sessions_spawn(task=...)` | 单一检查、不需要身份 | -| Scope Guard 异步检查 | Subagent | `sessions_spawn(task=...)` | 轻量、一次性 | -| L1 机械校验(文件存在/JSON格式) | **不走 AI** | Daemon 直接执行 | 纯机械操作,不需要 AI | - -**简化规则**:黑板上有名字的角色(庞统/司马懿/张飞/关羽/赵云/姜维)走 Full Agent。没有名字的一次性检查走 Subagent。纯机械检查 Daemon 自己做。 - -**OpenClaw spawn 具体实现**: - -```python -def spawn_agent(agent_id, task_id, mandate): - """Spawn Full Agent(有身份、有专属工具)""" - session_id = f"moziplus-{task_id}-{agent_id}-{uuid4().hex[:8]}" - bootstrap_msg = format_mandate_message(mandate) - - subprocess.run([ - "openclaw", "agent", - "--agent", agent_id, - "--session-id", session_id, - "--message", bootstrap_msg - ], capture_output=True, text=True) - - # 记录到黑板 events 表 - blackboard.execute( - "INSERT INTO events (task_id, event_type, data) VALUES (?, 'agent_spawned', ?)", - task_id, json.dumps({"session_id": session_id, "agent_id": agent_id}) - ) - -def spawn_subagent(task_id, task_description, context=None): - """Spawn Subagent(无身份、通用工具、一次性)""" - # 通过 OpenClaw API spawn 轻量 worker - # 返回结果直接处理,不写入黑板 reviews 表 - # 适用于 L2 Guardrail、Scope Guard 等轻量检查 - pass -``` - -**庞统主 session 的隔离策略**: - -庞统主 session 做轻量调度(L1 构建、状态检查、黑板 tick 处理)。复杂的任务拆解和裁决 spawn 一个 pangtong-fujunshi 的隔离 session,避免主 session 上下文膨胀(课题2 D2-6:不需要 Auto-compact,但庞统是唯一可能有累积的 session)。 - ---- - -## 5. 遗留 TODO - -| # | 待解决事项 | 归属 | 说明 | -|---|----------|------|------| -| ~~T3-1~~ | ~~reviews 表的 CLI 命令~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-2~~ | ~~Guardrail YAML 解析 + 执行引擎~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-3~~ | ~~对抗辩论模式的具体黑板协议~~ | ~~Phase 3~~ | ✅ 方案已定(见 §5.1),仅在用户明确要求时启用 | -| ~~T3-4~~ | ~~挑战者池的选择策略~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-5~~ | ~~confidence 低于阈值自动升级~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-6~~ | ~~评审详情文件的 Schema 定义~~ | ~~Phase 2~~ | ✅ 方案已定(见 §5.2),待司马懿评审确认 | -| ~~T3-7~~ | ~~low 风险任务 Guardrail 自动通过~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-8~~ | ~~Review Protocol 模板文件编写~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T3-9~~ | ~~反驳权 Daemon 流控~~ | ~~Phase 2~~ | ✅ 方案已定(见 §5.3),轮次上限兜底不设超时 | -| ~~T3-10~~ | ~~Agent 调度判据~~ | ~~Phase 2~~ | ✅ 方案已定(见 §5.4),配置表驱动非 AI 判断 | - -### 5.1 对抗辩论黑板协议(T3-3 方案) - -> **触发条件**:仅在用户明确在任务意图中包含"辩论"等字样时启用。大多数任务走标准挑战机制(单审 + 反驳权)足够。 - -**调研来源**:TradingAgents(Bull vs Bear → Manager 裁决)、学术界 MAD(critic/defender/judge 三角色、稳定性检测早停)、claude-goal(none/adversarial/double 三级)。 - -**三角色 × 最多 5 轮 × 2 种结束条件**: - -- **正方(Proponent)**= 执行者 -- **反方(Opponent)**= 挑战者池按任务类型选择 -- **裁决者(Judge)**= 庞统(隔离 session) - -**黑板交互格式**: - -每个参与者用 comments 表写入,按类型区分: -- `comment_type = "debate_argument"` → 正方/反方论点 -- `comment_type = "debate_rebuttal"` → 反驳 -- `comment_type = "debate_judgment"` → 庞统裁决 - -每条 comment 的结构: -```json -{ - "role": "proponent | opponent | judge", - "round": 1-5, - "claims": [ - {"point": "...", "evidence": "文件路径/行号/数据", "severity": "critical|major|minor"} - ], - "concession": ["我接受的观点..."], - "stance": "hold | yield | partial" -} -``` - -**流程**: - -``` -Round 1: 正方写 scope_declaration + arguments → 反方读后写反驳 -Round 2+: 正方回应反驳 → 反方回应 -每轮结束后 Daemon 检查结束条件 -``` - -**结束条件**(任一满足即停): - -1. **达成共识**:一方 stance=yield 或双方 stance=partial -2. **轮次耗尽**:达到 max_rounds(默认 5) -3. **稳定性检测**:连续 2 轮无新论点(claims 无变化)→ 提前终止 - -达成共识 → 按共识结论执行。轮次耗尽/稳定性 → 庞统裁决。 - -庞统裁决输出:写入 reviews 表(verdict + summary + detail_path),verdict = approved | rejected | needs_revision。 - -**reviews 表变更**:新增 `debate_context` JSON 字段(存辩论参与者和轮次数)。 - -### 5.2 评审详情文件 Schema(T3-6 方案) - -> **待司马懿评审确认。** - -**调研来源**:SARIF(Static Analysis Results Interchange Format)result 结构、GitHub PR Review、Hermes comment metadata JSON。 - -**Schema**(`{task_id}/reviews/{review_id}.json`): - -```json -{ - "$schema": "review-detail-v1", - "review_id": "rev-001", - "task_id": "task-001", - "reviewer": "simayi-challenger", - "review_type": "output_review", - "verdict": "approved", - "confidence": 0.85, - "round": 1, - - "summary": "一句话结论", - - "issues": [ - { - "id": "iss-1", - "status": "open|accepted|rejected|deferred", - "severity": "critical|major|minor|info", - "category": "correctness|security|performance|style|scope_deviation|architecture|robustness", - "location": { - "file": "src/strategy.py", - "line_start": 42, - "line_end": 55, - "context": "def calculate_signal(df):\n ..." - }, - "title": "简短描述", - "description": "详细说明", - "evidence": "代码片段或引用", - "suggestion": "修复建议", - "remediation_effort": "low|medium|high" - } - ], - - "evidence": [ - { - "type": "file_content", - "path": "src/strategy.py", - "relevant_lines": [42, 43, 44] - }, - { - "type": "command_output", - "command": "pytest tests/ -x", - "exit_code": 1, - "output": "失败信息..." - } - ], - - "positives": ["做得好的地方"], - - "meta": { - "review_protocol": "output_review", - "files_reviewed": 3, - "model_used": "zhipu/glm-5.1" - } -} -``` - -**SARIF 借鉴**:issues 数组结构(severity + location + message)是 SARIF result 核心字段,但我们简化了——不需要 SARIF 的完整 taxonomy 和 rule metadata,我们的场景是 Agent 审查而非静态分析工具。 - -### 5.3 反驳权 Daemon 流控(T3-9 方案) - -**本质问题**:审查者和执行者意见不一致时,Daemon 怎么管理协商过程。 - -**调研来源**:TradingAgents(Bull/Bear → Manager 裁决)、claude-goal(double 审计)、superpowers(verify→fix loop)。 - -**触发条件**(不是所有 review 都触发): - -- review verdict=needs_revision,且 issues 中有 critical/major severity → 触发反驳 -- review verdict=rejected → 触发反驳 -- review verdict=approved → 不触发,直接 done -- review verdict=needs_revision,但只有 minor → 不触发,执行者直接改 - -**流程**: - -``` -审查者提交 review(verdict=needs_revision/rejected) - ↓ -Daemon tick 检测到 review → 检查 issues severity → 判定需要反驳 - ↓ -Daemon spawn 原执行者,注入: - "你收到了一份审查意见。对每个 issue,你必须表态: - ACCEPT / REJECT(说明理由) / PARTIAL(说明接受哪些) - 不允许全部接受不加思考。" - ↓ -执行者写入 comments(comment_type="rebuttal") - ↓ -Daemon 检查 rebuttal 结果: - │ - ├── 全部 ACCEPT - │ → 执行者修改 → 重新提交 → 审查者 re-review - │ - └── 有 REJECT/PARTIAL - → Daemon spawn 审查者看 rebuttal - ↓ - 审查者写入 comments(comment_type="rebuttal_response") - ↓ - Daemon 检查: - ├── 双方达成一致 → 执行者按协商结果修改 → re-review - ├── 审查者认可执行者反驳 → 执行者修改 → re-review - └── 仍有分歧 - → round++ - → round < max_rounds → 回到执行者继续协商 - → round >= max_rounds → 升级庞统裁决 -``` - -**唯一的兜底机制**:协商轮次达到 max_rounds 仍未一致 → 升级庞统裁决。庞统裁决也达不成结论 → 升级用户。 - -**不设超时**——Agent 可能被长任务 block,超时不能等同于"放弃反驳"。轮次上限已经是自然的终止条件。 - -**进度检测**(催促通知,非阻塞):如果同一轮次超过 `task_timeout`(10 分钟)没有任何新 comment 写入 → Daemon 通过 Inbox 发一个催促通知给当前应回应的 Agent。不是强制终止,只是提醒。 - -**reviews 表新增字段**: -- `rebuttal_status`: "pending" | "completed" | "escalated" -- `debate_round`: INTEGER(当前协商轮次,默认 1) - -**和 T3-3 对抗辩论的区别**:反驳权是审查后的协商机制(大多数任务),对抗辩论是正式辩论(仅用户明确要求时)。 - -### 5.4 Agent 调度判据(T3-10 方案) - -**调研来源**:Phil Schmid 四种子 Agent 模式、Addy Osmani Code Agent Orchestra、oh-my-opencode 三层调度。 - -**现有设计已完整**(D3-10 五问题判据 + 场景映射 + 简化规则),补充的是 **Daemon 自动调度逻辑**。 - -**三级决策树(配置表驱动,非 AI 判断)**: - -```python -DISPATCH_RULES = { - "L1_guardrail": "daemon", # Daemon eval guardrails.yaml assert - "L2_ai_check": "subagent", # sessions_spawn 单一检查 - "scope_guard": "subagent", # 轻量异步检查 - "execute": "full_agent", # 走 registered agent - "review": "full_agent", # 走 registered agent - "rebuttal": "full_agent", # 走原执行者 - "debate_proponent": "full_agent", # 走原执行者 - "debate_opponent": "full_agent", # 走挑战者 - "debate_judge": "full_agent", # 庞统隔离 session - "plan_decomposition": "full_agent", # 庞统 -} - -def dispatch(task, action_type, project_config): - # 从项目配置动态读取可用 Agent 列表,不硬编码 - registered_agents = project_config.get("agents", []) - - # Level 1: 纯机械检查 → Daemon 直接执行 - if action_type in ("L1_guardrail", "format_check", "file_exists_check"): - return execute_locally(task) - - # Level 2: 有名字的角色 → Full Agent - if task.assignee in registered_agents: - if action_type == "adjudication": - return spawn_full_agent(task.assignee, new_session=True) - return spawn_full_agent(task.assignee) - - # Level 3: 无名字的一次性任务 → Subagent - if DISPATCH_RULES.get(action_type) == "subagent": - return spawn_subagent(task_description=action_type) - - # Level 4: 未知 action_type → 庞统裁决 - return spawn_full_agent("pangtong-fujunshi", new_session=True) -``` - -**简化规则**:黑板上有名字的角色(庞统/司马懿/张飞/关羽/赵云/姜维)走 Full Agent。没有名字的一次性检查走 Subagent。纯机械检查 Daemon 自己做。未知类型交给庞统裁决。 - -**spawn 方式**(和课题11异步计数器模型一致): -- **Full Agent**:`subprocess.Popen`(非阻塞)启动 `openclaw agent` CLI,不等返回,下次 tick 检查产出 -- **Subagent**:`sessions_spawn`(Gateway 内部 API),等返回 - -## 6. 和现有设计的对齐检查 - -| 已有设计 | 课题3 补充 | 一致性 | -|---------|-----------|--------| -| §9.5 分级审查矩阵(课题1) | D3-1 分级流水线(补充每级内部流程) | ✅ 四级风险对应四级流水线 | -| §4.7 Guardrail 吹哨人(课题1) | D3-4 Guardrail 结果入 reviews 表 | ✅ 吹哨后写 observation + 入 reviews | -| §3.7 Schema 校验(课题2) | D3-8 Guardrail YAML 和 Schema 对齐 | ✅ L1 检查用 Schema,YAML 声明规则 | -| §4.2 Tick+Inbox(课题2) | 评审触发也走 inbox 通知 | ✅ 挑战者 review 写完后通知 Daemon | -| §5.1 Handoff Comment(课题2) | §4 评审与 Handoff 关系 | ✅ Handoff 是执行者写的,review 是挑战者写的 | -| §3.2 SQLite Schema | reviews 表新增 | ✅ 独立新表,不修改现有表 | diff --git a/docs/design/topic4-decomposition-skill-proposal.md b/docs/design/topic4-decomposition-skill-proposal.md deleted file mode 100644 index 0edb28c..0000000 --- a/docs/design/topic4-decomposition-skill-proposal.md +++ /dev/null @@ -1,638 +0,0 @@ -# 课题4 设计方案:智能拆解机制 + Skill 体系工程落地 - -**作者**: 庞统士元 -**日期**: 2026-05-15 -**状态**: 方案待确认 -**前置**: 课题1/2/3 已结题 -**调研报告**: `docs/research/v2.6-research-04-decomposition-skills.md` - ---- - -## Part A:智能拆解机制 - -### 1. 设计目标 - -v1.0 的拆解是"关键词匹配 → 固定 DAG 模板",问题是: -- 模板太死板(5个固定DAG),无法根据需求动态调整 -- 关键词匹配粗糙,复杂需求容易匹配错模板 -- 庞统拆解时靠 LLM 理解,没有结构化引导 - -课题4要解决:**庞统收到用户需求后,如何把它变成黑板上的任务(含依赖关系、角色分配、风险等级)。** - -### 2. 和课题1/2/3的关系 - -课题1-3 已经定义了"任务在黑板上长什么样"(tasks 表 schema、状态机、reviews 表、Guardrail 配置)。课题4定义的是"任务怎么被创建出来"。 - -``` -课题4(创建) → 课题2(触发执行) → 课题3(审查) → 课题1(失败处理) -``` - -### 3. 设计决策 - -#### D4-1:模板组件库替代固定 DAG - -> **参考实践**: -> - **MetaGPT**:SOP 自动链 + `_watch` 声明式订阅,不是固定 DAG -> - **get-shit-done**:discuss→plan→execute→verify→ship 五阶段,但每阶段内部灵活 -> - **open-multi-agent**:Goal → DAG 自动分解,按 complexity 决定粒度 -> - **v1.0**:5 个固定模板(full_role_development / circular_review_test / multi_stage_review / data_acquisition / multi_edge_discussion) - -**方案**:不是 5 个完整 DAG,而是**模板组件库(Phase Components)**。庞统按需组合。 - -```yaml -# template_components.yaml -components: - research: - trigger: "需要调研/分析" - mandatory: false - steps: - - id: lit_search - name: "资料调研" - agent: zhaoyun-data - output: "research_report.md" - - id: analysis - name: "分析总结" - agent: pangtong-fujunshi - output: "analysis.md" - - design: - trigger: "需要架构/设计" - mandatory: false - steps: - - id: arch_design - name: "架构设计" - agent: pangtong-fujunshi - output: "design.md" - - id: design_review - name: "设计评审" - agent: simayi-challenger - review_type: plan_review - - coding: - trigger: "需要编码/实现" - mandatory: false - steps: - - id: implement - name: "编码实现" - agent: zhangfei-dev - output: "code/" - - id: code_review - name: "代码评审" - agent: simayi-challenger - review_type: output_review - - data: - trigger: "需要数据获取/处理" - mandatory: false - steps: - - id: data_acquire - name: "数据获取" - agent: zhaoyun-data - output: "data/" - - id: data_validate - name: "数据验证" - agent: zhaoyun-data - - deploy: - trigger: "需要部署" - mandatory: false - steps: - - id: deploy - name: "部署" - agent: jiangwei-infra - output: "deploy_log.md" - - id: deploy_verify - name: "部署验证" - agent: jiangwei-infra - - risk_control: - trigger: "涉及实盘/资金/策略" - mandatory: true # 触发条件满足时强制包含 - steps: - - id: risk_check - name: "风控检查" - agent: guanyu-dev - - review: - trigger: "always" - mandatory: true # 铁律 IR-1 - steps: - - id: final_review - name: "最终审查" - agent: simayi-challenger - review_type: final_review - - custom: - trigger: "none" # 不自动触发,庞统主动选择 - description: "庞统自由定义的 phase,结构与标准组件相同" - steps: [] # 空的,庞统运行时填充 -``` - -**custom 组件接口**:与标准组件同 schema(steps 列表,每个 step 有 id/name/agent/output/review_type)。Plan Checker 对 custom 组件的验证和标准组件相同——检查覆盖性、依赖性、粒度、铁律。唯一区别是 custom 不从库中引用,而是庞统在 Step 2 中自由定义。 -``` - -**庞统的拆解流程**: - -``` -Step 1:需求结构化 - - 分析用户输入,提取需求列表 - - 每个需求标注类型(数据/编码/分析/部署/风控) - - 评估复杂度(简单/中等/复杂) - - 隐含需求挖掘(如用户说"回测"→ 隐含需要数据获取) - - 产出:structured_requirements → 写入黑板 decisions 表 - -Step 2:组件选择 + 组合 - - 根据需求类型,从组件库选择需要的 phases - - 检查 mandatory 组件是否被遗漏 - - 组合 phases,排列依赖关系 - - AI 增强:增删调整(如"这次数据已有,跳过 data_acquire") - - 产出:draft_plan → 写入黑板 decisions 表 - -Step 3:Plan Checker 验证 - - 独立验证(不是庞统自己验证自己) - - 检查项: - - 覆盖性:每个需求是否被至少一个 step 覆盖 - - 依赖性:step 依赖是否合理(无环、无遗漏前置) - - 粒度性:step 是否过大/过小 - - 铁律:IR-1(每任务至少2 Agent)是否满足 - - 风险:risk_level 是否合理,高风险任务是否有对抗审查 - - 不通过 → 回 Step 2 调整(最多 2 轮) - - 2 轮不通过 → 庞统带着 Plan Checker issues 创建任务,标注 risk_level=high,由审查者在 plan_review 阶段进一步验证。不升级用户(庞统能处理) - - 通过 → 进入 Step 4 - -Step 4:任务创建 - - 将 plan 转为黑板 tasks - - 每个 step → 一个 task(含 truths / artifacts / constraints / risk_level / assigned_to) - - 依赖关系 → tasks 的 depends_on 字段 - - 庞统在黑板上写 scope_declaration - - 产出:tasks 写入黑板 → Daemon 检测 → 开始执行 -``` - -#### D4-2:需求结构化的输出格式 - -庞统分析用户需求后,输出结构化需求到黑板: - -```json -{ - "original_input": "实现一个双均线策略的回测", - "requirements": [ - { - "id": "req-001", - "description": "获取标的分钟线数据", - "type": "data", - "acceptance_criteria": "数据文件存在于 NAS 指定路径", - "complexity": "simple", - "implicit": true - }, - { - "id": "req-002", - "description": "编写双均线策略逻辑", - "type": "coding", - "acceptance_criteria": "策略代码通过单元测试", - "complexity": "medium", - "implicit": false - }, - { - "id": "req-003", - "description": "回测并生成报告", - "type": "analysis", - "acceptance_criteria": "回测报告包含收益曲线和关键指标", - "complexity": "medium", - "implicit": false - } - ], - "inferred_components": ["data", "coding", "analysis"], - "risk_level": "standard", - "estimated_tasks": 3 -} -``` - -#### D4-3:Plan Checker 独立验证 - -> **参考实践**: -> - **superpowers**:plan-document-reviewer subagent 独立验证方案 -> - **oh-my-claudecode Critic**:预判→验证→自审五阶段 -> - **get-shit-done**:Scope Reduction Detection(完成后反向检查覆盖) - -Plan Checker 是一个 Subagent(不需要 Agent 身份),Daemon spawn 后执行: - -```yaml -# review_protocols/plan_check.yaml -name: plan_check -description: "验证庞统的拆解方案是否合理" - -dimensions: - - id: coverage - description: "每个需求是否被至少一个 task 覆盖" - weight: critical - method: "需求 ID → task ID 映射矩阵,检查是否有遗漏" - - - id: dependency - description: "依赖是否合理" - weight: critical - method: "拓扑排序检查无环,前置是否完整" - - - id: granularity - description: "粒度是否合适" - weight: major - method: "检查每个 task 的 truths 是否是一个 Agent 一次 spawn 能完成的" - - - id: iron_rules - description: "铁律是否满足" - weight: critical - method: "IR-1: 至少 2 Agent 参与;IR-2: 每个 task 有 truths" - - - id: risk_alignment - description: "风险等级是否合理" - weight: major - method: "涉及资金/部署 → high;纯分析 → research" - -output_schema: "schemas/plan-check-output.schema.json" -verdict_options: ["approved", "needs_revision"] -``` - -#### D4-4:复杂度驱动的粒度控制 - -> **参考实践**: -> - **open-multi-agent**:按 complexity 决定是否拆分子任务 -> - **wanman**:Token 预算控制执行深度 - -| 复杂度 | 判断标准 | 拆解策略 | truths 定义 | -|--------|---------|---------|------------| -| **simple** | 单一文件、单一操作、< 50 行 | 1 个 task | 1-2 条 truths | -| **medium** | 多文件、有依赖、需要验证 | 1-3 个 task + 依赖 | 每个 2-3 条 truths | -| **complex** | 跨模块、多角色、需要调研 | 多 phase 组件组合 | 每个 3-5 条 truths | - -**粒度自检规则**(庞统的 Skill 中定义): -- 一个 task 的 truths > 5 条 → 拆分 -- 一个 task 需要跨 3+ 角色 → 拆分 -- 一个 task 预计 > 30 分钟 → 拆分 - -#### D4-5:用户确认点 - -> **参考实践**: -> - **get-shit-done**:discuss phase 是必须的用户确认步骤 -> - **v2.0 PRD**:Phase 1 需求探索(苏格拉底对话) - -不是每步都问用户,但在关键节点确认: - -| 检查点 | 触发条件 | 用户看到什么 | -|--------|---------|------------| -| 需求确认 | Step 1 完成后 | 结构化需求列表 + 隐含需求 | -| 方案确认 | Step 2 完成后(complex 任务) | 任务列表 + 依赖关系 + 预估时间 | - -simple 任务跳过方案确认,直接创建。complex 任务必须确认。 - -**用户干预三种模式**(来源:2026 human-in-the-loop 综述 + OpenAI Agents SDK): - -| 模式 | 机制 | 适用 | -|------|------|------| -| 审批式 | Agent 暂停等用户确认 | high 任务方案确认、部署前 | -| 通知式 | 推送消息,用户可回复可不回复 | standard 任务产出审查后 | -| 中断式 | Agent 主动问用户 | 歧义需求、争议仲裁 | - -**当前落地**:通过庞统 session(webchat/Signal)。**未来**:dashboard Checkpoint 面板。 - -**干预点由 risk_level 动态决定**:low→无干预、standard→通知式、high→审批式、用户随时可中断。 - ---- - - -## Part B:Skill 体系工程落地 - -### 4. 设计目标 - -三个课题产出了大量"Agent 需要知道怎么做"的内容(28 项 Skill TODO),需要一个系统的 Skill 体系来承载。 - -### 5. 核心洞察:被动触发不可靠 - -> **问题**:我们写了好多个 Skill,但除了用户明文指定"用 PAV""用 delegation",几乎没见哪个 Skill 被主动触发过。 -> -> **根因**:Agent 不知道自己不知道什么。OpenClaw 的 Skill 列表注入 system prompt 后,Agent 需要自己判断需要哪个——但 Agent 根本不知道"我应该先读一个 Skill 来学怎么做"。 -> -> **优秀实践验证**:superpowers / GSD / open-multi-agent 没有一个是靠被动 Skill 触发的——全部是编排器构造好 prompt 直接注入。 - -**结论**:moziplus 的关键操作规范不走被动 Skill 发现,走引擎注入。 - -### 5.1 铁律定义(L0 内容) - -铁律是**所有 Agent 共享的**强制规则,不可定制。角色追加的规则放在 L1(SOUL.md)。 - -| 编号 | 铁律 | 来源 | -|------|------|------| -| IR-1 | 每个任务至少 2 个 Agent 参与(做 + 挑战) | v1.0 PRD | -| IR-2 | 每个 task 必须有 truths(可观测行为标准) | 课题1 must_haves | -| IR-3 | 结论必须有证据(file:line 或具体引用) | oh-my-claudecode Critic | -| IR-4 | 语义含糊先确认,不擅自执行 | SOUL.md 底线 | -| IR-5 | 改代码先改文档(需求→设计→测试→部署) | v1.0 实践 | - -**Token 估算**:5 条铁律,每条 ~30 token(编号+一句话),总计 ~150 token + XML 包装 ~50 token = **~200 token**。加上角色追加(L1 SOUL.md 已计入),L0 预算 ~200-300 token,远低于 ~500 上限。 - -### 6. 设计决策 - -#### D4-6:四层上下文架构 - -> **参考实践**: -> - **superpowers**:每个 subagent 有独立 prompt 模板(implementer-prompt.md / spec-reviewer-prompt.md),模板里写死操作步骤 -> - **open-multi-agent**:`buildTaskPrompt()` 拼 task title + description + 依赖产出 + messages -> - **GSD**:每个 executor 拿到 PLAN.md + PROJECT.md + STATE.md + CONTEXT.md,按 context window 大小自适应 -> - **OpenClaw**:Hook `agent_turn_prepare` 可注入 prependContext/appendContext - -共同模式:**操作规范(模板)+ 项目背景(固定)+ 任务上下文(动态)**,编排器拼装,不靠 Agent 自己找。 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Layer 0: 铁律(Iron Rules) │ -│ 机制: OpenClaw Hook agent_turn_prepare │ -│ 保证: 每轮强制注入 | Token: ~500 │ -│ 内容: IR-1~IR-5(每个 Agent 可定制) │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 1: 角色(Agent Identity) │ -│ 机制: SOUL.md + IDENTITY.md + AGENTS.md │ -│ 保证: 每轮强制 | Token: ~2000 │ -│ 内容: 角色定义 + 行为准则 + 记忆 │ -│ 变化频率: 极少(换平台不变) │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 2: 操作规范 + 任务上下文(引擎注入) │ -│ 机制: Daemon spawn 时拼装 bootstrap 消息 │ -│ 保证: 每次 spawn 强制注入 | Token: ~1000-2000 │ -│ 三段式: │ -│ ① 角色操作规范模板(executor.md / reviewer.md / planner.md)│ -│ → 固定部分,写死关键操作步骤(scope/output/handoff等) │ -│ → 变化频率: 很少(moziplus 平台变了才改) │ -│ ② 项目背景(项目路径/框架/NAS/回测服务) │ -│ → 半固定部分,按项目配置 │ -│ ③ 任务上下文(truths/artifacts/constraints + depends_on产出)│ -│ → 动态部分,每次 spawn 不同 │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 3: 参考信息(OpenClaw Skill 被动发现) │ -│ 机制: OpenClaw Skill 列表注入 system prompt │ -│ 保证: ❌ 不保证触发(锦上添花) │ -│ 内容: 具体操作方法(回测流程/数据获取/部署步骤等) │ -│ 优化: description 中加触发场景+示例对话+触发词(见D4-8) │ -└─────────────────────────────────────────────────────────────┘ -``` - -**张飞编码时靠什么**: -- L0+L1:身份和纪律(每轮都有) -- L2 ① executor.md:"读黑板→写scope→执行→写output→写Handoff"(引擎注入,不会忘) -- L2 ③ 任务上下文:"这次要做什么、truths 是什么、前序产出是什么"(引擎注入) -- L3 quant-backtest Skill:具体回测代码怎么写(自己判断要不要读) - -#### D4-7:L2 引擎注入的拼装原则 - -> **参考实践**: -> - **superpowers**:implementer/spec-reviewer/code-quality-reviewer 三个独立 prompt 模板,只包含该角色需要的信息 -> - **open-multi-agent**:`buildTaskPrompt()` 只注入依赖链上的产出(default-deny),不是全量 -> - **GSD**:按 context window 大小自适应(200K 只给任务级,1M 给全量) - -**原则:按角色和任务阶段精确注入,不统一拼装,不多不少。** - -| Agent 角色 | 注入内容 | 不注入 | -|-----------|---------|--------| -| **执行者** | executor.md + Guardrail 配置 + 任务上下文 | Review Protocol | -| **审查者** | reviewer.md + Review Protocol + required_reading | Guardrail 配置 | -| **庞统(规划)** | planner.md + template_components + Plan Checker | Review Protocol | -| **庞统(裁决)** | adjudicator.md + 正方 comment + 反方 comment | Guardrail 配置 | -| **反驳者** | rebuttal.md + 审查者 review + ACCEPT/REJECT 指令 | Review Protocol | -| **Plan Checker** | plan_checker.md + 需求列表 + 拆解方案 | 全部角色模板 | - -**拼装逻辑**(Daemon 代码): -```python -def build_bootstrap(agent_id, role, task): - parts = [] - - # ① 角色操作规范模板 - template = load_template(f"prompt_templates/{role}.md") - parts.append(template) - - # ② 项目背景(半固定) - parts.append(format_project_context(task)) - - # ③ 任务上下文(动态) - parts.append(format_task_context(task)) - - # 按角色追加(只注入该角色需要的) - if role == "executor": - parts.append(format_guardrails(task.task_type)) - elif role == "reviewer": - parts.append(format_review_protocol(task.review_type)) - parts.append(format_required_reading(task)) - elif role == "planner": - parts.append(format_template_components()) - elif role == "rebuttal": - parts.append(format_review_to_respond(task)) - - return "\n\n".join(parts) -``` - -#### D4-8:L3 Skill 被动触发的优化写法 - -> **参考实践**: -> - **superpowers code-reviewer**:description 里嵌入 `` 标签,用对话示例教 Agent 触发场景 -> - **wiki-query**:description 里加 Chinese/English triggers 列表 -> - **gstack plan-design-review**:加 `Proactively suggest when...` 指令 -> - **SkillRouter 论文**(2026):Agent 自主搜索(agentic)比纯关键词/纯语义准确率最高 - -即使 L3 不保证触发,也要优化写法提高命中率。**moziplus 的 Skill 全部按以下规范写 description**: - -```yaml ---- -name: quant-backtest -description: > - 标准化量化回测技能。 - - When to use: 当需要回测量化策略、验证策略历史表现、测试策略参数时。 - - Chinese triggers: 回测、backtest、策略测试、历史表现验证、参数优化。 - English triggers: backtest, strategy test, historical performance, parameter optimization. - - Proactively suggest when: 用户提到策略实现完成后,主动建议运行回测验证。 - - Example: user says "帮我测一下双均线策略" → use quant-backtest skill. ---- -``` - -**description 四要素**: -1. **功能描述**:这个 Skill 干什么 -2. **触发场景**(When to use):什么情况下该用 -3. **触发词**(Chinese/English triggers):中英文关键词 -4. **主动建议**(Proactively suggest):Agent 看到相关场景时主动推荐 - -#### D4-9:Skill 诚实边界 - -> **参考实践**:nuwa-skill Agentic Protocol 要求每个 Skill 声明自己做不到什么 - -每个 Skill 必须包含 `## 诚实边界` section: - -```markdown -## 诚实边界 -- 本 Skill 不覆盖实盘交易逻辑(实盘请参考 live-trading Skill) -- 性能问题需要实际测试数据,纯代码审查无法确认 -- 复杂策略可能需要拆分为多个 task -``` - -#### D4-10:moziplus 目录结构 - -``` -sanguo_moziplus/ - prompt_templates/ ← L2 ① 角色操作规范(Daemon 读取拼装) - executor.md ← 执行者操作步骤(读黑板→scope→执行→output→handoff) - reviewer.md ← 审查者操作步骤(读Protocol→审查→写review) - planner.md ← 庞统拆解步骤(需求结构化→组件选择→PlanChecker) - adjudicator.md ← 庞统裁决步骤(正方反方→判断→写决策) - rebuttal.md ← 反驳步骤(读review→ACCEPT/REJECT/PARTIAL) - plan_checker.md ← Plan Checker 步骤(覆盖性→依赖→粒度→铁律) - - review_protocols/ ← L2 审查协议(Daemon 构造 reviewer bootstrap 时读取) - plan_review.yaml ← 方案审查协议 - output_review.yaml ← 产出审查协议 - guardrail_l2.yaml ← L2 轻量AI检查协议 - analysis_review.yaml ← 分析审查协议 - plan_check.yaml ← Plan Checker 协议 - - schemas/ ← CLI 层 Schema 校验(blackboard.py 读取) - handoff.schema.json ← Handoff 格式校验 - output.schema.json ← Output 格式校验 - decide.schema.json ← Decision 格式校验 - observe.schema.json ← Observation 格式校验 - review-output.schema.json ← Review 格式校验 - - template_components.yaml ← 模板组件库(庞统拆解时参考) - guardrails.yaml ← 声明式 Guardrail(Daemon 执行检查) - project_context.yaml ← L2 ② 项目背景(NAS路径/回测服务/框架) -``` - -**各目录作用**: - -| 目录/文件 | 谁读 | 作用 | 变化频率 | -|----------|------|------|----------| -| `prompt_templates/` | Daemon | 按 Agent 角色拼装 bootstrap 消息 | 很少变 | -| `review_protocols/` | Daemon | 构造审查者的 bootstrap 消息 | 偶尔变 | -| `schemas/` | blackboard.py CLI | 写入时校验数据格式 | 偶尔变 | -| `template_components.yaml` | 庞统(通过 L2 注入) | 拆解需求时选择组件 | 偶尔变 | -| `guardrails.yaml` | Daemon | 自动检查产出 | 偶尔变 | -| `project_context.yaml` | Daemon | 拼装项目背景信息 | 换项目时变 | - -**注意**:Agent 不直接读这些文件——全部由 Daemon 读取后拼装到 bootstrap 消息中注入。 - -#### D4-11:L2 引擎注入示例(张飞编码场景) - -Daemon spawn 张飞时拼装的完整 bootstrap 消息: - -``` -═══ 操作规范(executor.md 模板)═══ - -你是任务执行者。按以下步骤工作: - -## 1. 理解任务 -- 读黑板任务详情(blackboard.py read --task {task_id}) -- 理解 truths(可观测行为标准)、artifacts(必须产出)、constraints(约束) - -## 2. 声明范围 -- 写 scope_declaration(blackboard.py decide --task {task_id} --decider zhangfei-dev) -- 格式:{"decision": "我计划做什么", "rationale": "为什么", "artifacts": ["路径"]} - -## 3. 执行工作 -- 按你的专业能力完成任务 -- 发现风险 → blackboard.py observe --task {task_id} --severity warning -- 需要协作 → blackboard.py comment --task {task_id} @mention - -## 4. 提交产出 -- blackboard.py output --task {task_id} --summary "一句话" --content-path "文件路径" - -## 5. 写交接 -- blackboard.py comment --task {task_id} --author zhangfei-dev \ - --handoff '{"completed":"...", "artifacts":["..."]}' - -## 6. 通知引擎 -- 写 inbox 文件通知 Daemon 完成 - -═══ 项目背景(project_context.yaml)═══ - -项目:sanguo_quant_live -框架:vnpy -NAS 数据:/Volumes/stock/ -回测服务:http://192.168.2.154:8088 -编码规范:类型注解必须、禁止 any、match 现有风格 - -═══ 任务上下文(黑板动态读取)═══ - -任务 ID: task-001 -任务标题: 实现双均线策略 -truths: ["策略代码通过单元测试", "回测结果包含收益曲线"] -artifacts: ["task-001/strategy.py", "task-001/backtest_report.pdf"] -constraints: ["使用 vnpy 框架", "不超过 500 行"] -risk_level: standard - -═══ 前序信息(depends_on 产出)═══ - -[赵云 Handoff] 完成了分钟线数据下载,产出 task-000/data/ -数据路径: /Volumes/stock/min_data/IF888.csv - -═══ 可选参考 ═══ - -如果需要具体操作方法,用 read 加载: -- 编码:~/.openclaw/skills/coding-implementation/SKILL.md -- 回测:~/.openclaw/skills/quant-backtest/SKILL.md -``` - -**对比审查者(司马懿)收到的 bootstrap**: - -``` -═══ 操作规范(reviewer.md 模板)═══ - -你是质量审查者。按 Review Protocol 执行审查: - -## 审查步骤 -1. 预判:先不读产出,预测 3-5 个最可能出问题的点 -2. 验证:读实际产出(不是报告),逐条验证每个 truth -3. 多视角:从安全/新人/运维三个角度看产出 -4. 缺口分析:不只看"什么有问题",还看"什么缺失了" -5. 自审:给每个 finding 打 confidence,低 confidence 降级 - -## 写 Review -- blackboard.py review --task {task_id} --verdict [approved|rejected|needs_revision] ... - -[... 对抗性指令 ...] - -═══ Review Protocol(output_review.yaml)═══ - -[审查维度、方法、输出 Schema 等详细协议内容] - -═══ 任务上下文 ═══ -[同上] - -═══ Required Reading ═══ -- 执行者 scope_declaration: ... -- 产出物: task-001/strategy.py -- must_haves truths: [...] -``` - ---- - -## 7. 和现有设计的对齐检查 - -| 已有设计 | 课题4 补充 | 一致性 | -|---------|-----------|--------| -| §3.2 tasks 表 schema | D4-1 模板组件输出 → tasks 写入 | ✅ truths/artifacts/constraints/risk_level/depends_on 对齐 | -| §9.2 分级审查矩阵 | D4-4 复杂度→粒度控制 | ✅ risk_level 在创建时标注 | -| §4.2 Tick+Inbox | D4-1 Step 4 创建任务后 Daemon tick 检测 | ✅ 创建是事件源 | -| §9.4 Review Protocol Registry | D4-10 review_protocols/ 目录 | ✅ Daemon 构造审查者 bootstrap 时读取 | -| §9.9 Agent vs Subagent | D4-3 Plan Checker 走 Subagent | ✅ 无身份一次性检查 | -| Skill TODO 28项 | D4-10 prompt_templates/ 承载关键操作 | ✅ S-01~S-28 的关键操作写死在模板中 | - -## 8. 遗留 TODO - -| # | 待解决事项 | 归属 | 说明 | -|---|----------|------|------| -| ~~T4-1~~ | ~~template_components.yaml 完整定义~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T4-2~~ | ~~Plan Checker 的 plan_check.yaml 实现~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T4-3~~ | ~~prompt_templates/ 编写~~ | ~~Phase 1~~ | ✅ 方案已定,开发实现 | -| ~~T4-4~~ | ~~project_context.yaml 定义~~ | ~~Phase 1~~ | ✅ 方案已定,开发实现 | -| ~~T4-5~~ | ~~Daemon build_bootstrap() 拼装逻辑~~ | ~~Phase 1~~ | ✅ 方案已定,开发实现 | -| ~~T4-6~~ | ~~铁律 Hook 实现(L0)~~ | ~~Phase 1~~ | ✅ 方案已定,开发实现 | -| ~~T4-7~~ | ~~现有 Skill description 优化(加四要素)~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T4-8~~ | ~~Skill 诚实边界补充~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T4-9~~ | ~~Skill 测试框架~~ | ~~Phase 3~~ | ➡️ 移至工具链课题(开发工具链的一部分,不属于拆解机制) | -| ~~T4-10~~ | ~~Skill 进化/蒸馏机制~~ | ~~Phase 3~~ | ➡️ 合并进课题6(经验→Skill的进化是经验沉淀闭环,课题6已有两级蒸馏+Skill生命周期) | diff --git a/docs/design/topic4-skill-checklist-draft.md b/docs/design/topic4-skill-checklist-draft.md deleted file mode 100644 index c1a5951..0000000 --- a/docs/design/topic4-skill-checklist-draft.md +++ /dev/null @@ -1,96 +0,0 @@ -# 课题4 Skill 设计清单(草稿) - -**背景**:三个课题(1/2/3)产出了27项设计决策,其中大量内容需要 Agent 在 Skill 层面"知道怎么做"。当前 TODO 只列了4项(T1-5/6/7 + T2-8),远不够。 - -## 一、按角色分类的 Skill 需求 - -### 1. 所有 Agent 通用 Skill - -| # | Skill 内容 | 来源 | 说明 | -|---|-----------|------|------| -| S-01 | blackboard.py CLI 使用手册 | 课题1 §5.2 | 所有黑板操作的 CLI 命令、参数、Schema 校验说明 | -| S-02 | 读黑板 L1 消息 → 判断是否需要 L2/L3 | 课题2 §4.4 | Agent 收到 L1 后自主决定信息是否足够,不够就主动调 L2/L3 | -| S-03 | 写 Handoff Comment | 课题2 §5.1 | 结束前必须写结构化交接(强烈建议,不强制) | -| S-04 | 读 Handoff Comment | 课题2 §5.1 | 收到上一个 Agent 的 Handoff 后如何利用 | -| S-05 | 写 observation 的时机和格式 | 课题1 §4.7 | 工作中发现风险时主动写 observation + severity | -| S-06 | 写 decision 的时机和格式 | 课题1 §9.4 | 每个关键决策必须记录,哪怕是自己的决策 | -| S-07 | 写 output 的 Schema 约束 | 课题2 §3.7 | --output 必须有 summary + content_path,CLI 校验 | -| S-08 | 收到 Guardrail 打回时的处理 | 课题3 §9.3 | L1 机械失败→修改重提交;L2 语义问题→评估是否合理 | -| S-09 | @mention 的使用规范 | 课题1 §5.2 | 需要协作时 @mention + 附带上下文 | - -### 2. 执行者 Agent(张飞/关羽/赵云/姜维) - -| # | Skill 内容 | 来源 | 说明 | -|---|-----------|------|------| -| S-10 | claim 任务后写 scope_declaration | 课题1 §4.7 | "我计划做什么、产出什么",格式和内容要求 | -| S-11 | scope_declaration 的格式 | 课题1 T1-5 | truths 字段格式、声明方式 | -| S-12 | 收到 review needs_revision 时的处理 | 课题3 §9.5 | 读 issues → 逐条 ACCEPT/REJECT/PARTIAL,写 comment 回应 | -| S-13 | 收到 Guardrail reject 的处理 | 课题3 §9.8 | 理解 assert 失败原因,修改重提交 | -| S-14 | must_haves 三件套理解 | 课题1 §9 | truths/artifacts/constraints 的含义和自检方法 | - -### 3. 审查者 Agent(司马懿 + 挑战者池) - -| # | Skill 内容 | 来源 | 说明 | -|---|-----------|------|------| -| S-15 | 收到 Review Protocol 后的审查流程 | 课题3 §9.4 | 五阶段 Investigation Protocol 的执行方法 | -| S-16 | 多视角审查方法 | 课题3 §9.4 | 代码视角(安全/新人/运维)、方案视角(执行者/利益相关者/怀疑论者) | -| S-17 | 写 review 的 Schema 约束 | 课题3 §9.6 | verdict 必须是枚举,issues 必须有 evidence | -| S-18 | 信心度自评方法 | 课题3 §9.6 | confidence 打分标准,< 0.7 意味着什么 | -| S-19 | 收到反驳(REJECT)后的处理 | 课题3 §9.5 | 评估执行者的反驳是否合理,决定坚持还是让步 | -| S-20 | plan_review 协议 | 课题3 §9.4 | 假设提取+评级、pre-mortem、依赖审计、歧义扫描 | -| S-21 | output_review 协议 | 课题3 §9.4 | 需求追踪、scope 对齐、正确性验证、缺口分析 | -| S-22 | analysis_review 协议 | 课题3 §9.4 | 逻辑跳跃、无支撑结论、数据来源可靠性 | - -### 4. 协调者 Agent(庞统) - -| # | Skill 内容 | 来源 | 说明 | -|---|-----------|------|------| -| S-23 | 创建任务时的 truths/artifacts/constraints 定义 | 课题1 §9 | 用户视角的可观测行为、必须存在的文件、继承的约束 | -| S-24 | 风险等级自动判断 | 课题3 §9.2 | task_type→risk_level 的映射规则 | -| S-25 | 挑战者选择策略 | 课题3 §9.10 | 按任务类型选挑战者(编码→司马懿、风控→关羽、数据→赵云、部署→姜维) | -| S-26 | 对抗辩论裁决方法 | 课题3 §9.10 | 综合正方反方观点做最终判断 | -| S-27 | escalated 任务的用户沟通 | 课题3 §9.7 | 超轮次升级时如何向用户呈现争议和选项 | -| S-28 | confidence 低时的升级判断 | 课题3 T3-5 | confidence < 0.7 时升级策略 | -| S-29 | 任务拆解方法 | 课题1 §5.1 | 复杂任务如何拆成子任务,依赖如何声明 | -| S-30 | L1 消息构建逻辑 | 课题2 §4.4 | 给 Agent spawn 时的 bootstrap 消息拼装 | - -## 二、按阶段分类 - -### Phase 1 必须有(Agent 能基本工作) - -- S-01 blackboard.py CLI 使用手册 -- S-03 写 Handoff Comment -- S-10 claim 后写 scope_declaration -- S-14 must_haves 理解 -- S-23 创建任务(庞统) -- S-24 风险等级判断(庞统) -- S-30 L1 消息构建(庞统/Daemon) - -### Phase 2 必须有(审查体系生效) - -- S-02 L2/L3 按需读取 -- S-04 读 Handoff Comment -- S-05/06 observation/decision 写入 -- S-07 output Schema 约束 -- S-12 收到 review 的处理(反驳权) -- S-15~22 审查者全套 Skill -- S-25 挑战者选择(庞统) -- S-26 对抗辩论裁决(庞统) - -### Phase 3 可选(高级功能) - -- S-16 多视角审查深化 -- S-20~22 三种审查协议细化 -- S-27 escalated 用户沟通 -- S-28 confidence 升级策略 -- S-29 任务拆解方法深化 - -## 三、和现有 TODO 的对齐 - -课题4 的 Skill 设计需要覆盖以上 30 项。现有的 T1-5/6/7 和 T2-8 只是其中4项。 - -建议:课题4 的正式设计把这 30 项整理成 Skill 体系文档,明确: -1. 哪些是 Skill 文件(Agent 读的 Markdown) -2. 哪些是 Protocol 文件(Daemon 注入的 YAML) -3. 哪些是 CLI 帮助信息(blackboard.py --help) -4. 三者的关系和优先级 diff --git a/docs/design/topic6-experience-loop-proposal.md b/docs/design/topic6-experience-loop-proposal.md deleted file mode 100644 index 3557218..0000000 --- a/docs/design/topic6-experience-loop-proposal.md +++ /dev/null @@ -1,337 +0,0 @@ -# 课题6:经验沉淀闭环设计方案 - -**版本**: v1.0 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-15 -**评审**: 待司马懿评审 -**状态**: 待评审 - ---- - -## 1. 设计目标 - -moziplus 的 Agent 在工作中产出了大量经验(决策、错误修正、最佳实践),但当前没有机制沉淀这些经验,导致每次任务都在"重新发明轮子"。本课题设计一个闭环系统,让经验自动采集、蒸馏、验证、应用、进化。 - -## 2. 核心洞察 - -> **问题**:v1.0 运行两个月,张飞修了 7 次类似的 bug,赵云踩了 3 次相同的数据格式问题。每次都是"重新发现",没有变成组织记忆。 -> -> **根因**:经验只存在于 Agent 的 session 上下文中,session 结束就消失了。MEMORY.md 是手动维护的,不够系统化。 -> -> **优秀实践**: -> - **Claude Code Memory**:四类记忆法,但只记录不蒸馏 -> - **Hermes**:闭环学习(记忆→Skill→搜索→改进),最完整 -> - **nuwa-skill**:五阶段蒸馏管线,蒸馏的是思维方式不是行为模仿 -> - **GSD**:plan template 可复用,Spec-driven 经验 - -**结论**:需要 DISCOVER → DISTILL → VERIFY → APPLY → IMPROVE 五阶段闭环,但必须实用——不搞复杂的 AI 蒸馏管线,从最简单的开始。 - -## 3. 设计决策 - -### D6-1:经验的三种载体 - -> **参考实践**: -> - **Claude Code**:MEMORY.md(Markdown,按 user/feedback/project/reference 分类) -> - **Hermes**:SKILL.md(步骤化菜谱)+ FTS5 索引 -> - **nuwa-skill**:SKILL.md(心智操作系统:心智模型 + 决策启发式 + 反模式 + 诚实边界) -> - **共享意识研究**:Memory → Skills → Rules 三层压缩 - -经验不是一种东西,有三种形态,载体不同: - -| 载体 | 内容 | 格式 | 生命周期 | 例子 | -|------|------|------|---------|------| -| **Rule(规则)** | 确定性的操作规范 | guardrails.yaml / prompt_templates/ | 长期稳定 | "所有 output 必须通过 JSON Schema 校验" | -| **Skill(技能)** | 半确定性的操作方法 | SKILL.md(Markdown) | 中期演进 | "如何正确下载分钟线数据" | -| **Memory(记忆)** | 非确定性的经验片段 | experiences 表 / MEMORY.md | 短期,可蒸馏为 Skill/Rule | "vnpy 的 load_bar 方法在 Python 3.12 有兼容问题" | - -**进化方向**:Memory → Skill → Rule。频繁出现的 Memory 被蒸馏为 Skill,高频引用的 Skill 可固化到 prompt_templates(Rule)。 - -``` -Memory(发现)→ Skill(方法)→ Rule(铁律) - ↓ 蒸馏 ↓ 固化 ↓ 强制 -非确定性 半确定性 确定性 -短命 中等 长命 -``` - -### D6-2:DISCOVER(信息发现) - -> **参考实践**: -> - **Hermes**:Agent-curated Memory + 任务执行过程自动记录 -> - **GSD**:verify phase 的产出作为经验来源 - -**信息源**(按价值排序): - -| 信息源 | 采集方式 | 价值 | 难度 | -|-------|---------|------|------| -| 错误与修正 | Guardrail 拦截 + Retry 记录 + 审查打回 | ⭐⭐⭐⭐⭐ | 低 | -| Comments(Handoff + 讨论) | 黑板 comments 表,含"为什么这么决策"的上下文 | ⭐⭐⭐⭐⭐ | 低(已有) | -| 黑板 decisions | 自动记录 Agent 决策过程和理由 | ⭐⭐⭐⭐ | 低(已有) | -| 审查记录 | reviews 表的 findings + verdict | ⭐⭐⭐⭐ | 低(已有) | -| Agent observations | 黑板 observations(风险、协作、异常) | ⭐⭐⭐ | 低(已有) | -| 节点 output | 任务完成后的产出文件 | ⭐⭐⭐ | 低(已有) | -| 反驳记录 | ACCEPT/REJECT/PARTIAL 的理由 | ⭐⭐⭐ | 中(Phase 2) | - -> **Comments 的价值说明**:执行者的 Handoff comment 包含"我做了什么+为什么",审查者的 comment 包含"问题在哪里+建议怎么改",讨论过程的 comment 包含"为什么选 A 不选 B"。这些是蒸馏经验的关键上下文,价值不低于 decisions。 - -**采集机制**:不新增采集逻辑。所有信息源都已在课题1-4的设计中存在(黑板 decisions、reviews 表、observations、Guardrail 记录)。DISCOVER 阶段只是**读取已有数据**。 - -### D6-3:DISTILL(蒸馏)——两级蒸馏 - -> **参考实践**: -> - **nuwa-skill**:五阶段蒸馏(发现→采集→提取→验证→精炼),蒸馏的是思维方式 -> - **Hermes**:复杂任务完成 → 自动创建 SKILL.md -> - **GSD**:plan template 复用 - -不做 nuwa-skill 那样复杂的 AI 蒸馏管线。**两级蒸馏,从简单开始**: - -#### 一级蒸馏(实时,每个任务完成后自动) - -**触发**:任务状态变为 completed 或 failed 时。 - -**执行者**:庞统(在创建下一个任务前,花 ~30s 蒸馏上一个任务的经验)。 - -**过程**: -1. 读黑板上该任务的所有 comments(Handoff + 讨论)、decisions、observations、reviews -2. 提取 1-3 条关键经验("什么有效""什么无效""下次注意什么") -3. 写入黑板 experiences 表 - -**产出格式**: -```json -{ - "experience_id": "exp-001", - "source": "task_completion", - "task_id": "task-006", - "tags": ["vnpy", "strategy-coding", "error-handling"], - "summary": "vnpy 的 load_bar() 在 Python 3.12 下需要显式指定 end=None,否则只返回最后一根 bar", - "category": "pitfall", - "confidence": 0.9 -} -``` - -**Token 成本**:~500 token/任务(读黑板数据 + 生成摘要)。 - -#### 二级蒸馏(周期性,按需触发) - -**触发条件**(满足任一): -- 同类 experience 累积 ≥ 5 条(按 tag 聚合) -- 庞统发现重复问题出现 3 次 -- 用户要求"把最近的经验整理一下" - -**执行者**:庞统(Subagent 模式,独立 session)。 - -**过程**: -1. 聚合同 tag 的所有 experiences -2. 提取模式:"遇到 X 情况,应该 Y"(决策启发式) -3. 提取反模式:"遇到 X 情况,千万不要 Z"(避坑指南) -4. 标注适用范围(诚实边界) -5. 生成 SKILL.md 草稿 - -**产出**:SKILL.md 写入 `skills/` 目录(L3 被动参考层)。 - -**蒸馏格式**(简化版 nuwa-skill): -```markdown -# Skill: vnpy 分钟线数据加载 - -## 决策启发式 -- 加载分钟线数据时,优先用 load_bar(end=None) 显式指定全量 -- 数据文件超过 1GB 时,用 chunksize 分批读取 - -## 反模式 -- ❌ 不指定 end 参数 → 只返回最后一根 bar -- ❌ 全量读取大文件 → 内存溢出 - -## 诚实边界 -- 仅适用于 vnpy 的数据加载 API -- 不覆盖数据清洗逻辑 -``` - -### D6-4:VERIFY(验证) - -> **参考实践**: -> - **nuwa-skill**:3 个已知问题测试 + 1 个未知问题测试 + 双 Agent 精炼 -> - **Hermes**:幻觉门控——验证产出是否真实存在 - -**一级蒸馏(Memory)的验证**:轻量级。 -- 庞统自己 review(confidence 打分) -- 不需要独立验证环节 - -**二级蒸馏(Skill)的验证**:标准验证。 - -| 验证步骤 | 方法 | 通过标准 | -|---------|------|---------| -| 格式验证 | JSON Schema / Markdown 结构检查 | 结构完整 | -| 内容验证 | 庞统 review | 逻辑合理,无幻觉 | -| 实用性验证 | 标记为 "draft",下次任务引用后根据效果升级 | 被引用 ≥ 2 次且采纳率 > 50% | - -**Skill 生命周期**: -``` -draft → active → deprecated - ↑ │ - └── improved -``` - -- draft → active:被引用 ≥ 2 次且采纳率 > 50% -- active → deprecated:30 天无引用 或 采纳率 < 20% -- active → improved:发现更好的做法时更新 - -### D6-5:APPLY(应用)——经验的反哺机制 - -> **参考实践**: -> - **Claude Code Memory**:每次 session 启动自动加载 -> - **Hermes**:FTS5 跨 session 搜索 + LLM 摘要 + periodic nudge -> - **课题4 D4-6**:L2 引擎注入 + L3 被动参考 - -经验的反哺走课题4定义的四层架构: - -| 载体 | 反哺方式 | 层次 | 机制 | -|------|---------|------|------| -| **Rule** | 写入 guardrails.yaml / prompt_templates/ | L2 引擎注入 | Daemon 自动执行 | -| **Skill** | 写入 skills/ 目录 | L3 被动参考 | Agent 按需加载 | -| **Memory** | 写入 experiences 表 | L2 引擎注入(相关 Memory) | Daemon 按任务 tag 注入 | - -**Memory 的注入逻辑**(Daemon build_bootstrap 时): -```python -def get_relevant_experiences(task_tags, limit=3): - """按任务 tag 检索相关经验,注入 bootstrap 消息""" - experiences = query_experiences(tags=task_tags, status="active", limit=limit) - if experiences: - return format_as_context(experiences) # "注意:之前遇到过类似问题..." - return None -``` - -**注入位置**:在 L2 bootstrap 消息末尾,作为"历史经验提醒": -``` -═══ 历史经验提醒 ═══ -[exp-003] vnpy 分钟线加载:需要显式指定 end=None -[exp-007] 回测报告:确保包含收益曲线和最大回撤 -``` - -**Token 成本**:每条经验 ~50 token,最多 3 条 = ~150 token。 - -### D6-6:IMPROVE(进化) - -> **参考实践**: -> - **Hermes**:Skill 使用时自动改进 -> - **nuwa-skill**:Darwin.skill 八维评估 + 棘轮机制(只进不退) - -**进化机制**: - -| 机制 | 触发 | 执行者 | -|------|------|--------| -| **使用追踪** | Agent 在 Handoff 中标注"参考了 exp-003" | 执行者 | -| **效果对比** | 月度统计:采纳经验 vs 未采纳的任务成功率 | 庞统 | -| **淘汰** | 30 天无引用 → 标记 deprecated | Daemon 定期清理 | -| **更新** | 发现经验过时 → 庞统更新内容 | 庞统 | - -**棘轮原则**:经验只能改进不能退化。如果更新后效果更差,回退到上一个版本。 - -### D6-7:experiences 表设计 - -```sql -CREATE TABLE experiences ( - experience_id TEXT PRIMARY KEY, - source TEXT NOT NULL, -- task_completion / error_correction / review_finding / manual - task_id TEXT, -- 来源任务(FK → tasks) - summary TEXT NOT NULL, -- 经验摘要(200字以内) - category TEXT NOT NULL, -- pitfall / best_practice / pattern / anti_pattern - confidence REAL DEFAULT 0.8, -- 0-1 - status TEXT DEFAULT 'active', -- draft / active / deprecated - skill_id TEXT, -- 关联的 Skill(二级蒸馏后填入) - usage_count INTEGER DEFAULT 0, -- 被引用次数 - last_used_at TEXT, -- 最后引用时间 - created_at TEXT NOT NULL, - created_by TEXT NOT NULL, -- 庞统 / 用户 - updated_at TEXT, - deprecated_reason TEXT -); - --- tags 多对多关联表(替代 JSON array,走 B-tree 索引) -CREATE TABLE experience_tags ( - experience_id TEXT NOT NULL REFERENCES experiences(experience_id), - tag TEXT NOT NULL, - PRIMARY KEY (experience_id, tag) -); -CREATE INDEX idx_exptags_tag ON experience_tags(tag); -``` - -**查询示例**(D6-5 注入逻辑): -```sql -SELECT DISTINCT e.* FROM experiences e -JOIN experience_tags et ON e.experience_id = et.experience_id -WHERE et.tag IN ('vnpy', 'strategy-coding') - AND e.status = 'active' -LIMIT 3; -``` - -### D6-8:和课题4四层架构的关系 - -``` -L0 铁律 ──── 最稳定的 Rule(IR-1~IR-5) - ↑ 极少固化(需要用户确认) - -L2 引擎注入 ─ prompt_templates/ + 相关 Memory - ↑ 庞统手动更新模板 - ↑ Daemon 自动注入相关 experience - -L3 被动参考 ─ skills/ 目录(二级蒸馏产出) - ↑ Agent 按需加载 - -黑板 experiences 表 ─ 原始 Memory - ↑ 一级蒸馏写入 - ↑ Daemon 按任务 tag 检索注入 -``` - -**进化路径**: -``` -experiences 表(Memory) - ↓ 累积 ≥ 5 条同类 -skills/ 目录(Skill) - ↓ 引用 ≥ 2 次且采纳 > 50% -prompt_templates/(Rule/L2 注入) - ↓ 用户确认 -guardrails.yaml / IR 铁律(L0) -``` - -### D6-9:实施优先级 - -| 优先级 | 内容 | Phase | 理由 | -|--------|------|-------|------| -| P0 | experiences 表创建 | Phase 1 | 基础设施 | -| P0 | 一级蒸馏(庞统任务完成后自动提取) | Phase 1 | 最小闭环 | -| P1 | Memory 注入 build_bootstrap() | Phase 2 | 经验反哺 | -| P1 | 使用追踪(Handoff 标注引用) | Phase 2 | 效果度量 | -| P2 | 二级蒸馏(Skill 生成) | Phase 3 | 需要足够多的 Memory 积累 | -| P2 | Skill 生命周期管理 | Phase 3 | draft/active/deprecated | -| P3 | 效果对比 + 棘轮机制 | Phase 4 | 需要长期数据 | - -**最小闭环(Phase 1)**:experiences 表 + 一级蒸馏 + Dashboard 可查看 = 从 0 到 1。 - ---- - -## 4. 和现有设计的对齐检查 - -| 已有设计 | 课题6 补充 | 一致性 | -|---------|-----------|--------| -| §3.2 SQLite Schema | D6-7 experiences 表加入黑板的 6 张表 | ✅ 同一数据库 | -| §4.4 Agent Spawn 上下文 | D6-5 Memory 注入 build_bootstrap() | ✅ 在 L2 末尾追加 | -| §4.7 Guardrail 体系 | D6-1 Rule 载体 = guardrails.yaml | ✅ 经验固化的终点 | -| §5.1 Agent 工作流程 | D6-2 信息源 = 黑板已有数据 | ✅ 不新增采集逻辑 | -| §9.4 Review Protocol | D6-2 审查记录作为经验源 | ✅ reviews 表数据 | -| 课题4 D4-6 四层架构 | D6-8 经验载体与四层架构对齐 | ✅ Memory→Skill→Rule 路径清晰 | -| 课题4 D4-10 目录结构 | skills/ 目录承载二级蒸馏产出 | ✅ L3 被动参考层 | -| MEMORY.md | D6-1 Memory 载体包含但超越 MEMORY.md | ✅ 系统化的 experiences 表替代手动维护 | - -## 5. 遗留 TODO - -| # | 待解决事项 | 归属 | 说明 | -|---|----------|------|------| -| ~~T6-1~~ | ~~experiences 表 tags 索引实现~~ | ~~Phase 1~~ | ✅ 已解决:使用 experience_tags 关联表,B-tree 索引 | -| ~~T6-2~~ | ~~一级蒸馏 prompt 模板~~ | ~~Phase 1~~ | ✅ 方案已定,开发实现 | -| ~~T6-3~~ | ~~build_bootstrap() 经验注入逻辑~~ | ~~Phase 2~~ | ✅ 方案已定,开发实现 | -| ~~T6-4~~ | ~~二级蒸馏触发条件实现~~ | ~~Phase 3~~ | ✅ 方案已定,开发实现 | -| ~~T6-5~~ | ~~Skill draft → active 的自动化验证~~ | ~~Phase 3~~ | ✅ 方案已定,开发实现 | -| ~~T6-6~~ | ~~经验淘汰定时任务~~ | ~~Phase 3~~ | ✅ 方案已定,开发实现 | -| ~~T6-7~~ | ~~效果对比报表(Dashboard 展示)~~ | ~~Phase 4~~ | ✅ 方案已定,开发实现 | -| ~~T4-10~~ | ~~Skill 进化/蒸馏机制~~ | ~~Phase 3~~ | ✅ 合并进课题6,已被两级蒸馏+Skill生命周期覆盖 | - -> **课题6结题**:全部方案已定,无设计遗留。 diff --git a/docs/design/topic7-9-interaction-dashboard-proposal.md b/docs/design/topic7-9-interaction-dashboard-proposal.md deleted file mode 100644 index c2e6e1c..0000000 --- a/docs/design/topic7-9-interaction-dashboard-proposal.md +++ /dev/null @@ -1,247 +0,0 @@ -# 课题7+9 设计方案:AI Native 人机交互 + Dashboard - -> **日期**: 2026-05-16 -> **作者**: 庞统(副军师)🐦 -> **状态**: 设计完成,可结题 -> **前置**: 课题1-4、课题6、课题11 已完成设计 - ---- - -## 一、课题7:AI Native 人机交互 - -### 1. 核心问题 - -v1.0 的交互是"用户点按钮触发操作"——传统 Web 应用。v2.0 要实现 AI Native:**AI 主动推送、用户自然语言对话、关键决策拉人、其余自主完成**。 - -### 2. 四种交互模式 - -| 模式 | 描述 | 场景 | 人的参与密度 | -|------|------|------|------------| -| **沉浸观察** | 用户看 Dashboard 实时状态,不干预 | 任务自主执行中 | 低 | -| **轻触确认** | AI 做完关键步骤,推一个确认卡片给用户 | Checkpoint 验证、风控审批 | 中 | -| **即时对话** | 用户主动找 AI 聊(或 AI 主动找人聊) | 需求变更、异常处理、方向调整 | 高 | -| **被动通知** | AI 完成后推送结果摘要 | 任务完成、日报、异常告警 | 低 | - -### 3. 推送级别分级 - -| 级别 | 含义 | 推送方式 | -|------|------|---------| -| 🔴 Critical | 必须立即处理 | 即时推送(webchat/Signal/Telegram) | -| 🟡 Warning | 需要关注,可稍后 | Dashboard 高亮 + 可选推送 | -| 🟢 Info | 一般进展通知 | Dashboard 记录 | -| 🔵 Silent | 仅记录 | 日志/历史 | - -### 4. 三层信息架构 - -| 层级 | 名称 | 内容 | 场景 | -|------|------|------|------| -| L1 | 一眼全局 | 活跃项目数、进行中任务数、最近告警 | 扫一眼知道系统在干什么 | -| L2 | 看板视图 | 任务卡片列表、状态、负责人、进度 | 日常浏览和操作 | -| L3 | 详情面板 | 单任务完整上下文(对话、产出、评审、经验) | 深入了解某个任务 | - -### 5. 双入口对等 - -- **Agent 对话**(webchat/Signal)= 主交互入口 -- **Dashboard**(Web UI)= 可视化入口 -- 共享同一套黑板数据,操作等效 -- 可以在 Dashboard 点按钮,也可以直接跟庞统说"暂停任务 xxx" - -### 6. Checkpoint 交互规范 - -三种 Checkpoint(来自 M3 设计,课题3 挑战/评审体系): - -| Checkpoint 类型 | 颜色 | 交互 | 场景 | -|----------------|------|------|------| -| 🔍 验证 Checkpoint | 蓝色 #6a9eff | 自动核验 + 人工核验 + 双按钮审批 | 产出物御览确认 | -| 🎯 决策 Checkpoint | 紫色 #818cf8 | 三方案并排对比 + 利弊展示 + 推荐标签 + 御批备注 | 方向选择 | -| 🔧 执行 Checkpoint | 橙色 #f59e0b | 分步打勾 + 命令行高亮 + 进度条 + 验证确认 | 分步执行确认 | - -### 7. 异常通知触发条件 - -| 触发条件 | 推送级别 | 通知内容 | -|---------|---------|---------| -| Agent 执行失败 | 🔴 | 任务ID + Agent + 错误摘要 + retry 按钮 | -| 任务超时(超过 eta) | 🟡 | 任务ID + 已耗时 + 预估剩余 | -| 重试次数达到上限 | 🔴 | 任务ID + 累计失败次数 + 建议操作 | -| Daemon 健康异常 | 🔴 | 异常类型 + 影响范围 | -| 任务完成 | 🟢 | 任务ID + 产出摘要 + 产出链接 | -| 经验提取完成 | 🔵 | 经验标题 + 关联任务数 | - -### 8. AI Briefing 格式 - -**日报**(每天 22:00 自动生成,🟢 级别推送): -``` -📊 今日战报 (2026-05-16) -━━━━━━━━━━━━━━━━━━━━ -📋 完成任务:3 个(动量因子策略、数据清洗、代码审查) -🔄 进行中:2 个(配对交易研究、风控规则开发) -⚠️ 需关注:1 个(task-012 超时,预计 30min) -💰 今日 Token 消耗:127K(预算 200K,剩余 36.5%) -🧠 新增经验:2 条(pytest 参数优化、SQLite 并发保护) -``` - -**周报**(每周日 22:00,🟢 级别): -- 本周完成任务数/平均耗时 -- Agent 利用率(谁最忙谁最闲) -- Token 消耗趋势 -- 经验沉淀汇总 -- 下周建议 - ---- - -## 二、课题9:Dashboard 设计 - -### 1. 核心原则 - -- **v1.0 已有的 11 个 Tab 页全部保留** -- **只有"任务看板"需要重新设计和实现**(配合 v2.0 黑板架构) -- 其他 10 个 Tab 功能基本不变,适配新的 Daemon API 即可 - -### 2. v1.0 已有 Tab 页(保留,不重新设计) - -| Tab | 组件 | 功能 | v2.0 变更 | -|-----|------|------|----------| -| 🏛️ 军议大厅 | `CourtDiscussion` | Agent 间对话/讨论 | 适配新 API | -| 🔌 编排调度 | `MonitorPanel` | Daemon/编排状态监控 | 适配新 API | -| 🤺 将军总览 | `OfficialPanel` | Agent 列表/状态/配置 | 适配新 API | -| 🤖 模型配置 | `ModelConfig` | LLM 模型选择/参数 | 适配新 API | -| 🎯 技能配置 | `SkillsConfig` | Skill 注册/启用/禁用 | 适配新 API | -| 🔭 传令巡哨 | `SessionsPanel` | Agent session 列表/状态 | 适配新 API | -| 💰 花费总览 | `UsagePanel` | Token 消耗/预算/趋势 | 适配新 API | -| 📜 奏折阁 | `MemorialPanel` | 已完成/已取消任务归档 | 适配新 API | -| 📋 任务模板 | `TemplatePanel` | 任务模板管理 | 适配新 API | -| ⚙️ 系统设置 | `SettingsPanel` | Daemon 配置/项目管理 | 适配新 API + 课题11 项目管理 | - -### 3. 需要重新设计的 Tab - -#### 📜 任务看板(EdictBoard)— 重新设计 + 重新实现 - -**v1.0 现状**:基于 moziplus v1 DAG 架构的任务列表,状态是 v1 的状态机(pending/planning/executing/completed/failed/cancelled)。 - -**v2.0 重设计**: - -##### 3.1 任务卡片 - -``` -┌─────────────────────────────────────────────┐ -│ 📜 task-013 动量因子策略回测 │ -│ 项目: quant-momentum | 状态: executing 🔄 │ -│ 负责: 张飞⚔️ | 进度: ████░░ 67% │ -│ ⏱️ 已耗时 12min | 📊 3/5 节点完成 │ -│ ⚠️ 1个告警 │ -│ ─────────────────────────────────────────── │ -│ [暂停] [取消] [查看详情] │ -└─────────────────────────────────────────────┘ -``` - -##### 3.2 任务详情面板(TaskModal v2) - -点击任务卡片展开详情,包含: - -| 区域 | 内容 | -|------|------| -| **基本信息** | 标题、需求描述、项目、创建时间 | -| **状态流转** | 状态按钮(基于课题3状态机守卫 ACTION_GUARDS) | -| **执行图** | 任务拆解后的节点图(节点状态 + Agent + 产出) | -| **Checkpoint** | 三种 Checkpoint 面板(验证/决策/执行) | -| **Flow Log** | 执行日志流(时间线格式) | -| **产出物** | 产出文件预览/下载(来自 ArtifactPanel) | -| **评审记录** | 评审意见 + 评审结果(来自 reviews 表) | -| **关联经验** | 该任务沉淀的经验(来自 experiences 表) | - -##### 3.3 项目切换器(配合课题11) - -Header 区域新增项目下拉: -``` -三国量化 · 编排台 | [▼ 动量因子策略] | ✅ 同步正常 | 3 个任务 -``` - -- 下拉列出所有 active 项目 -- 切换后任务看板只显示当前项目的任务 -- "全部项目"选项显示所有项目的任务(带项目标签) - -##### 3.4 推送通知中心 - -Header 区域新增通知铃铛: -``` -🔔 (3) → 点击展开通知面板 -``` - -通知面板: -- 按推送级别分组(🔴 > 🟡 > 🟢 > 🔵) -- 每条通知:时间 + 级别图标 + 内容摘要 + 关联任务链接 -- 🔴 通知可展开操作按钮(确认/忽略/查看详情) -- 支持标记已读/全部已读 - -##### 3.5 AI Briefing 页面(新增 Tab) - -新增第 12 个 Tab:**📊 战报简报** - -- 日报/周报自动生成(§7.8 定义的格式) -- 趋势图表(完成任务数、Token 消耗、Agent 利用率) -- 经验沉淀摘要 -- 下周建议(AI 生成) - -### 4. 技术栈(不变) - -- React + Vite + TypeScript + Zustand -- CSS 变量体系:`--bg`, `--fg`, `--muted`, `--acc`, `--line`, `--panel`, `--panel2` -- API 层:`api.ts`,对接 Daemon HTTP API -- 构建产物:`dashboard/dist/`,uvicorn 8082 端口直接服务 -- UI 参考:OpenClaw Edict UI + Control Center - -### 5. 实时推送机制 - -**选择 SSE(Server-Sent Events)**: - -- Daemon 端新增 `/events` SSE 端点 -- 前端 `EventSource` 监听,收到事件后更新 Zustand store -- 事件类型:`task.status_changed`、`checkpoint.created`、`notification.pushed`、`agent.state_changed` -- 降级方案:SSE 连接失败时退回轮询(当前 v1.0 已有的 5s 轮询) - -理由: -- 比 WebSocket 简单(单向推送够用) -- 比 pure 轮询实时性好 -- HTTP 兼容性好(代理/防火墙不拦) - -### 6. 开发清单 - -| # | 任务 | 优先级 | 说明 | -|---|------|--------|------| -| 1 | Daemon `/events` SSE 端点 | P0 | 实时推送基础 | -| 2 | 任务看板 v2(EdictBoard 重写) | P0 | 核心页面 | -| 3 | TaskModal v2(详情面板) | P0 | 含 Checkpoint/Artifact/Review 集成 | -| 4 | 项目切换器 | P1 | 课题11 前端对接 | -| 5 | 推送通知中心 | P1 | Header 铃铛 + 通知面板 | -| 6 | AI Briefing 页面 | P2 | 日报/周报自动生成 | -| 7 | 其他 10 个 Tab 适配新 API | P1 | 主要是 api.ts 对接 | -| 8 | SSE 降级轮询 | P1 | 连接失败时自动回退 | - ---- - -## 三、与其他课题的关系 - -| 课题 | 关系 | -|------|------| -| 课题1-2(执行模型+事件驱动) | Dashboard 的数据来源 + SSE 事件源 | -| 课题3(挑战/评审) | Checkpoint 面板的交互 + 状态流转按钮 | -| 课题4(拆解+上下文) | TaskModal 中的执行图展示 | -| 课题6(经验沉淀) | AI Briefing 的内容来源 + TaskModal 关联经验 | -| 课题11(多项目) | 项目切换器 + 任务卡片项目标签 | -| M3(Checkpoint+Artifact) | Checkpoint 三种组件 + ArtifactPanel 集成 | - ---- - -## 四、结题说明 - -课题7+9 的设计决策在 v2.6.6 已确定(四种交互模式、推送分级、信息架构、5页结构),本次方案文档补充了: - -- ✅ Checkpoint 交互规范(三种类型 + 颜色 + 交互方式) -- ✅ 异常通知触发条件和内容模板 -- ✅ AI Briefing 格式(日报/周报模板) -- ✅ v1.0 已有 11 个 Tab 页完整清单和保留策略 -- ✅ 任务看板 v2 重新设计(卡片/详情/项目切换/通知中心) -- ✅ 实时推送机制选型(SSE + 降级轮询) -- ✅ 开发清单(8 项,按优先级排序) - -**剩余工作属于编码范畴**,设计层面可结题。 diff --git a/docs/design/topic7-interaction-dashboard-proposal.md b/docs/design/topic7-interaction-dashboard-proposal.md deleted file mode 100644 index 9006f06..0000000 --- a/docs/design/topic7-interaction-dashboard-proposal.md +++ /dev/null @@ -1,456 +0,0 @@ -# 课题7+9:AI Native 人机交互 + Dashboard 设计方案 - -**版本**: v1.0 -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-15 -**评审**: 待司马懿评审 -**状态**: 待评审 - ---- - -## Part A:AI Native 人机交互(课题7) - -### 1. 设计目标 - -定义 moziplus 中人与 AI Agent 的交互范式——不是给 AI 团队做 ERP 界面,而是让 AI 主动、精准、不扰民地和用户协作。 - -### 2. 核心洞察 - -> **传统模式**:用户主动查 Dashboard、主动问进度、主动分配任务。 -> **AI Native 模式**:AI 主动推送关键信息,用户只在需要决策时轻触确认。 - -参考六种 AI Native 交互范式(调研报告 §3.2): - -| # | 范式 | 代表 | moziplus 适用度 | -|---|------|------|----------------| -| 1 | REPL 对话 | Claude Code CLI | ⭐⭐⭐ WebChat 已有 | -| 2 | Canvas/Artifacts | ChatGPT Canvas | ⭐⭐⭐⭐ 产出物交互 | -| 3 | 沉浸式观察 | Devin | ⭐⭐⭐ 长任务监控 | -| 4 | Command Palette | Raycast | ⭐⭐⭐⭐ 快捷操作 | -| 5 | Contextual Inline | Cursor | ⭐⭐ 不适用(非IDE) | -| 6 | Event-Driven Notification | Linear | ⭐⭐⭐⭐⭐ 核心模式 | - -**结论**:moziplus 的核心交互范式是 **Event-Driven Notification + 轻触确认**,辅助以 WebChat 深度对话和 Dashboard 全局观察。 - -### 3. 设计决策 - -#### D7-1:四种交互模式 - -| 模式 | 触发方式 | 用户注意力 | AI 主动性 | 适用场景 | -|------|---------|-----------|----------|---------| -| **沉浸观察** (Dashboard) | 用户主动打开 | 高 | 低 | 全局监控、任务详情 | -| **轻触确认** (Approval) | AI 主动推送 | 中 | 高 | 方案审批、风控告警 | -| **即时对话** (WebChat) | 双向触发 | 中高 | 中 | 复杂指令、探索性需求 | -| **被动通知** (Notify) | AI 主动推送 | 低 | 高 | 任务完成、阶段转换 | - -> **参考实践**: -> - **Cursor**:4 种交互粒度(Tab/Cmd+K/Cmd+L/Cmd+I),从被动到主动覆盖所有场景 -> - **Devin**:沉浸式观察,用户只在关键节点介入 -> - **Linear**:精准通知,不噪音 - -#### D7-2:推送级别分级 - -| 推送级别 | 示例 | 默认行为 | 用户可配置 | -|---------|------|---------|----------| -| 🔴 **必须确认** | 方案审批、风控告警、预算超限 | 始终推送 + 阻塞等待 | ✗ 不可关闭 | -| 🟡 **重要进展** | 任务完成、阶段转换、产出交付 | 默认推送 | 可降级为摘要 | -| 🟢 **一般信息** | Agent 启动、中间步骤、心跳正常 | 默认不推送 | 可开启 | -| 🔵 **定时摘要** | 每日工作汇总、Token 消耗报告 | 默认开启 | 频率可调 | - -**推送频率控制**: -- 任务执行中:每 5 分钟一次进展摘要(不是每步都推) -- 任务完成/阻塞/异常:立即推送 -- 正常心跳:不推送,只在 Dashboard 上更新 - -> **参考实践**: -> - **Linear**:关键变更推送 + 精准通知,不噪音 -> - **课题4 D4-5**:审批式/通知式/中断式三种确认模式,按 risk_level 动态决定 - -#### D7-3:推送渠道 - -| 渠道 | 推送级别 | 交互深度 | 延迟 | -|------|---------|---------|------| -| **WebChat** | 🔴🟡 | 深(可对话) | 实时 | -| **Dashboard 通知** | 🟡🟢 | 浅(点击查看) | 实时 | -| **Signal/Telegram** | 🟡🔵 | 极浅(只读) | 实时 | -| **邮件** | 🔵 | 极浅(只读) | 批量 | - -**当前落地**:WebChat(主) + Signal(辅)。Dashboard 通知随 M2 前端一起上线。 - -#### D7-4:用户决策点设计 - -用户不是全程驾驶,而是在关键节点参与: - -``` -用户参与密度: -Phase 1 需求探索 ──── 高(苏格拉底对话) -Phase 2 动态规划 ──── 中(方案确认,可跳过 simple 任务) -Phase 3 自主执行 ──── 低(只收通知,不干预) -Phase 4 验收汇报 ──── 中(审查产出,确认交付) -``` - -**🔴 级别统一超时策略**(适用于所有 🔴必须确认 的决策点) - -> 核心原则:用户不在时,系统宁可暂停也不自动推进高风险操作。 - -| 时间段 | 行为 | -|--------|------| -| 0-1h | 正常等待,无额外动作 | -| 1-4h | 每 30min 重发提醒(频率可配,通过 WebChat + Signal 双渠道) | -| 4h 后 | 任务标记 `escalated` + **暂停**(不继续执行,不自动 reject),通知渠道升级 | -| 用户回来 | 手动恢复(approve/reject/cancel),移除 escalated 标记 | - -**具体决策点**: - -| 决策点 | 触发条件 | 交互模式 | 超时兜底(覆盖上面的通用策略) | -|--------|---------|---------|---------| -| 需求确认 | 庞统完成需求结构化后 | 即时对话 | 30min 后提醒,4h 后暂停 | -| 方案审批 | complex 任务规划完成后 | 轻触确认 | 同通用策略(含 risk_level=high) | -| 风控告警 | 关羽触发风控规则 | 必须确认 | 同通用策略:4h 后 escalated+暂停,保护资金安全 | -| 产出验收 | 任务全部完成后 | 沉浸观察 | 24h 自动确认(仅 risk_level=low,其余走通用策略) | -| 异常升级 | Agent 无法自主解决 | 即时对话 | 同通用策略 | - -> **参考实践**: -> - **v2.0 PRD**:人的参与密度从高到低 -> - **GSD discuss phase**:必须确认步骤 -> - **课题4 D4-5**:三种确认模式 - -#### D7-5:Agent 主动汇报规范 - -Agent 不是沉默执行器——在关键节点主动向用户汇报: - -| 汇报时机 | 谁汇报 | 内容 | 渠道 | -|---------|--------|------|------| -| 任务开始 | 庞统 | "已创建任务 T-006,5个节点,预估30分钟" | WebChat | -| 阶段转换 | 庞统 | "T-006 编码完成,进入审查阶段" | 通知 | -| 遇到阻塞 | 执行者 | "发现 XXX 问题,建议 YYY 方案" | 即时对话 | -| 任务完成 | 庞统 | "T-006 完成!年化收益15.3%,最大回撤8.2%" | WebChat + 通知 | -| 定时摘要 | 庞统 | "今日完成 3 个任务,Token 消耗 ¥12.5" | 通知/邮件 | - -**汇报格式**(简洁、可操作): -``` -✅ T-006 双均线策略 — 完成 - 执行:张飞→司马懿→赵云 - 产出:策略代码 + 回测报告 - 关键指标:年化15.3% / 回撤8.2% / 夏普1.8 - [查看详情] [查看产出] -``` - -#### D7-6:多 Agent 协作可视化 - -用户观察多 Agent 协作过程的三层视图(调研报告 §4.4): - -| 层次 | 展示什么 | 类比 | 用户操作 | -|------|---------|------|---------| -| **全局层** | DAG 拓扑 + 节点状态 | GitHub Actions | 点击节点 | -| **任务层** | 管道进度 + Agent 头像 | Edict MiniPipe | 审批/暂停 | -| **Agent 层** | 当前操作 + 产出物预览 | Devin 工作屏 | 评论/指导 | - ---- - -## Part B:AI Native Dashboard 设计(课题9) - -### 4. 设计目标 - -Dashboard 不是监控面板,是**人 Agent 协作界面**——用户在这里观察、确认、指导 AI 团队的工作。 - -### 5. 设计决策 - -#### D9-1:Dashboard 定位 - -> **参考实践**: -> - **Hermes Kanban**:Dashboard 是协作界面,不是展示墙。Comment Thread 是人 Agent 讨论的最佳实践 -> - **Edict**:看板 + 管道 + 操作按钮 + 皇帝发言 = 完整协作 -> - **Linear**:极简 + 键盘优先,不信息过载 - -**三不做**: -1. ❌ 不做第二个 Grafana(纯监控面板) -2. ❌ 不做第二个 Slack(闲聊频道) -3. ❌ 不做第二个 Jira(重到没人用) - -**定位**:轻量协作界面——展示关键状态 + 快速操作入口 + 深入查看能力。 - -#### D9-2:三层信息架构 - -``` -L1: 一眼看到(顶部栏,永远可见) -┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ -│ 全局健康 │ │ 活跃任务 │ │ 待审批 │ │ Token │ -│ 🟢 正常 │ │ 3 │ │ 1 ⚠️ │ │ ¥12.5 │ -└──────────┘ └──────────┘ └──────────┘ └──────────┘ - -L2: 任务看板(主体区域) -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ T-003 策略回测│ │ T-004 数据采集│ │ T-005 风控 │ -│ ⚙️ 执行中 │ │ 📋 规划中 │ │ ✅ 完成 │ -│ ████░░ 张飞 │ │ ░░░░░░ 赵云 │ │ █████ 赵云 │ -│ 3/5 节点完成 │ │ 等待审批 │ │ 产出已交付 │ -└─────────────┘ └─────────────┘ └─────────────┘ - -L3: 任务详情(点击卡片展开) -- 管道进度:规划→审查→执行→交付 -- 产出物:代码 diff / Markdown / 图表 -- 评论线程:Agent 和用户的讨论 -- 操作历史:所有状态变更记录 -``` - -#### D9-3:页面结构 - -**延续 v1.0 的 Tab 结构**,但大幅精简为 5 个核心页面: - -| 页面 | 展示内容 | 核心交互 | 优先级 | -|------|---------|---------|--------| -| **任务看板** | 活跃任务卡片 + 管道进度 | 审批/暂停/取消、点击查看详情 | P1 | -| **全局监控** | DAG 拓扑 + Agent 状态 + 心跳 | 点击节点/Agent、Token 统计 | P1 | -| **产出档案** | 已完成任务 + 产出物 | 预览/下载/复制 | P2 | -| **系统配置** | 模型/技能/Guardrail 配置 | 编辑/保存 | P2 | -| **AI Briefing** | 今日摘要 + 待处理 + 风险预警 | 一键操作 | P3 | - -**砍掉的 v1.0 页面**(合并或降级): -- "将军总览" → 合并到全局监控的 Agent 状态面板 -- "军议大厅" → 合并到任务详情的评论线程(黑板 comment 已支持) -- "奏折阁" → 合并到产出档案 -- "会话监控" → 合并到全局监控 -- "编排调度" → 合并到任务看板(DAG 拓扑) - -> **理由**:5 个页面覆盖所有场景。v1.0 的 10 Tab 有冗余(编排调度 vs 任务看板、奏折阁 vs 产出档案)。参考 Linear 的极简哲学——少即是多。 - -#### D9-4:任务看板设计 - -**任务卡片**(L2 主体): - -``` -┌─────────────────────────────────────┐ -│ T-006 双均线交叉策略 │ -│ 📋 规划中 ─── 🟡 risk: standard │ -│ │ -│ MiniPipe: █████░░░░░░ 2/5 节点 │ -│ 张飞 🟢 编码中 → 司马懿 ⏳ 等待审查 │ -│ │ -│ 预估 30min | 已用 15min | Token ¥2.3 │ -│ │ -│ [⏸暂停] [📋详情] [💬评论] │ -└─────────────────────────────────────┘ -``` - -**卡片上的操作按钮**(按任务状态动态显示): - -| 状态 | 可用操作 | 说明 | -|------|---------|------| -| pending | 取消、编辑 | 规划前可改 | -| planned | 审批、驳回 | 等用户确认方案 | -| executing | 暂停、查看详情 | 执行中可暂停 | -| reviewing | 查看详情 | 审查中只观察 | -| completed | 查看产出、重新执行 | 完成后可复查 | -| failed | 重试、取消、查看详情 | 失败后可重试 | - -> **参考实践**: -> - **Edict TaskModal**:叫停/恢复/取消统一入口 -> - **Hermes Kanban**:7 状态完整状态机 + 按钮动态显示 -> - **课题3 §9.7**:状态机守卫(ACTION_GUARDS),前端按钮与后端对齐 - -#### D9-5:全局监控设计 - -**DAG 拓扑视图**: - -``` - T-006 双均线策略 - ┌──────┐ ┌──────┐ ┌──────┐ - │ 赵云 │────→│ 张飞 │────→│ 司马懿│ - │ 数据 │ ✅ │ 编码 │ ⚙️ │ 审查 │ ⏳ - └──────┘ └──────┘ └──────┘ - │ - ▼ - ┌──────┐ - │ 关羽 │ - │ 风控 │ ⏳ - └──────┘ -``` - -- 颜色:✅ 完成(绿) / ⚙️ 执行中(蓝动画) / ⏳ 等待(灰) / ❌ 失败(红) -- 点击节点 → 滑出该节点的详情面板 -- 连线 = depends_on 依赖关系 - -**Agent 状态面板**: - -``` -┌────────────────────────────────────┐ -│ 张飞 翼德 🟢 │ -│ 当前: T-006 节点2 编码中 │ -│ 心跳: 30s前 | Session: 45min │ -│ 今日: 完成 2 任务 | Token ¥5.2 │ -└────────────────────────────────────┘ -``` - -> **参考实践**: -> - **GitHub Actions**:DAG 拓扑 + 点击节点看日志 -> - **Hermes Kanban**:心跳可视化 + 僵尸检测 -> - **课题2 §4.5**:续杯与心跳机制 - -#### D9-6:AI Briefing(AI 晨报) - -> **参考实践**: -> - **v2.0 PRD Phase 4**:主动汇报,不等人查 -> - **superpowers**:每次 session 开始时自动 summary - -位置:Dashboard 首页顶部卡片。 - -**内容**(AI 自动生成,不是固定模板): - -``` -┌─────────────────────────────────────────┐ -│ 🌅 今日简报 — 2026-05-16 │ -│ │ -│ 📊 活跃任务: 3 | 待审批: 1 | 今日完成: 2 │ -│ │ -│ ⚠️ 需要你处理: │ -│ • T-006 方案审批(等待 2h) │ -│ │ -│ 📈 今日进展: │ -│ • T-004 数据采集完成(赵云) │ -│ • T-005 风控检查通过(关羽) │ -│ │ -│ 💰 Token 消耗: ¥12.5 / 今日预算 ¥50 │ -│ │ -│ [审批 T-006] [查看全部] │ -└─────────────────────────────────────────┘ -``` - -**生成时机**: -- 用户打开 Dashboard 时自动刷新 -- 每日定时推送(可配置) -- 关键事件后更新 - -#### D9-7:Command Palette(⌘K) - -> **参考实践**: -> - **Raycast**:全局快捷键 + 模糊搜索 + 即用即走 -> - **cmdk(shadcn/ui)**:成熟的 React command palette 组件 - -**支持的命令**: - -| 类别 | 示例 | -|------|------| -| 任务操作 | "暂停任务 3" "查看 T-006 详情" "创建新任务" | -| 状态查询 | "张飞在干什么" "Token 用了多少" "有多少待审批" | -| 导航 | "打开产出档案" "跳转到 T-005" | -| 配置 | "切换模型" "查看技能列表" | - -**实现**:Phase 2,使用 cmdk 组件。 - -#### D9-8:评论线程 - -> **参考实践**: -> - **Hermes Kanban**:per-task comment thread,人和 Agent 都可评论 -> - **黑板 comment 机制**(课题1 §5.2):已设计结构化 comment + @mention - -每个任务有独立评论线程,展示在 L3 任务详情中: - -``` -┌─────────────────────────────────────────┐ -│ 💬 T-006 讨论线程 │ -│ │ -│ [庞统] 任务已创建,5个节点,预估30min │ -│ [张飞] ## Handoff: 编码完成 ✅ │ -│ 产出: strategy.py │ -│ [司马懿] 🔍 发现2个问题... │ -│ verdict: needs_revision │ -│ [张飞] REJECT: 问题1已修复,问题2不同意 │ -│ [你] → 输入评论... │ -└─────────────────────────────────────────┘ -``` - -**用户可以直接在评论线程中发言**,发言会被 Agent 看到(通过黑板 comment + inbox 通知)。 - -#### D9-9:技术方案 - -| 方面 | 方案 | 理由 | -|------|------|------| -| **前端框架** | React 18 + Vite + Zustand + Tailwind | 延续 v1.0 + Edict 已验证 | -| **实时通信** | M2: HTTP 轮询(5s) → M3: WebSocket | 渐进升级,延续 v1.0 策略 | -| **DAG 可视化** | React Flow | GitHub Actions 式 DAG,成熟方案 | -| **Command Palette** | cmdk(shadcn/ui) | 成熟的 command palette 组件 | -| **图表** | Recharts | 轻量级,已有使用经验 | -| **后端 API** | FastAPI + SQLite(黑板数据库) | 延续 v1.0 技术栈 | -| **UI 风格** | 深色主题(延续 Edict CSS 变量体系) | 已有完整设计系统 | - -> **注意**:v1.0 前端源码在 `~/.sanguo_projects/sanguo_moziplus/dashboard/`(React + Vite + TypeScript),可直接在此基础上重构。 - -#### D9-10:API 设计 - -Dashboard 后端 API 直接读取黑板数据库: - -| 端点 | 方法 | 用途 | -|------|------|------| -| `/api/tasks` | GET | 任务列表(支持 state/agent 过滤) | -| `/api/tasks/{id}` | GET | 任务详情(含 comments、reviews) | -| `/api/tasks/{id}/action` | POST | 操作(pause/resume/cancel/approve/reject) | -| `/api/agents` | GET | Agent 状态列表 | -| `/api/stats` | GET | 全局统计(活跃/完成/Token) | -| `/api/briefing` | GET | AI Briefing 内容 | -| `/api/comments/{task_id}` | GET/POST | 评论线程读写 | -| `/ws/events` | WebSocket | 实时事件推送(M3) | - -**并发与权限保护**: - -1. **来源标识**:action 端点必须带 `source` 字段(`dashboard` / `daemon` / `cli`),Daemon 处理时检查来源合法性 - ```json - POST /api/tasks/T-006/action - { "action": "approve", "source": "dashboard", "reason": "方案可行" } - ``` - -2. **乐观锁**:action 请求必须带 `expected_version` 字段,Daemon 校验当前 version 是否匹配,不匹配则拒绝(409 Conflict) - ```json - { "action": "approve", "source": "dashboard", "expected_version": 3 } - ``` - → Dashboard 前端在读取任务详情时获取 version,提交时带回,防止 Dashboard 和 Daemon 并发冲突。 - -3. **用户身份**:Dashboard 评论的 `author` 统一为 `"user"`,与 Agent 的 `"pangtong-fujunshi"` / `"zhangfei-dev"` 等区分 - -**关键设计**: -- 所有数据来自黑板数据库(单一数据源) -- Dashboard 不维护独立状态,是黑板数据的只读视图 + 操作入口 -- 操作端点(approve/reject)通过来源标识 + 乐观锁安全地调用 Daemon API - ---- - -## 6. 和现有设计的对齐检查 - -| 已有设计 | 课题7+9 补充 | 一致性 | -|---------|-------------|--------| -| §4.2 Tick+Inbox 事件驱动 | D7-2 推送级别 → inbox 事件驱动通知 | ✅ Inbox 是推送机制的基础 | -| §5.1 Agent spawn 流程 | D7-5 主动汇报 → spawn 后庞统通知用户 | ✅ 庞统作为协调者汇报 | -| §3.4 原子操作 | D9-10 API action 端点 → 调用原子操作 | ✅ Dashboard 操作走黑板原子操作 | -| §3.5 评论线程 | D9-8 Dashboard 评论 → 黑板 comment | ✅ 单一数据源 | -| §9.7 状态机守卫 | D9-4 卡片按钮 → ACTION_GUARDS 对齐 | ✅ 前后端一致 | -| §4.5 续杯与心跳 | D9-5 Agent 心跳状态 → Dashboard 展示 | ✅ 心跳数据可视化 | -| v1.0 10 Tab 设计 | D9-3 精简为 5 页 | ✅ 合并冗余,保留核心 | -| Edict Dashboard | D9-2 三层信息 + D9-4 卡片 + D9-5 DAG | ✅ 直接复用模式 | - -## 7. Phase 落地规划 - -| Phase | 人机交互 | Dashboard | -|-------|---------|-----------| -| **Phase 1** | WebChat 推送(已有)+ 🔴必须确认 | 无前端(CLI + WebChat 够用) | -| **Phase 2** | 🟡通知 + 🟢信息 + ⌘K | 任务看板 + 全局监控 + 评论线程 | -| **Phase 3** | 🔵定时摘要 + AI Briefing | 产出档案 + 系统配置 + WebSocket | -| **Phase 4** | Canvas 产出物交互 | 全功能 + 优化 | - -## 8. 遗留 TODO - -> **注意**:新版 `topic7-9-interaction-dashboard-proposal.md` 已完成设计,以下遗留全部由新版覆盖或为开发实现。 - -| # | 待解决事项 | 归属 | 说明 | -|---|----------|------|------| -| ~~T7-1~~ | ~~推送级别配置 UI~~ | ~~Phase 2~~ | ✅ 新版已设计推送分级,用户偏好配置为开发实现 | -| ~~T7-2~~ | ~~超时兜底机制~~ | ~~Phase 2~~ | ✅ 新版已设计超时推送(🟡级别),具体党底在课题3解决 | -| ~~T7-3~~ | ~~WebChat 推送格式标准化~~ | ~~Phase 1~~ | ✅ 开发实现 | -| ~~T9-1~~ | ~~React Flow DAG 组件~~ | ~~Phase 2~~ | ✅ 开发实现 | -| ~~T9-2~~ | ~~cmdk Command Palette~~ | ~~Phase 2~~ | ✅ 开发实现 | -| ~~T9-3~~ | ~~AI Briefing~~ | ~~Phase 3~~ | ✅ 新版已设计日报/周报格式 | -| ~~T9-4~~ | ~~Dashboard 评论→黑板~~ | ~~Phase 2~~ | ✅ 开发实现 | -| ~~T9-5~~ | ~~WebSocket 实时推送~~ | ~~Phase 3~~ | ✅ 新版选 SSE + 降级轮询替代 | -| ~~T9-6~~ | ~~v1.0 前端重构~~ | ~~Phase 2~~ | ✅ 新版已设计 5 页面结构 | -| ~~T9-7~~ | ~~产出物预览 Canvas~~ | ~~Phase 4~~ | ✅ 开发实现 | - -> **⏳ 待用户确认**:课题7+9设计方案待用户进一步探讨后结题。 diff --git a/docs/design/topic9-dashboard-design.md b/docs/design/topic9-dashboard-design.md deleted file mode 100644 index dbdc37b..0000000 --- a/docs/design/topic9-dashboard-design.md +++ /dev/null @@ -1,466 +0,0 @@ -# 课题9:Dashboard 前后端完整设计方案 - -**版本**: v2.0(评审修订版) -**作者**: 庞统(副军师)🐦 -**日期**: 2026-05-17 -**状态**: 待司马懿确认 -**评审记录**: Mail #278(司马懿 P0+P1 评审)→ 已全部修复 → Mail #279(验证通过) - ---- - -## 一、核心定位 - -### 1.1 Dashboard 不是管理控制台,是 AI 工作可视化窗口 - -| 传统 Dashboard | AI Native Dashboard(v2.0) | -|---|---| -| 用户主动操作(点按钮、填表单) | AI 主动展示(推送状态、产出、决策请求) | -| 用户监控一切 | AI 只在关键点拉人,其余自主 | -| 表格+表单为主 | 时间线+卡片+对话为主 | -| 用户是驾驶员 | 用户是乘客,偶尔导航 | - -### 1.2 参考的优秀实践 - -| 实践 | 借鉴点 | -|------|--------| -| **Edict MiniPipe** | 任务卡片上的状态管线可视化 | -| **Hermes Kanban** | 卡片 + 结果区 + 评论线程 + 事件流 + Nudge 按钮 | -| **Claude Code App** | 多 Tab 切换详情面板(产出/评审/事件) | -| **Linear** | 精准通知不噪音;状态流转按钮守卫 | -| **Devin** | AI 自主执行的观察窗口理念 | -| **OpenClaw Control Center** | 双入口对等(对话 + Dashboard) | - ---- - -## 二、数据模型 - -### 2.1 V2Task 类型(对齐后端 tasks 表) - -前端核心类型,字段完全对齐后端 `src/blackboard/db.py` 的 `tasks` 表: - -```typescript -interface V2Task { - // 基础字段(直接对应 tasks 表列) - id: string; - title: string; - description: string; - status: TaskStatus; // 8 状态枚举,见 §2.2 - assignee: string | null; - assigned_by: string | null; - depends_on: string | null; // 依赖任务 ID - parent_task: string | null; // 父任务 ID - priority: number; // 1-10 整数(P0 修复:前端曾用 string,已改 number) - task_type: string; - created_at: string; - updated_at: string; - claimed_at: string | null; - completed_at: string | null; - started_at: string | null; - deadline: string | null; - retry_count: number; - max_retries: number; - risk_level: 'low' | 'standard' | 'high' | 'critical'; - escalated: number; // 0/1 整数(与后端 INTEGER 一致) - - // 聚合字段(后端 summary API 或前端多次查询) - comments_count: number; - outputs_count: number; - review_status: 'none' | 'pending' | 'approved' | 'rejected' | 'rebuttal'; - latest_event: string | null; - project_id: string; -} -``` - -### 2.2 状态机(8 状态,对齐后端 VALID_STATUSES) - -``` -pending ──→ claimed ──→ working ──→ review ──→ done - │ │ │ │ - │ │ ├→ blocked ├→ failed - │ │ │ │ - └→ cancelled←┴───────────┴───────────┘ -``` - -**线性管线**(5 步):`pending → claimed → working → review → done` - -**旁路状态**:`failed`(标记在 working 位置)、`blocked`(标记在 working 位置) - -**终态**:`done`、`cancelled` - -### 2.3 状态流转守卫(对齐后端 VALID_TRANSITIONS) - -```typescript -const VALID_TRANSITIONS: Record = { - pending: ['claimed', 'cancelled'], - claimed: ['working', 'pending', 'cancelled'], - working: ['review', 'blocked', 'failed', 'cancelled'], - review: ['done', 'pending', 'failed', 'cancelled'], - blocked: ['pending', 'cancelled'], - failed: ['pending'], - done: [], - cancelled: [], -}; -``` - -与后端 `src/blackboard/db.py` 的 `VALID_TRANSITIONS` 完全一致。 - -### 2.4 Priority 映射 - -后端 `priority` 是 INTEGER(1-10),前端映射: - -```typescript -const PRIORITY_META: Record = { - 1: { color: '#6b7280', label: '低' }, - 2: { color: '#6b7280', label: '低' }, - 3: { color: '#3b82f6', label: '中' }, - 4: { color: '#3b82f6', label: '中' }, - 5: { color: '#f59e0b', label: '高' }, - 6: { color: '#f59e0b', label: '高' }, - 7: { color: '#ff5270', label: '紧急' }, - 8-10: { color: '#ff5270', label: '紧急' }, -}; -``` - ---- - -## 三、前端页面设计 - -### 3.1 页面结构 - -| 页面 | 组件 | 优先级 | 状态 | -|------|------|--------|------| -| **📜 任务看板** | EdictBoard | P0 | ✅ Mock UI 已完成,待对接 API | -| **🔍 任务详情** | TaskModal | P0 | ✅ Mock UI 已完成,待对接 API | -| **🏛️ 朝堂议政** | CourtDiscussion | P1 | 迁移自 v1.0 | -| **🔌 编排调度** | MonitorPanel | P1 | 迁移自 v1.0 | -| **🤺 将军总览** | OfficialPanel | P1 | 迁移自 v1.0 | -| **🔔 通知中心** | NotificationCenter | P1 | 占位(Header 铃铛) | -| **📊 AI Briefing** | AIBriefing | P2 | 占位页面 | -| **⚙️ 系统设置** | SettingsPanel | P1 | 迁移自 v1.0 | - -### 3.2 任务看板(EdictBoard) - -#### 顶部统计(4 格) - -| 格 | 内容 | 数据来源 | -|---|------|---------| -| 活跃任务 | working + claimed + review 数 | tasks 表 count | -| 已完成 | done 数 | tasks 表 count | -| 失败/阻塞 | failed + blocked 数 | tasks 表 count | -| 审查中 | review 数 | tasks 表 count | - -#### 状态管线可视化(5 步线性) - -``` -📋待认领 → 👤已认领 → ⚔️执行中 → 🔍审查中 → ✅已完成 -``` - -- 管线上游步骤显示 ✓(绿色),当前步骤高亮(蓝色),下游步骤灰显 -- `failed` 状态:在 working 位置显示 ✗(红色) -- `blocked` 状态:在 working 位置显示 🚧(黄色) -- `cancelled` 状态:管线全部灰显 - -#### 筛选栏 - -8 个状态筛选按钮(all/pending/claimed/working/review/done/failed/blocked)+ 搜索框。 - -#### 任务卡片 - -每张卡片展示: -- **状态管线**(5 步 MiniPipe) -- **ID + 标题** -- **标签行**:状态标签 + 负责人(agent emoji)+ 优先级 + 升级标记 + 依赖标记 -- **元信息行**:更新时间 + 产出数 + 评论数 + 审查状态图标 -- **最新事件**:单行文本截断 -- **底部**:风险等级 + 重试次数 + 截止时间 + 详情按钮 - -### 3.3 任务详情面板(TaskModal) - -点击任务卡片弹出 Modal,4 Tab 布局: - -#### Tab 1: 📋 总览 - -| 区域 | 内容 | 数据来源 | -|------|------|---------| -| 需求描述 | task.description | tasks 表 | -| 状态操作 | 基于 VALID_TRANSITIONS 的操作按钮 | §2.3 守卫 | -| 任务信息 | 类型/优先级/风险/重试/时间/分配人(8 字段网格) | tasks 表 | -| 事件时间线 | 逆序事件列表(图标 + 描述 + 时间 + agent) | events 表 | -| 评论/交接 | handoff/progress/review/rebuttal/debate/observation 6 种类型 | comments 表 | -| 决策记录 | 决策人 + 决策类型 + 理由 | decisions 表 | -| Checkpoint | 占位(虚线框 + "v2.7 提供") | ❌ 后端未实现 | - -#### Tab 2: 📦 产出 - -产出物列表:agent + 内容 + 类型标签 + 尝试次数 + 时间。数据来源:outputs 表。 - -#### Tab 3: 🔍 审查 - -审查意见:评审人 + verdict(APPROVE/REJECT)+ 置信度 + 风险等级 + 辩论轮次 + 摘要。数据来源:reviews 表。 - -#### Tab 4: 🧠 经验 - -关联经验:标题 + 摘要 + 标签。左侧彩色边框区分 best_practice(绿)/ pitfall(红)/ environment(蓝)。数据来源:experiences 表。 - -### 3.4 通知中心(Header 铃铛) - -``` -🔔 (3) → 点击展开通知面板 -``` - -- 按 4 级分组(🔴 Critical > 🟡 Warning > 🟢 Info > 🔵 Silent) -- 每条:时间 + 级别图标 + 内容摘要 + 关联任务链接 -- 🔴 可展开操作按钮 -- 支持标记已读/全部已读 - -**当前状态**:SSE broker 已实现(F17),ticker 未推送事件。前端做铃铛 UI + 占位。 - -### 3.5 AI Briefing - -日报/周报自动生成。格式: - -``` -📊 今日战报 (2026-05-17) -━━━━━━━━━━━━━━━━━━━━ -📋 完成任务:3 个 -🔄 进行中:2 个 -⚠️ 需关注:1 个 -💰 Token 消耗:127K -🧠 新增经验:2 条 -``` - -**当前状态**:后端未实现。前端做占位页面。 - ---- - -## 四、后端 API 设计 - -### 4.1 已有 API(F5 实现) - -| 端点 | 方法 | 说明 | -|------|------|------| -| `/api/projects/{pid}/tasks` | GET | 任务列表(支持 status 筛选) | -| `/api/projects/{pid}/tasks` | POST | 创建任务 | -| `/api/projects/{pid}/tasks/{tid}` | GET | 单任务基本信息 | -| `/api/projects/{pid}/tasks/{tid}` | PATCH | 更新任务 | -| `/api/projects/{pid}/tasks/{tid}/status` | POST | 状态流转 | -| `/api/projects/{pid}/tasks/{tid}/claim` | POST | 认领任务 | -| `/api/projects/{pid}/tasks/{tid}/comments` | GET/POST | 评论 CRUD | -| `/api/projects/{pid}/tasks/{tid}/outputs` | GET/POST | 产出 CRUD | -| `/api/projects/{pid}/tasks/{tid}/decisions` | GET/POST | 决策 CRUD | -| `/api/projects/{pid}/tasks/{tid}/observations` | GET/POST | 观察 CRUD | -| `/api/projects/{pid}/tasks/{tid}/reviews` | GET/POST | 审查 CRUD | -| `/api/events` | GET (SSE) | SSE 事件流 | -| `/api/daemon/status` | GET | Daemon 状态 | -| `/api/projects` | GET/POST | 项目管理 | - -### 4.2 需新增的 API - -#### API-1: `GET /api/projects/{pid}/tasks/{tid}?expand=comments,outputs,reviews,events,decisions` - -**优先级**: P0 -**说明**: 任务详情聚合,一次返回全部关联数据,避免前端多次请求。 -**实现方案**: 在 `blackboard_routes.py` 的 `get_task` 端点增加 `expand` 查询参数。 - -```python -@router.get("/tasks/{task_id}") -async def get_task( - task_id: str, - expand: str = "", # 逗号分隔: "comments,outputs,reviews,events,decisions" - db_path: Path = Depends(get_db_path), -): - task = queries.get_task(db_path, task_id) - if not task: - raise HTTPException(404, "Task not found") - - result = dict(task) - - if expand: - for rel in expand.split(","): - rel = rel.strip() - if rel == "comments": - result["comments"] = queries.list_comments(db_path, task_id) - elif rel == "outputs": - result["outputs"] = queries.list_outputs(db_path, task_id) - elif rel == "reviews": - result["reviews"] = queries.list_reviews(db_path, task_id) - # 聚合 review_status - if result["reviews"]: - latest = result["reviews"][-1] - result["review_status"] = "approved" if latest["verdict"] == "APPROVE" else "rejected" - else: - result["review_status"] = "none" - elif rel == "events": - result["events"] = queries.list_events(db_path, task_id) - # 聚合 latest_event - if result["events"]: - result["latest_event"] = result["events"][-1]["detail"] - elif rel == "decisions": - result["decisions"] = queries.list_decisions(db_path, task_id) - - # 聚合计数 - result["comments_count"] = queries.count_comments(db_path, task_id) - result["outputs_count"] = queries.count_outputs(db_path, task_id) - - return result -``` - -**工作量**: 30min(queries.py 已有基础方法,只需组合) - -#### API-2: `GET /api/projects/{pid}/tasks/{tid}/events` - -**优先级**: P0 -**说明**: 任务事件时间线查询。 -**实现方案**: 新增路由端点。 - -```python -@router.get("/tasks/{task_id}/events") -async def get_task_events( - task_id: str, - limit: int = 50, - offset: int = 0, - db_path: Path = Depends(get_db_path), -): - return queries.list_events(db_path, task_id, limit=limit, offset=offset) -``` - -**前置**: `queries.list_events()` 已存在(events 表 CRUD 已实现)。 -**工作量**: 15min - -#### API-3: `GET /api/projects/{pid}/tasks/{tid}/experiences` - -**优先级**: P1 -**说明**: 查询与任务关联的经验(通过 task_id 检索 experiences.jsonl)。 -**实现方案**: 新增路由端点 + queries 方法。 - -```python -@router.get("/tasks/{task_id}/experiences") -async def get_task_experiences( - task_id: str, - project_id: str, -): - # 从 experiences.jsonl 读取,过滤 source_task == task_id - experiences = experience_store.list_by_task(project_id, task_id) - return experiences -``` - -**工作量**: 30min(需在 experience_store 增加 `list_by_task` 方法) - -### 4.3 已有 API 但需增强 - -| 端点 | 增强 | 说明 | -|------|------|------| -| `GET /api/projects/{pid}/tasks` | 增加 `counts` 返回 | 任务列表 API 返回各状态计数,供统计卡片使用 | -| `GET /api/events` (SSE) | ticker 事件推送 | ticker 状态变更时自动通过 SSE 推送(F17 SSEBroker 已实现,只需在 ticker 中调用 publish) | - -### 4.4 完整路由表 - -``` -/api/projects GET/POST -/api/projects/{pid} GET/DELETE -/api/projects/{pid}/tasks GET/POST -/api/projects/{pid}/tasks/{tid} GET/PATCH/DELETE -/api/projects/{pid}/tasks/{tid}/status POST -/api/projects/{pid}/tasks/{tid}/claim POST -/api/projects/{pid}/tasks/{tid}/comments GET/POST -/api/projects/{pid}/tasks/{tid}/outputs GET/POST -/api/projects/{pid}/tasks/{tid}/decisions GET/POST -/api/projects/{pid}/tasks/{tid}/observations GET/POST -/api/projects/{pid}/tasks/{tid}/reviews GET/POST -/api/projects/{pid}/tasks/{tid}/events GET ← 新增 API-2 -/api/projects/{pid}/tasks/{tid}/experiences GET ← 新增 API-3 -/api/events GET (SSE) -/api/daemon/status GET -``` - ---- - -## 五、实现方案 - -### 5.1 后端改动清单 - -| # | 改动 | 文件 | 优先级 | 工作量 | -|---|------|------|--------|--------| -| B1 | `get_task` 增加 `expand` 参数 | `blackboard_routes.py` + `queries.py` | P0 | 30min | -| B2 | 新增 `GET /tasks/{tid}/events` 路由 | `blackboard_routes.py` | P0 | 15min | -| B3 | 新增 `GET /tasks/{tid}/experiences` 路由 | `blackboard_routes.py` + `experience_store.py` | P1 | 30min | -| B4 | 任务列表 API 返回状态计数 | `blackboard_routes.py` | P1 | 15min | -| B5 | ticker 状态变更推送 SSE | `ticker.py` | P1 | 1h | - -### 5.2 前端改动清单 - -| # | 改动 | 文件 | 优先级 | 工作量 | -|---|------|------|--------|--------| -| F1 | EdictBoard Mock → 对接真实 API | `EdictBoard.tsx` | P0 | 1h | -| F2 | TaskModal Mock → 对接真实 API | `TaskModal.tsx` | P0 | 1.5h | -| F3 | api.ts 补新 API 方法 | `api.ts` | P0 | 30min | -| F4 | store.ts 补状态字段 | `store.ts` | P0 | 15min | -| F5 | SSE EventSource 替代轮询 | `App.tsx` + `store.ts` | P1 | 1h | -| F6 | 通知中心铃铛占位 | `NotificationCenter.tsx` + `App.tsx` | P1 | 30min | -| F7 | AI Briefing 占位页面 | `AIBriefing.tsx` | P2 | 15min | -| F8 | 其余 v1.0 Tab 适配 | 各 Panel 组件 | P1 | 2h | - -### 5.3 执行顺序 - -``` -Phase 1: 后端补 API(B1-B3) ~1.5h -Phase 2: 前端核心对接(F1-F4) ~3h -Phase 3: SSE + 通知占位(B5+F5+F6) ~2.5h -Phase 4: 其余 Tab 适配(F8) ~2h -Phase 5: 联调 + E2E 验证 ~1h -总计: ~10h -``` - -### 5.4 不在本轮范围 - -| 功能 | 原因 | 计划 | -|------|------|------| -| Checkpoint(验证/决策/执行) | 后端无数据模型,需新设计 | v2.7 专项 | -| AI Briefing 生成 | 后端聚合逻辑未设计 | v2.7 专项 | -| Artifact 文件预览/下载 | 后端无文件存储机制 | v2.7 评估 | -| 朝堂议政迁移 | 需要 CourtDiscussion 适配 v2.6 API | v2.7 | - ---- - -## 六、UI 组件文件结构 - -``` -src/frontend/src/ -├── App.tsx # 主布局 + Header + Tab 路由 -├── api.ts # API 层(所有后端调用) -├── store.ts # Zustand 全局状态 -├── types.ts # V2Task 等类型定义(从 EdictBoard 提取) -└── components/ - ├── EdictBoard.tsx # 任务看板(卡片 + 筛选 + 搜索) - ├── TaskModal.tsx # 任务详情(4 Tab) - ├── NotificationCenter.tsx # 通知铃铛(占位) - ├── CourtDiscussion.tsx # 朝堂议政(迁移自 v1.0) - ├── MonitorPanel.tsx # 编排调度(迁移自 v1.0) - ├── OfficialPanel.tsx # 将军总览(迁移自 v1.0) - ├── ModelConfig.tsx # 模型配置(迁移自 v1.0) - ├── SkillsConfig.tsx # 技能配置(迁移自 v1.0) - ├── SessionsPanel.tsx # 传令巡哨(迁移自 v1.0) - ├── MemorialPanel.tsx # 奏折阁(迁移自 v1.0) - ├── SettingsPanel.tsx # 系统设置(迁移自 v1.0) - └── GlobalSearch.tsx # 全局搜索 -``` - ---- - -## 七、评审修订记录 - -### 司马懿 Mail #278 评审 → 修复 - -| 项目 | 级别 | 修复内容 | -|------|------|---------| -| P0: priority 类型 | 必修 | `string` → `number`(1-10),前端 PRIORITY_META 按整数映射 | -| P1: 状态管线 | 必修 | 4 步 → 5 步线性 + blocked 旁路,8 状态全覆盖 | -| P1: VALID_TRANSITIONS | 必修 | 完全对齐后端 `db.py` 的 8×N 映射 | -| P2: 遗漏字段 | 建议 | V2Task 补 `depends_on`, `parent_task` | - -### 司马懿 Mail #279 确认 - -> ✅ P0 priority: number -> ✅ P1 STATUS_ORDER 含 review/blocked -> ✅ P1 TaskModal 状态映射含 reviewing/blocked/cancelled(17 处引用) -> 构建产物 12:37 部署在 8083,前端可访问。 diff --git a/docs/design/v2.7-subtask-model.md b/docs/design/v2.7-subtask-model.md deleted file mode 100644 index 0056444..0000000 --- a/docs/design/v2.7-subtask-model.md +++ /dev/null @@ -1,348 +0,0 @@ -# v2.7 数据模型设计:Project → Task → SubTask - -> 日期:2026-05-18 -> 版本:v2.1(经 08:02 ~ 10:56 讨论 + 评审修正定稿) -> 作者:庞统 -> 状态:评审通过 -> 司马懿评审:Mail #298(2 必修 + 4 OBS,全部采纳) - ---- - -## 一、设计推导过程 - -### 1.1 起因:Card 层为什么被回滚 - -v2.7 原设计是 `Project → Card → Task` 三级层次结构。经过讨论发现 Card 是一个位置尴尬的中间层: - -| 概念 | 用户视角 | Card 层的位置 | -|------|---------|-------------| -| Project | 仓库/组织(sanguo_quant_live) | ✅ 一致 | -| Task | 用户的一条需求/目标("动量策略v1") | Card 吃掉了 Task 的语义 | -| 原子步骤 | Agent 执行的具体动作(张飞编码) | Card 下的 Task 才是真正的原子步骤 | - -**Card 既不是 Task(用户不会说"帮我建一个 Card"),也不是 SubTask(太粗了),更不是 Campaign(被锁在 Project 内)。** 它是一个多余抽象。 - -正确模型应该是用户自然使用的三层: - -``` -Project(项目/仓库) - └── Task(用户需求/意图/目标) - └── SubTask(Agent 执行的原子任务/Stage) -``` - -### 1.2 SubTask 表的讨论:为什么最终没建 - -**提出**:最初设计新建 `subtasks` 表,包含独立的状态、指派、依赖。 - -**质疑**: -1. **调度意义**:没有 SubTask 表也能调度——当前 Ticker 扫描 Task 就调度了。SubTask 作为调度单位的好处理论上是并行,但 Task 自引用(parent_task)已经能做到。 -2. **和已有设计的重叠**:v2.6 的 Task 表已有 `parent_task` 字段,拆解结果就是子 Task(Task 自引用)。课题4 的 planner.md 已定义了拆解流程(四步+组件库+PlanChecker)。新建 SubTask 表和这些重叠。 -3. **plan_json 也重叠**:已有 planner.md 模板指导庞统拆解,产出是黑板上的子 Task,不需要额外的 plan_json 字段。 - -**结论**:**不建 SubTask 表,复用 Task 自引用(parent_task)。** 子 Task 和父 Task 用同一张表,通过 `parent_task` 字段形成父子关系。 - -### 1.3 跨项目协作的讨论 - -**最初设计**:SubTask 级跨项目(姜维在 vnpy 为 quant_live 的某个 SubTask 工作)。 - -**修正**: -- 跨项目应该在 Task 级,不是 SubTask 级 -- 把 Task 整体信息(outputs、comments、黑板讨论)共享出去,具体用哪部分由 Agent 自己判断 -- 通过 bootstrap 注入实现(build_bootstrap 在 Agent 启动前把依赖 Task 的产出摘要拼装进 prompt) - -**结论**:Task 级跨项目,不改 API,通过 bootstrap 注入。 - -### 1.4 关系模型的讨论 - -任务之间存在三种可能的关系: - -| 关系 | 字段 | 语义 | 例子 | -|------|------|------|------| -| **父子** | `parent_task` | 组成归属——"我是谁的一部分" | 因子研究 **属于** 动量策略v1 | -| **依赖** | `depends_on` | 执行顺序——"我必须等谁完成" | 策略编码 **必须等** 因子研究完成 | -| **引用** | 新字段? | 成果物关联——"我用了谁的产出" | 动量策略v1 **用了** 回测引擎搭建的产出 | - -**背靠背讨论**(Mail #302 → #297): - -司马懿的独立判断: -- **父子**:有必要,提供聚合进度 + 前端分组。Card 回滚后 parent_task 是唯一的分组聚合手段 -- **依赖**:已确定必须保留,调度引擎消费 -- **引用**:当前没必要,没有消费者。和依赖有本质区别(依赖是调度的前置条件,引用是审计的溯源链),但当前审计场景不存在 -- **补充**:第三种关系可能是"模板/实例"(多个策略用相同 stages 流程),但 stages_json 已能表达 - -**最终结论**: - -| 字段 | 保留 | 用途 | -|------|------|------| -| `parent_task` | ✅ | 聚合进度 + 前端分组 | -| `depends_on` | ✅ | 调度顺序 | -| 引用 | ❌ | 未来按需加 `references TEXT` | -| `stages_json` | ✅ 新增 | 动态 Stage 定义 | - -### 1.5 状态的讨论 - -**最初提出** `planning` / `challenging` 两个新状态。 - -**修正**:课题3 已定义了完整的审查流水线(plan_review / output_review / guardrail / final_review),课题4 已定义了拆解流程。不需要额外加这两个状态。 - -**结论**:不加新状态,复用已有机制。 - -### 1.6 前端展示的讨论 - -**Stage 进度条**:和当前的固化 5 阶段(pending→claimed→working→review→done)一个意思,只是变成动态的 stages_json 定义的阶段。 - -**百分比问题**:Stage 是动态的,后续节点还没执行到,百分比不可靠。改用 `■■□□□ 2/5` + 当前 Stage 名。 - ---- - -## 二、最终设计 - -### 2.1 数据模型 - -**不建新表,只改现有 Task 表:** - -```sql --- 新增字段 -ALTER TABLE tasks ADD COLUMN stages_json TEXT DEFAULT '[]'; -``` - -**stages_json 格式**: - -```json -[ - {"id": "research", "label": "因子研究", "order": 1}, - {"id": "data_prep", "label": "数据准备", "order": 2}, - {"id": "coding", "label": "策略编码", "order": 3}, - {"id": "backtest", "label": "回测验证", "order": 4}, - {"id": "optimize", "label": "参数优化", "order": 5} -] -``` - -**父子关系**(已有字段,启用使用): - -```sql --- 已存在,不需要 ALTER -parent_task TEXT -- 子 Task 填父 Task 的 id,顶层 Task 为 NULL -``` - -**关系总结**: - -| 字段 | 语义 | 消费者 | -|------|------|--------| -| `parent_task` | 组成归属 | 前端分组、进度聚合 | -| `depends_on` | 执行顺序 | Ticker 调度引擎 | -| `stages_json` | 动态阶段定义(仅顶层 Task) | 前端 Stage 进度展示 | - -### 2.2 父子关系行为规则 - -**子 Task 创建**: -- 庞统拆解时,为每个原子步骤创建子 Task(`parent_task=父Task.id`) -- 子 Task 有自己独立的 `status`、`assignee`、`depends_on`、`stage` -- 子 Task 的 `stage` 字段绑定到父 Task 的 `stages_json` 中的某个 stage id - -**父 Task 状态 = 子 Task 聚合**(v2.8 更新): - -| 父 Task 状态 | 推导规则 | -|-------------|---------| -| `pending` | 所有子 Task 都是 pending,或无子 Task | -| `escalated` | 有子 Task escalated(v2.8 新增,视同 working) | -| `waiting_human` | 有子 Task waiting_human(v2.8/M3 新增,视同 working) | -| `working` | 有子 Task 在 claimed / working | -| `review` | 有子 Task 在 review | -| `done` | 所有子 Task 都是 done | -| `failed` | 有子 Task failed 且无 active 子 Task | -| `blocked` | 有子 Task blocked 且无 active 子 Task | -| `cancelled` | 用户/AI 取消(手动设置,**不参与聚合**) | -| `paused` | 用户暂停(手动设置,**不参与聚合**) | - -**聚合优先级**(v2.8 更新): -escalated > waiting_human > review > working > pending > failed > blocked - -**不参与聚合**:paused, cancelled(手动状态) - -**手动状态保护**:父 Task 被手动设为 cancelled 或 paused 后,聚合不覆盖。 - -**聚合刷新时机**:每次 Ticker tick 时,全量扫描有子 Task 的父 Task,刷新聚合状态。 - -**边界条件**: -1. 全量扫描(当前任务量 <100,性能无忧) -2. 手动状态不覆盖(cancelled 的父 Task 不参与聚合) - -### 2.3 示例数据 - -``` -tasks 表: - -| id | title | parent_task | depends_on | stage | status | assignee | -|-----------|-------------|-------------|---------------|-----------|---------|-----------| -| task-001 | 动量策略v1 | NULL | NULL | NULL | working | NULL | ← 父 Task -| task-002 | 因子研究 | task-001 | [] | research | done | zhangfei | ← 子 Task -| task-003 | 分钟线下载 | task-001 | [] | data_prep | done | zhaoyun | ← 子 Task -| task-004 | 策略编码 | task-001 | [task-002,task-003] | coding | working | zhangfei | ← 子 Task -| task-005 | 回测验证 | task-001 | [task-004] | backtest | pending | NULL | ← 子 Task - -task-001 的 stages_json: -[{"id":"research","label":"因子研究","order":1}, - {"id":"data_prep","label":"数据准备","order":2}, - {"id":"coding","label":"策略编码","order":3}, - {"id":"backtest","label":"回测验证","order":4}, - {"id":"optimize","label":"参数优化","order":5}] -``` - -### 2.4 跨项目协作 - -**方式**:通过 bootstrap 注入(不改 API)。 - -`build_bootstrap()` 在 spawn Agent 前: -1. 查黑板找到依赖 Task 的 outputs 和 comments -2. 拼装到 Agent 的 prompt 里 -3. Agent 开箱就能看到依赖信息,不需要自己跨项目查询 - -### 2.5 Ticker 变更 - -**和 v2.6 基本一致,只加两步**: - -```python -async def _tick_project(self, project_id, project_info): - # 1. 扫描当前状态(不变) - # 2. 依赖推进(不变) - # 3. 僵尸/超时处理(不变) - # 4. 调度 pending 子 Task(不变——和调度普通 Task 一样) - # 5. 调度审查(不变) - # 6. 【新增】聚合父 Task 状态 - # - 全量扫描所有有子 Task 的父 Task - # - 跳过手动状态(cancelled)的父 Task - refresh_parent_task_statuses(db_path) - # 7. 写 daemon_tick 事件(不变) -``` - -**调度逻辑完全不变**——子 Task 和普通 Task 一样被扫描、调度。唯一的变更是 tick 末尾加一步聚合刷新。 - -### 2.6 前端改动 - -#### 2.6.1 Task 看板(EdictBoard.tsx) - -- 任务列表只显示**顶层 Task**(`parent_task IS NULL`) -- Task 卡片新增: - - `■■□□□ 2/5`(已完成子 Task 数 / 总子 Task 数) - - `当前:策略编码`(当前活跃子 Task 的 stage label) - -#### 2.6.2 Task 详情弹窗(TaskModal.tsx) - -- **Stage 进度条**:从 `stages_json` 读取,按子 Task 状态标记每个 Stage(✅ done / 🔄 working / ⬜ pending) -- **子 Task 列表**:按 Stage 分组展示,每个子 Task 显示标题、Agent、状态、产出 -- 和现有的进度条 + 实时动态是一个意思,只是从固化的 5 阶段变成动态 Stage - -#### 2.6.3 Mail Tab - -- 新增 Tab,列表展示 Sanguo Mail -- 后续独立实现 - -#### 2.6.4 不做的 - -- 不做独立的 SubTask 管理页 -- 不做跨 Project 的 Product 视图 -- 不做 Card 看板 - ---- - -## 三、回滚范围 - -### 3.1 代码回滚(开发目录 `~/.openclaw/sanguo_projects/sanguo_moziplus_v2/`) - -| 文件 | 操作 | -|------|------| -| `src/blackboard/models.py` | 删除 Card dataclass + CARD_VALID_STATUSES + CARD_TYPE_SET + CARD_MANUAL_STATUSES | -| `src/blackboard/db.py` | 删除 `_CARDS_SCHEMA`、`_migrate_v27()`;tasks DDL 移除 `card_id`(**保留 `stage`**);保留其他所有内容 | -| `src/blackboard/operations.py` | 删除 CardOps 类全部方法 | -| `src/blackboard/queries.py` | 移除 card_id 过滤参数 | -| `src/api/card_routes.py` | **删除整个文件** | -| `src/api/mail_routes.py` | **删除整个文件**(Mail Tab 延后,后续重新设计后端 API) | -| `src/daemon/ticker.py` | 移除 per-card 扫描逻辑(`_tick_card`、`CardOps` 引用、`from ... import CardOps`),恢复 per-project 直扫 | -| `src/main.py` | 移除 card_routes、mail_routes 注册;移除 v2.7 迁移调用;版本号保持 2.7.0 | -| `tests/test_v27_cards.py` | **删除整个文件** | - -### 3.2 设计文档 - -| 文件 | 操作 | -|------|------| -| `docs/design/v2.7-three-tier-hierarchy.md` | 移至 `docs/design/archive/` | - -### 3.3 保留 - -| 文件/功能 | 原因 | -|-----------|------| -| `registry.db` + ProjectRegistry + 自动发现 | Project 管理需要 | -| per-project `blackboard.db` | 多项目隔离需要 | -| Project API 路由 | 前端项目选择器需要 | -| `docs/design/product-direction-notes.md` | 新增,Product 方向备忘 | - ---- - -## 四、新增内容 - -### 4.1 后端 - -| 序号 | 任务 | 文件 | 说明 | -|------|------|------|------| -| 1 | tasks DDL 加 `stages_json` | `db.py` | `ALTER TABLE tasks ADD COLUMN stages_json TEXT DEFAULT '[]'` | -| 2 | 子 Task 聚合函数 | `operations.py` | `refresh_parent_status(db_path, parent_task_id)` | -| 3 | 按 parent_task 查询 | `queries.py` | `list_subtasks(db_path, parent_task_id)` | -| 4 | Ticker 加聚合刷新 | `ticker.py` | tick 末尾调用聚合函数 | -| 5 | API 路由微调 | `blackboard_routes.py` | Task 详情接口返回子 Task 列表 + Stage 进度 | - -### 4.2 前端 - -| 序号 | 任务 | 文件 | 说明 | -|------|------|------|------| -| 6 | Task 卡片加进度指示 | `EdictBoard.tsx` | `■■□□□ 2/5` + 当前 Stage 名 | -| 7 | Task 详情弹窗改造 | `TaskModal.tsx` | 动态 Stage 进度条 + 子 Task 列表 | -| 8 | store/api 适配 | `store.ts` + `api.ts` | 加载子 Task 数据、Stage 进度 | - -### 4.3 测试 - -| 序号 | 任务 | 文件 | -|------|------|------| -| 9 | 子 Task CRUD + 聚合 | `test_v27_subtasks.py` | -| 10 | 现有测试不回归 | 全量 pytest | - ---- - -## 五、实施顺序 - -| 阶段 | 任务 | 预计 | -|------|------|------| -| **Phase 1:回滚** | 删除 Card 层代码 + 归档设计文档 | 2h | -| **Phase 2:后端** | stages_json + 聚合 + 查询 + Ticker + API | 4h | -| **Phase 3:前端** | 卡片指示器 + 详情弹窗改造 | 4h | -| **Phase 4:测试** | 新测试 + 全量回归 | 2h | -| **Phase 5:评审** | 发司马懿评审 + 修复 | 2h | -| **合计** | | **~14h** | - ---- - -## 六、Product 方向备忘(不实施,记录未来) - -详见 `docs/design/product-direction-notes.md`。 - -核心思路:Product = 跨 Project 的 Task 聚合,多个 Task 的产出物组合成完整产品。和 Project 是正交维度。等有实际场景再建模。 - ---- - -## 七、讨论参与 - -| 时间 | 参与者 | 内容 | -|------|--------|------| -| 08:02 | 用户 | 质疑 Card 层存在必要性,提出 Project → Task → SubTask 三层 | -| 08:07 | 用户 | 提出 Campaign/Product 概念(跨项目 Task 聚合) | -| 08:13 | 用户 | 确认四层:Project → Task → SubTask → Product(未来),决定回滚 Card | -| 08:21 | 用户 | 确认三条:回滚 Card、完善三层、记录 Product 方向 | -| 08:47 | 用户 | 追问 SubTask 表用途、parent_project 归属、plan_json 重叠、状态必要性 | -| 09:55 | 用户 | 确认 Task 级跨项目、不需要 planning/challenging 状态、追问 bootstrap 注入含义 | -| 09:59 | 用户 | 确认进度展示方式,追问 parent_task 含义 | -| 10:00 | 用户 | 追问父子 vs 引用 vs 依赖的业务选择 | -| 10:03 | 用户 | 要求背靠背发给司马懿讨论 | -| 10:10 | 司马懿 | 回复:留父子+依赖,砍引用,补充模板/实例关系 | -| 10:50 | 用户 | 确认最终方案,要求更新设计文档并统一发评审 | -| 10:56 | 司马懿 | 评审:2 必修 + 4 OBS,方向正确 | -| 10:56+ | 庞统 | 全部采纳:claimed 归入 working、聚合优先级、手动状态保护、stage 保留、Mail 延后 | diff --git a/docs/design/v2.7.1-mail-envelope-separation.md b/docs/design/v2.7.1-mail-envelope-separation.md deleted file mode 100644 index c26dca0..0000000 --- a/docs/design/v2.7.1-mail-envelope-separation.md +++ /dev/null @@ -1,172 +0,0 @@ -# v2.7.1 Mail 信封/载荷分离优化 - -**版本**: v1.0 -**日期**: 2026-05-24 -**作者**: 庞统 -**状态**: 实施中(评审通过,用户确认) - ---- - -## 1. 背景 - -v2.7.0 Mail 飞鸽传书功能已稳定上线。实测"庞统发 request 给赵云"一封来回邮件: - -- 庞统侧 6 次 LLM 调用(input 1,702 + output 430) -- 赵云侧 4 次 LLM 调用(input 12,528 + output 277) -- 合计 10 次 LLM 调用,~14,937 tokens(不含 cache) - -**核心问题**:Agent 同时管信封(状态流转)和载荷(业务内容)。状态转换命令(claimed→working→done)占用 LLM tokens,且不可靠。 - -## 2. 业界调研 - -| 系统 | 信封(系统管) | 载荷(Agent/LLM 管) | -|------|--------------|---------------------| -| **Hermes Kanban** | Dispatcher 负责 claim → spawn → reclaim → 状态流转;Agent 通过 `kanban_complete()` 声明完成,状态写入由系统工具执行 | Agent 调 `kanban_show()` 读任务、调 `kanban_heartbeat()` 保活、调 `kanban_complete(summary)` 声明完成 | -| **MCP Agent Mail** | 系统管投递、线程聚合、消息路由 | Agent 调 `fetch_inbox()` + `acknowledge_message()` 读内容和 ack | -| **ClawTeam Inbox** | JSON 文件即消息,系统管 claim → ack 原子操作 | Agent 读文件内容、执行业务、写产出文件 | -| **EIP 信封模式** | Header(from/to/type/routing)由消息基础设施处理 | Body(业务内容)由接收方处理 | - -**共识**:状态流转是信封,属于系统。业务内容是载荷,属于 Agent。 - -## 3. 设计原则 - -1. **系统管信封**:claimed → working → done/failed 全部由系统完成 -2. **Agent 管载荷**:只看业务内容,只做业务决策(回复/不回复/触发行动) -3. **统一超时机制**:Mail 任务走标准 task 超时路径(`default_task_timeout_minutes`),不开特殊路径 -4. **产出验证**:request 类型邮件验证 Agent 是否真的回复了(幻觉门控) - -## 4. 新生命周期 - -### 4.1 正常流 - -``` -系统(Dispatcher) Agent(Main Session) -───────────── ───────────── -1. 邮件创建(Mail API) -2. Dispatcher 路由到目标 Agent -3. [新增] 系统标 working → 5. 收到精简 prompt -4. spawn Agent → 6. LLM 理解内容 - → 7. [inform] 只读,无需操作 - → [request] 回复发件者(curl) - → 8. Agent turn 结束 -9. on_complete 触发(进程退出) -10. [新增] 系统标 done/failed: - - inform → 直接标 done - - request → 幻觉门控验证后标 done/failed -``` - -### 4.2 异常流 - -| 异常 | 处理方 | 方式 | 现有机制? | -|------|--------|------|-----------| -| spawn 失败 | 系统 | 标 failed(reason: spawn_failed),不标 working | ❌ 需新增 | -| Agent 执行超时/崩溃 | 系统 | Ticker `_check_timeouts`:working 超 `default_task_timeout_minutes` 标 failed | ✅ 已有 | -| Agent 忘记回复(request) | 系统 | 幻觉门控:on_complete 时查 DB 有无 in_reply_to 的回复邮件 | ❌ 需新增 | -| Agent 回复 type 写错 | prompt | 模板提醒 | ✅ 已有 | -| on_complete 标 done API 失败 | 系统 | 重试 3 次 + 写日志 | ❌ 需新增 | - -### 4.3 on_complete 的触发时机 - -on_complete 在 openclaw 子进程退出时触发(spawner `_handle_exit`)。子进程的生命周期是: - -``` -moziplus spawn openclaw 子进程 - → 子进程调 Gateway API 投递消息到 Agent session - → Agent main session 收到 prompt - → LLM 生成回复 - → turn 结束 - → openclaw 子进程退出 - → spawner 检测退出 → on_complete 触发 -``` - -**on_complete 触发 = Agent 已处理完这封邮件(turn 结束)**。 - -如果 Agent spawn 了子任务继续干,on_complete 仍会触发(主 turn 结束 ≠ 子任务完成)。这是合理的——邮件投递 = 信息传递到位,不等后续衍生工作。 - -### 4.4 超时机制 - -Mail 任务走标准 task 超时路径,不做特殊处理: - -``` -Ticker 每 30s 扫一次 working 状态任务 - → 有 task.deadline?→ timeout = deadline - started_at - → 无 deadline?→ timeout = default_task_timeout_minutes(30 分钟) - → elapsed > timeout → 标 failed -``` - -## 5. Prompt 模板 - -### 5.1 inform 模板(~80 tokens,当前 ~200 tokens) - -``` -你收到一封飞鸽传书(纯通知)。 - -发件者: {from_agent} -主题: {title} -内容: {text} - -已阅即可,无需操作。 -``` - -### 5.2 request 模板(~150 tokens,当前 ~400 tokens) - -``` -你收到一封飞鸽传书,需要处理并回复。 - -发件者: {from_agent} -主题: {title} -内容: {text} - -请处理后回复发件者: -curl -s -X POST http://localhost:8083/api/mail \ - -H 'Content-Type: application/json' \ - -d '{"from": "{agent_id}", "to": "{from_agent}", "title": "回复: {title}", "text": "你的回复内容", "type": "inform", "in_reply_to": "{task_id}"}' - -⚠️ 将"你的回复内容"替换为实际回复。type 必须用 inform 防止循环。 -``` - -**变化**:去掉所有状态转换命令(working/done 的 curl),Agent 只看业务内容。 - -## 6. 幻觉门控 - -**来源**:Hermes v0.13 实践——Agent 声称完成时验证产出是否真实存在。 - -**Mail 场景**:request 类型邮件 on_complete 时,系统查 `_mail` DB 有无 `in_reply_to = task_id` 的回复邮件记录: - -``` -on_complete 触发 - → _classify_outcome 判断 outcome - → outcome 正常(release_counter) - → inform → 直接标 done - → request → 查 DB 有无回复邮件 - → 有 → 标 done - → 无 → 标 failed(reason: no_reply_found) -``` - -inform 类型不需要门控——不需要回复,on_complete 触发即完成。 - -## 7. 改动范围 - -| 文件 | 改动 | 行数 | 说明 | -|------|------|------|------| -| `src/daemon/spawner.py` | 精简两个模板常量 | 改 ~40 行 | 去掉 working/done curl 命令 | -| `src/daemon/dispatcher.py` | spawn 前标 working + on_complete 增强 + 幻觉门控 | 新增 ~50 行 | `_mail` 专用的 on_complete 回调 | -| `src/daemon/ticker.py` | 不改 | 0 | 走标准超时机制 | - -总计 ~90 行改动,涉及 2 个文件。 - -## 8. 预期效果 - -| 场景 | 当前 v2.7.0 | 优化后 v2.7.1 | 说明 | -|------|------------|-------------|------| -| inform 邮件(Agent 侧) | 4 次 LLM, ~13k tokens | **1 次 LLM** | Agent 读内容后无操作,系统标 done | -| request 邮件(Agent 侧) | 4 次 LLM, ~13k tokens | **2 次 LLM** | Agent 读+回复,系统标 done | -| inform prompt | ~200 tokens | **~80 tokens** | 省掉状态转换命令 | -| request prompt | ~400 tokens | **~150 tokens** | 省掉状态转换命令 | -| 状态可靠性 | 依赖 LLM 遵守 | **系统保证** | 最大收益 | - -## 9. 不做的事 - -- ❌ isolated session 投递(必须是主 Agent 来决定是否 spawn) -- ❌ Mail 专用超时配置(走标准 task 超时路径) -- ❌ inform 类型跳过 LLM(Agent 需要读内容,信息不能丢) diff --git a/docs/design/v2.7.2-counter-lifecycle-fix.md b/docs/design/v2.7.2-counter-lifecycle-fix.md deleted file mode 100644 index 3c7bc37..0000000 --- a/docs/design/v2.7.2-counter-lifecycle-fix.md +++ /dev/null @@ -1,173 +0,0 @@ -# v2.7.2 防重复调用 & 防无限续杯 — 完整方案 - -**版本**: v2.0 -**日期**: 2026-05-26 -**作者**: 庞统 -**状态**: 已实现 + 待最终评审 - ---- - -## 1. 核心原则 - -> **每次 agent 调用都是独占的。** openclaw 无论成功失败都会返回,最差情况 timeout 返回。谁占用谁持有,进程退出就 release。 - ---- - -## 2. 根因分析 - -### 2.1 2026-05-25/26 事件复盘 - -司马懿 + 庞统被 moziplus-v2 连续 spawn,叠加 API 调用触发 zhipu 429,双模型不可用导致 Gateway 假死。 - -### 2.2 发现的三个根因 - -| 根因 | 影响 | 严重度 | -|------|------|--------| -| **P0:`_parse_stdout_json` 解析路径错误** | `data.get("meta")` 应为 `data["response"]["meta"]`。68% 的 spawn 结果 transport=null(62/91 次)。A 场景分类全部失效 | **致命** | -| counter 生命周期是任务级 | retry 绕过 dispatcher 直接 spawn,不检查 counter | 高 | -| spawn 前缺 session state 检查 | webchat 占用 main session 时仍然 spawn,注定失败 | 高 | - -### 2.3 P0 详细说明 - -openclaw agent `--json` 输出格式: -```json -{ "kind": "agent-response", "response": { "meta": { "transport": "gateway", ... } } } -``` - -代码取的是 `data.get("meta")` 而不是 `data["response"]["meta"]`,导致: -- 所有 transport=null → A1/A5/A6 分类失效 -- session lock 阻塞退出被误判为 gateway_timeout(A2/A3)→ 续杯循环 -- 续杯循环叠加 API 调用 → 429 → Gateway 假死 - ---- - -## 3. 完整场景清单 - -### 3.1 Spawn 前检查(拦截无效 spawn) - -| # | 场景 | 检测方法 | 检测到后方案 | -|---|------|---------|-------------| -| L1 | moziplus 内部并发 | counter.can_acquire() | AgentBusyError → 等 ticker | -| L2 | API 429 冷却期 | counter.is_cooling_down() | AgentBusyError → 等 ticker | -| L3a | main session 被外部占用 | _check_session_state → lock_pid_alive | AgentBusyError → 等 ticker | -| L3b | main session 正在执行 | _check_session_state → status=processing | AgentBusyError → 等 ticker | -| L3c | main session 正在 compact | _check_session_state → recent_compact | AgentBusyError → 等 ticker | - -L1-L3 统一结果:不 spawn,任务保持 working,ticker 30 秒后重新调度。 - -L1+L3 互补:counter 防 moziplus 内部并发,session state 防外部占用(webchat/Control UI/cron)。 - -### 3.2 Spawn 后 — 进程退出(情况 A,0-630 秒内) - -| # | 场景 | 检测方法 | 条件 | 检测到后方案 | -|---|------|---------|------|-------------| -| A1 | 正常完成 | stdout transport + 任务状态 | exit=0 + transport≠embedded + 终态 | release counter → 结束 | -| A2/A3 | Gateway timeout | stdout transport + 任务状态 | exit=0 + 非终态 + transport 正常 | release counter → 续杯 | -| A4 | Agent 自标 failed | 任务状态 | 任务=failed | release counter → 结束 | -| A5/A6 | Gateway fallback | stdout transport=embedded + fallbackReason | exit=0 + transport=embedded | release counter → 标 failed + escalate | -| A7 | 认证失败 | exit≠0 + stderr | exit≠0 + stderr 含 401/403 | release counter → 标 failed + escalate | -| A8 | Gateway 不可达 | exit≠0 + stderr | exit≠0 + stderr 含 ECONNREFUSED/ETIMEDOUT | release counter → 等 ticker | -| A9 | API 429 | exit≠0 + stderr | exit≠0 + stderr 含 rate_limit/500/503 | release counter → 推回 pending + 冷却 120s | -| A10 | Compact 失败 | exit≠0 + stderr | exit≠0 + stderr 含 compaction-diag | release counter → 等 ticker | -| A11 | Session lock 冲突 | exit≠0 + stderr | exit≠0 + stderr 含 lock/busy/concurrent | release counter → 等 ticker | -| A12 | 兜底未知错误 | 兜底 | exit≠0 不匹配以上 | release counter → 等 ticker | -| A2+P2 | transport=null 兜底 | stderr 辅助判断 | exit=0 + transport=null + 非终态 | 检查 stderr → lock/compact/api_error 或走 gateway_timeout | - -**续杯:只有 A2/A3 才触发续杯。其他都 release counter,等 ticker 或推回 pending。** - -### 3.3 Spawn 后 — 进程不退出(情况 B,630 秒后) - -| # | 场景 | 检测方法 | 条件 | 检测到后方案 | -|---|------|---------|------|-------------| -| B1 | 假死(lock PID 死了) | _check_session_state | lock_pid 存在但 PID 不存活 | 标 failed + escalate + release counter | -| B2 | Compact 进行中 | _check_session_state + stderr | lock_pid 存活 + stderr 有 compact | 继续等(不递增计数,独立上限) | -| B3 | 进程不退出 | _check_session_state | lock_pid 存活 + 无 compact | 继续等(递增计数,≥3 次 → failed) | -| B4 | Session 状态不匹配 | _check_session_state | sessions.json status≠running | 等 60s → 按 B3 处理 | - -### 3.4 续杯限制 - -| # | 计数器 | 上限 | 超限后方案 | -|---|--------|------|-----------| -| R1 | retry_count | 3 次(每次 ~10 分钟) | 标 failed + escalate | -| R2 | connect_retry_count | 3 次 | 标 failed | -| R3 | api_retry_count | 3 次 | 标 failed | -| R4 | lock_retry_count | 3 次 | 标 failed | -| R5 | monitor_timeout_count | 3 次 | 标 failed | - -### 3.5 Ticker 兜底(每 30 秒检查) - -| # | 检查 | 方案 | -|---|------|------| -| T1 | 进程存活性:counter 占用但 PID 不存活 | release counter + 推回 pending | -| T2 | 任务超时:working 超时(默认 30 分钟) | 标 failed | - -### 3.6 假死复活术(2026-05-04 经验) - -**现象**:sessions.json 状态为 running 但 agent 长时间无响应。 - -**根因**:Gateway 认为 session 还活着(running)但实际连接已断开,无超时清理。 - -**复活步骤**: -1. 修改 sessions.json,把对应 session 的 status 从 running 改为 idle -2. 发心跳激活(把当前任务再发一遍给 agent) - -**待设计**:将此复活术集成到 ticker `_check_timeouts` 中,检测到 sessions.json status=running 但 lock PID 不存活时自动执行。 - ---- - -## 4. 改动文件清单 - -| 文件 | 改动 | 版本 | -|------|------|------| -| `counter.py` | 新增 cooldown 机制(is_cooling_down / set_cooldown) | v1 | -| `spawner.py` | 新增 AgentBusyError | v1 | -| `spawner.py` | spawn_full_agent 内部 counter acquire/release + wrapped_on_complete(try/finally) | v1 | -| `spawner.py` | spawn_full_agent 加 L3 session state 检查 | v2 | -| `spawner.py` | _classify_outcome 去掉 release_counter,只有 A2/A3 触发 retry | v1 | -| `spawner.py` | _classify_outcome 加 P2 transport=null 兜底 | v2 | -| `spawner.py` | _handle_exit:A9 推回 pending + 冷却,A5/A6 标 failed + context 日志 | v1 | -| `spawner.py` | _do_retry 手动 release counter + 通过 spawn_full_agent 重试 | v1 | -| `spawner.py` | P0:_parse_stdout_json 改为 data["response"]["meta"] | v2 | -| `spawner.py` | 新增 get_session_by_agent(进程存活性检查用) | v1 | -| `dispatcher.py` | 去掉 counter acquire/release,on_complete 只含业务逻辑 | v1 | -| `ticker.py` | _spawn_available_agents 不再管 counter | v1 | -| `ticker.py` | _check_timeouts 加进程存活性检查 + 推回 pending | v1 | -| `ticker.py` | 新增 _is_pid_alive | v1 | -| `main.py` | counter 创建提前,传给 spawner | v1 | - ---- - -## 5. 司马懿评审记录 - -### 第一次评审(mail-1779726169654) - -| 评审意见 | 结论 | 理由 | -|---------|------|------| -| wrapped_on_complete 加 try/finally | ✅ 采纳 | 防御性编程 | -| A5/A6 加 context 日志 | ✅ 采纳 | 排查方便 | -| per-provider 冷却 | ⏭ 延后 | 低优先级 | -| crash_count per-agent 累计,禁用 agent | ❌ 不采纳 | 崩溃可能是任务问题不是 agent 问题 | -| can_acquire 失败推回 claimed | ❌ 不采纳 | asyncio 单线程无竞态 | -| release 和 acquire 之间有竞态窗口 | ❌ 不存在 | 内存同步操作 | - -### 第二次评审(_do_retry counter 时序) - -| 评审意见 | 结论 | -|---------|------| -| _do_retry 续杯退化为 AgentBusyError | ✅ 采纳:_do_retry 入口手动 release counter | - -### 第三次评审(P0/P1/P2 补丁) - -| 评审意见 | 结论 | -|---------|------| -| P0 stdout 解析路径修复 | 待评审 | -| P1 spawn 前检查 | 待评审 | -| P2 transport=null 兜底 | 待评审 | - ---- - -## 6. 待办 - -- [ ] 假死复活术集成到 ticker -- [ ] 历史数据清理(P0 修复前 62 条 transport=null 的 task_attempts) -- [ ] per-provider 冷却(低优先级) diff --git a/docs/design/v2.8-changelog.md b/docs/design/v2.8-changelog.md deleted file mode 100644 index 016715e..0000000 --- a/docs/design/v2.8-changelog.md +++ /dev/null @@ -1,59 +0,0 @@ -# v2.8+ 变更记录 - -> 日期:2026-05-20 -> 作者:庞统 - -## 项目下拉改造 - -### 项目发现 -- 自动扫描 `~/.openclaw/sanguo_projects/sanguo_*` 目录(`discover_sanguo_projects()`) -- 启动时扫描 + 注册到 registry(`source: sanguo_projects_scan`) -- 下拉菜单:📋全部任务 / 📝一般任务(`_general`)/ 正式项目 / ➕新建 - -### 项目 CRUD -| 操作 | API | 说明 | -|------|-----|------| -| 自动发现 | 启动时自动 | 扫描 sanguo_projects 目录 | -| 手动创建 | `POST /api/projects` | 填写 id + name | -| 归档 | `POST /api/projects/{id}/archive` | status→archived,不显示 | -| 删除 | `DELETE /api/projects/{id}` | status→deleted(逻辑删除,不物理删) | -| 修改 | `PATCH /api/projects/{id}` | 更新 name/description | -| 移动任务 | `POST /api/projects/{id}/tasks/{tid}/move` | 跨项目移动任务 | - -### 项目状态 -- `active` → 正常显示 -- `archived` → 不显示(归档) -- `deleted` → 不显示(逻辑删除) -- 两个状态独立,不重叠 - -### 特殊项目 -| ID | 用途 | 显示 | -|----|------|------| -| `_general` | 一般任务(无归属) | 下拉菜单 + 看板 | -| `_mail` | 邮件系统 | 独立 Tab | - -## 轮询 → SSE 改造 - -### 改造前(轮询) -- 每 5-30 秒 `loadAll()` → `loadLive()` + `loadProjects()` + `loadV2Tasks()` -- 全量替换 `v2tasks` 数组 → 卡片全部重渲染 → 闪屏 + 输入失焦 - -### 改造后(SSE + 轻量轮询) -- **轮询**:只刷 `loadLive()`(ticker 数据),10-30 秒间隔 -- **SSE**:监听 `/api/events`,收到 `task_updated`/`task_completed`/`task_failed` 时只更新单个任务 -- **任务列表**:首次加载 / 切项目 / 手动刷新时才全量拉 -- **输入保护**:有输入焦点时跳过轮询,blur 2s 后补刷 - -## 铃铛通知中心 - -### 对接设计文档(topic7-9) -- 按推送级别分组(🔴🟡🟢🔵) -- 每条通知:时间 + 级别图标 + 内容摘要 + 关联任务链接 -- 展开操作按钮(查看任务 / 标记已读) -- 全部已读 -- 数据来源:未读邮件 + SSE 实时事件 - -## TaskModal 项目归属选择器 - -- 任务详情页显示当前所属项目 -- 下拉选择可移动任务到其他项目(调用 move API) diff --git a/docs/design/v2.8-state-enhancement.md b/docs/design/v2.8-state-enhancement.md deleted file mode 100644 index 1131643..0000000 --- a/docs/design/v2.8-state-enhancement.md +++ /dev/null @@ -1,432 +0,0 @@ -# v2.8 + M3 完整设计:状态增强 + 前端易用性 + Checkpoint + Artifact - -> 状态: ✅ 评审通过(Mail #303/#304/#305),待用户确认 -> 日期: 2026-05-18 -> 作者: 庞统 -> 评审: 司马懿 - ---- - -## 📋 概述 - -### 背景 -v2.7 已完成 Card 回滚、SubTask 聚合、Mail Tab。本设计覆盖 v2.8 状态增强 + 前端易用性 + M3 Checkpoint/Artifact。 - -### 问题 -1. v2.7 状态机缺少 `paused`、`escalated`、`waiting_human`(v1.0 已有) -2. 前端缺少已取消/已归档筛选,无法管理已完成任务 -3. 卡片没有快捷操作按钮,每次操作需点进 Modal -4. 项目切换在 header,不在筛选栏 -5. Checkpoint 人工干预和成果物展示是 M3 核心功能,依赖 `waiting_human` 状态 - -### 目标 -1. 补齐 3 个缺失状态 -2. 归档机制 + 前端两行筛选栏 + 卡片快捷按钮 -3. 项目下拉移入筛选栏 + 跨项目聚合 -4. Checkpoint 三种类型 + Artifact 成果物面板 -5. 新建军令 title 可选 - ---- - -## 一、状态机增强 - -### 1.1 新增状态定义 - -| 状态 | 中文 | 颜色 | 类型 | 说明 | -|------|------|------|------|------| -| `paused` | 已暂停 | `#818cf8` 紫 | 手动 | 用户主动暂停,不参与聚合、不超时 | -| `escalated` | 已升级 | `#ff5270` 红 | 手动 | Agent 升级求助,需人工介入 | -| `waiting_human` | 等待人工 | `#f59e0b` 橙 | 中间态 | Checkpoint 等人工确认后推进 | - -### 1.2 完整状态转换表(共 11 个状态) - -``` -pending: [claimed, cancelled] -claimed: [working, paused, pending, cancelled] -working: [review, blocked, failed, paused, escalated, waiting_human, cancelled] -paused: [working, cancelled] -review: [done, pending, failed, escalated, waiting_human, cancelled] -blocked: [pending, escalated, cancelled] -failed: [pending, escalated, cancelled] -escalated: [working, pending, cancelled] -waiting_human: [working, done, cancelled] -done: [] -cancelled: [] -``` - -### 1.3 各状态设计细节 - -#### paused(已暂停) -- **触发**:用户点暂停按钮(卡片右上角 ⏸) -- **恢复**:用户点 ▶ 继续 → 回到 working -- **调度**:Ticker 跳过(不 claim、不超时回收) -- **聚合**:不参与父 Task 聚合(同 cancelled) -- **超时**:无,无限等待 - -#### escalated(已升级) -- **触发**:Agent 遇到无法处理的问题,通过 status API 设置 -- **恢复**:用户介入后手动恢复到 working 或 pending -- **调度**:Ticker 跳过 -- **聚合**:视同 working 参与(表示活跃但需关注) -- **字段迁移**:废弃 `task.escalated` 布尔字段判断,改用 `task.status === 'escalated'`。保留 DB 字段做向后兼容 -- **旧数据处理**:旧 DB 中 `escalated=1` 但 `status=working` 的任务,按 working 继续调度,可接受 -- **UI**:卡片红色高亮 + ⚠️ 图标 - -#### waiting_human(等待人工) -- **触发**:Agent 执行到 Checkpoint,写 checkpoint 数据 + 设置此状态 -- **推进**:人工 approve → working/done;reject → working(带回退信息) -- **调度**:Ticker 跳过(等人工不等超时) -- **聚合**:视同 working 参与 -- **UI**:卡片橙色高亮 + 🔔 图标 + Checkpoint 面板入口 - -### 1.4 聚合规则 - -``` -父 Task 聚合优先级(高→低): - escalated > waiting_human > review > working/claimed > pending > failed > blocked - -不参与聚合: - paused, cancelled(手动/被动状态) - -全部 done → done -``` - -### 1.5 Ticker 调度规则 - -| 状态 | claim | 超时回收 | review 调度 | -|------|-------|---------|------------| -| pending | ✅ | - | - | -| claimed | - | ✅(超时→pending) | - | -| working | - | ✅(超时→failed) | - | -| review | - | - | ✅ | -| paused | ❌ | ❌ | ❌ | -| escalated | ❌ | ❌ | ❌ | -| waiting_human | ❌ | ❌ | ❌ | -| done | 终态 | | | -| failed | ❌ | | | -| blocked | ❌ | | | -| cancelled | 终态 | | | - ---- - -## 二、归档机制 - -### 2.1 后端 - -- `tasks` 表新增:`archived INTEGER DEFAULT 0`、`archived_at TEXT` -- `Task` dataclass 新增:`archived: int = 0`、`archived_at: Optional[str] = None` -- `Blackboard` 新增方法: - - `archive_task(task_id)` → archived=1 - - `unarchive_task(task_id)` → archived=0 - - `archive_all_done()` → 批量归档当前项目 done + cancelled 的任务(单项目操作) -- API: - - `POST /api/projects/{pid}/tasks/{tid}/archive` — 归档/取消归档 - - `POST /api/projects/{pid}/tasks/archive-done` — 一键归档 -- 列表查询 API 默认 `?archived=0` - -### 2.2 前端 - -- 第 1 行:活跃/归档/全部 切换 -- 一键归档按钮(有可归档任务时显示) -- 卡片上 done/cancelled 显示归档按钮 - -### 2.3 Schema 迁移(db.py) - -```sql --- 新增字段 -ALTER TABLE tasks ADD COLUMN archived INTEGER DEFAULT 0; -ALTER TABLE tasks ADD COLUMN archived_at TEXT; - --- 更新 CHECK 约束(重建 tasks 表或用新连接 pragma) --- 完整 CHECK: status IN ('pending','claimed','working','review','done','failed','blocked','cancelled','paused','escalated','waiting_human') -``` - ---- - -## 三、前端易用性改造 - -### 3.1 筛选栏两行布局(仿 v1.0) - -**第 1 行(项目 + 归档控制)**: -``` -[📦 项目▾] [活跃] [归档] [全部] 活跃12 · 归档5 · 共17 [📦 一键归档] -``` -- 项目下拉:`📋 全部任务`("")→ `📁 一般任务`("projects")→ 动态项目列表 -- 全部任务:聚合所有项目的 tasks - -**第 2 行(状态筛选 + 搜索)**: -``` -[📋全部] [📋待认领] [👤已认领] [⚔️执行中] [🔍审查中] [⏸已暂停] [⚠️已升级] [🔔等人工] [✅已完成] [❌失败] [🚧阻塞] [🚫已取消] 🔍[搜索框] -``` - -### 3.2 卡片改造 - -**右上角状态图标**: -- working/claimed → `⏸` 暂停按钮 -- paused → `▶` 恢复按钮 -- escalated → `⚠️` 图标 -- waiting_human → `🔔` 图标 - -**卡片按钮(最多 3 个)**: - -| 状态 | 卡片按钮 | -|------|---------| -| pending | `👤 认领` `🚫` | -| claimed | `⚔️ 开始` `🚫` | -| working | `🔍 提审` `⏸` `🚫` | -| paused | `▶ 继续` `🚫` | -| review | `✅ 通过` `🚫` | -| escalated | `▶ 继续` `🚫` | -| waiting_human | `✅ 确认` `🚫` | -| done | `📦 归档` | -| failed | `🔄 重试` | -| blocked | `🔄 解除` `🚫` | -| cancelled | `📦 归档` | - -**Modal 完整按钮(最多 4 个)**: - -| 状态 | Modal 按钮 | -|------|-----------| -| pending | `👤 认领` `🚫 取消` | -| claimed | `⚔️ 开始` `⏸ 暂停` `🚫 取消` | -| working | `🔍 提审` `⏸ 暂停` `⚠️ 升级` `🚫 取消` | -| paused | `▶ 继续` `🚫 取消` | -| review | `✅ 通过` `🔄 打回` `❌ 驳回` `⚠️ 升级` | -| escalated | `▶ 继续` `🔄 重置` `🚫 取消` | -| waiting_human | `✅ 确认` `❌ 驳回` | -| done | `📦 归档` | -| failed | `🔄 重试` `⚠️ 升级` `🚫 取消` | -| blocked | `🔄 解除` `⚠️ 升级` `🚫 取消` | -| cancelled | `📦 归档` | - -- 点击标题区域打开 Modal -- 动作按钮直接调 API + toast 反馈 - -### 3.3 新建军令 title 自动生成 - -- 前端 title 可选,placeholder "不填将自动生成" -- 后端 title 为空时:description 前 30 字 + "…" - ---- - -## 四、Checkpoint 机制(M3) - -### 4.1 概念 - -Agent 执行到需要人工确认的节点时,创建 Checkpoint 并设置 `waiting_human` 状态。用户在前端看到 Checkpoint 面板,审核后任务继续。 - -### 4.2 三种 Checkpoint 类型 - -| 类型 | 图标 | 主色 | 场景 | 核心交互 | -|------|------|------|------|----------| -| **验证** | 🔍 | `#6a9eff` 蓝 | 产出物确认、效果验收 | 自动核验 + 人工核验 + 双按钮审批 | -| **决策** | 🎯 | `#818cf8` 紫 | 多方案定夺、技术选型 | 三方案并排 + 利弊 + 推荐 + 确认 | -| **执行** | 🔧 | `#f59e0b` 橙 | 人工操作、环境变更 | 分步打勾 + 命令行 + 进度条 | - -### 4.3 approve/reject 状态推进规则 - -| Checkpoint 类型 | approve → | reject → | -|----------------|-----------|----------| -| verify | **done** | working(带回退信息) | -| decision | working(Agent 按选定方案继续) | working(带驳回信息) | -| action | working(Agent 继续后续步骤) | working(带失败信息) | - -**统一规则**:verify approve → done(验证通过即完成),其余 approve → working。reject 一律 → working。 - -### 4.4 后端数据模型 - -#### checkpoints 表 - -```sql -CREATE TABLE checkpoints ( - id TEXT PRIMARY KEY, - task_id TEXT NOT NULL, - type TEXT NOT NULL CHECK (type IN ('verify', 'decision', 'action')), - title TEXT NOT NULL, - description TEXT, - status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')), - payload TEXT NOT NULL, -- JSON,含 version 字段 - created_at TEXT NOT NULL DEFAULT (datetime('now')), - resolved_at TEXT, - resolved_by TEXT, - resolve_note TEXT, - FOREIGN KEY (task_id) REFERENCES tasks(id) -); -``` - -#### payload 结构(version: 1) - -**验证(verify)**: -```json -{ - "version": 1, - "auto_checks": [{"label": "测试通过", "passed": true}], - "human_steps": ["确认代码风格", "确认功能正确"], - "preview_url": "http://..." -} -``` - -**决策(decision)**: -```json -{ - "version": 1, - "options": [ - {"id": "opt1", "label": "方案A", "desc": "...", "pros": ["..."], "cons": ["..."], "recommended": true}, - {"id": "opt2", "label": "方案B", "desc": "...", "pros": ["..."], "cons": ["..."]}, - {"id": "opt3", "label": "方案C", "desc": "...", "pros": ["..."], "cons": ["..."]} - ] -} -``` - -**执行(action)**: -```json -{ - "version": 1, - "steps": [ - {"id": "s1", "desc": "备份数据库", "command": "mysqldump ..."}, - {"id": "s2", "desc": "执行迁移", "command": "python migrate.py"} - ], - "verify_command": "python check.py" -} -``` - -### 4.5 API - -``` -GET /api/projects/{pid}/tasks/{tid}/checkpoints — 列出 task 的所有 checkpoint -POST /api/projects/{pid}/tasks/{tid}/checkpoints — Agent 创建 checkpoint -POST /api/projects/{pid}/tasks/{tid}/checkpoints/{cid}/approve — 用户通过 -POST /api/projects/{pid}/tasks/{tid}/checkpoints/{cid}/reject — 用户驳回 -``` - -approve/reject 时后端自动将 task 从 `waiting_human` 推进: -- approve: verify → done, decision/action → working -- reject: 一律 → working - -### 4.6 Agent 触发流程 - -``` -1. Agent 执行到需要人工确认的节点 -2. Agent 调 POST /checkpoints 创建 checkpoint(type + payload) -3. Agent 调 POST /tasks/{id}/status 设置 waiting_human -4. Ticker 检测到 waiting_human → 跳过调度 -5. 前端轮询检测到 waiting_human + checkpoint → 渲染 CheckpointPanel -6. 用户 approve/reject -7. 后端自动推进状态 → working/done -8. Ticker 下次 tick 继续调度 -``` - -### 4.7 前端 CheckpointPanel 组件 - -文件已存在:`CheckpointPanel.tsx`(350 行),未被引用。**扩展不重写**,对接 v2.8 新 API。 - -在 TaskModal 中当 `status=waiting_human` 时自动显示 CheckpointPanel: -- **验证面板**:自动核验结果 ✅/❌ + 人工核验步骤 + 预览链接 + 双按钮 -- **决策面板**:三列方案卡片 + 利弊 + 推荐 + 御批备注 + 确认 -- **执行面板**:步骤列表(可打勾)+ 命令行高亮 + 进度条 + 验证确认 - ---- - -## 五、Artifact 成果物面板(M3) - -### 5.1 后端 - -扩展现有 `outputs` 表: - -```sql -ALTER TABLE outputs ADD COLUMN file_name TEXT; -ALTER TABLE outputs ADD COLUMN file_size INTEGER; -ALTER TABLE outputs ADD COLUMN file_path TEXT; -- NAS/本地路径 -ALTER TABLE outputs ADD COLUMN mime_type TEXT; -``` - -API: -``` -GET /api/projects/{pid}/tasks/{tid}/outputs — 列出成果物 -GET /api/projects/{pid}/tasks/{tid}/outputs/{oid}/download — 下载文件 -``` - -### 5.2 前端 ArtifactPanel 组件 - -文件已存在:`ArtifactPanel.tsx`(185 行),未被引用。**扩展不重写**。 - -- **文件类型图标**:📄文档 / 💻代码 / 📊数据 / ⚙️配置 / 📁其他 -- **卡片布局**:文件名 + 类型标签 + 大小 + 时间 + 下载按钮 -- **下载**:直接下载 - ---- - -## 六、影响范围 - -### 后端(8 文件) - -| 文件 | 改动 | 阶段 | -|------|------|------| -| `db.py` | CHECK 约束加 3 状态 + archived 字段 + checkpoints 表 | v2.8 | -| `models.py` | Task 加 archived/archived_at | v2.8 | -| `operations.py` | 归档方法 + 状态转换 + Checkpoint CRUD | v2.8+M3 | -| `queries.py` | 聚合规则更新 | v2.8 | -| `blackboard_routes.py` | 归档 API + title 自动生成 + 新状态 | v2.8 | -| `ticker.py` | 调度规则 + 聚合更新 | v2.8 | -| **新增** `checkpoint_routes.py` | Checkpoint CRUD API | M3 | -| `main.py` | 注册 checkpoint router | M3 | - -### 前端(7 文件) - -| 文件 | 改动 | 阶段 | -|------|------|------| -| `EdictBoard.tsx` | 两行布局 + 项目下拉 + 卡片按钮 + 新状态 | v2.8 | -| `TaskModal.tsx` | 新状态转换按钮 | v2.8 | -| `App.tsx` | 移除 header 项目 select | v2.8 | -| `store.ts` | 全项目聚合 + 归档 + 新状态 | v2.8 | -| `api.ts` | 新 API | v2.8+M3 | -| `CheckpointPanel.tsx` | 扩展对接新 API | M3 | -| `ArtifactPanel.tsx` | 扩展对接新 API | M3 | - ---- - -## 七、实施计划 - -### Phase 1:v2.8 后端状态增强 -1. `db.py` — schema migration + CHECK + MANUAL_STATUSES -2. `models.py` — archived/archived_at -3. `operations.py` — 归档方法 + 新状态转换 -4. `queries.py` — 聚合规则 -5. `blackboard_routes.py` — 归档 API + title 自动生成 -6. `ticker.py` — 调度规则 -7. 测试验证 - -### Phase 2:v2.8 前端改造(可与 Phase 3 并行) -1. `store.ts` — 全项目聚合 + 归档 -2. `EdictBoard.tsx` — 两行布局 + 项目下拉 + 卡片按钮 -3. `App.tsx` — 移除 header select -4. `TaskModal.tsx` — 新状态按钮 -5. 前端构建验证 - -### Phase 3:M3 Checkpoint 后端(可与 Phase 2 并行) -1. checkpoints 表 + CRUD -2. checkpoint_routes.py(approve/reject 推进表写入注释) -3. Agent 触发流程 -4. outputs 表扩展 - -### Phase 4:M3 前端(依赖 Phase 2 + 3) -1. CheckpointPanel.tsx 对接 API -2. ArtifactPanel.tsx 对接 API -3. TaskModal 集成 -4. E2E 测试 - -### Phase 5:集成 + 部署 -1. 全量测试 -2. 前端构建 -3. 部署上线 - ---- - -## 八、评审记录 - -| # | 时间 | 内容 | -|---|------|------| -| Mail #303 | 2026-05-18 22:45 | 庞统发设计文档给司马懿 | -| Mail #304 | 2026-05-18 22:48 | 司马懿评审:BUG-29(escalated 字段冲突)+ BUG-30(approve 推进)+ 5 OBS + 2 Q | -| Mail #304 | 2026-05-18 22:50 | 庞统回复:BUG-29 废弃布尔改 status、BUG-30 明确推进规则、采纳 OBS-30/32/33 | -| Mail #305 | 2026-05-18 22:51 | 司马懿最终结论:✅ 评审通过 | diff --git a/docs/design/v3.0-router-refactor.md b/docs/design/v3.0-router-refactor.md deleted file mode 100644 index d08d6dc..0000000 --- a/docs/design/v3.0-router-refactor.md +++ /dev/null @@ -1,475 +0,0 @@ -# v3.0 调度重构方案:去掉独立 LLM,改为广播认领 + 确定性交接 - -**日期**: 2026-05-21 -**状态**: 待司马懿评审 -**影响文件**: `router.py`, `dispatcher.py`, `ticker.py`, `main.py`, `config/default.yaml` - ---- - -## 1. 问题 - -### 1.1 LLMDriver 是设计之外的野路子 - -当前 Router 的 `LLMDriver` 用独立 `OpenAI()` 客户端直连 zhipu API 做路由决策: -- 不属于 L1(调了 LLM)、不属于 L2(不是 openclaw agent)、不属于 L3(不是完整 Agent) -- 不走 Gateway(无模型路由、无 fallback、无计费) -- 需要单独维护 api_base/api_key(和 Gateway 配置重复) - -**已导致实际故障**:`general-20260521-0004` 因凭据为空反复调度失败。 - -### 1.2 _dispatch_pending 绕过了认领机制 - -设计文档核心原则(§1.2): -> "编排是 AI Agent 在黑板上**自主领活**(动态协作)" -> "Daemon 是**投递员,不是决策者**" - -但 `_dispatch_pending` 的实际行为是: -``` -pending → Router 决定 agent → 强制 claimed + spawn → Agent 被动执行 -``` - -代码里认领的全部基础设施(API + CAS + inbox 事件)都已经实现,但被跳过了。 - -### 1.3 设计违背 - -architecture-v2.6.md §1.1: -> "Agent 决策,Daemon 执行" — 庞统做 plan、张飞领任务、关羽发现风险,都写在黑板上。 - -Daemon 替 Agent 做决策(Router 决定分给谁),违反了"Agent 决策"原则。 - ---- - -## 2. 设计文档中已有的完整方案 - -### 2.1 认领基础设施(已实现) - -| 组件 | 代码位置 | 说明 | -|------|----------|------| -| **claim API** | `POST /tasks/{id}/claim` | 原子 CAS 认领 | -| **claim_task()** | `operations.py:155` | `WHERE status='pending' AND (assignee IS NULL OR assignee=?)` | -| **inbox agent_claim** | `inbox.py:264` | Agent 通过 JSONL 通知 Daemon 认领 | -| **claim_timeout** | `ticker.py:530` | claimed 超时 5 分钟 → 重置为 pending(续杯) | - -### 2.2 竞态解决(已设计,§3.6) - -1. **默认:先到先得** — SQLite CAS,谁先 claim 谁做 -2. **升级:庞统仲裁** — 争议时 @庞统 请求仲裁 -3. **最终:用户拍板** — @user 请求用户决定 - -### 2.3 续杯兜底(已实现) - -``` -pending → 广播 → 无人认领 → claim_timeout(5min) → pending → 再广播 → ... → 庞统兜底 -``` - -`claim_timeout_minutes = 5.0`,`_check_timeouts` 自动把超时的 claimed 重置为 pending。 - -### 2.4 三层执行模型(§4.2) - -| 层级 | 方式 | 命令 | 适用场景 | -|------|------|------|---------| -| **L1 Daemon 直接操作** | SQLite/文件 | — | 状态更新、机械验证 | -| **L2 spawn sub** | 隔离 session | `openclaw agent --agent --session-id ` | scope guard、格式校验 | -| **L3 run agent** | 完整黑板参与者 | `openclaw agent --agent ` | 编码、审查、策略 | - -> **核心原则:系统只有两种 LLM 调用方式,都通过 Gateway,没有第三种。** - ---- - -## 3. 方案:广播认领 + 确定性交接 - -### 3.1 核心思路 - -任务调度分两条路径: - -**确定性路径**:已明确知道下一步该谁做 → 直接 spawn,不需要广播 -- retry → 原执行者 -- Agent handoff(next_capability)→ 能力匹配 -- 有 assignee → 直接分 -- review 生命周期 → 能力匹配 - -**广播认领路径**:首次分配 / 不确定场景 → spawn 所有空闲 Agent,自主 claim -- Agent 读黑板 → 自己判断是否适合 → claim -- 无人认领 → 续杯 → 庞统兜底 - -### 3.2 完整调度流程 - -``` -Ticker._tick_project() - │ - ├─ 1. 扫描状态 - ├─ 2. 依赖推进 - ├─ 3. 超时检测(claimed 5min → pending,working 30min → failed) - │ - ├─ 4. 调度 pending 任务:_dispatch_pending() - │ for each pending task: - │ ├─ 有 next_capability?→ 能力匹配 → 直接 spawn 对应 Agent - │ ├─ 有 assignee?→ 直接 spawn 该 Agent - │ ├─ retry?→ spawn 原执行者 - │ └─ 都没有?→ _broadcast_claim() - │ → spawn 每个空闲 Agent,传入"黑板上有新任务,请认领"的 prompt - │ → Agent 读黑板(GET /tasks?status=pending) - │ → Agent 判断是否适合自己 - │ → Agent claim(POST /tasks/{id}/claim)或退出 - │ - ├─ 5. 调度 review 任务:_dispatch_reviews()(不变) - └─ 6-10. 其他处理(不变) -``` - -### 3.3 广播认领的 Spawn Prompt - -广播 spawn 的 Agent 收到的 prompt: - -``` -你是 {agent_id}。黑板上有新的待认领任务。 - -## 操作 -1. 读黑板查看待认领任务: - curl http://{api_host}:{api_port}/api/projects/{project_id}/tasks?status=pending - -2. 分析每个 pending 任务,判断是否适合你: - - 你的能力:{capabilities} - - 任务类型、描述、优先级是否匹配你的专长 - -3. 如果有适合你的任务,立即认领: - curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/claim \ - -H 'Content-Type: application/json' \ - -d '{"agent": "{agent_id}"}' - -4. 认领成功后,开始执行(状态改为 working) - -5. 如果没有适合你的任务,直接退出 -``` - -### 3.4 Claim 竞争处理 - -多个 Agent 同时 claim 同一个任务: -- **SQLite CAS**:`WHERE status='pending'` — 只有第一个成功,其余 rowcount=0 -- **Agent 行为**:claim 失败 → 检查其他 pending 任务 → 没有适合的 → 退出 -- **自然负载均衡**:不同 Agent 倾向认领不同类型的任务(张飞→coding,司马懿→review) - -### 3.5 无人认领兜底 - -``` -广播 → 5 分钟内无人 claim → claim_timeout → pending → 下轮 ticker 再广播 -→ 连续 3 轮无人认领 → spawn 庞统 → 庞统决定分配或自己执行 -``` - -连续无人认领的检测:在 `events` 表中记录 `broadcast_sent` 事件,`_check_timeouts` 中统计广播轮次,超过阈值 escalate to 庞统。 - ---- - -## 4. 改动清单 - -### 4.1 删除 `LLMDriver` 类(router.py) - -删除整个 `LLMDriver` 类(~120 行)。`AgentRouter.route()` 末尾改为返回 delegate。 - -### 4.2 `AgentRouter.__init__` 去掉 `llm_driver` 参数 - -### 4.3 新增 `_broadcast_claim`(ticker.py) - -司马懿建议:攒一批任务,每轮 ticker 最多广播一次(而非每个 pending 任务触发一次广播)。 -5 个任务只需 spawn 5 个 Agent,而不是 25 个。 - -广播前检查全局并发(司马懿建议 1):接近上限时跳过本轮广播。 - -```python -async def _broadcast_claim(self, tasks, db_path, project_id): - """广播一批待认领任务给所有空闲 Agent,每轮最多广播一次""" - # 全局并发检查 - if self.counter and self.counter.global_active >= self.counter._max_global - 1: - logger.info("Skipping broadcast: global concurrent near limit") - return [] - - idle_agents = self._get_idle_agents() - if not idle_agents: - return [] - - spawned = [] - for agent_id in idle_agents: - if not await self.counter.can_acquire(agent_id): - continue - prompt = self._build_claim_prompt(agent_id, tasks, project_id) - await self.counter.acquire(agent_id) - session_id = await self.spawner.spawn_full_agent( - agent_id=agent_id, - message=prompt, - on_complete=lambda aid, _: self.counter.release(aid), - ) - spawned.append(agent_id) - return spawned -``` - -广播 prompt 包含所有 pending 任务列表: -```python -def _build_claim_prompt(self, agent_id, tasks, project_id): - task_list = "\n".join([ - f"- ID: {t.id}, 标题: {t.title}, 类型: {t.task_type}, 优先级: {t.priority}" - for t in tasks - ]) - return f"""你是 {agent_id}。黑板上有 {len(tasks)} 个待认领任务。 - -## 待认领任务 -{task_list} - -## 操作 -1. 读黑板查看详情: - curl http://{api}/api/projects/{project_id}/tasks?status=pending - -2. 选择适合你的任务并认领: - curl -X POST http://{api}/api/projects/{project_id}/tasks/{task_id}/claim \ - -H 'Content-Type: application/json' -d '{{"agent": "{agent_id}"}}' - -3. 认领成功后开始执行(状态改为 working) -4. 没有适合你的任务则退出 -""" -``` - -### 4.4 Dispatcher 增加 delegate 模式(dispatcher.py) - -`_build_spawn_message` 增加 `delegate` 分支(庞统兜底 prompt),和广播认领共存: -- 广播认领失败 → 任务回到 pending → 多轮后 → spawn 庞统 delegate - -### 4.5 main.py 去掉 LLMDriver 初始化 - -### 4.6 config/default.yaml 去掉 routing 节 - -### 4.7 无人认领检测(复用 retry_count) - -司马懿建议:不需要新增 broadcast_sent 事件。无人认领重置 pending 时 retry_count 已在递增,直接用它判断阈值。当 `retry_count >= 3` 时 escalate to 庞统。 - ---- - -## 5. 广播认领完整生命周期 - -> 本节记录从任务创建到最终闭环的完整链路,包括所有已实现但此前未在设计文档中记录的机制。 - -### 5.1 完整流程图 - -``` -用户创建任务 → pending - ↓ -Ticker 检测到 pending 任务 - ↓ -有确定性路径?(retry/handoff/assignee/生命周期) - ├─ 是 → 直接 spawn 指定 Agent - └─ 否 → 进入广播认领 - ↓ - 广播前检查全局并发(counter.is_near_limit()) - → 接近上限?跳过本轮 - ↓ - 获取空闲 Agent 列表(从 config agents 列表取) - → 无空闲?log warning,不递增 retry_count,等下轮 - ↓ - spawn 所有空闲 Agent,传入 claim prompt - retry_count++(本轮广播计数) - ↓ - ┌─ 有人 claim → 正常执行流程 - └─ 无人 claim - ↓ - claimed 超时(5min)→ _check_timeouts 重置为 pending - retry_count 已递增 - ↓ - retry_count < 3?→ 下轮 Ticker 继续广播 - retry_count >= 3?→ escalate to 庞统 -``` - -### 5.2 retry_count 的三种递增场景 - -| 场景 | 触发位置 | retry_count++? | 说明 | -|------|----------|-----------------|------| -| 广播后无人认领 | `_broadcast_claim` L536 | ✅ 是 | 每轮广播递增,记录广播轮次 | -| claimed 超时回收 | `_check_timeouts` L724 | ✅ 是 | Agent 认领后未执行,视为失败 | -| 无空闲 Agent | `_broadcast_claim` L527 | ❌ 否 | 系统容量问题,不是任务问题 | - -### 5.3 超时阈值(与 working 状态一致) - -| 状态 | 超时 | 处理 | -|------|------|------| -| claimed | `claim_timeout_minutes = 5.0` | → pending(retry_count < 3)或 escalated(retry_count >= 3) | -| working | `default_task_timeout_minutes = 30.0` | → failed | - -超时后 retry_count >= 3 时的处理与 working 超时一致:标记 escalated,触发用户介入流程。 - -### 5.4 庞统兜底(delegate 模式) - -当任务进入以下状态时,Router 路由到 `FALLBACK_AGENT = "pangtong-fujunshi"`,mode="delegate": -- 广播 3 轮无人认领(retry_count >= 3) -- 确定性路由无法匹配(模糊场景) - -庞统收到的 delegate prompt 包含:任务信息 + 团队能力列表 + API 端点。庞统可以: -1. 直接分配给指定 Agent(通过 claim API) -2. 自己执行 -3. 判断任务不合理 → cancel - -### 5.5 用户介入链路(已完整实现) - -当任务 escalated 后,用户介入的完整链路: - -``` -Agent 超时/广播无人认领 - → Ticker 标记 status="escalated" - → SSE broker.publish_sync("task_updated", {...}) - → 前端 EventSource 实时收到事件 - → 通知中心推送 "{old_status} → escalated" - → 卡片红色高亮 + ⚠️ 图标 - → 用户点开 TaskModal - → 看到 escalated 按钮: - · ▶ 继续执行 (→ working) - · 🔄 重新分配 (→ pending, retry_count 重置) - · 🚫 取消 (→ cancelled) - ▸ 高级操作: - · (可扩展更多手动干预) - → 用户点击 → POST /tasks/{id}/status → SSE 推送 → 状态更新 -``` - -**已实现代码对应表:** - -| 环节 | 代码文件 | 具体实现 | -|------|----------|----------| -| 标记 escalated | `ticker.py` _check_timeouts / _broadcast_claim | _transition_status(conn, task_id, "escalated", ...) | -| SSE 推送 | `blackboard_routes.py` update_status | broker.publish_sync("task_updated", ...) | -| SSE 基础设施 | `sse.py` / `sse_routes.py` | SSEBroker + EventSource endpoint | -| 前端 SSE 监听 | `store.ts` startSSE() | addEventListener('task_updated', ...) | -| 前端通知中心 | `store.ts` _pushSseEvent() | 通知列表 + 未读计数 | -| escalated 按钮 | `TaskModal.tsx` PRIMARY_ACTIONS[escalated] | ▶ 继续执行 + 🔄 重新分配 | -| escalated 高级操作 | `TaskModal.tsx` ADVANCED_ACTIONS | 🚫 取消 | -| 状态转换 API | `blackboard_routes.py` POST /tasks/{id}/status | VALID_TRANSITIONS 校验 | -| 转换矩阵 | `db.py` VALID_TRANSITIONS | escalated → {working, pending, cancelled} | - -### 5.6 前端按钮完整矩阵(v3.1 用户确认版) - -**设计原则:** -- 暂停和取消是用户的通用权利,所有非终态都有 -- AI 自动流转的操作(认领、开始、提审、通过、打回)不需要按钮 -- escalated / waiting_human 是 AI 发起后等待用户决策,不是通用按钮 -- blocked 任务本身没在动,暂停无意义 -- failed 的升级是 AI 发起不是按钮 - -#### 状态定义 - -| 状态 | 含义 | 谁触发 | 颜色 | -|------|------|--------|------| -| pending | 等待 AI 认领 | 用户创建/AI流转 | 蓝 | -| claimed | AI 已认领 | AI 认领 | 紫 | -| working | AI 执行中 | AI 开始 | 蓝 | -| paused | 用户暂停 | 用户 | 紫 | -| review | AI 审查中 | AI 提交审查 | 紫 | -| failed | 失败 | AI 或 daemon | 红 | -| blocked | 缺条件卡住 | Agent 遇到外部阻塞 | 橙 | -| escalated | AI 解决不了,升级求助 | Agent | 红 | -| waiting_human | 流程验收点等用户确认 | Agent Checkpoint | 橙 | -| done | 完成 | 审查通过/用户确认 | 绿 | -| cancelled | 取消 | 用户 | 灰 | - -**escalated vs waiting_human 区别:** -- escalated:Agent 遇到**意外问题**解决不了(如技术选型不确定)→ 红色 → 严重 -- waiting_human:执行到**计划中的验收点**(如 Checkpoint)→ 橙色 → 正常进度 - -#### 按钮矩阵 - -| 状态 | 用户按钮 | 说明 | -|------|---------|------| -| **pending** | ⏸ 暂停、🚫 取消 | 等 AI 认领时用户可暂停或取消 | -| **claimed** | ⏸ 暂停、🚫 取消 | AI 已认领未开始 | -| **working** | ⏸ 暂停、🚫 取消 | AI 执行中 | -| **paused** | ▶ 恢复、🚫 取消 | 恢复回暂停前状态 | -| **review** | ⏸ 暂停、🚫 取消 | AI 审查中 | -| **failed** | 🔄 重试、🚫 取消 | 重试回 pending | -| **blocked** | 🔓 解除(→pending)、🚫 取消 | 任务本身没在动,暂停无意义 | -| **escalated** | ▶ 继续(→working)、🔄 重分配(→pending)、🚫 取消 | AI 发起升级后用户决策 | -| **waiting_human** | ✅ 确认、🔄 拒绝(→working)、🚫 取消 | 流程验收点 | -| **done** | 📦 归档 | 终态 | -| **cancelled** | 🔄 重新启动(→pending)、📦 归档 | 可重启保留历史 | - -#### 状态机变更 - -暂停是通用权利,需要在更多状态允许 → paused 转换: - -```python -# 当前 -VALID_TRANSITIONS = { - "pending": {"claimed", "cancelled"}, - "claimed": {"working", "paused", "pending", "cancelled"}, - "working": {"review", "blocked", "failed", "paused", "escalated", "waiting_human", "cancelled"}, - "paused": {"working", "cancelled"}, - "review": {"done", "pending", "failed", "escalated", "waiting_human", "cancelled"}, - "blocked": {"pending", "escalated", "cancelled"}, - "failed": {"pending", "escalated", "cancelled"}, - "escalated": {"working", "pending", "cancelled"}, - "waiting_human": {"working", "done", "cancelled"}, - "done": set(), - "cancelled": set(), -} - -# 变更后 -VALID_TRANSITIONS = { - "pending": {"claimed", "paused", "cancelled"}, # +paused - "claimed": {"working", "paused", "pending", "cancelled"}, # 不变 - "working": {"review", "blocked", "failed", "paused", "escalated", "waiting_human", "cancelled"}, # 不变 - "paused": {"pending", "cancelled"}, # 恢复统一回 pending - "review": {"done", "pending", "failed", "paused", "escalated", "waiting_human", "cancelled"}, # +paused - "blocked": {"pending", "escalated", "cancelled"}, # 不变(blocked暂停无意义) - "failed": {"pending", "escalated", "cancelled"}, # 不变(暂停语义不清晰) - "escalated": {"working", "pending", "paused", "cancelled"}, # +paused - "waiting_human": {"working", "done", "paused", "cancelled"}, # +paused - "done": {"cancelled"}, # +cancelled(取消已完成任务) - "cancelled": {"pending"}, # +pending(重新启动) -} -``` - -**paused 恢复机制:** paused 记录 `resumed_from` 字段,恢复时回到暂停前状态,从暂停的节点继续执行(允许一个 stage 的冗余重跑)。 -- working → paused → ▶恢复 → working -- review → paused → ▶恢复 → review -- claimed → paused → ▶恢复 → claimed - -#### 取消 ADVANCED_ACTIONS 折叠区 - -v3.1 不再使用 ADVANCED_ACTIONS 折叠区。所有用户操作直接展示在主操作区。 -原因:暂停和取消是通用权利,不应隐藏在折叠区里。 - ---- - -## 6. 场景对比 - -| 场景 | 改前(独立 LLM 分配) | 改后(广播认领 + 确定性交接) | -|------|---------------------|---------------------------| -| retry | 原执行者 | 不变(确定性) | -| Agent handoff | 能力匹配 | 不变(确定性) | -| 有 assignee | 直接分 | 不变(确定性) | -| review 生命周期 | 能力匹配 | 不变(确定性) | -| **首次分配** | **独立 LLM 决定分给谁** | **广播所有空闲 Agent,自主认领** | -| **无人认领** | **无此场景(强制分配)** | **续杯 → 庞统兜底** | - ---- - -## 7. 代码量 - -- **删**:~130 行(`LLMDriver` + routing config 初始化 + config.yaml routing 节) -- **改**:~30 行(`Router.route()` 末尾 + `Dispatcher._build_spawn_message()` delegate 分支) -- **新增**:~60 行(`_broadcast_claim` + `_build_claim_prompt`) -- **净减**:~40 行 - ---- - -## 8. 风险与缓解 - -| # | 风险 | 评估 | 缓解 | -|---|------|------|------| -| 1 | 广播 spawn 消耗资源(每个 pending 任务都 spawn 所有空闲 Agent) | 中 | 只有"无确定性路径"的任务才广播;且 Agent 读黑板后无适合任务会快速退出 | -| 2 | 多 Agent 竞争 claim | 低 | SQLite CAS 先到先得,已实现 | -| 3 | 无人认领 | 低 | 续杯机制兜底,多轮后庞统接管 | -| 4 | Agent 认领了不适合的任务 | 低 | Agent 有完整上下文(SOUL+AGENTS+能力),比 LLM 判断更准确 | -| 5 | 广播速度比直接分配慢 | 低 | 首次分配不需要快,准确比快重要 | - ---- - -## 9. 实施步骤 - -1. `router.py`:删除 `LLMDriver` 类 + `AgentRouter` 去掉 `llm_driver` + `route()` 末尾改 delegate -2. `ticker.py`:新增 `_broadcast_claim` + `_build_claim_prompt`,修改 `_dispatch_pending` 增加 广播路径 -3. `dispatcher.py`:`_build_spawn_message()` 增加 delegate 分支(庞统兜底) -4. `main.py`:删除 `llm_driver` 初始化块 -5. `config/default.yaml`:删除 `routing` 节 -6. 测试:创建 pending 任务 → 观察广播 spawn → Agent claim → 执行 diff --git a/docs/design/v3.0-ux-simplification.md b/docs/design/v3.0-ux-simplification.md deleted file mode 100644 index 668fd67..0000000 --- a/docs/design/v3.0-ux-simplification.md +++ /dev/null @@ -1,119 +0,0 @@ -# v3.0 UX 精简 — AI Native 交互原则 - -> 日期:2026-05-21 -> 作者:庞统 -> 状态:待评审 - -## 1. 核心原则 - -**AI Native 的交互:Agent 能解决的 Agent 解决,需要人类干预才人类干预。** - -| 原则 | 说明 | -|------|------| -| 看板只看不做 | 卡片 = 状态标签 + 信息,不是操作面板 | -| 操作台按需呈现 | TaskModal 只显示**当前状态人需要做的那个动作** | -| 意图必须明确 | 按钮文字必须是"你要我做什么",不是"系统状态转换" | -| 非必要不出现 | Agent 自动流转的状态不显示操作按钮 | - -## 2. 状态与人机交互矩阵 - -| 状态 | 含义 | Agent 自行解决? | 需要人干预? | 给人的按钮 | 卡片显示 | -|------|------|:---:|:---:|------|------| -| pending | 等待调度 | ✅ 自动认领 | — | 无 | 🟡 待调度 | -| claimed | 已分配 | ✅ 自动开始 | — | 无 | 🟡 已分配 | -| working | 执行中 | ✅ Agent 执行 | — | 无 | 🟢 执行中 | -| review | 审查中 | ✅ Agent 审查 | — | 无 | 🔵 审查中 | -| **waiting_human** | 等人工确认 | — | ✅ Agent 明确请求 | **确认完成 / 拒绝** | 🟠 待确认 | -| **escalated** | 升级求助 | — | ✅ Agent 处理不了 | **继续 / 重新分配** | 🔴 需介入 | -| **failed** | 失败 | ✅ 自动重试 | 超重试上限 | **重试 / 取消** | 🔴 失败 | -| **blocked** | 阻塞 | — | ✅ 需外部帮助 | **解除阻塞** | 🔴 阻塞 | -| paused | 用户暂停 | — | 用户主动 | **继续 / 取消** | ⚪ 已暂停 | -| done | 完成 | ✅ 终态 | 可选归档 | **归档**(可选) | ⚪ 已完成 | -| cancelled | 取消 | — | 终态 | **归档**(可选) | ⚪ 已取消 | - -## 3. 卡片设计变更 - -### 3.1 卡片 = 状态标签 + 信息 - -**移除**:卡片上所有操作按钮(working 的"人工审核"、pending 的"认领"等) - -**保留**: -- 标题、描述摘要 -- 状态指示圆点(替代原来的 risk_level 标签) -- 指派人、时间 -- 点击打开 TaskModal - -### 3.2 状态指示圆点 - -替代卡片左下角的 `risk_level: standard` 标签: - -| 圆点 | 颜色 | 对应状态 | -|------|------|---------| -| 🟢 | `#2ecc8a` 绿色 | working, claimed | -| 🟡 | `#f5c842` 黄色 | pending, review | -| 🟠 | `#f59e0b` 橙色 | waiting_human | -| 🔴 | `#ff5270` 红色 | failed, blocked, escalated | -| ⚪ | `#6b7280` 灰色 | done, cancelled, paused | - -**非标准风险**(high/critical)时,圆点旁额外显示风险标签(如"⚠️ 高风险")。 - -## 4. TaskModal 状态操作变更 - -### 4.1 移除冗余的"状态操作"区域 - -当前 TaskModal 总览里有一个"🔄 状态操作"区域,列出所有合法状态转换按钮(working 有 7 个)。 - -**改为**:只显示**当前状态需要人做的那个动作**,用意图明确的按钮。 - -### 4.2 各状态的按钮方案 - -| 状态 | 显示的按钮 | 按钮含义 | -|------|----------|---------| -| pending | 无 | 等待调度引擎自动分配 | -| claimed | 无 | 等待 Agent 自动开始 | -| working | **暂停** | 主动暂停任务 | -| review | 无 | 等待审查 Agent 完成 | -| waiting_human | **✅ 确认完成** / **🔄 拒绝,继续做** | 对 Agent 的 Checkpoint 做出决策 | -| escalated | **▶ 继续执行** / **🔄 重新分配** | 对升级任务给出指导 | -| failed | **🔄 重试** | 手动重试 | -| blocked | **🔓 解除阻塞** | 帮助 Agent 继续前进 | -| paused | **▶ 继续** / **🚫 取消** | 用户主动恢复或取消 | -| done | **📦 归档** | 可选归档 | -| cancelled | **📦 归档** | 可选归档 | - -### 4.3 高级操作(收起) - -对于需要手动干预的高级场景(如手动提交审查、强制标记失败),提供"高级操作"收起区域,不默认展示。 - -## 5. Ticker 扫描 _general 修复 - -### 问题 -`_general` 不在 `registry.db` → ticker 不扫描 → 任务创建后不被调度。 - -### 方案 -在 `Ticker._tick_all()` 中,遍历 registry 项目后,额外检查 `_general/blackboard.db` 是否存在。存在则作为虚拟项目扫描一次。 - -```python -# ticker.py _tick_all 方法末尾 -general_db = Path(self.registry.root) / "_general" / "blackboard.db" -if general_db.exists(): - pr = await self._tick_project("_general", {"status": "active", "source": "virtual"}) - results["projects"]["_general"] = pr -``` - -## 6. 变更范围 - -### 前端 -- `EdictBoard.tsx`:移除 CARD_ACTIONS,卡片不再显示操作按钮;risk_level 改为状态圆点 -- `TaskModal.tsx`:精简 StatusButtons,每个状态只显示必要的按钮 - -### 后端 -- `ticker.py`:_tick_all 额外扫描 _general - -### 设计文档 -- 本文档为 v3.0 UX 精简设计,评审通过后归档 - -## 7. 不改的 -- 状态机本身(11 个状态、转换矩阵不变) -- 后端 API(PATCH/POST 端点保留,前端不再调用不代表后端删掉) -- TaskModal 其他选项卡(产出/审查/经验/子任务不变)