Files
sanguo_moziplus_v2/docs/design/deployment-v2.6.md
T
2026-05-17 00:10:40 +08:00

494 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.11moziplus venv |
| Node.js | v22.22.1 |
| PM2 | 6.0.14 |
| OpenClaw Gateway | 127.0.0.1:18789 |
| NAS | 192.168.2.154SMB /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 ticker30s
├── asyncio background task: Inbox watcher1s 轮询)
├── Agent spawn: asyncio.create_subprocess_exec(异步非阻塞)
└── ActiveAgentCounterasyncio.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) 影响