Skip to Content
架构Task Runner POC

Task Runner POC — 技术选型与架构

openspec-agent-platform v2 · POC ✓ E1+E2 真跑通过

把单条 Task 在容器里完整跑完 explore → propose → apply → archive 四阶段,输出真实 GitHub PR,并把 status / stage / diff stats 实时回传到主 API。本期 POC 同时验证两条部署路径:公有云(Fly Machines)+ 私有化(VPS + Docker),通过 Sandbank 沙箱中间层抽象切换。

公有云栈
Fly Machines
私有化栈
VPS + Docker + Sandbank
Agent SDK
Claude Agent SDK
工期估算
~13 工作日

§1 背景

现状缺口与本期目标。

openspec/specs/tasks/spec.md 已经定义了 Task 数据模型、4 个 REST API 和 3 张设计稿(04 / 04a / 04b)。缺失的是执行引擎—— status: proposing → running → awaiting_review → donestage: explore → propose → apply → archive 这些字段被前端 MiniStages 拿来画动画,但没有任何后端代码会真正推进它们。

现状

Task DB 行存在,但 stage 永远是 explore、status 永远是 proposing。

POC 要补的能力
  • Task 派发器(主 API → 容器)
  • 双部署路径并行:公有云 + 私有化
  • Agent SDK + Claude + git + openspec
  • 状态与工件回传链路

**本期重要变化(vs 上一版报告):**不再”POC 先公有云、私有化是远期阶段 2”。本期 POC 同时验证 公有云(Fly Machines)和私有化(VPS + Docker)两条部署路径,通过 Sandbank 沙箱中间层(详见 §6.1.1)抽象切换。商业上等于”SaaS + 自托管”双轨上线,技术上等于 day 1 就消除供应商锁定。

§2 影响选型的硬约束

下表所列的每条约束都会拍掉一批候选——选型不应以”功能丰富”为先,而应以”红线不踩”为先。

约束描述影响
单任务时长 10–30 minnpm install 30-90s + 4 阶段 Claude 调用串联排除任何 ≤ 5 min 硬上限的方案
每任务一个干净容器仓库间隔离 + 失败重跑不污染上一次状态排除 reuse 型 long-living 进程
凭据要安全注入GitHub installation token(PR 写权限)+ Anthropic API key排除共享容器、可被 sidecar 偷窥的方案
状态要实时回传前端 MiniStages 要看到 stage 跳变排除”跑完一次返回总结果”的模型
工件要 commit 到 gitopenspec/changes/{date-slug}/proposal.md 进仓库排除不提供出口流量 / 不能装 git 的方案
POC 阶段2-3 周见 demo(双轨 ~13d),不上规模、迭代速度优先排除重平台耦合、要写大量胶水的方案

§3 云端容器:7 选 1

速览矩阵 + 每个候选的展开。✩ = 5 分制契合度。

#候选形态冷启动单任务上限计费维度本项目契合
1Fly Machines POC 首选OCI / Firecracker7.7s(S8.C 实测 · cpu=1 / 256MB · node:22-slim · cold start)无硬上限机器 RAM·秒 + 出口★★★★★
2Vercel Sandbox 备选 1Firecracker microVM1–2s(snapshot 亚秒)默认 300s(Pro 可调)Active CPU + RAM + 调用★★★★☆
3E2B 备选 2 · 协议宿主Firecracker microVM~150ms24h$0.000014/vCPU/s + $0.0000045/GiB/s · Hobby Free + $100 credit★★★★☆
4Cloudflare Sandbox SDK 排除Docker on Durable Object秒级(休眠后慢)10 min 不活跃睡眠Worker 调用 + 容器★★☆☆☆
5Daytona Sandboxes 排除Docker 容器秒级小时级按小时 + 存储★★☆☆☆
6Modal 排除Firecracker1–5s秒级 CPU/GPU★☆☆☆☆
7Anthropic Code Execution Tool 排除Claude 托管 server-side即起multi-turn ✓(2026 起 container 可复用)包含在 token 费里★☆☆☆☆

3.1 Fly Machines(POC 首选)

3 个决定性因素让 Fly 跑赢其他候选:

优势 1

已经在栈里。 Fly token、Fly Postgres、fly-audit 都已经跑着,再加一个 task-runner app 是熟门熟路。

优势 2

无时长上限。 4 阶段串联跑 30 min 不会撞红线(对比 Vercel Sandbox 默认 300s)。

优势 3

同 VPC callback。 容器回写主 API 可走内网,不暴露 internal endpoint。

API 调用形态

# 主 API 调 Fly Machines REST 创建一台短命机器 POST https://api.machines.dev/v1/apps/openspec-task-runner/machines { "config": { "image": "ghcr.io/openspec/task-runner:latest", "env": { "TASK_ID": "<uuid>", "INSTALLATION_TOKEN": "<short-lived>", "ANTHROPIC_API_KEY": "<key>", "CALLBACK_URL": "https://api.douglasdong.com/internal/tasks/<id>/events" }, "auto_destroy": true, "guest": { "cpu_kind": "shared", "cpus": 4, "memory_mb": 4096 } } }

劣势

  • 没有现成 SDK,要包装 Fly Machines REST API(增量 ~150 行 NestJS service)
  • 没有原生 sandbox 抽象(但 microVM 隔离已足够)
  • 状态回传要自己实现 HMAC 验签 + callback URL pattern

3.2 备选与排除理由

Vercel Sandbox · 备选 1

**优点:**与前端同栈、OIDC 自动认证、snapshot 机制亚秒冷启动。

**致命点:**默认 300s 超时——一个 30 min 的 4 阶段 task 会撞红线,Pro 可调到多少官方文档未明示。

**触发条件:**若官方文档/支持确认 Pro 可调到 ≥ 1800s,切回首选评估。

E2B · 备选 2 · 协议宿主

双产品形态:

  • E2B Cloud(SaaS)— Hobby Free + $100 one-time credit(免信用卡),Pro $150/mo 起 + 用量。E2B 自己运维,客户与 AWS 无关
  • E2B Infragithub.com/e2b-dev/infra · Apache-2.0 · ★1.1k)— 同一套软件开源版,Terraform 部署到自有 GCP(fully supported)/ AWS(Beta)。

按秒单价(Cloud · 两 tier 同价):

  • vCPU $0.000014/s ≈ $0.0504/h
  • RAM $0.0000045/GiB/s ≈ $0.0162/h
  • 4vCPU+4GB · 15 min task = $0.067(约为 Fly 同配置 ~29×)
  • 存储 Hobby 10 GiB / Pro 20 GiB 含在套餐,无 API call / egress 费

**协议宿主地位:**E2B 的 HTTP API 已成事实标准——腾讯 CubeSandbox(§9.8.1)通过反向兼容 E2B 协议实现自托管 microVM。本期 fork sandbank 新增的 E2BAdapter,将统一覆盖 E2B Cloud / E2B Infra / CubeSandbox 三个 backend

**触发条件:**① Fly Machines 跑不通;② 客户私有化场景需要 E2B 协议兼容的 backend(含 Cube);③ 多 backend 协议层验证(POC 用 Hobby tier 的 free credit)。

Cloudflare Sandbox SDK · 排除

当前栈无任何 CF 资产,POC 阶段引入新平台增量过大。10 min 睡眠超时需要 alarm/heartbeat 维持。

Daytona · 排除

按小时计费——20 min 任务也按 1 小时算,定价模型对 POC 不友好。

Modal · 排除

Python 优先,Node/TS 是二等公民。decorator-based API 与 NestJS 主导的范式不匹配。

Anthropic Code Execution · 排除

2026 起已支持 multi-turn + container 复用,但仍是 Claude 托管 sandbox,不能 git push 外部仓库(Anthropic Files API 输出不直通 GitHub),且费用包含在 token 价里(无独立计量),不适合长 PR 工作流。

§4 Agent SDK:Claude vs Codex

头部 2 个候选并排对照。OpenAI Agents SDK / Vercel ToolLoopAgent / LangGraph 三者契合度 ≤ ★★,本节略去。

4.1 并排对照

Claude Agent SDK

推荐

@anthropic-ai/claude-agent-sdk

执行模型
内含 native Claude Code 二进制作为 optional dependency,无需单独安装 CLI
关键能力
内置 Read / Write / Edit / Bash / Glob / Grep / WebFetch / WebSearch / Monitor / AskUserQuestion 等工具循环。
本仓库 .claude/skills/openspec-*
开箱即用——通过 settingSources: ["user","project"] + skills: "all" 自动加载。
.claude/commands/opsx/*
开箱即用——可直接写 /opsx:propose ... 调用。
Hooks(pre/post tool use)
有。PostToolUse 监听 Write 即可作为 stage 切换的天然挂点。
Subagent 程序化定义
有 declarative agents: { "code-reviewer": {...} }
沙箱 OS 级实现
无原生,依赖容器隔离。
模型 / Provider
Claude(Anthropic Direct / Bedrock / Vertex / Azure 多 provider)。
计费
API key → token 计费。2026-06-15 起 subscription 走独立 Agent SDK 额度:Pro $20/mo · Max $200/mo。

Codex SDK

@openai/codex-sdk

执行模型
TS SDK wraps codex CLI,spawn 进程后用 JSONL over stdin/stdout 通讯。容器镜像必须额外装 codex CLI 二进制
关键能力
Codex 类 + startThread + Thread.run / runStreamed + resumeThread。结构化输出走 JSON Schema。
本仓库 .claude/skills/openspec-*
需手工引导——文件能被 Read 工具读到,但 Codex 没有"按需触发"机制;要在 AGENTS.md 里写引导(详见 §4.3)。
.claude/commands/opsx/*
需手工拼到 prompt——没有等价 slash commands 机制。
Hooks
无。只能轮询 events stream(item.completed / turn.completed)或让 agent 主动调用 report 工具。
Subagent 程序化定义
无 declarative,要手动 spawn sub-thread。
沙箱 OS 级实现
read-only / workspace-write / danger-full-access 三档;macOS Seatbelt + Linux Landlock + seccomp。
模型 / Provider
OpenAI(gpt-5 系、o-系列推理模型)。
计费
OpenAI API token 计费;订阅与 Codex 配额合并(具体费率官方文档未细化)。

4.2 决定性差异速读

维度Claude Agent SDKCodex SDK
二进制依赖内置必须装 codex CLI
.claude/skills/openspec-*自动按需触发手工引导(git clone 后文件在容器里,agent 能 Read,但不自动)
.claude/commands/opsx/*原生 slash command 调用需手工拼到 prompt 头
Hooks(pre/post tool)
Subagent declarative 定义
OS 级沙箱无原生Seatbelt / Landlock
POC 实施成本低(复用现有 skills,零搬运)中(要在 AGENTS.md 引导 + 部分 prompt 内联)
Token 效率(context 占用)高(未触发的 skill 全文不进 context)低(如全量挂在 AGENTS.md,常驻 ~1K-1.5K 行)

**修正后的关键判据:**差异不在”能不能跑通”——Codex 也能在容器里通过 git clone + Read 工具访问到这些 markdown。差异在于 实施代码量(Claude 零搬运 vs Codex 需在 AGENTS.md 写引导段)和 token 效率(Claude 按需载入 vs Codex 易全量常驻)。POC 阶段两者都可行,Claude 更省事;规模化后 token 成本差距会放大(token 成本由你独立评估,不在本报告范围)。

4.3 Skill 加载机制再评估(承认上一版表述偏激)

**修正:**上一版报告说 Codex SDK “完全不识别 .claude/skills/openspec-*“——这话技术上不准确。只要容器把仓库 git clone 进来,这些 markdown 文件本身就在文件系统上,Codex agent 同样可以通过 Read 工具看到、按需读取它们。差异不在”能不能”,而在”怎么用”。

能力Claude Agent SDKCodex SDK
SKILL.md 自动按需触发原生支持。Agent 看到任务时自动决定要不要载入 skill 全文需手工:在 prompt / AGENTS.md 里告诉 agent “若任务匹配 X 就读 .claude/skills/X/SKILL.md”
Slash command 调用 /opsx:propose ...原生支持。.claude/commands/opsx/propose.md 直接生效需手工:把 commands/opsx/*.md 内容拼到 prompt 头
Skill 内 frontmatter(trigger keywords 等)SDK 按 description 字段自动匹配忽略 frontmatter,全靠 agent 自己理解
Token 效率未触发的 skill 全文不进 context(只 metadata)如果在 AGENTS.md 写全部 4 个 skill,约 800-1500 行常驻 context
双轨维护成本无:仓库已有的 .claude/ 文件就是 SDK 的输入中:要在 AGENTS.md 写”如何使用 .claude/skills/“的引导段

修正后的判定:差异从”原生支持 vs 完全不识别”降级为”自动按需加载 vs 手工引导 + 全量常驻”。Claude 仍占优,但优势主要在 token 效率实施代码量,不在”能不能跑通”。

4.4 鉴权复杂度并排

维度Claude Agent SDKCodex SDK
容器内最小鉴权ANTHROPIC_API_KEY 一个环境变量两件事:OPENAI_API_KEY + 容器镜像装 codex CLI 二进制
多 provider 切换环境变量切换:Bedrock / Vertex / Azure / Anthropic Direct仅 OpenAI(含 Azure OpenAI)
订阅 vs API key 双账单清晰隔离:API key → 按 token 计费(POC 走这条,永远不变);Pro/Max 订阅 → 2026-06-15 起 SDK 调用走独立月度池(Pro $20 / Max $200),与 Claude.ai 交互额度分开复杂:sign in 方式决定走 ChatGPT plan 额度还是 API key 计费,容易混淆
容器镜像构建复杂度npm i 自带原生 binary,单层需多一层 npm i -g @openai/codex 装 codex CLI
本地 dev 与 CI 走同一路径完全一致本地 dev 可能用 ChatGPT 登录,CI 必须 API key

4.5 速率限制与并行能力

POC 阶段单任务串行可忽略,但只要规划 ≥ 5 并发就要正视。下表汇总 API 层(不算容器层)的速率限制。

维度Anthropic(Claude)OpenAI(Codex / GPT-5)
分级体系Tier 1–4,预付 + 累计支出Tier 1–5,预付 + 累计支出 + 账户年龄
Tier 1 入门门槛$5 prepay$5 prepay + 7 天等待
Tier 4/5 顶层门槛$400 + 时间$1000 + 30 天
Tier 1 RPM(Sonnet 等)50 RPM500 RPM(GPT-5 系)
Tier 4 RPM4000 RPM10,000+ RPM
实际节流粒度官方文档说”每分钟”,实际按每秒节流同上
缓存 token 是否计入 ITPM不计入(90% off + 不消耗 ITPM 额度)部分计入
POC 推荐 tierTier 2($40 prepay → 1000 RPM)足够 5-10 并发Tier 2 类似
本项目实际并发瓶颈不在 API tier,而在主 API 端的”同用户同仓库并发上限”控制(见 §5.5)

**关键洞察:**对 POC 来说,API tier 限制远远高于业务实际需要的并发——5 task 并发 × 平均 30s 一次 API 调用 ≈ 10 RPM,连 Tier 1 都不会触顶。瓶颈不在 SDK / 模型一侧,而在主 API 怎么管”同用户/同仓库的 task 并发”。

4.6 推荐用法示意

import { query } from "@anthropic-ai/claude-agent-sdk"; // 在容器入口 run.js 内执行 for await (const msg of query({ prompt: "/opsx:propose " + task.prompt, options: { cwd: "/workspace/repo", settingSources: ["project"], // ← 容器内 user 级通常为空 skills: "all", allowedTools: ["Bash", "Edit", "Write", "Read", "Grep", "Glob"], permissionMode: "bypassPermissions", // ← 容器必须 non-root(S7/S8 实测:claude binary 拒绝 root + bypassPermissions) maxTurns: 200, hooks: { PostToolUse: [{ matcher: "Write", hooks: [async (input) => { const fp = input.tool_input?.file_path ?? ""; if (fp.endsWith("/proposal.md")) { await reportStage("propose", "done", fp); } return {}; }], }], }, }, })) { if (msg.type === "result") { await reportStage("propose", "done", msg); } }

4.7 何时反悔切回 Codex SDK

  1. Anthropic API 严重不可用或配额政策大变
  2. 核心任务在 GPT-5 推理模型上效果显著更好
  3. 业务需要”双 Agent 互评”(Claude 提案 + Codex review)

前两个是完全切换场景,到那时做一次完整迁移评估更划算;只有第 3 个会让两者并存。POC 阶段不必为这种弹性做投资。

§5 容器机器成本估算(联网校准)

所有数字均为 2026-05 联网拉到的官方公开价。只算基础设施成本,Token 成本是 Agent SDK / 模型选型的独立维度,不在本节范围。

**明确边界:**本节只评估”机器开多久花多少钱”。模型推理 token 成本随 prompt 设计、缓存策略、模型选择剧烈波动,把它放进容器对比里会让基础设施决策失焦。容器钱是容器钱的事,token 钱是 token 钱的事——独立看。

5.1 真实月度账单(按规模档 · 已扣 free tier)

这张表怎么算的(vs 上一版的修正):

  • I/O wait 修正:Vercel 按 Active CPU 收费,Claude task 大约 50% 时间在等模型 API 返回。Vercel 数字按 0.5x 利用率算(其他家按墙钟时间,不变)。
  • Free tier 已扣:E2B $100 one-time credit、Cloudflare Workers Paid 含 375 vCPU-min/月、Vercel Hobby 5h CPU/月、Vercel Pro $20 credit/月——都已经在计算里扣掉。
  • 月费已加:E2B Pro $150/月(> 100 并发要)、Cloudflare $5/月、Vercel Pro $20/月——按规模触发的月费已加入合计。
  • 单任务 30 min,4 vCPU + 4-8 GB(按各家最近档位)。
候选POC 规模 (100 task/月)中规模 (1K task/月)规模化 (5K task/月)Free tier / 月费
Fly Machines shared-cpu-8x · 4GB(推荐 POC 配置)$1.85$18.5$92无 free tier · 无月费 · 按秒
Fly Machines shared-cpu-1x · 256MB(最小 · S8.C 实测)~$0.05~$0.5~$2.5S8.C 实测启动 7.7s · 单次 ~10min · 资源够 alpine/slim base + node 跑 SDK
Fly Machines performance-2x · 4GB$4.5$45$223同上
E2B Hobby 4 vCPU · 4GB$0 (用 credit)$33 (credit 用完后)$666$100 one-time credit ≈ 750 task
E2B Pro 100 并发上限$816 (含 $150 Pro)$150/月 · > 20 并发或 > 1h session 要
Cloudflare Containers 4 vCPU · 8GB$22 ($5 月费 + 超额)$185$905Workers Paid $5/月 · 含 375 vCPU-min ≈ 3 task
Vercel Sandbox Hobby 4 vCPU · 8GB · I/O wait 50%$0 (在免费额度)超 Hobby超 Hobby5h Active CPU/月 ≈ 10 task
Vercel Sandbox Pro 4 vCPU · 8GB · I/O wait 50%$21 ($20 月费包 $1 超额)$213$1,064$20/月 base · 含 $20 credit

5.2 规模化(5K task/月)月度账单可视化

5K task/月真实月度账单 · 已扣 free tier $0 ──────────► $1,064 Fly shared-8x (4GB) ▮▮▮▮▮▮▮▮ $92 (1x) Fly perf-2x (4GB) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ $223 (2.4x) E2B Hobby (4 vCPU·4GB) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ $666 (7.2x) E2B Pro (+$150/月) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ $816 (8.9x) Cloudflare (4 vCPU·8GB) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ $905 (9.8x) Vercel Pro (4 vCPU·8GB) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ $1,064 (11.6x)

修正后的核心结论(vs 上一版):

  • POC 规模(100 task/月)下,E2B 和 Vercel Hobby 实际免费——free tier 完全覆盖。Fly $1.85 反而不是最便宜的(虽然差距小到没意义)。
  • 规模化(5K/月)Fly 仍领先——比次便宜的 E2B Hobby 便宜 7.2x(不是上一版说的 19x;上一版忽略了 I/O wait 与 free tier)。
  • Vercel Pro 在规模化时贵了 11.6x——主要是月费 + 单价两端都贵,Active CPU 模型只在 idle 任务上省钱。
  • Cloudflare 的 free tier 几乎可忽略——$5 月费含的容量只够 ~3 个 30min task,规模化后跟 E2B Pro 接近。

5.4 数据精度说明

① 单价来自 2026-05 官方公开 pricing 页面(见 §12 参考资料)。 ② I/O wait 50% 假设是中等估算——Claude task 实际 I/O 占比 30-60%,意味着 Vercel 数字波动范围 $0.6x-$1.4x。 ③ Egress 流量在月度量级下可忽略(150 MB × 5K = 750 GB,Fly NA/EU 区 $0.02/GB ≈ $15 在 $92 容器费上 +16%)。如果跨区域流量大需单独算。 ④ 不含模型推理 token 成本(独立维度,由你评估)。 ⑤ 实际生产成本还有冷启动重试、监控、Postgres、Egress 跨区域等——这些是各家通用的,不影响选型决策。

5.5 死循环 / 账单失控防护(重要)

用户反馈:“避免 Cloudflare 这种由于死循环直接刷 10000 刀以上的案例”——这是基于真实社区报告的合理担忧。下表对各候选的自带防护与必须自建的防护做并列。

候选原生 spend cap并发上限(默认)单容器最长寿命失控风险评级
Vercel SandboxSpend Management · 项目级暂停Pro 2000 · Hobby 10Pro 5h · Hobby 45 min
Fly Machines无原生 cap,但 auto_destroy=true 跑完销毁未公开默认,需注册时与支持沟通无硬上限
E2B未明示Free 20 · Pro 100 · Enterprise 1100Free 1h · Pro 24h
Cloudflare Containers无月度 spend cap · 仅单 invocation CPU time(5 min max)max_instances 自定,但默认无硬上限10 min 不活跃睡眠

POC 必须落地的防护清单(多层)

第 1 层 · 主 API 端
  • tasks 表加 claimed_at + runner_machine_id
  • 同一 user 同一 24h 内最多 20 个 task
  • 同一仓库同时最多 3 个 task 并发
  • dispatch 前查并发计数,超限直接拒
第 2 层 · 容器内
  • run.js 入口顶级 setTimeout(默认 60 min hard kill)
  • SDK query() 调用包 try/catch,失败回报失败状态后立刻退出
  • S5/S7 实测:4 阶段流转跑在单一 query() 内部(SDK skill 自调度),无法精确切分各阶段独立 timeout;顶层 hard kill 已足够防护
第 3 层 · 平台账单(容器侧)
  • Fly 账户:开启 daily spend alert(Fly 邮件通知)
  • GitHub installation token:1h 过期即自然失效
  • Token 侧账单(Anthropic / OpenAI console 的 monthly hard cap)属于模型选型决策,由你独立配置
第 4 层 · 监控
  • 主 API 每 10 min 巡检:查询 Fly Machines API,把 created_at > 60 min 但仍 running 的机器强制 DELETE
  • 复用现成 apps/fly-audit 做活跃 Machine 监控
  • 每日 cron 检查 task_events 表,无新事件 30 min 的 task 标记为 stalled

**结论:**即便选了无原生 spend cap 的 Fly,靠”多层防护清单”已经能把容器侧死循环风险压到极低——POC 阶段最坏情况是单任务 1h hard kill × 3 并发 × 24h = 72 任务/天,容器纯开销 ≈ 72 × $0.037 ≈ $2.7/天。模型 token 侧的失控防护属于另一条线(Anthropic console / OpenAI dashboard 的 monthly spend hard cap),由你独立设置即可。

§6 POC 推荐架构

三层拓扑 + 状态机推进 + 关键接口契约。

6.1 拓扑图 · 可插拔双轨

关键设计:Sandbank 沙箱中间层 隔离”主 API 的派发逻辑”与”具体后端”。POC 同时启用 FlyAdapterBoxLiteAdapter 两个 adapter,容器入口 run.js 完全相同

┌─────────────────────────────────────────────────────────────────────────┐ │ WEB · VERCEL │ │ ┌───────────────────────────────────────┐ │ │ │ apps/web · Next.js │ │ │ │ POST /tasks │ │ │ │ GET /tasks/:id/events (SSE) │ │ │ └───────────────────────────────────────┘ │ │ │ HTTPS · session │ │ ▼ │ │ API · FLY.IO │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ apps/api · NestJS │ │ │ │ ┌──────────────────────────┐ ┌─────────────────────────┐ │ │ │ │ │ TasksController │→ │ TaskEventsController │ │ │ │ │ │ + POST /tasks/:id/ │ │ POST /internal/tasks/ │ │ │ │ │ │ dispatch │ │ :id/events │ │ │ │ │ │ + GET /tasks/:id/events │ │ HMAC 验签 + SSE 广播 │ │ │ │ │ └──────────────────────────┘ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ Sandbank · 沙箱中间层(多 backend 抽象) │ │ │ │ │ │ provider.create / exec / destroy │ │ │ │ │ │ 按 env 切换 adapter — 业务代码 99% 共用 │ │ │ │ │ │ — 公有云 / 私有化双轨切换的核心抽象 — │ │ │ │ │ │ ↓ 两个 adapter │ │ │ │ │ │ FlyAdapter(内置) │ │ │ │ │ │ BoxLiteAdapter(内置) │ │ │ │ │ │ (详见 §6.1.2 - §6.1.4) │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ FlyAdapter │ BoxLiteAdapter │ │ ▼ ▼ │ │ ⬢ 公有云 · FLY MACHINE ▣ 私有化 · VPS DOCKER │ │ ┌──────────────────────────────┐ ┌────────────────────────────────┐ │ │ │ 短命 Machine (auto_destroy) │ │ BoxLite microVM (via Sandbank) │ │ │ │ guest: 4 vCPU / 4 GB │ │ host: 普通 Linux VPS (KVM) │ │ │ │ spec: shared-cpu-8x │ │ isolation: microVM (每 task) │ │ │ │ ┌────────────────────────┐ │ │ ┌──────────────────────────┐ │ │ │ │ │ run.js (容器入口) │ │ │ │ run.js (同一份代码) │ │ │ │ │ ├────────────────────────┤ │ │ ├──────────────────────────┤ │ │ │ │ │ 1. git clone + checkout│ │ │ │ 1. git clone + checkout │ │ │ │ │ │ 2. /opsx:explore → │ │ │ │ 2-4. 同公有云路径 │ │ │ │ │ │ propose │ │ │ │ 5. 同公有云路径 │ │ │ │ │ │ 3. /opsx:apply │ │ │ └──────────────────────────┘ │ │ │ │ │ (typecheck+lint) │ │ │ │ │ │ │ │ 4. /opsx:archive + │ │ │ callback: Tailscale tailnet │ │ │ │ │ git push │ │ │ (私有,26ms) 或公网 HTTPS │ │ │ │ │ 5. curl POST /pulls + │ │ │ (239ms · HMAC) │ │ │ │ │ report 完成 │ │ │ │ │ │ │ └────────────────────────┘ │ │ 同一镜像 ghcr.io/openspec/ │ │ │ │ 同一镜像 ghcr.io/openspec/ │ │ task-runner │ │ │ │ task-runner │ │ │ │ │ └──────────────────────────────┘ └────────────────────────────────┘ │ │ ╲ ╱ │ │ ╲ 两条路径的 callback 都打回同一个 events controller ╱ │ │ ╲ ╱ │ │ ◀──── (异步回调 dashed) ◀── │ └─────────────────────────────────────────────────────────────────────────┘ Legend: 主进程/服务 ▢ 抽象接口(虚线)▭ 公有云容器 私有化容器 ── 实时数据流 ┄┄ 异步回调

6.1.1 沙箱中间层 · 用 Sandbank(不自定义 Provider)

**关键决策:**本期 POC 不再自定义 Provider 接口——业界已经有专门做”多 backend 抽象”的开源项目。联网调研发现 3 个候选,其中 chekusu/sandbank 几乎完美匹配本场景——直接内置 Fly.io(公有云)+ BoxLite Docker(私有化)+ Cloudflare 等多个 backend,一个 npm 包同时覆盖本期 POC 的两条部署路径

Sandbank Provider 接口形态

import { createProvider, FlyAdapter, BoxLiteAdapter } from "sandbank"; // 按 env 切换 — 业务代码 99% 共用 const provider = process.env.TASK_RUNNER_MODE === "private" ? createProvider(new BoxLiteAdapter({ /* ... */ })) : createProvider(new FlyAdapter({ apiKey: process.env.FLY_API_TOKEN })); // 派发一个 task — 容器入口由 image 决定 const sandbox = await provider.create({ image: "ghcr.io/openspec/task-runner", env: { TASK_ID: task.id, INSTALLATION_TOKEN: installationToken, ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, CALLBACK_URL: `https://api.douglasdong.com/internal/tasks/${task.id}/events`, HMAC_SECRET: process.env.TASK_HMAC_SECRET, REPO_FULL_NAME: branch.repoFullName, AGENT_BRANCH: agentBranch, PROMPT: task.prompt, }, }); // 容器内 run.js 串行跑 4 阶段,结束后 sandbox.destroy()

6.1.2 三个中间层候选对比

候选Stars最新版TS SDKFly.io本地 Docker本项目契合
chekusu/sandbank 推荐163v0.6.2 · 2026-05完整Firecracker 3-5sBoxLite adapter★★★★★
cased/sandboxes97v0.7.0 · 2026-02偏 CLISprites(Fly 旗下 sandbox 产品)★★☆☆☆
hyperterse/sandboxer6不明外置Fly Machines APIDocker★☆☆☆☆

Sandbank 详解

github.com/chekusu/sandbank · MIT · ★ 163 · v0.6.2 (2026-05)

Sandbank · 中间层最佳匹配 ⭐

核心理念:“Unified sandbox SDK for AI agents — write once, run on any cloud”,capability detection 替代假兼容(这个工程态度重要:明确告诉你 provider X 不支持 feature Y,不静默降级)。

内置 provider:

  • Daytona · 全 VM · ~10s 冷启动
  • Fly.io · Firecracker microVM · ~3-5s ← 本期公有云路径
  • Cloudflare · V8 isolate + 容器 · ~1s · 全球 edge
  • BoxLite(Docker) ← 本期私有化路径
  • db9(PostgreSQL service adapter)

API:

import { createProvider, FlyAdapter, BoxLiteAdapter } from "sandbank"; // 按 env 切换 — 业务代码 99% 共用 const provider = process.env.TASK_RUNNER_MODE === "private" ? createProvider(new BoxLiteAdapter({ dockerHost: '...' })) : createProvider(new FlyAdapter({ apiKey: process.env.FLY_API_TOKEN })); const sandbox = await provider.create({ image: "ghcr.io/openspec/task-runner" }); const result = await sandbox.exec("/app/run.js"); await provider.destroy(sandbox.id);

**关键工程价值:**省掉自写两个 Provider implementation 的工作量 —— Sandbank 内置 adapter 直接可用,POC 工期落在 ~13d(含双轨)。

Sandbank 的限制(已知)

  • 无 E2B provider——E2B 兼容不在 sandbank 范围(如果想未来切到 E2B 还要自己加 adapter)
  • 无 streaming 支持——文档明示 “No streaming or snapshot support yet”,长任务的实时日志要走自己的 callback HTTPS(与 §6.3 设计一致)
  • 无 git 工作流业务封装——Sandbank 只管容器,不管 git 操作。run.js 自己调 git CLI(clone / commit / push)即可,本场景”每 task 一容器”模型下也够用
  • star 数 163 仍属早期——POC 前建议做 1-2 小时 spike 验证 Fly + BoxLite 两个 provider 在 30 min 长任务下的稳定性

修订后的设计决策

选择理由
容器调用抽象Sandbank(不自写)省 2.5 天工期,且 capability detection 比自写更严谨
公有云 backendSandbank 的 Fly Adapter3-5s 冷启动,符合 §3 选型
私有化 backendSandbank 的 BoxLite AdapterS3 实测:Apple Silicon Mac mini + Hypervisor.framework 等效 KVM,开箱即用
Agent 内部调用run.js 用 Claude Agent SDK 直接调run.js 本身就是 sandbox 内的入口,无需额外编排层
git 工作流run.js 直接调 git CLI每 task 一个干净容器已是隔离边界,无需 worktree(详见 §10.7)

**结论:**引入 Sandbank 后,主 API 不再需要自定义 Provider 接口——直接用 Sandbank 提供的 Provider 抽象即可。§7 任务清单按 接入 Sandbank 1.5d(公有云)+ 1.5d(私有化) 计算,POC 工期落在 ~13d。POC 前要 spike 验证 Sandbank 在 30 min 长任务下的稳定性。

6.1.3 Sandbank 私有化接入完整度 + Fork 策略

用户提出的关键问题:Sandbank 对私有化的接入全吗?能不能 fork 改?深入核实后的事实:

BoxLite Adapter 能力完整度

能力BoxLite本场景需要判定
Create / Destroy / List对齐
Execute commands对齐
Read/Write files✓(via exec base64)对齐
exec.stream(流式输出)长任务日志必需对齐
terminal(交互式)POC 不强求富余
port.exposePOC 不需要富余
snapshot未来 task 状态延续可用富余
sleep(休眠/唤醒)未来 task 状态延续可用富余
volumes(持久卷)不需要(每 task 一次性容器)无影响
services(数据服务绑定)不需要(DB 在主 API 端)无影响

核心结论:BoxLite adapter 对本场景的私有化需求是齐全的,没有关键缺失。 所有本场景需要的能力(create/destroy/exec/files/streaming)都对齐,BoxLite 还额外提供了 terminal/snapshot/sleep/port.expose 等富余能力(未来可用)。

来自上游 issue #2 的能力补充(2026-05-16 维护者回复)

调研中发现 chekusu/sandbank #2 维护者 @guo-yu 详述了各 adapter 的 persistence 能力——补充以下事实:

  • BoxLite 是为”长期保留沙箱”设计的——配合 auto_remove: false + snapshot + sleep,维护者原话:“适合本机/自托管的长期保留沙箱”。本场景虽然是”每 task 一次性容器”,但未来如果想做 agent 状态延续(如人工 review 中途暂停 task),能力已现成。
  • Fly.io adapter 也支持 volumes——已有集成测试覆盖 volume mount 写入读回。这意味着公有云路径也有持久化能力可选(POC 不用,但未来扩展不需切 backend)。
  • Cloudflare adapter 支持 snapshot + volumes(配置 storage 时)。
  • E2B adapter 上游仍在 TODO——我们已 fork 自实现:@douglas-agent/sandbank-e2b 提供 E2BAdapter + CubeSandboxAdapter(共享 E2BProtocolAdapter 基类)。详见 change add-sandbank-fork-aio-e2b

BoxLite 适用范围

BoxLite adapter 背后用的是 BoxLite 的嵌入式 microVM 库。可跑的硬件覆盖了所有目标场景:

  • Apple Silicon Mac(Hypervisor.framework): S3 实测 M4 Mac mini 等效 KVM,PoC demo 推荐硬件
  • 裸金属 / 主流云 VM: AWS / GCP / Hetzner / DigitalOcean 等开 nested virt 的环境

极少数仅 OpenVZ 类无虚拟化能力的 VPS 不在 POC 支持范围——这类客户应改选有 nested virt 的 VPS 或自带 Apple Silicon 硬件,不再保留”自写纯 Docker adapter”作为退路(容器内 Docker 没有 microVM 隔离边界,与本项目”每 task 一个独立沙箱”的安全前提冲突)。

Fork 可行性评估

指标数据对 fork 决策的影响
LicenseMITFork 友好,可商用闭源修改
仓库结构每个 adapter 独立 npm 包 @sandbank.dev/boxlite / @sandbank.dev/flyio扩展点清晰,新加 adapter 不影响原有
现有 forks13 个已有先行者
最小 adapter 契约6 个核心方法自写工作量可控
上游维护响应issue #2(2026-03 发起)维护者 @guo-yu 2026-05-16 详细回复修正:上游活跃——issue 2 个月后还得到代码级回复 + 3 个可实现点 + TODO 路线图共享。贡献 PR 比 fork 自维护更现实
contributor 数至少 @chekusu + @guo-yu 两人活跃不是单人项目
路线图透明docs/TODO.md 已含 E2B adapter 等待办项未来可能要的 adapter 已经在计划中,未必要自己写

两个 fork 改造方案

方案 A · POC 阶段

不 fork · 直接用上游

**判断:**BoxLite adapter 对 POC 完全够用。host 选 Apple Silicon Mac mini 或支持 nested virt 的 VPS,BoxLite 直接能跑。

**工作量:**0 额外开发

**触发条件:**本期 POC 默认选这个。E1+E2 真跑通过已证明可行。

方案 B · 高性能私有化

Fork + 加 CubeSandboxAdapter

**动机:**把腾讯 CubeSandbox(§10.8)接到 Sandbank。CubeSandbox 已经是 E2B 接口兼容——基本上可以参照 sandbank 的 flyio adapter 风格快速实现。

**额外收益:**VM-level 隔离 + 60ms 冷启动 + 2000+/单机并发

**工作量:**0.5-1 天

**触发条件:**国产化合规场景 / 需要高密度并发。

修订后的 POC 决策

阶段私有化栈工作量
本期 POCSandbank 上游 + BoxLite adapter1d 接入
规模化阶段(国产化)Fork Sandbank + 自写 CubeSandboxAdapter+0.5-1d

核心判断:

  • BoxLite adapter 对 POC 私有化需求是齐全的(功能完整度核实 OK + E2 真跑通过)
  • Fork 友好度高(MIT + 13 forks + 独立包结构 + 6 方法契约)
  • 本期 POC 选方案 A(不 fork,直接用 BoxLite adapter)
  • 未来若有国产化合规需求,CubeSandboxAdapter 是 0.5-1 天的事,不是阻塞性风险

6.1.4 其他私有化 Runtime 的 Sandbank 接入评估

除了 BoxLite,§10 列出的其他私有化 sandbox runtime(AIO Sandbox / Daytona OSS / Microsandbox / CubeSandbox / OpenSandbox / OpenHands / Docker sbx)——Sandbank 能不能接?逐个核实它们的接口形态后,结论是大多数都能在 1 天内接入

接入难度矩阵(联网核实每个 runtime 的官方接口)

RuntimeSandbank 内置接口形态接入路径工作量推荐度
BoxLite✓ 已内置嵌入式库直接用0d★★★★★
Daytona OSS✓ DaytonaAdapterNestJS RESTful API(self-hosted 和 cloud 同套代码)改 base URL + OIDC token0.5d★★★★★
AIO Sandbox(字节)RESTful /v1/* + OpenAPI + 官方 @agent-infra/sandbox npm写 AIOSandboxAdapter 包装官方 npm SDK1d★★★★☆
CubeSandbox(腾讯)✓ 已 fork 自实现E2B 兼容协议(reverse proxy 实现 E2B SDK 接口)@douglas-agent/sandbank-e2bCubeSandboxAdapter(与 E2BAdapter 共享基类)0-1d★★★★★
MicrosandboxJSON-RPC 2.0 over HTTP(/api/v1/rpc写 MicrosandboxAdapter(JSON-RPC client)1-1.5d★★★☆☆
OpenSandbox(阿里)有官方 npm @alibaba-group/opensandbox写 OpenSandboxAdapter 包装官方 SDK0.5-1d★★★☆☆
OpenHandsagent + sandbox 一体(要逆向拆解出 sandbox 部分)不划算3-5d☆☆☆☆☆
Docker sbx2026-03 新出 · API 未公开披露观察 3-6 个月未知⚠ 观察

关键洞察

洞察 1 · Daytona OSS 几乎零成本

Daytona OSS 与 cloud 用同一套 NestJS RESTful 代码,Sandbank 现有 DaytonaAdapter 只需切 base URL + OIDC token——0.5 天就能接入 self-hosted Daytona。

洞察 2 · CubeSandbox 的红利

CubeSandbox 是 E2B 协议兼容——我们已 fork sandbank 并实现 E2BProtocolAdapter 基类,E2BAdapter(E2B Cloud/Infra)和 CubeSandboxAdapter(自托管 Cube)共享同一协议层。同一份代码同时覆盖国产化 + 海外公有云。

洞察 3 · 整体接入成本可控

除了 OpenHands(定位错位)和 Docker sbx(太新),所有候选都能在 1-1.5 天内接入 Sandbank。如果未来要把私有化矩阵铺满,加 5 个 adapter 总工期不到 5 天。

洞察 4 · 上游路线图的红利

Sandbank 维护者在 issue #2  公开了 docs/TODO.md。意味着未来想要的 adapter 可能不用自己写——贡献 PR 加快上游进度比 fork 自维护更划算。POC 期可以先用 BoxLite,未来根据需要去 follow 上游路线图。

对原来”Sandbank 私有化支持”的修正

原结论:“Sandbank 私有化只有 BoxLite,限于 KVM 环境”——这话只在不写 adapter 且不看上游 TODO 的前提下对。

**修正结论:**Sandbank 的真实私有化能力 = “BoxLite 内置 + Daytona/Fly/CF 现有 adapter 各自的 volumes/snapshot + 上游 TODO 路线图(含 E2B)+ 4 个 1 天可接入的 backend”。Sandbank 是 adapter 模式产品 + 活跃的上游——私有化覆盖面取决于”等多久”和”愿意贡献多少”,不取决于”是否被锁死”。

POC 阶段的最优组合(修订)

路径本期 POC 选什么规模化阶段加什么
公有云Sandbank + 内置 FlyAdapter规模化时加 CubeSandbox(公有云高密度场景)
私有化(默认)Sandbank + 内置 BoxLite adapterS3 实测:Apple Silicon Mac mini + Hypervisor.framework 等效 KVM
私有化(国产合规)本期不做 · 评估 Sandbank E2BAdapter(同时接 CubeSandbox)规模化时 1d 接入 CubeSandbox + 真 E2B 双线
私有化(成熟方案兼容)本期不做未来 0.5d 接入 Daytona OSS(已有 adapter)

**最终判断:**Sandbank 对私有化的真实支持是足够的——不是因为它内置覆盖广,而是因为它的 adapter 模式让扩展成本压到了 1 天数量级。本期 POC 用 BoxLite adapter 即够,未来需求出现时,每加一个 backend 都在可控工期内。

6.2 状态机推进

proposing ─ dispatch ▶ running / explore ▶ running / propose ▶ running / apply ▶ running / archive ─ PR 创建 ▶ awaiting_review ─ 合并 ▶ done

失败分支running/* 任何阶段(含 proposing 阶段下游)若抛 SDK ERR / hard kill / API_retry 耗尽,→立刻转 failed,写 tasks.failedReason(max 1KB),SSE 推 type=failed 事件后退出。failed 终态,由 web 端给”重试”按钮(创建新 task)。proposal.md 中标记为 BREAKING change(status enum 加 failed)—— D7 已固化。

6.3 容器 → 主 API 的事件 callback

POST /internal/tasks/{taskId}/events Content-Type: application/json X-Task-Hmac: <hex of hmac-sha256(secret, body)> { "type": "stage_change", // | "log" | "completed" | "failed" "stage": "propose", "status": "running", "data": { "additions": 0, "deletions": 0, "filesChanged": 0, "logTail": "..." // 最近几行 stdout,用于 SSE 转发 } }

6.4 前端 SSE 事件流

GET /projects/:r/branches/:b/tasks/:t/events → SSE stream event: stage_change data: {"stage":"propose","status":"running"} event: log data: {"tail":"npm install completed in 47s"} event: completed data: {"prUrl":"https://github.com/owner/repo/pull/123"}

§7 POC 任务拆解(双轨)

粗粒度时间线 · 单人 ~13 工作日。双轨架构在 day 1 落地,不是远期改造

公共部分(两条路径共用,9 项)

  1. 设计 task_events 表 + Prisma schema — 0.5d
  2. 接入 Sandbank · 配置工厂方法(按 env 切 FlyAdapter / BoxLiteAdapter,见 §6.1.1)— 0.5d
  3. 主 API 加 POST /tasks/:id/dispatch(调 sandbank provider.create)— 0.5d
  4. 主 API 加 POST /internal/tasks/:id/events(HMAC 验签)— 0.5d
  5. 主 API 加 GET /tasks/:id/events(SSE)— 0.5d
  6. 写 task-runner Docker 镜像(node:22-slim + bash + non-root + git + @fission-ai/openspec + Claude Agent SDK,平台无关)— 1d
  7. run.js 容器入口(git clone 用 x-access-token URL + 单 query() 调用 + PostToolUse hook 触发 stage_change,不依赖任何平台 API)— 2d
  8. installation token 短命发放(复用现有 GitHub App 路径)— 0.5d
  9. 前端 task 详情页接 SSE,MiniStages 实时更新 — 1d

公共小计 — 7d

⬢ 公有云路径(Fly Machines,3 项)

  1. 配置 Sandbank FlyAdapter + Fly token 注入(§6.1.2)— 0.5d
  2. 设置 Fly secrets · 部署到 staging — 0.5d
  3. 真跑一遍 · 公有云路径:真实仓库 + 真 PR — 1d

公有云小计 — 2d

▣ 私有化路径(VPS + Docker,4 项)

  1. 配置 Sandbank BoxLiteAdapter(详见 §6.1.3)— 1d
  2. 准备一台 4 vCPU 8 GB 演示 VPS(Hetzner / DigitalOcean),装 Docker — 0.5d
  3. 容器 → 主 API 的 callback 走公网 + HMAC(验证防火墙穿透)— 0.5d
  4. 真跑一遍 · 私有化路径:从 VPS 跑通同一个 task — 1d

私有化小计 — 3d

联调与验收(2 项)

  1. E2E:MSW 模拟两种 provider 的 dispatch + callback 链路 — 0.5d
  2. 双轨切换演示:同一个 task,切换 env 后两条路径都能跑通 — 0.5d

POC 合计 — ~13d

vs 单轨 POC 的工期对比:纯公有云路径 ~9 天,加私有化路径多花 4 天(其中 1 天是 BoxLiteAdapter 配置,1 天是 VPS 准备 + callback,1 天是私有化真跑,1 天是接口/联调)——用 44% 的额外工期换取消除供应商锁定 + 双轨商业模式。关键在于这 4 天必须放在 day 1——等公有云路径全跑通后再追加,工作量会翻倍(adapter 切换重构 + run.js 解耦 + 端到端回归)。引入 Sandbank 后,provider 抽象成本几乎归零——这也是为什么用了现成的沙箱中间层。

§8 POC 验证清单

把前面 §1-§7 的调研结论凝结成可执行的验证矩阵。每条都对应一个具体决策点的不确定性——验证不过就要切换方案。

为什么需要这张清单:报告积累了 30+ 个调研结论(Sandbank capability、CubeSandbox E2B 兼容、I/O wait 50% 修正、BoxLite 长任务等),其中一部分是基于官方文档的事实,另一部分是需要本场景实测才能确认的。这张清单只列后者——给 POC 前期 spike + 实施期 + 真跑期一个明确的”check 列表”。

8.1 高优 spike(POC 启动前完成)· 状态总览

这些验证如果失败,整个 POC 设计要重做2026-05-18 全部 8 个 spike 通过,详见 docs/spikes/

#验证项状态实际发现详见
S1Claude Agent SDK 加载本仓库 .claude/skills/openspec-*✓ 通过SDK init message 含 skills 字段,无需进 turn 即枚举出 4 个 openspec skills(不消耗 token)spikes/s1-claude-agent-sdk-skills.md
S1.5SDK 鉴权 fallback(OAuth vs API key)✓ 通过SDK 从 keychain 读 OAuth + 调 /api/oauth/claude_cli/create_api_key 换临时 API key;Max 订阅吸收实际成本。容器内仍必须用 API key(合规边界)同上(追加部分)
S2Sandbank 集成可行性(3 个等价验证)✓ 通过S2.a Provider capability 契约固化(FlyioAdapter 无 exec.stream 但架构不依赖)· S2.b NestJS dispatch + HMAC 端到端 · S2.c BoxLite local microVM 真启动(独立 Linux 6.12.76 内核)spikes/s2-sandbank-capabilities.md
S3+S4远程 macmini + Tailscale callback(合并验证)✓ 通过Apple M4 Mac mini 上 BoxLite microVM 真启动 + Tailscale tailnet 直连 26ms RTT callback。原 VPS+KVM 改成 Mac mini+Hypervisor.framework——硬件门槛大幅降低spikes/s3-s4-remote-macmini.md
S5OpenSpec 4 阶段流转 + 长时稳定性✓ 通过4.16 min · 42 turns · $1.15 · 98% cache hit。一个 query() 走完 explore → propose → apply → archive 全程,真改 src/math.ts + tests,跑通 typecheck/test/validate。Agent 还自主修复了 2 个 pre-existing 项目配置问题(缺 @types/node、tsconfig 缺 allowImportingTsExtensions)。S5 spike 本身不含 git push(agent 保守不自做高风险动作),PR 闭环由 S8.B 单独验证 → PR #2 spikes/s5-four-stages-pipeline.md
S6microVM 内部 → 主 API 出口验证✓ 通过S3+S4 之前 callback 是从 macmini host 发的,未覆盖 microVM 内部出口。本次补:alpine box 内 wget/curl 主 API + HMAC 验签(远端 Python sig 与 NestJS createHmac 一致)。BoxLite 自带 NAT/DNS proxy 0 配置出网。microVM 内 RTT p50 35ms / avg 54ms(vs host 直发 26ms),NAT 一跳 +10ms 可忽略spikes/s6-microvm-egress.md
S7macmini microVM 内 SDK 长时复杂任务✓ 通过合并验证 “SDK 能在 microVM 内跑 + 长时稳定性”。BST 完整实现 + 19 测试,9.66 min · 92 turns · 98.5% cache hit关键新发现:(a)通过 subapi 中转(ANTHROPIC_BASE_URL+API_KEY)让 SDK 跑通——D11 双轨鉴权多了第三条路径;(b)Claude binary 拒绝 root 用 --dangerously-skip-permissions,容器必须用 non-root user;(c)Agent 在 OpenSpec CLI 缺失时手动 mkdir+mv 模拟 archive,工程灵活性体现spikes/s7-microvm-long-task.md
S8实施期 must-have gap 验证(A + B + C)✓ 通过S7 后用户 review 识别的 3 个缺口集中验证:A·openspec CLI 真实包名是 @fission-ai/openspec v1.3.1(不是 npm 上的空包 openspec v0.0.0);B·microVM 内端到端 git push 真 PR PR #2 ——关键技巧 https://x-access-token:$TOKEN@github.com/... 0 配置 credential helper;C·Fly machine 真启动 7.7s · AMD EPYC · 256MB,box→subapi RTT 239ms。sandbank flyio 内部 bash -c 包装,base image 必须含 bash(用 node:22-slim 不是 alpine)spikes/s8-abc-implementation-gaps.md

spike 期间识别的额外架构发现

  • ESM/CJS blocker · sandbank ESM-only,apps/api 原 CJS。已切 NodeNext ESM(实际 3 小时工作量,比预估 1-2 天快)。详见 docs/task-runner-sandbank-integration.md
  • D11 双轨鉴权(design.md 新增决策)· 容器内 API key + 本地 dev SDK 自动 OAuth fallback
  • Tailscale = 天然 callback 通道 · 跨网段不需要 cloudflared / ngrok
  • 非 admin 用户也能私有化 · 用 uv 替代 brew 装 Python,全程不需要 sudo
  • Cache hit rate 98%(S5 实测)· OpenSpec skill 是 stable system prompt,重复 turn 几乎零增量。S5 简单 prompt $1.15、S7 BST 复杂 prompt $3.03——cache 命中让重复 turn 几乎零增量,远低于”每 turn full input 重传”的一般 multi-turn 估算
  • Agent 自主修复工程问题(S5 + S7 实测)· S5 typecheck 失败时主动 git stash 验证预存在问题;S7 OpenSpec CLI 缺失时手动 mkdir+mv 模拟 archive、自我反思修测试期望——LLM-as-engineer 真实表现稳定
  • subapi 中转作为第三条鉴权路径(S7 实测)· 把 ANTHROPIC_BASE_URL 指向用户自管 subapi 实例,ANTHROPIC_API_KEY 用 subapi 内部 key——彻底绕开”OAuth 给容器外用”合规风险,但用户得自己运维 subapi。补强 D11 双轨 → 三轨
  • 容器必须 non-root(S7 实测)· Claude Code binary 拒绝 root 用 --dangerously-skip-permissions。alpine 默认 root,要用 node:22-alpine 内置 node user(uid 1000)或 Dockerfile USER 切换
  • BoxLite 实施细节(S7 实测)· 默认 disk 太小要 disk_size_gb=10copy_in() 不可靠改用 base64 注入或 box 内 git clone;alpine 默认缺 ca-certificates + bash + ripgrep
  • openspec 包名是 scoped(S8 实测)· 正确装法 npm install -g @fission-ai/openspec v1.3.1。npm 上的 openspec 是空占位包 v0.0.0 不要用
  • git push 用 x-access-token URL 注入(S8 实测)· https://x-access-token:$TOKEN@github.com/owner/repo.git — 0 配置 credential helper,token 由 D11 容器内续期机制提供(GitHub App installation token)
  • Fly base image 必须含 bash(S8 实测)· sandbank flyio adapter 内部 bash -c "<cmd>" 包装 exec。推荐 node:22-slim(Debian 自带 bash),不要用 alpine 默认
  • 公有云 vs 私有路径实测对比(S8 实测)· Fly 启动 7.7s vs BoxLite macmini 1.5s(5x,cloud overhead 合理);callback RTT Fly→subapi 239ms vs LAN 35ms(7x,跨 ocean)。印证 D2 双轨设计:私有低延迟,公有云弹性

8.2 实施期边做边验(POC 13 天中嵌入)

这些不需要前置 spike,但在对应任务完成时必须验证

#验证项嵌入哪个 §7 任务通过标准失败应对
I1同一镜像在 Fly Machine + 本地 Docker 两端都能跑§7 任务 06(写镜像)同一 docker pull 后,两端运行 node /app/run.js --self-test 都返回 0把平台特定逻辑拆到 entrypoint,run.js 保持纯净
I2Sandbank env 切换在主 API 实际生效§7 任务 02(接入 Sandbank)TASK_RUNNER_MODE=private / =public 切换后,/tasks/:id/dispatch 派发到不同 backend,无需重启进程用工厂方法在 dispatch 调用时实例化 provider(而非 module load 时)
I3SSE 长连接不被中间代理切断§7 任务 05(SSE endpoint)前端订阅 SSE 后保持 30 min,期间收到 ≥ 4 个 stage_change 事件(对应 4 阶段)检查 Fly proxy buffer / Vercel edge timeout · 加心跳 ping 事件
I4HMAC 验签防止 callback 伪造§7 任务 04(events controller)构造 invalid HMAC 的 POST 返 401 · valid HMAC 但替换 body 返 401检查 HMAC 算法是否对 body 全量签名(不只是 header)
I5installation token <1h 过期续期机制§7 任务 08(token 发放)容器跑超过 50 min 时主动调主 API /internal/tasks/:id/token,拿到新 token 后 git push 仍成功把 token 续期内嵌到 run.js 的 git 调用 wrapper

8.3 真跑验收(POC 完成时一次性 demo · §7 任务 12 + 16)

这是 POC 的”end-to-end 合格判定”——只有这一关过了,才能给团队/客户演示。

#验证项通过标准
E1公有云路径端到端从前端 04a 页面 submit prompt → Fly Machine 启动 → 跑 4 阶段 → 生成真 PR → 前端 MiniStages 实时显示 stage 跳变 → 30 min 内完成
E2私有化路径端到端同上,但容器跑在演示 VPS 而非 Fly · 同一份 prompt / 同一个仓库
E3双轨切换不破坏功能TASK_RUNNER_MODE env 在两个值之间切换 3 次,每次都能成功派发到对应 backend,且前端体验无差
E4故障容错故意让一个 task 中途失败(如 Anthropic API 报 5xx),容器要把 status=failed 回传,前端正确显示
E5账单防护(§5.5 中的 4 层防护)启动一个会”自动循环”的恶意 prompt(如要求 agent 无限递归),容器 60 min hard kill · 主 API 并发计数生效 · 总账单 ≤ $5

8.4 验证清单可视化(spike 优先级图)

spike 优先级(从上到下:阻塞性高 → 边做边验) 工期 0h ───► 0.5d S1 Claude SDK + skills (关键支点) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ 1-2h S2 Sandbank 长任务 (新库稳定性) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ 0.5d S3 BoxLite + KVM (私有化前置) ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ 2h S4 防火墙穿透 (私有化前置) ▮▮▮▮▮▮▮▮ 1h

怎么用这张清单:

  • POC 启动前:花 1-2 天跑完 S1-S4 四个 spike。任何一个失败 → 走对应”失败应对”或重新规划。
  • POC 13 天实施期:每完成一个 §7 任务,对照 I1-I5 验证项打勾。
  • POC 结束 demo:E1-E5 必须全过,才能宣告 POC 完成、向上汇报。
  • 把这张表存到 OpenSpec change 的 tasks.md——每个验证项就是一个 task。

§9 风险与未决问题

POC 落地前需要解的硬骨头。每个风险都对应 §8 验证清单里的一项。

  1. Claude Agent SDK 在容器里加载本仓库 .claude/skills/ 的实际行为

    官方文档说 settingSources + skills:“all” 能加载,但本仓库 .claude/skills/openspec-* 是否 100% 兼容 SDK 期望的 SKILL.md 格式需要 spike 验证。 → 验证项 S1(POC 启动前 1-2h 完成)

  2. 网络出口与 callback 路径

    容器要访问 api.github.com / api.anthropic.com;callback 回主 API 走公网或 Fly 内网。POC 阶段建议公网 + HMAC 验签更省事。 → 验证项 S4(私有化路径下额外要测防火墙穿透)+ I4(HMAC 验签)

  3. Spec 缺一个 failed 状态值

    现有 status enum 只有 proposing/running/awaiting_review/done,跑失败没地方放。POC 实施前要先扩 spec。 → 验证项 E4(真跑阶段的故障容错演示需要这个状态值存在)

  4. installation token 1h 过期

    GitHub App installation token 默认 1h,容器寿命 > 1h 会失效。需要在主 API 加 /internal/tasks/:id/token 端点用于续期。 → 验证项 I5(实施期边做边验,跑超 50 min 时测续期)

  5. 并发与排队

    并发上限由 §5.5 列的 4 层防护管(同用户 24h ≤ 20 task / 同 repo ≤ 3 并发 / 全局排队),不另起队列服务——计数走 Postgres tasks 表 + 主 API dispatch 前查询。规模化阶段如出现高并发可换专用队列。 → POC 不验证(推迟到规模化阶段)

  6. 取消 / 强制停止

    设计稿 04 task-menu-stop-{id} 已设 disabled。未来支持时调 Sandbank provider.destroy(id)(不直接绑 Fly REST)。 → POC 不验证(前端按钮保持 disabled)

  7. apply 阶段跑多少测试

    POC 阶段只跑 typecheck + lint,避免 30 min 上限。完整 jest/playwright 留给 GitHub Actions CI(PR 创建后自动触发)。 → 验证项 E1/E2(30 min 端到端时间预算的约束)

  8. 账单失控(死循环 / 恶意调用 / 容器卡死)

    Fly Machines 没有原生 spend cap;Cloudflare Containers 有用户实测比 GCP 贵 2.5x 且无月度 hard cap 的反馈。POC 阶段必须同时落 §5.5 列出的 4 层防护:主 API 并发上限 + 容器内 hard timeout + 平台账单告警 + 主 API 巡检 cron。(模型 token 侧的账单防护属于另一条线,由你在 Anthropic / OpenAI console 独立配置。) → 验证项 E5(真跑阶段用恶意 prompt 演示 4 层防护都生效)

§10 私有化部署路径

私有化路径的完整调研(深度大于 §6.1.2-§6.1.4 的 POC 集成讨论)。本期 POC 已经包含私有化栈(Sandbank + BoxLiteAdapter),本节展开未来阶段可能要的扩展方案与 Runtime 矩阵。

10.1 私有化的精确口径

本节口径:“私有化部署” = 能在一台普通 Linux 服务器上通过 docker / docker-compose 启动。不要求云供应商账号、不要求 Kubernetes、不要求 Terraform / Nomad 等复杂 orchestration。一条命令在 VPS / 自建机房 / 任意 Ubuntu 22.04+ 上跑起来才算。

触发私有化需求的真实场景:

  • 合规要求代码 / 凭据不出企业网络边界
  • 开源 task-runner 平台让客户自己 docker 跑
  • 对推理质量有定制需求,要接入自己微调的模型
  • 规模化后单月 task 量 ≥ 5000,托管 SaaS 成本超过自托管运维成本

10.2 开源沙箱运行时速览(按 docker-only 可行性分组)

A · 真正 docker-friendly(符合用户口径)

#候选启动命令隔离技术License本项目契合
1AIO Sandbox 单 docker rundocker run -p 8080:8080 ghcr.io/agent-infra/sandbox容器级(共享 host kernel)Apache 2.0 · ★ 4.7k★★★★☆
2Daytona OSS docker-composedocker compose -f docker/docker-compose.yaml up -d容器级(Docker,沙箱间网络默认隔离)AGPL 3.0★★★☆☆
3BoxLite 嵌入式 microVM作为库嵌入 + KVM device 透传到 dockerOCI 兼容 microVM(KVM / Hypervisor.framework)Apache 2.0 · v0.9.5 · ★ 2.1k★★★☆☆
4Microsandbox 嵌入式 microVM同上(libkrun 路径)libkrun microVM(320ms 启动)Apache 2.0 · ★ 4.7k★★★☆☆
5OpenHands agent + sandbox 一体docker run(默认 sandbox 即 Docker)Docker 容器(每 session 独立)MIT · ★ 68.6k★★☆☆☆
6Docker Sandboxes (sbx) 2026-03 新出sudo apt-get install docker-sbxmicroVM(每 sandbox 独立 Docker daemon)License 未公开披露★★☆☆☆

B · 云依赖型自托管(不符合”docker-only”口径,仅供对比记录)

#候选实际部署要求为什么不符合
7E2B Infra 需 GCP/AWSTerraform + Nomad + Cloudflare DNS只支持 GCP / AWS Beta;Docker Compose 部署仍是 feature request
8Google Agent Sandbox 需 K8sKubernetes 集群 + gVisor/Kata 节点整个项目就是 K8s CRD,没有 docker-compose 路径
9Firecracker 直接自建从零搭 orchestrator3-6 人月起步;docker-friendly 候选已经覆盖此能力
10Cloudflare Sandbox SDK必须跑在 Cloudflare 平台不算”私有化”

**修正声明:**上一版报告把 E2B Infra 列为”优选迁移路径”是误导——它实际上需要 GCP/AWS 账号 + Terraform。按用户的 docker-only 口径,E2B Infra 不符合,AIO Sandbox / Daytona OSS 才符合

10.3 docker-friendly 候选展开

github.com/agent-infra/sandbox · ★ 4.7k · Apache 2.0

① AIO Sandbox · 单 docker run

定位:“all-in-one” 单容器沙箱——一条 docker run 即起。

启动:

docker run --security-opt seccomp=unconfined --rm -it \ -p 8080:8080 ghcr.io/agent-infra/sandbox:latest

包含: Browser (VNC + CDP) · VSCode Server · Shell · 文件操作 · MCP · Jupyter

**适配本场景:**对 Claude Agent SDK + openspec 4 阶段而言:Shell + 文件操作 + MCP 三块直接对得上;Browser / Jupyter 在 POC 范围内用不到,但不增加部署负担。

缺点:隔离是容器级(共享 host kernel),不是 microVM。若对抗恶意代码不是重点(POC 内场景由 GitHub App 信任边界保护),这够用。

**为什么推荐为 A 类首选:**启动门槛极低(一条命令),生态成熟(4.7k stars),未来如需多机扩展可叠加 docker-compose / swarm。

github.com/daytonaio/daytona · AGPL 3.0

② Daytona OSS · docker-compose

**定位:**完整的”dev environment 编排平台”自托管版。Daytona 自己不用 K8s/Nomad,直接 docker-compose

部署:

git clone github.com/daytonaio/daytona cd daytona docker compose -f docker/docker-compose.yaml up -d # 访问 http://localhost:3000 — 无需公网域名

包含 11 个服务: API / Proxy / Runner / SSH Gateway / Postgres / Redis / Dex (OIDC) / Registry / MinIO / Jaeger 等

最小配置: 4 GB RAM(推荐 8 GB)· Ubuntu 22.04+ / Debian 12+ / Fedora 39+

沙箱间网络: INTER_SANDBOX_NETWORK_ENABLED=false 默认禁用——强制隔离

致命警告:AGPL 3.0——任何”通过网络给用户访问的修改版”都被强制传染同 license。对商业产品是地雷。

github.com/microsandbox/microsandbox · Apache 2.0

③ Microsandbox · 嵌入式 microVM

**定位:**嵌入式 microVM 库(libkrun),适合写进 task-runner 镜像作为隔离层。

性能: 启动 320ms(精确实测)vs Firecracker 808ms

多 OS: Linux KVM / macOS HVF / Windows WSL2

凭据模型: 真实密钥永不进入 guest,通过 TLS allowlist 主机时才注入

跑在 Docker 里的方式: 需要把 /dev/kvm 透传到容器(--device /dev/kvm),且 host 启用 KVM。

用法:不是独立服务,是——在 task-runner 入口脚本里调用,给每个 task 拉一个 microVM。隔离强度高于 AIO Sandbox(VM-level)。

github.com/boxlite-ai/boxlite · Apache 2.0

④ BoxLite · 嵌入式 microVM

**定位:**与 Microsandbox 同赛道——也是 “SQLite of sandbox” 库。

成熟度: v0.9.5(接近 1.0)· 454 commits · ★ 2.1k

多 SDK: Rust / Python / Node.js / Go / C(比 Microsandbox 多 Go / C)

差异化: 持久化 Box(QCOW2 + snapshot/fork)——install 包/创建文件后下次能接续。规模化后可做”task 状态延续”。

缺点: 底层 microVM 引擎在公开文档里只说”OCI 兼容”,未明示是 libkrun 还是自研——技术栈透明度不如 Microsandbox。

BoxLite vs Microsandbox 二选一: 看成熟度选 BoxLite;看明确底层引擎与精确性能数据选 Microsandbox。

github.com/OpenHands/OpenHands · MIT · ★ 68.6k

⑤ OpenHands · agent + sandbox 一体

**定位:**前 OpenDevin。完整的”agent 平台 + sandbox 一体”——每个 session 一个独立 Docker 容器,含 SSH + Jupyter + BrowserGym。

部署: docker-compose,需要 Docker socket 访问。

本场景适配度问题: OpenHands 自带 agent loop(PromptHands 风格),而我们已经选了 Claude Agent SDK 作为 agent——OpenHands 的 agent 部分对我们冗余。能不能只用它的 sandbox 部分?理论可以(agent-server Docker 镜像是 MIT),但要逆向拆解,复杂度不低。

判定: 如果未来想换”OpenHands agent + OpenHands sandbox” 整套方案再考虑;本期 POC 不直接用。

2026-03 发布 · docs.docker.com/ai/sandboxes/

⑥ Docker Sandboxes (sbx) · 太新

**定位:**Docker 官方在 2026-03 推出的 microVM sandbox——每个 sandbox 一个独立 Docker daemon。

安装:

# macOS brew install docker/tap/sbx # Linux sudo apt-get install docker-sbx # 运行 sbx run claude

已知支持的 agent: Claude Code / Codex / Gemini CLI / GitHub Copilot CLI

未知点: license 未公开披露 · 完全 self-host 行为未文档化 · POC 阶段稳定性数据少

建议: 观察 3-6 个月,等社区积累生产经验后再评估。

10.4 单机自托管 vs 托管 SaaS 成本

以 Fly Machines(托管中最便宜)为基准 vs 单台 Linux VPS 跑 AIO Sandbox 做盈亏分析。只算容器成本,token 不算(与 §5 边界一致)。

每月 task 数Fly Machines(按量)VPS + AIO Sandbox(包月)结论
100$1.8$40(一台 4 vCPU 8GB)Fly 胜
1,000$18$40接近,Fly 仍胜
5,000$90$80(一台 8 vCPU 16GB)VPS 胜
10,000$180$160(两台 8 vCPU)VPS 胜
50,000$900$600(5 台 8 vCPU)VPS 胜

VPS 价按 Hetzner / OVH 一台 4 vCPU 8GB ≈ $40/月、8 vCPU 16GB ≈ $80/月(2026-05 公开价)估。**注意:**① SRE 人月成本未计入——按 0.2 FTE × $8K/月 ≈ $1600/月,真实盈亏点右移到 ≥ 30K task/月;② 上表只看容器成本,token 不算;③ 单台 VPS 无 auto-scaling,并发取决于机器规格。

关键反转:因为不再需要云供应商基础设施常驻成本,VPS + AIO Sandbox 的盈亏点远低于上一版报告说的 E2B Infra 路径——从月 task ≥ 1K 开始就接近,≥ 5K 时 VPS 全面胜出。这就是用户 docker-only 口径的真实价值:用一台普通 VPS 就能搞定的事,不必上 GCP 24 vCPU + 2500GB SSD

10.5 私有化路径的渐进升级图

阶段 1 POC 双轨 ─ 13 天 ▶ 阶段 2 单机扩容 ─ ≥ 5K task/月 ▶ 阶段 3 多机集群 ─ ≥ 100K task/月 ▶ 阶段 4 K8s
阶段私有化栈触发条件切换工作量
阶段 1Sandbank + BoxLiteAdapter(POC 双轨私有化路径)当前 POC3d · 见 §7
阶段 2Sandbank + CubeSandboxAdapter(自写,E2B 兼容协议)需要 microVM VM-level 隔离 / 国产化合规+1d · 详见 §6.1.4
阶段 3多机 + docker-compose 自建调度单机容量到顶2-3 周 · 写简单 queue + worker
阶段 4K8s + Google Agent Sandbox(gVisor/Kata)团队已有 K8s 平台 / task 量 ≥ 100K/月2-3 月 · K8s 基础设施 + 重写 dispatcher

POC 阶段已经做对的设计(让未来阶段切换成本最小):

  • 引入 Sandbank 沙箱中间层——未来切换 backend 只需新加或换 adapter,业务代码不动(详见 §6.1.2)。
  • 容器入口 run.js 只依赖 stdin/stdout 与 HTTPS callback——不绑任何平台 API,所有 sandbox runtime 都能跑同一个镜像。
  • 所有 Fly Machines / VPS Docker 调用走 Sandbank Provider 抽象,主 API 不直接接触平台 REST。

10.6 避坑清单(按 docker-only 口径修订)

  • **E2B Infra:**需 GCP/AWS + Terraform,不符合 docker-only 口径。除非已经决定上云做 Nomad 集群,规模化后也不应作为迁移目标。
  • **Daytona AGPL:**部署方式(docker-compose)符合口径,但 license 是地雷——商业产品避开,开源社区项目可以用。
  • **AIO Sandbox 隔离强度:**容器级(共享 host kernel)。POC 内场景由 GitHub App 信任边界保护够用,但跑不可信代码需要叠 Microsandbox / BoxLite 做 microVM 二次隔离。
  • **OpenHands:**是 agent + sandbox 一体方案。已选 Claude Agent SDK 作为 agent,OpenHands 的 agent 部分冗余;要只取 sandbox 需逆向拆解。本期不推荐。
  • **Docker Sandboxes (sbx):**2026-03 新出,license 未公开披露,self-host 行为未文档化。观察 3-6 个月。
  • **BoxLite vs Microsandbox 二选一:**API 形态几乎一致。看成熟度选 BoxLite(v0.9.5);看明确底层引擎与精确性能选 Microsandbox。
  • Cloudflare Sandbox SDK:“开源”≠“可自托管”——必须跑在 CF 平台。
  • **Firecracker 直接自建:**不要做。Microsandbox / BoxLite 已经把”嵌入式 microVM”做完且开源。
  • **K8s 引入:**除非已经有 K8s 平台,否则不要为 task-runner 单独引入——基础设施投资远超收益。

10.7 Agent 业务编排层(澄清版)

上一版本节命名混乱,已重写。之前把”Sandbox SDK”和”编排层”两个不同的事混在一起,还错把 OpenSandbox / Beam Cloud(属于 Runtime 层)列在这里。本节现在专门讲Agent 业务编排层

先画清楚三层架构

┌───────────────────────────────────────────────────────────────────────┐ │ LAYER 3 Agent 业务编排层 │ │ 在 sandbox 之上做业务封装:git worktree、agent 多 session、agent 协议适配 │ │ Sandcastle(git worktree 隔离) · rivet-dev/sandbox-agent │ │ 本场景结论:不需要(详见下方分析) │ ├───────────────────────────────────────────────────────────────────────┤ │ LAYER 2 中间层 / 多 backend 抽象 │ │ 统一 API 包装多种 Runtime — “写一次,切换 backend 不改业务代码” │ │ Sandbank(推荐) · cased/sandboxes · sandboxer │ │ 本场景结论:用 Sandbank(见 §6.1.2 / §6.1.3 / §6.1.4) │ ├───────────────────────────────────────────────────────────────────────┤ │ LAYER 1 Runtime(实际跑容器的运行时) │ │ 真正提供隔离 + 执行的实体 │ │ 公有云:Fly Machines · Vercel Sandbox · E2B · Cloudflare Containers │ │ 私有化:BoxLite · AIO Sandbox · Microsandbox · CubeSandbox · OpenSandbox │ │ 本场景结论:POC 用 Fly Machines(公有云)+ BoxLite(私有化) │ └───────────────────────────────────────────────────────────────────────┘

本场景为什么不需要 Layer 3?

引入 Sandbank(Layer 2)+ “每 task 一个干净容器”的设计之后,Layer 3 提供的两大功能在本场景里都用不上:

Layer 3 功能 A · git worktree 隔离

**Sandcastle 卖点:**同一个容器内,给多个 agent 开多个 git worktree(每个 agent 一个分支),结束后干净 patch 回 host。

本场景不需要原因:我们的设计是每 task 一个全新容器,容器内只跑一个 agent。容器即隔离,不需要在容器内再做 worktree 二次隔离。git push 直接在容器里调 git push origin agentBranch 即可。

Layer 3 功能 B · 多 agent 协议适配

**rivet-dev/sandbox-agent 卖点:**统一 API 同时控制 Claude Code / Codex / OpenCode / Cursor / Amp / Pi 等不同 coding agent。

本场景不需要原因:§4 已经决定用 Claude Agent SDK 一种。我们的 run.js 直接 import { query } from "@anthropic-ai/claude-agent-sdk" 调用,不需要一个统一协议来适配多 agent。

Layer 3 什么场景下才需要

触发条件需要哪个 Layer 3 项目本场景是否触发
单容器并发跑多个 agent 任务(复用容器节省启动开销)Sandcastle(git worktree 多任务隔离)否 — 每 task 一个容器
业务上要让用户在 Claude Code / Codex / Gemini CLI 之间切换 agentrivet-dev/sandbox-agent否 — §4 已锁定 Claude Agent SDK
需要让一个 agent session 跨容器/跨重启延续Sandcastle / rivet-dev 的 session 管理未来可能 — POC 阶段不做
本场景的实际需求(每 task 一个容器跑 Claude Agent SDK)run.js 直接调 SDK,不需要 Layer 3

本节保留是因为…

虽然本场景不用 Layer 3,但这一节保留作为决策记录有两个价值:

  • 明确”为什么不用 Sandcastle”——避免后续团队成员重新考虑这件事
  • 未来如果业务变化(如要支持多 agent、单容器多任务复用),知道去哪里找方案

联网调研发现的 Layer 3 候选(仅供未来参考):

#候选定位License / Stars未来触发场景
1Sandcastlegit worktree 隔离 · TS-firstMIT · mattpocock/sandcastle单容器多任务复用
2rivet-dev/sandbox-agent跑在 sandbox 内的 agent 统一控制 HTTP serverApache 2.0 · ★ 1.4k需要多 agent 协议适配

**本节最终结论:**Layer 3 在本场景下不引入——run.js 直接调 Claude Agent SDK,git push 直接在容器里执行。这让架构简化、依赖减少、调试更直接。仅当业务变成”单容器多任务”或”多 agent 切换”时再回头评估 Sandcastle / rivet-dev。

10.8 大厂开源方案专项

把全球主要科技公司开源的 sandbox 方案系统化对比。对国产化 / 合规部署场景尤其重要

厂商项目定位LicenseStars关键卖点
腾讯CubeSandbox 2026-04 新出microVM sandbox runtime · E2B 接口兼容Apache 2.0★ 5.7k60ms 冷启动 · 2000+/单机并发 · <5MB 实例开销
阿里OpenSandbox通用 sandbox 平台 · 多语言 SDK · CNCFApache 2.0★ 10.7k5 种语言 SDK · gVisor/Kata/Firecracker 三选一
字节AIO Sandbox单容器 all-in-one · UI-TARS 生态Apache 2.0★ 4.7k一条 docker run · Browser+VSCode+MCP 一站式
字节SandboxFusion评估导向 sandboxApache 2.010+ 编程评估数据集 · 偏 model benchmark 场景
字节DeerFlow 2.0SuperAgent harness(含 sandbox)Apache 2.0★ 35.3k含 LangGraph 集成 · 偏完整 agent 框架
微软AutoGen → MAF多 agent 编排 · 含 Docker code executorMIT★ 50k+AutoGen 已 maintenance · 新项目 MS Agent Framework

9.8.1 腾讯 CubeSandbox(重点关注)

github.com/TencentCloud/CubeSandbox · Apache 2.0 · ★ 5.7k · Rust+Go+C

CubeSandbox · 2026-04 开源 ⭐

性能数据(官方实测):

  • 单并发裸金属:<60ms 冷启动
  • 50 并发:平均 67ms · P95 90ms · P99 137ms
  • 单 96 核服务器:2000+ sandbox 并发
  • 单实例内存开销:<5MB

隔离: RustVMM + KVM · 每个 agent 独立 Guest OS kernel(VM-level,强于 AIO Sandbox 的容器级)

部署: 一键脚本(不是 docker-compose)

# 普通云 VM(PVM 启用 KVM)/ WSL 2 / 物理机均可 curl -sL https://github.com/tencentcloud/CubeSandbox/raw/master/deploy/one-click/online-install.sh | bash

E2B SDK 兼容(关键卖点):

# 改一个环境变量即可,业务代码零修改 export E2B_API_URL=http://localhost:<port> # 然后用任何 E2B SDK,背后跑的就是 CubeSandbox

CubeSandbox 与本场景的契合度

替代 §10.5 阶段 2 的优势:

  • 性能远胜 AIO Sandbox:60ms vs 未明示
  • 隔离强度:VM-level vs 容器级
  • 并发密度:2000+/单机——单台 96 核服务器就够撑 50K task/月
  • E2B 兼容意味着:如果未来还要切到托管 E2B Cloud 或自托管 E2B Infra,客户端代码不动。这是非常优雅的”未来不锁死”设计。

已知风险:

  • 太新:v0.2.1,2026-04 才开源,生产案例尚未积累
  • 没有 TS SDK:官方只展示 Python SDK;E2B TS SDK 是否能直接指向 CubeSandbox 未实测验证(理论上能)
  • 需要 KVM 支持:部分廉价 VPS(OpenVZ)不支持 nested virtualization
  • 主战场中国:海外社区采用案例少;中文文档优于英文文档

建议: POC 阶段先用 Fly。私有化阶段做对比 spike——AIO Sandbox(成熟)vs CubeSandbox(性能 + 兼容性更优)。

9.8.2 字节生态澄清

之前 §10.2A 列的 AIO Sandbox (agent-infra/sandbox) 实际上是字节生态的一部分——字节的 bytedance/UI-TARS-desktop 项目已经从内部 omni-tars core 迁移到 agent-infra sandbox。字节系完整图谱:

项目定位与本场景适配度
AIO Sandbox agent-infra/sandbox单容器 sandbox runtime · UI-TARS 的执行环境已在 §10.2A 推荐
SandboxFusion bytedance/SandboxFusion多语言代码执行 + 评估 sandbox偏 benchmark/eval 场景,不是”长跑 agent task”——与本场景定位错位
DeerFlow 2.0 bytedance/deer-flow · ★ 35.3kSuperAgent harness(含 sandbox-aware execution)与 Claude Agent SDK 重叠——是 agent 框架不是 sandbox 工具
Seed-OSS字节 Seed Team 的 LLM 系列不在本期 sandbox/agent SDK 选型范围

9.8.3 微软方案

AutoGen 已进入 maintenance mode(2026-Q1 起不再添加新特性,社区维护)。新项目 Microsoft Agent Framework (MAF) 是企业级继任者。AutoGen 内置 DockerCommandLineCodeExecutor——但这是 agent 框架自带的代码执行能力,不是独立的 sandbox runtime/SDK 产品,与本场景的”task-runner 跑 Claude Agent SDK”定位错位。

判定: 微软方案与本场景不直接对接。

9.8.4 更新后的推荐栈(含国产备选)

场景推荐备选
POC(本期)Fly Machines + Claude Agent SDK
私有化阶段 2 · 海外团队VPS + Sandbank(BoxLiteAdapter)VPS + AIO Sandbox(字节系)
私有化阶段 2 · 国产化合规VPS + CubeSandbox(腾讯,E2B 兼容)VPS + OpenSandbox(阿里)/ AIO Sandbox(字节)
需要 microVM 强隔离CubeSandbox(VM-level)Microsandbox / BoxLite(嵌入式库)
规模化 ≥ 50K task/月CubeSandbox(单 96 核服务器即可)K8s + Google Agent Sandbox

关键洞察:国产大厂的 sandbox 方案在 2026 年集中爆发——腾讯 CubeSandbox(2026-04)、阿里 OpenSandbox、字节 AIO Sandbox/SandboxFusion,都比同期海外开源方案性能更激进(CubeSandbox 的 60ms 冷启动 + 2000 并发数据没有海外同类项目能匹敌)。这对国产化 / 合规场景是意外的红利——不必为了”私有化”牺牲性能。

§11 下一步

双轨 POC 的 3 条可选路径。

路径 A · 推荐

直接立双轨 OpenSpec change

把本报告精简为 add-task-runner-poc change,proposal.md 明确双部署目标,§7 任务清单直接落到 tasks.md,开 /opsx:apply 按 13 天工期实施。

适合: 方向已校准(公有云 + 私有化双轨),信心高,接受 13 天投入。

路径 B · 谨慎

跑完 §8 验证清单的 S1-S4,再立 change

4 个高优 spike 总工期 1-1.5 天,是 13 天 POC 投入前的 risk hedge:

  • S1 · Claude Agent SDK 加载 .claude/skills/(1-2h)
  • S2 · Sandbank 长任务稳定性(0.5d)
  • S3 · BoxLite + KVM 在目标 VPS 上能跑(2h)
  • S4 · 防火墙穿透 callback(1h)

适合: 避免 13 天 POC 中途遇到根本性阻塞返工——每个 spike 失败都有明确替代方案(见 §8)。

路径 C · 减法

分两期 POC:先公有云,再加私有化

第 1 期(9 天)只做公有云 + Sandbank FlyAdapter;第 2 期(4 天)追加 BoxLiteAdapter + 私有化路径。

适合: 需要尽早 9 天有可演示版本(牺牲一些日 1 双轨同步收益)。注意: 第 1 期必须先接入 Sandbank(不直接调 Fly REST),否则第 2 期会大返工。

**三条路径的核心差异:**A 是”信心拉满,一次到位”;B 是”先验证关键技术假设,避免大返工”;C 是”先有公有云 demo,私有化再追”。三条路径都保留双轨架构的核心 — Sandbank 接入必须在第 1 个工作日就完成,才能让后续切换 adapter 不破坏业务代码。

§12 POC 实施进度 2026-05-19 · E1+E2 真跑通过

本节是 v1(2026-05-17 explore 阶段)后的实施期记录。原 §7 任务清单 ~13 工作日实际工期内完成,含 8 个 spike(S1-S8 + 私有化 S9)+ 双轨真跑生成真实 GitHub PR。

⬢ 公有云路径 · 验证项 E1

Fly Machines · 真跑通过

  • app: openspec-task-runner(NRT 区域 · shared-cpu-4x 4GB · node:22-slim amd64)
  • API: api.douglas-agent.com(Fly app openspec-api
  • image: registry.fly.io/openspec-task-runner(buildx --platform linux/amd64 解决 macOS arm64 vs Fly amd64)
  • 真实产物:PR #12  — agent SDK 经 subapi 中转 4 阶段跑通 + x-access-token push + curl POST /pulls
  • 耗时:dispatch → PR 出 ~12 min · 累计 5 次 dispatch + 1 debug machine $0.012
▣ 私有化路径 · 验证项 E2

Mac mini Hypervisor + BoxLite · 真跑通过

  • host: vibe-zlyan(Apple M4 16GB · Hypervisor.framework)
  • microVM: boxlite 0.9.5 单 binary(含 CLI + Python SDK + serve mode)· node:22-alpine + chown node user
  • 跨网段: Tailscale tailnet(开发者 100.64.3.43 ↔ macmini 100.101.167.99 · p50 26ms)
  • 真实产物:PR #13  — boxlite microVM 内运行 task-runner 等价流程
  • 耗时:dispatch → PR 出 ~10 min · 私有化路径 host 已自有,零额外 compute 费

11.1 § 9.4 集成路径解锁 — BoxLite serve = BoxRun

v1 (2026-05-17) Open Questions 中标 “BoxRun daemon 来源未知”,实施期解决:boxlite-ai/boxlite 单 binary 含 boxlite serve REST mode = BoxRun。

# macmini 装单 binary(不需要 Python venv) curl -fsSL https://github.com/boxlite-ai/boxlite/releases/latest/download/install.sh | sh # 启 daemon nohup ~/.local/bin/boxlite serve --host 0.0.0.0 --port 8100 > /tmp/boxlite.log 2>&1 & # 主 API 集成(Sandbank BoxLiteAdapter remote 模式) const adapter = new BoxLiteAdapter({ mode: 'remote', apiUrl: 'http://100.101.167.99:8100', // Tailscale tailnet prefix: 'default', // 必传 - boxlite serve 默认 prefix='default',sandbank 默认 prefix='' 404 }); await adapter.listSandboxes(); // 实测返历史 box 列表

capability 集 ['exec.stream', 'port.expose', 'sleep', 'snapshot', 'terminal'] 与 S2.c spike 一致。实施期 follow-uptask-runner-provider.factory.ts 在 private 模式下使用此配置(spike 已通,主 API 集成路径已打通 · 见 tasks.md §12.4)。

11.2 实测成本验证(vs §5 估算)

§5 估算(2026-05-17)实测(2026-05-19)差异
Fly Machine · 单次 30min task$0.0027(shared-cpu-2x 2GB)~$0.0023(shared-cpu-4x 4GB · 实际 ~12 min)持平(估算偏高,实际任务比假定的 30 min 短)
POC 真跑总成本(5 次 dispatch + 1 debug)≤ $0.05(5 次预算)$0.012(compute)+ $0.04/月(image storage)低于预算 4 倍以上
私有化 Mac mini host 月费$0(自有硬件)$0(vibe-zlyan 已运行其他服务)一致
当前状态(POC 结束后)task-runner app fly scale count 0 + suspended;0 active machine,仅留 image storage已经 0 compute 计费

11.3 8 个 spike 全部通过

Spike验证内容关键发现
S1Claude Agent SDK 加载 .claude/skills/openspec-*init message 携带 skills: string[],4 个 openspec skills 全部识别
S2Sandbank ESM + capability 契约 + BoxLite Python SDKFlyAdapter 无 exec.stream(架构不依赖);BoxLite 在 macOS Hypervisor.framework 真启动 microVM
S3远程私有化 host 上 BoxLite microVM换路径:原 Linux VPS+KVM 改 Apple Silicon Mac mini + Hypervisor.framework;非 admin 用户 uv 装即可
S4容器→主 API callback 防火墙穿透Tailscale tailnet 替代 cloudflared/ngrok;health 26ms RTT;4 个 callback 场景全部通过
S5Agent SDK 单 query() vs 多 turn单 query() 跑完 4 阶段,maxTurns≥200,PostToolUse hook 推 stage_change
S6microVM 内 → 主 API 网络延迟Tailscale p50 35ms / 公网 239ms(HMAC 加 ~2ms)
S7Claude binary 拒绝 rootDockerfile 必须 USER node + chown -R node:node /workspace;bypassPermissions 模式下额外硬约束
S8openspec CLI npm 包 + slim/alpine 兼容scoped 包 @fission-ai/openspec(裸 openspec 是空占位);slim 必须自带 bash;alpine 无 gh CLI 需 curl POST /pulls
S9§9.4 BoxLite serve remote mode 集成boxlite serve 单 binary = BoxRun;prefix: 'default' 必传;E2 PR #13 真跑通过

11.4 实施期遗留 follow-up

  • §12.3 提主 PR add-task-runner-poc(v2 报告 + 实施代码一并)
  • §12.4 task-runner-provider.factory.ts 在 private 模式下用 BoxLiteAdapter({ mode: 'remote', apiUrl, prefix: 'default' })
  • §12.5 run.js callback completed 一致性 bug(PR #12 真创建但 task 表没切 awaiting_review)
  • §12.6 DB ↔ GitHub App install repo 范围 sync gap(connect repo 前主动 reconcile)
  • §12.7 spec.md 加 BOXLITE_PREFIX(默认 'default')必填 env
  • §11.1-11.4 E3 双轨切换 demo 脚本 / E4 故障容错 / E5 账单防护 / demo 视频

11.5 后续 change · add-sandbank-fork-aio-e2b 2026-05-20 完成

POC 完成当日,立即启动并完成 sandbank fork + 新增 AIO/E2B adapter 工作:

  • Fork repoXeonice/sandbank-douglas-agent(MIT 衍生,weekly auto-merge 上游 main)
  • npm scope@douglas-agent — 13 个包发布(10 个上游 mirror + AIO + E2B + CLI)
  • 新 adapter@douglas-agent/sandbank-aio(AIOSandboxAdapter,dockerode + 可选 readinessProbe)+ @douglas-agent/sandbank-e2b(E2BAdapter + CubeSandboxAdapter,共享 E2BProtocolAdapter 基类)
  • TASK_RUNNER_MODE 扩展:5 项 public | private | e2b | cube | aio

E1/E2 等价真跑验证(B-full hard)

Backend路径结果
AIO mode主 API 本地起 + AIOSandboxAdapter + 本地 docker✅ 完整 E1 等价:PR #18  · 34 秒 4 阶段 · HMAC callback + DB 状态机推进
Cube mode主 API + CubeSandboxAdapter + douglas-wsl Tailscale△ 协议层校准通过(against cube-api/routes.rs 反查),listSandboxes/get/destroy 跨 Tailscale 真跑过;真创 sandbox 被 CubeSandbox upstream v0.2.x 自身 plugin loading bug 阻塞(issue #159 OPEN ),与 fork+adapter 无关

Cube upstream 阻塞分析

CubeSandbox 是 Tencent 2026-04-20 开源的早期项目(30 天 4 个 release),v0.2.0 / v0.2.1 / v0.2.2 都存在 cubelet.services.images.v1.Images gRPC service 未注册的系统性 bug:

  • 跨 AWS / Tencent CVM / 物理 PVM / WSL2 多个环境复现
  • maintainer 5/11 给的 workaround(down/up + cubecli unsafe init)多 reporter 实测仍 fail
  • PR #282(在 v0.2.2 中)只修了”v0.2.1 .vm not exist” 另一个 regression,没修 images.v1
  • 官方 troubleshooting docs 不覆盖(只列 XFS + 磁盘空间 2 条)

我们 fork 的 CubeSandboxAdapter 协议层已 ground-truth 校准 — 上游修复 #159 出 v0.2.3+ 后,adapter 不需任何改动即可用。

详见 openspec/changes/archive/2026-05-20-add-sandbank-fork-aio-e2b/

§13 参考资料

本报告所引用方案的官方资源。所有数据点均基于这些来源在 2026-05 的内容。

§3 · 云端容器(托管 SaaS)

§4 · Agent SDK

§10.A · docker-friendly 候选(符合用户口径)

§6.1.2 · 沙箱中间层(multi-backend 抽象,磨平公有云/私有化差异)

§10.7 · Sandbox SDK / 编排层(重要补充)

§10.8 · 大厂开源方案专项

§10.B · 云依赖型自托管(仅供对比记录,不符合 docker-only 口径)

隔离技术 / 通用对比


openspec-agent-platform · docs/task-runner-poc.html · v1 · 2026-05-17 · explore 阶段产物