docs: cloud architecture design (remote sandbox, dispatch, GitHub/Slack/push, multi-user, memory/KB, fallback/cost)

This commit is contained in:
host committed 2026-10-26 01:07:49 +08:00
1 parent 240eaacf50
commit 2f326f33f8
1 file changed
+247
+247
View File
@@ -0,0 +1,247 @@
# 云端架构设计(第三阶段)
日期:2026-10-26。范围是用户 2026-10-26 01:06 列出的 8 项:
远程沙箱、云端派发、GitHub 提 PR 和 CI、Slack 触发、推送通知、
多用户协作和权限、跨线程记忆和项目知识库、模型 fallback 和
真实 token 成本统计。
先说一句总的:这 8 项不是 host.py 再加几个命令能长出来的,
它们共用一个前提——**一个常驻的控制面**。没有控制面,派发、
多用户、推送、GitHub 回调都无处落脚。所以下面先定控制面,
再按依赖关系分 4 个子阶段,每项都写清楚:复用现有 host.py
的什么、还缺什么外部条件、风险和诚实边界。
## 总架构
```
CLI / Web / Slack / GitHub webhook
│ (HTTPS, 带用户身份)
▼
API 网关 ── 认证 (OAuth / API key) ── 权限 (RBAC, 见 §6)
│
▼
控制面:任务队列 + 状态库 (Postgres / SQLite 起步)
├─ 派发器:选空闲 worker,按优先级、依赖、预算派发
├─ 记忆/KB 服务:线程摘要 + 项目索引 (§7)
├─ 计费服务:模型 fallback + 成本统计 (§8)
└─ 通知服务:任务完成/失败/评审请求 → Slack/推送 (§5)
│
▼
Worker 池(每个 worker 跑一个 host.py + 一个 codex app-server)
│
▼
沙箱(每任务独立,见 §1)→ git worktree / 容器 → 产出分支
│
▼
合并门:自动验测 → GitHub PR + CI (§3) → 合并 / 回滚
```
**复用原则**:控制面不重写 agent 逻辑。worker 就是现在的
host.py:dispatch / run / goal / review / merge / review-comment /
fix-review / doctor / failures 都已实测可用,控制面只负责
“谁可以派、派给谁、状态在哪、结果通知谁”。重写 host 等于
把已验证的 19 个测试扔掉。
**常驻前提(诚实边界)**:这个 VM 会被平台定时回收,
WeChat 桥和 host daemon 都是前车之鉴。控制面跑在这台 VM
上只能算“演示”,生产要一台常驻机器(用户自己的 Mac/
服务器)或一个托管服务(Fly.io / Render / 云服务器)。
Muse cron 只能做定时检查,不能替代控制面。
## 1. 远程沙箱
目标:每个任务在隔离环境里跑,跑坏了不污染宿主、不偷
别的任务或用户的密钥。
- 现状:codex 自带沙箱(workspace-write / read-only,
bubblewrap),host.py 已实测 read-only 确实挡写、审批
deny 确实挡工具。这是**本地沙箱**,不是远程沙箱。
- 远程沙箱的三种实现,按成本递增:
1. 同机容器:每任务一个容器(Docker),worktree 挂载进
容器,网络默认断、只放行模型 API。**本 VM 没有 Docker,
这条路要先装 Docker 或换机器**。
2. 云容器:每任务起一个 Fly.io / 云服务器容器,跑完即毁。
隔离最好,成本按任务时长算,需要云账号和预算上限。
3. 专用 worker 机器:每个 worker 一台轻量 VM,任务结束
快照/重建。隔离和成本介于两者之间。
- 安全硬规则(不管选哪种):沙箱里不放宿主密钥,模型
key 只走 worker 的环境变量且不进日志;沙箱只能写自己的
worktree;网络 egress 白名单(模型 API + git 远端);
任务超时强制 kill;产出只以 git 分支形式出来,不直接
写主仓。
## 2. 云端派发
目标:任务不用在用户电脑上跑,提交后控制面排队、派发、
重试、取消,状态随时可查。
- 队列:任务 = {id, 项目, 提示, 模型优先级, 依赖, 优先级,
预算, 幂等键}。幂等键防重复提交(Slack 重发、GitHub
webhook 重发都只建一个任务)。
- 状态机:queued → running → completed / failed / interrupted
/ merged / merge-failed,和 host.py 现有状态对齐;
worker 心跳丢失超过阈值 → 标 interrupted,可续跑(已有的
recover 逻辑直接复用,thread 在 CODEX_HOME 里不丢)。
- 派发策略:先依赖拓扑(已有的 topo_sort),再优先级,
再预算(max_daily_turns + 成本上限,见 §8),worker
空闲才派,不超发。
- 取消:用户取消 → worker 收到 turn/interrupt → 状态
interrupted,分支和 worktree 保留,不静默丢。
## 3. GitHub 提 PR 和 CI
目标:任务做完不是只合到本地主分支,而是走标准 PR 流程,
CI 过了才合。
- 现状有利:本机有 `git` 和 `gh`,host.py 的 merge 已有
自动验测 + 回滚(本地 unittest),这套逻辑可以直接当
PR 的“本地预检”。
- 流程:任务分支 → push 到远端 → `gh pr create`(标题、
任务描述、评审评论自动带上)→ CI 跑
`.github/workflows/host-verify.yml`(host.py 生成,跑
unittest)→ `gh pr checks` 轮询 → 绿了才 `gh pr merge`,
红了把 CI 日志贴回任务并标 merge-failed,不强行合。
- 还需要用户给的:GitHub 仓库远端、一个 PAT 或 OAuth
授权(走 Secure Vault,绝不写进文件或聊天记录);
PR 评论触发 fix-review 靠 GitHub webhook 回调到控制面
(需要控制面有公网地址,本地 VM 演示时可用 webhook
转发工具顶一下,生产不行)。
- 边界:CI 是 GitHub Actions 的,不是 host.py 自己跑的;
host.py 只负责生成 workflow、读 checks 结果、按结果
合或不合。
## 4. Slack 触发
目标:在 Slack 里就能派任务、查状态、收完成通知,不用
开终端。
- 触发方式(从易到难):Slash 命令 `/codex <任务>` 派发;
频道里 @bot 查状态;交互按钮“批准合并 / 丢弃”直接调
控制面的 merge / discard(按钮点击要带用户身份和权限
校验,不能谁点都算数)。
- 安全:Slack 签名密钥验签(防伪造请求),Slack 用户
ID 映射到 host 用户(见 §6),频道权限 = 项目权限,
私有项目不在公开频道里回贴 diff。
- 还需要用户给的:一个 Slack workspace 管理员装 Slack App,
签名密钥和 Bot Token 走 Secure Vault;控制面要有公网
回调地址(同 GitHub webhook 的前提)。
## 5. 推送通知
目标:任务完成、失败、需要评审时,用户手机上能收到,
不用一直盯着终端。
- 事件(只发状态变化,不每轮刷屏,和本地看门狗的通知
纪律一致):completed、failed、goal-unmet、merge-failed、
review-requested(有新评审评论)。
- 通道(按接入成本排):Slack DM/频道(和 §4 同一套)、
Webhook(通用)、邮件、Web Push / APNs / FCM(真手机
推送,需要应用证书和用户订阅,成本最高)。
- 现实一点的起点:对本用户,Muse 聊天通知 + Slack 已能
覆盖“手机收到”;真 APNs/FCM 是产品级才需要的最后
一步,不要一开始就做。
- 用户偏好:每用户可设“即时 / 摘要 / 静默”,失败和
merge-failed 默认即时,completed 默认可摘要。
## 6. 多用户协作和权限
目标:多人用一个控制面,互相看不到不该看的项目,审批
和合并有据可查。
- 认证:OAuth(GitHub / Google 登录,推荐 GitHub,因为
和 §3 同一套身份)或每用户 API key;会话用 JWT,
短期有效 + 刷新。
- 权限(RBAC,每项目独立):
- owner:管成员、管计费、删项目;
- maintainer:派任务、批准合并、改配置;
- contributor:派任务、写评审评论;
- viewer:只读任务、diff、日志。
- 隔离:状态库按项目过滤(行级过滤,不是靠前端藏),
沙箱按用户+任务隔离,模型 key 按用户隔离(不共用一
个中转站 key),日志里不出现别的用户的任务内容。
- 审计:谁派了、谁批准了、谁合并了、谁改了配置,全部
记审计日志,不可删;这是多用户和单用户最大的区别,
单用户可以靠 host-run.log,多用户不行。
## 7. 跨线程记忆和项目知识库
目标:新任务不用从零开始,能用上以前线程的结论和项目
的既有知识,但不把无关历史全塞进上下文。
- 两层,分开做:
1. **线程记忆**:每回合结束,host 写一条摘要(目标、
结论、改了哪些文件、遗留问题),存在记忆库;新任务
派发时,按项目 + 相关文件召回最近的摘要,注入提示
前缀,不注入全文。摘要可删、可纠正(用户纠正一条,
旧摘要要标废弃,不能留着误导下一轮)。
2. **项目知识库**:索引仓库文件、README、docs、历史
任务、评审评论。起步用 SQLite FTS(全文检索,零外
部依赖),够用;以后再上 embedding(需要向量库和
embedding 模型,成本和运维都上一个台阶)。
- 检索时机:派发时 top-k 相关文件/文档注入提示;回合中
给 agent 一个 MCP search 工具,让它自己查 KB(比一次
性全注入省 token)。
- 隐私边界:KB 按项目隔离,不跨项目召回;记忆是项目
的,不是某个用户的私人笔记,除非用户明确标私有。
## 8. 模型 fallback 和真实 token 成本统计
目标:主模型挂了自动换备选,成本是真数不是估算,且
每项成本能对到具体任务。
- **先纠正一个旧说法**:之前说“协议本身不暴露 token 数”
是不准确的。查 2026-10-26 的 v2 schema:协议里有
`account/usage/read` 方法,返回 `ThreadUsage`
(estimatedUsageCreditsMicros / estimatedUsageUsdMicros)
和 `TokenUsageBreakdown`(input / output / reasoning /
cached / total)。但有两个前提:(a) 它是 ChatGPT 账号
的计费路线,(b) 中转站(xcmapi)走的是 API key 路线,
计费路线“可用时”才返回——**中转站上到底能不能读到,
必须实测,不能先写进承诺**。
- 落地顺序:
1. host 的 Server 事件循环先接住回合里的 usage 通知
(如果有),按回合记 input/output/total 到任务记录;
2. 每回合结束调一次 `account/usage/read`(threadId),
记 estimatedUsageUsdMicros;这个调用本身不跑模型,
不花模型钱;
3. 如果中转站上读不到(返回空/不可用),再走 provider
计费接口:OpenAI 的 /usage、xcmapi 的账单接口(如果
有,拿用户的中转站 key 临时测一次,不存文件);
4. 都读不到时才用估算:prompt + 输出字符数 / 4,并
**明确标“估算”**,绝不冒充真数。
- Fallback:配置里写模型优先级列表,例如
`[gpt-6.1-sol, gpt-6-sol, gpt-5.6-sol]`;主模型返回
404 / 429 / 5xx / 超时时,自动试下一个,任务记录里
记“实际用的模型”,fallback 发生时通知一次,不静默
降级;预算(回合数 + 成本上限)按实际模型累计,超
预算就停,不自动换更贵的模型续跑。
- 成本报告:按任务 / 按项目 / 按天汇总 token、美元、
回合数、fallback 次数,报告里真数和估算分开列。
## 分阶段落地(按依赖,不按热闹程度)
- **3A 控制面 + 多用户 + 记忆/KB**:API + 认证 + RBAC +
状态库 + 任务队列 + 线程摘要 + SQLite FTS 知识库。
不需要云账号、GitHub、Slack,可在本机先跑通。这是
后面所有项的地基,先做它。
- **3B 远程沙箱 + 云端派发**:worker 池 + 容器沙箱 +
心跳/中断/取消。需要 Docker 或云容器账号,是 3A 之后
的第一块硬骨头。
- **3C GitHub PR/CI + Slack + 推送**:外部集成三件套,
需要 GitHub PAT、Slack App、公网回调地址,三者可以
并行,但都依赖 3A 的认证和权限。
- **3D 模型 fallback + 真实成本**:usage 读取实测 +
fallback 列表 + 成本报告。依赖 3A 的任务记录,不依赖
3B/3C,可以和 3C 并行。
## 需要用户定的事(现在就可以定,不用等代码)
1. 控制面跑在哪:常驻机器(自己的 Mac/服务器)还是
托管服务(Fly.io / Render / 云服务器)?这决定 3B
的沙箱选型和 3C 的回调地址。
2. GitHub:用哪个仓库、给不给 PAT(走 Secure Vault)?
3. Slack:有没有 workspace 管理员权限装 App?
4. 推送:Slack + Muse 通知够不够,还是要真手机推送
(APNs/FCM)?
5. 计费实测:同不同意用中转站 key 临时测一次
`account/usage/read`(不存 key,只看返回有没有数)?