Docker 沙盒为编码 Agent 引入了 Token 费用外的第二个计费维度——运行时长,常见的 Token 守卫无法覆盖计算费用,重试循环极易超出预算。
Docker Sandboxes 本周正式发布——专为运行 Claude Code、Codex 和 Gemini 的编码 Agent 打造的安全微虚拟机。隔离的守护进程、文件系统、网络,计量计费。
这是 Agent 隔离的正确架构。但对大多数成本防护机制来说,这是一个错误的假设。
本地 Agent 执行的成本模型很简单:
Session cost = tokens × model rate
沙箱就是你的机器。算力是免费的。
迁移到 Docker Sandboxes、E2B、Daytona 或 Modal——现在有两个仪表同时运行:
Token cost: tokens × model rate ← 你的防护大概率在追踪这个
Compute cost: uptime × compute rate ← 你的防护大概率没有追踪这个
一个运行 45 分钟的重试循环不只是在消耗 token 预算。它让一个按量计费的微虚拟机存活了 45 分钟。按照 E2B 的标准费率约 $0.083/小时,这个循环除了消耗的 token 之外,还额外增加了 $0.062 的算力成本。
单独看很小。对只按 token 成本校准的会话预算来说,却是致命的。
一个生产环境的编码 Agent,$0.50 的会话预算,GPT-5.6 Terra 定价($2/$12 每百万 token):
Expected:
5 agent turns × ~3,000 tokens
Token cost: ~$0.050
Sandbox uptime: ~8 minutes
Compute cost: ~$0.011
Projected: ~$0.061 — well within budget
A tool call fails.
Agent retries.
Gets confused.
Loops.
Actual:
80+ tool calls, ~50,000 tokens
Token cost: ~$0.710
Compute cost: ~$0.065 (47 min × $0.083/hr)
Actual total: ~$0.775
Token 防护最终会在 token 消耗上触发拦截。但算力成本从沙箱启动的那一刻就在累积——没有任何 token 级别的防护能够触及它。只有算力预算检查或生命周期限制才能阻止它。
interface SandboxSession {
sandboxId: string;
startedAt: Date;
computeRatePerHour: number;
spentTokenCents: number;
reservedTokenCents: number;
totalLimitCents: number;
}
function currentComputeCost(session: SandboxSession): number {
const uptimeHours = (Date.now() - session.startedAt.getTime()) / 3_600_000;
return uptimeHours * session.computeRatePerHour * 100; // cents
}
function totalCurrentCost(session: SandboxSession): number {
return session.spentTokenCents + currentComputeCost(session);
}
每次模型调用前的防护检查综合总额:
function guardCall(
model: string,
estimatedInputTokens: number,
estimatedOutputTokens: number,
estimatedCallMinutes: number,
session: SandboxSession
): void {
const price = MODEL_PRICES[model];
if (!price) throw new Error(`Unregistered model: "${model}"`);
const tokenCost =
(estimatedInputTokens / 1_000_000) * price.inputPerM * 100 +
(estimatedOutputTokens / 1_000_000) * price.outputPerM * 100;
const additionalCompute =
(estimatedCallMinutes / 60) * session.computeRatePerHour * 100;
const projectedTotal =
totalCurrentCost(session) +
session.reservedTokenCents +
tokenCost +
additionalCompute;
if (projectedTotal > session.totalLimitCents) {
throw new BudgetExceededError({
sandboxId: session.sandboxId,
projectedTotal,
limitCents: session.totalLimitCents,
breakdown: {
currentTokenSpend: session.spentTokenCents,
currentComputeCost: currentComputeCost(session),
thisCallTokens: tokenCost,
thisCallCompute: additionalCompute,
},
});
}
session.spentTokenCents += tokenCost;
}
projectedTotal 包含了已累积的算力成本。如果会话已经运行了 20 分钟,currentComputeCost 会在防护评估下一次调用之前反映这一情况。防护看到的是真实的成本轨迹——而不仅仅是未来的 token 消耗。
每次调用的算力建模对你的场景来说太细粒度了?生命周期限制用一小部分复杂度换来了大部分的保护:
function guardSandboxLifetime(
session: SandboxSession,
maxLifetimeMinutes: number
): void {
const uptimeMinutes = (Date.now() - session.startedAt.getTime()) / 60_000;
if (uptimeMinutes >= maxLifetimeMinutes) {
throw new SandboxLifetimeExceededError({
sandboxId: session.sandboxId,
uptimeMinutes,
limitMinutes: maxLifetimeMinutes,
});
}
}
// Call at the start of every agent turn — before any tool calls
guardSandboxLifetime(session, 30);
30 分钟的硬上限是一个不需要精确费率建模的算力天花板。它也能直接终结循环场景——如果会话在 30 分钟时终止,Agent 无法运行到 47 分钟。
一个重要的细节:在每个 Agent 回合的开始调用此方法,而不仅仅是在 LLM 调用时。算力计费在工具执行、文件 I/O 和空闲等待期间都在累积——而不仅仅是推理期间。
如果你的 Agent 目前在本地运行,而你正计划迁移到 Docker Sandboxes 或任何按量计费提供商,在切换之前扩展你的预算模型。
在本地准确的纯 token 成本建模,到了按量计费基础设施上会变得结构性不完整。一个你在 token 成本上预算为 $0.06 的会话,一旦加入算力时间就变成了 $0.12——这还没算任何超支。这个差距不是边缘案例。它是基准线。
两张账单是真实存在的。防护需要同时看到两者。