# 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) 影响