第 18 章:使用云沙箱并行运行大规模评测
一组由四个 Task、三个重复组成的评测,在本地看起来只是 12 个 Trial。真正运行时,它却同时争用 Docker 构建缓存、CPU、模型 API 配额和磁盘;如果其中一个环境在退出前泄漏,下一次实验还会继续计费。把 --n-concurrent 从 4 改成 100 并不是“迁移到云”,只是把一个容量问题放大了一百倍。
本章把第 14 章的多容器事故任务迁移到 Daytona,并把它与前三个系统诊断 Task 组成受预算约束的批量 Job。我们会先从 Harbor v0.18.0 的真实 Provider 注册表选择环境,再建立镜像与快照策略、两级并发门、有限重试、可恢复 Job 和本地—云端一致性验收。所有费用数字都是按公开单价计算的教学性估算,不是本机实测账单;没有云凭据的读者仍可完成配置解析、支持矩阵和失败分类探针。
读完本章,你应当能够:
- 从 v0.18.0 支持矩阵中选择与 Task 能力相容的 Environment Provider;
- 用可解析的 Job 配置控制 Trial 并发、Agent 并发、重试和环境生命周期;
- 区分 Provider 内部重试、Job 级重试、Job 恢复与业务失败;
- 用明确公式估算沙箱、模型和重试的预算上界;
- 用 Reward、Artifact 摘要和 Task digest 验证本地与云端的一致性。
18.1 规模化之前先定义不变量
云沙箱改变的是 Environment 的位置和生命周期,不应改变 Task 的正确答案。迁移前先写下四个不变量:同一 Task 内容、同一 Agent/模型配置、同一 Verifier、同一网络与资源意图。运行时间不属于不变量;本地和云端的调度、镜像缓存、区域与网络链路不同,要求毫秒级相同既不现实,也会掩盖真正的语义差异。
本章采用三道发布门:
- 结构门:JobConfig、Task、Provider 能力和凭据预检通过;
- 可解门:单并发 Oracle 在云端连续通过;
- 一致性门:同一批 Task 在 Docker 与云端得到相同的确定性 Reward,并产生语义等价的 Artifact。
只有第三道门通过后才提高并发。若单 Trial 都无法复现,多开沙箱只会生成更多互相矛盾的日志。
18.2 v0.18.0 到底支持哪些环境
EnvironmentType 是 CLI 接受的内置名称,EnvironmentFactory 再把名称映射到实现类和可选安装 extra;模块采用惰性导入,所以“名称出现在 --help”不等于相应 SDK 已安装。12 下表严格按固定提交生成。类别是本章为了选型给出的工程分组,不是 Harbor API 字段。
type | 工程分组 | 安装 extra | 关键定位 |
|---|---|---|---|
docker | 本地容器 | — | 默认基线 |
apple-container | 本地容器 | — | Apple Container |
singularity | 本地/HPC | — | Singularity 运行时 |
daytona | 托管沙箱 | daytona | 单容器、Compose、快照 |
e2b | 托管沙箱 | e2b | 单容器 |
modal | 托管沙箱 | modal | 单容器与 Compose,能力随模式变化 |
runloop | 托管沙箱 | runloop | 单容器/Blueprint |
langsmith | 托管沙箱 | langsmith | 单容器与 Compose |
islo | 托管沙箱 | islo | 单容器与 Compose |
novita | 托管沙箱 | novita | 单容器与 Compose |
tensorlake | 托管沙箱 | tensorlake | 单容器/镜像与快照 |
cwsandbox | 托管沙箱 | cwsandbox | CoreWeave Sandbox |
wandb | 托管沙箱 | wandb | W&B Sandbox |
blaxel | 托管沙箱 | blaxel | 单容器与 Compose |
opensandbox | 沙箱服务 | opensandbox | 可连接自建服务 |
beam | 托管沙箱 | beam | 单容器与 Compose |
ec2 | 云基础设施 | ec2 | VM 与 Compose |
gke | 云基础设施 | gke | Kubernetes |
ack | 云基础设施 | — | 阿里云 ACK |
openshift | 集群运行时 | — | OpenShift |
use-computer | 远程桌面/CUA | use-computer | 桌面任务专用 |
cua-cloud | 远程桌面/CUA | cua | CUA 云池 |
harbor[cloud] 是多个 extra 的集合,但没有包含 ack、openshift、apple-container 或 singularity;不要把它解释为“安装后所有表中类型都立即可用”。固定版本还给各 SDK 设置了最低版本,例如 Daytona 为 >=0.192.0。3
选型时还要检查能力,而不只是名称。v0.18.0 的 EnvironmentCapabilities 分别声明 GPU、TPU、断网、Allowlist、动态网络策略、Windows、挂载与 Compose;资源能力又单独区分 CPU/内存的 limit 与 request。4 例如 Daytona 声明 CPU/内存 request,不是 hard limit;其 Compose 模式保留断网与 Compose 能力,却关闭 Allowlist 和动态网络切换。5 因而本章的多容器任务可以使用 no-network,但不能把第 15 章的分阶段域名 Allowlist 原样搬过来。
注意:仓库中的旧
examples/tasks/hello-mcp/README.md仍写多容器任务不支持云 Provider;固定提交的 Daytona 实现、测试和当前 Harbor 文档已经支持 Compose。版本事实应以本书锁定的可执行代码为准,而不是以孤立的旧说明为准。67
18.3 把多容器系统映射到 Daytona
第 14 章的 Task 包含 main、PostgreSQL、Nginx 和独立 Verifier。Daytona Provider 看到 environment/docker-compose.yaml 后选择 Docker-in-Docker(DinD)策略:本地 Harbor 进程创建一台 Daytona Sandbox,在其中启动 dockerd,再上传 Task 的 Compose 文件并执行 docker compose build、up -d;Agent 仍然在 main 服务中运行。8
本地 Harbor Job
└─ TrialQueue(全局并发门)
└─ Daytona Sandbox(每个 Trial 一台,DinD)
└─ docker compose
├─ main ← Agent
├─ db ← PostgreSQL 证据
└─ proxy ← Nginx 访问日志
这层嵌套带来两个经常被忽视的边界。第一,外层 Sandbox 必须能拉取 DinD 和 Task 镜像;no-network 由内层 Compose overlay 施加给任务服务,而不是让外层在构建前彻底离线。第二,Compose 服务的 CPU/内存限制与外层 Sandbox 的资源申请不是同一个对象;迁移验收要同时检查 Harbor 的资源策略和容器内的实际限制,不能看到配置字段就宣称“硬限制已经生效”。
18.3.1 镜像、快照和缓存不是同一件事
镜像定义容器文件系统;Snapshot 是 Provider 可复用的沙箱基线;构建缓存只是某次构建的中间加速数据。三者都能减少重复工作,但一致性属性不同。
对单容器 Daytona Task,v0.18.0 可以根据 Environment 内容哈希自动创建 Snapshot,也可以用 snapshot_template_name 指定模板;自动 Snapshot 使用内容哈希命名,并对并发创建加进程内锁。9 对本章的 Compose Task,真正有意义的是 dind_snapshot:它预热外层 DinD 基线,但内层的 docker compose build 仍会发生。Daytona 官方文档也把 Docker Compose 描述为在启用 DinD 的 Sandbox 中编排服务。10
因此采用以下策略:
- 所有 Task 镜像使用不可变 digest,不依赖浮动
latest; - 首轮不用 Snapshot,先证明 Dockerfile/Compose 能从声明构建;
- 第二轮若采用
dind_snapshot,把 Snapshot 名称和创建依据写进实验清单; - 修改 Task、基础镜像或 DinD 版本后创建新名称,不覆盖旧基线;
- 分别记录冷启动与复用路径,但不把本章未实测的启动时间写成性能结论。
Snapshot 不应包含 Solution、Verifier 私有数据、真实凭据或上一 Trial 的可写状态。快照加速的是可信基线,不是跨 Trial 共享工作目录。
18.4 安装、认证与离线预检
先安装与全书相同的 Harbor 版本和 Daytona extra:
uv tool install 'harbor[daytona]==0.18.0'
harbor --version
export DAYTONA_API_KEY='<daytona-api-key>'
预期版本行是 0.18.0。v0.18.0 的 Daytona preflight 接受 DAYTONA_API_KEY,或 DAYTONA_JWT_TOKEN 与 DAYTONA_ORGANIZATION_ID 的组合;该检查只证明凭据变量存在,不会证明余额、区域容量或远端授权有效。11
在仓库根目录保存下面的 configs/ch18-cloud.yaml。datasets/system-diagnosis-v1/ 应包含前文已经固定的四个 Task;provider/model-id 是故意保留的占位符,真实运行前必须换成实验登记表中的确定模型标识。
job_name: ch18-daytona-batch-v018
jobs_dir: jobs
n_attempts: 3
n_concurrent_trials: 4
quiet: false
debug: true
retry:
max_retries: 2
include_exceptions:
- DaytonaRateLimitError
- DaytonaError
- ConnectionError
- TimeoutError
wait_multiplier: 2.0
min_wait_sec: 5.0
max_wait_sec: 60.0
environment:
type: daytona
force_build: false
delete: true
cpu_enforcement_policy: request
memory_enforcement_policy: request
kwargs:
connection_pool_maxsize: 8
auto_stop_interval_mins: 15
auto_delete_interval_mins: 0
labels:
book: harbor-practical
experiment: ch18-daytona-batch-v018
agents:
- name: terminus-2
model_name: provider/model-id
n_concurrent: 2
concurrency_group: model-api
datasets:
- path: datasets/system-diagnosis-v1
artifacts:
- /app/incident
该配置表示:最多同时存在 4 个 Trial,但最多 2 个 Trial 同时进入这一 Agent 组的 agent.run();四个 Task × 三次重复共计划 12 个 Trial。JobConfig 要求 Agent 的 n_concurrent 不得大于全局 n_concurrent_trials,共享 concurrency_group 的 Agent 还必须使用相同上限。12
先做不访问云端的结构检查:
cd /path/to/system-diagnosis-benchmark
harbor run -c configs/ch18-cloud.yaml --print-config > /tmp/ch18-config.json
uv run --with 'harbor[daytona]==0.18.0' python - <<'PY'
import json
from pathlib import Path
from harbor.models.job.config import JobConfig
cfg = JobConfig.model_validate_json(Path('/tmp/ch18-config.json').read_text())
assert cfg.environment.type.value == 'daytona'
assert cfg.n_attempts == 3
assert cfg.n_concurrent_trials == 4
assert cfg.agents[0].n_concurrent == 2
assert cfg.retry.max_retries == 2
print('job contract: ok')
PY
--print-config 在 v0.18.0 中发生于 Provider preflight 之前,所以它只能验证 schema。接着把批量配置复制成 configs/ch18-canary.yaml,将 n_attempts、n_concurrent_trials 改为 1,Agent 改为 oracle,并只保留多容器 Task。真实云端发布门是:
harbor run -c configs/ch18-canary.yaml -y
只有 Oracle Reward 为 1、Sidecar Artifact manifest 全部为 ok、Sandbox 被删除且账单出现合理记录后,才运行批量配置。
18.5 并发、限流与背压
Harbor 的全局并发门是 TrialQueue 上的 asyncio.Semaphore,覆盖一个 Trial 的创建、Agent、Verifier 和清理。Agent 并发门则由 lifecycle hook 在 AGENT_START 获取、AGENT_END 释放;因此 Agent 上限为 2 时,仍可能有 4 个 Sandbox 同时存在,其中两个在环境准备或等待模型配额。13 这是成本与吞吐量之间的重要区别:降低 Agent 并发不一定降低同时存活的沙箱数。
Daytona 的资源池和 API 限额按组织层级共享,官方 Limits 页面把一般请求、Sandbox 创建和生命周期操作分别计数,并通过 X-RateLimit-* 与 Retry-After-* 响应头暴露状态。具体限额随账户层级变化,不能把网页上的某一档数字硬编码成 Harbor 的安全默认值。14
实操时采用阶梯升压,而不是一步跳到目标值:
n_concurrent_trials=1跑 Oracle;- 改为 2,记录 Sandbox 创建错误、模型 429、峰值资源和清理结果;
- 再尝试 4、8,每一级至少完成一整批;
- 任何一级出现持续 429、容量错误或清理滞后,就回退一级,并把该值写入实验配置。
记录的是每阶段错误率和 Provider 头信息,而不是“我的笔记本能开多少个”。云端并发还受模型 Provider 的 RPM/TPM、Registry 拉取、组织 vCPU/RAM 配额和 Verifier 外部依赖影响;最小上限决定实际吞吐。
18.6 两层重试:只重试基础设施异常
Daytona Provider 自己已经对创建、Snapshot 查询和部分传输做重试。v0.18.0 把 rate limit 与 capacity 视为瞬态错误,创建路径最多尝试 10 次并线性增加等待;普通错误最多 3 次,而 TimeoutError 和确定性的 Sandbox 构建失败不在这层重试。15
Job 级 RetryConfig 是第二层。它只检查 TrialResult.exception_info.exception_type;Reward 0 且没有异常的 Trial 会直接返回,不会重跑。命中 include、未命中优先级更高的 exclude 时,Queue 删除本次 Trial 目录,等待后重新创建整个 Trial。1617
这解释了配置中的四个原则:
max_retries=2是额外尝试数,一个 Trial 最多执行 3 次;include_exceptions使用窄白名单,避免把候选错误伪装成瞬态故障;- 默认 exclude 仍会阻止 Agent timeout、Verifier timeout、Reward 文件错误和模型 API usage limit 重试;
wait_multiplier=2才让当前实现按min_wait_sec × 2^attempt增长;默认值 1 会得到常数等待,而不是指数等待。
警告:不要把
RewardFileNotFoundError加进重试白名单来“提高成功率”。缺 Reward 通常是 Verifier 合约或打包错误;自动重跑会烧掉模型与沙箱预算,却不修复确定性缺陷。
失败分类应先于重试:
| 现象 | 分类 | 动作 |
|---|---|---|
| 429、连接重置、短时容量不足 | 基础设施瞬态 | 有限重试并降并发 |
| Dockerfile 构建失败、缺文件、错误 digest | Task/镜像确定性失败 | 停止批量,修复并重新版本化 |
| Agent 交付错误但 Verifier 正常给 0 | 候选失败 | 保留结果,不重试 |
| Verifier 无 Reward 或解析失败 | Benchmark 基础设施失败 | 修复 Verifier,不计入模型能力 |
| Sandbox 删除被拒绝 | 清理/权限失败 | 停止扩容,人工清点资源 |
18.6.1 为每次失败保留最小证据包
并发运行最容易犯的错误,是只保存最后一条异常。一次 DaytonaError 可能发生在 Sandbox 创建、文件上传、Compose build、Artifact 下载或删除阶段;异常类相同,处置却完全不同。每个失败 Trial 至少保留以下字段:trial_name、attempt 序号、Task digest、Environment type、异常类与消息、生命周期阶段、Sandbox/session 标识、开始与结束时间、Reward 是否存在、Artifact manifest 状态。模型凭据、Provider token 和完整环境变量不得进入证据包。
排查顺序也要固定。先看 result.json 的 exception_info 和 timing,判断失败发生在 Environment、Agent 还是 Verifier;再在 job.log 中按 trial_name 搜索 Provider 重试与清理;随后检查 Trial 的 Agent/Verifier 日志和 Artifact manifest;最后才到 Provider 控制台关联 Sandbox。反过来从一条云端 429 猜测模型问题,常会把创建限流、模型限流和 Registry 限流混成一类。
建议为批量实验维护一张只追加的失败台账。相同 Task digest、相同阶段、相同异常归为一个事件,并记录首次/末次出现、受影响 Trial 数和处置。这样可以避免十二个并行 Trial 产生十二张重复工单,也能发现“重试后表面成功,但每轮都先失败一次”的隐性容量问题。
18.7 中断后恢复,而不是复制一个新 Job
Harbor 会把解析后的 config.json、result.json、Trial 子目录与 lock.json 写进 Job 目录。Lock 包含 Harbor 版本/可得的 Git 提交、Task digest、Agent、Environment、Verifier、并发和重试配置,用于解释“这批结果实际跑了什么”。18
进程中断后使用原目录恢复:
harbor job resume -p jobs/ch18-daytona-batch-v018
v0.18.0 只把与计划配置匹配且已有有效结果的 Trial 视为完成;没有 result.json 的残留目录会被删除并重新计划。恢复配置必须与原 config.json 相等,否则拒绝运行。CLI 默认删除异常类型为 CancelledError 的 Trial 目录再恢复;若已确认某个 DaytonaRateLimitError 结果应重跑,可显式增加过滤器。1920
harbor job resume \
-p jobs/ch18-daytona-batch-v018 \
-f CancelledError \
-f DaytonaRateLimitError
不要删除 Reward 0 的 Trial 来制造更高成功率,也不要修改保存的 config.json。若实验设计改变,应使用新 job_name 开一个新 Job;恢复的目标是完成同一实验,不是悄悄替换实验条件。
恢复前还要检查 Provider 控制台。Job 进程死亡不代表远端资源已经删除。Daytona Compose 策略按临时 Sandbox 处理,即使 Job 传 delete=false 仍会删除;但若删除操作因认证或授权失败,Provider 会退化为 stop,并明确警告 Sandbox 将留在账户中。21 因此恢复清单应包含:按 harbor.session_id/实验 label 搜索遗留 Sandbox、核对状态、删除孤儿,再重新启动 Job。
18.8 用公式而不是感觉控制成本
一个批次的成本至少包含四项:
C_total = C_sandbox + C_model + C_artifact + C_network
C_sandbox = Σ_i t_i × (
vcpu_i × p_cpu
+ ram_gib_i × p_ram
+ billed_disk_gib_i × p_disk
+ gpu_i × p_gpu
)
C_model = Σ_i (
input_tokens_i / unit × p_input
+ output_tokens_i / unit × p_output
)
其中 t_i 必须使用计费状态的秒数,而不是仅用 Agent 时长。Daytona 官方计费说明:started 以及 creating/starting/stopping 等过渡状态按预留的 CPU、RAM、磁盘计费;stopped/paused 通常只计磁盘;archived/deleted 不计 Sandbox 资源。22
下面是教学性估算。访问于 2026-07-16 的 Daytona 价格页列出每秒 vCPU $0.00001400、每 GiB RAM $0.00000450,存储前 5 GB 免费后每 GB·秒 $0.00000003。价格会变化,出版和实际运行前必须重新查询。23 假设每个 Trial 使用 2 vCPU、2 GiB RAM、4 GiB 磁盘并处于计费状态 180 秒,不含 GPU,则:
单 Trial 沙箱估算
= 180 × (2 × 0.000014 + 2 × 0.0000045 + 0)
= $0.00666
12 个首轮 Trial
= 12 × 0.00666
= $0.07992
若 12 个 Trial 全部用满 2 次额外重试,沙箱上界
= 12 × (1 + 2) × 0.00666
= $0.23976
该计算没有包含模型 token、镜像构建、Artifact 长期存储、网络、税费,也假定每次重试都消耗完整 180 秒,所以只能用于预算门,不是账单预测。实际模型成本应从每个 TrialResult 的 usage/cost 与模型官方价格表核对;Provider 控制台的 per-sandbox 资源秒是沙箱成本的最终证据。
并发主要改变墙钟时间和同时预留的资源,并不会神奇地消除资源秒。更高并发还可能因 429、冷构建和容量等待增加重试成本。因此批量运行前同时设置三项上限:计划 Trial 数、每 Trial 最大时长、额外重试数;运行中再设置余额告警和孤儿 Sandbox 告警。
18.8.1 把预算变成运行门,而不是事后报表
在启动 Job 前生成 budget.json,记录价格访问时间、货币、单价来源、计划 Trial 数、最大 attempt 数、每 Trial 时长上限、沙箱上界、模型上界和总批准额。该文件属于实验输入,应与 Job 配置一同归档,但不要写入任何付款或账户信息。
运行中设置两个停止阈值。软阈值用于降并发,例如实际消耗达到批准额的 60% 时不再提高 n_concurrent_trials;硬阈值用于终止实验,例如达到 80% 且剩余预算不足以完成最坏情况下的下一批 Trial 时停止提交。百分比只是教学策略,团队应按自己的告警延迟和结算延迟确定数值。关键是不把“账户还有余额”当作本次实验仍获授权。
结束后做三方对账:Harbor 汇总的模型 cost、Provider 的 per-sandbox 资源明细、预算文件的公式结果。三者的统计口径不同,不要求数值相等;应解释差异来自过渡状态、重试、免费额度、价格单位还是缺失 usage。无法解释的差异是发布阻断项,而不是四舍五入误差。
18.9 本地与云端一致性
一致性验收不要比较日志文本全文。Provider 会加入不同的容器名、时间戳和路径。应比较下面四层:
lock.json中 Task digest、Harbor 版本、Agent、Environment 以外的实验字段;- 确定性 Reward 和各维度
reward.json; - 规范化 Artifact,例如对排序后的数据库行、Nginx 指令和结构化报告计算 SHA-256;
- 失败类型分布,尤其是是否出现只在云端发生的 Environment/Artifact 错误。
先各跑一次 Oracle:
harbor run -c configs/ch18-canary-docker.yaml -y
harbor run -c configs/ch18-canary.yaml -y
然后用一个只读脚本加载两侧 result.json,断言 Task 名集合、Reward map 和规范化 Artifact 摘要相同。时长只做观测字段,不设相等断言。若 Docker 通过而 Daytona 失败,按“构建 → 服务 readiness → Agent → collect hook → Artifact 下载 → Separate Verifier”的顺序定位;不要先调整模型 Prompt。
资源语义也必须进入报告。Daytona 在 v0.18.0 只声明 CPU/内存 request,因此本章显式使用 request;若 Benchmark 的公平性依赖严格 hard limit,应选择能声明并验证 limit 的 Provider,或者把该 Provider 标记为不兼容,不能用 ignore 掩盖差异。
18.10 凭据、网络和数据安全
DAYTONA_API_KEY 是 Harbor 主机访问 Provider 控制面的凭据,不应通过 agents[].env 或 Task 的 [environment].env 传给候选 Agent。模型凭据若必须进入 Agent,应使用最小权限、独立项目、支出上限和短周期轮换。
Daytona 提供组织级 Secret:Sandbox 内只看到占位符,出口代理仅在目标主机匹配 Allowlist 时替换成明文。官方文档建议为每个 Secret 配置 host Allowlist。24 但 Harbor v0.18.0 的 Daytona Provider 明确禁止在 Compose/DinD 模式使用 secrets,所以本章多容器 Task 不能声称获得这一保护;需要 Provider Secret 的任务应改为受支持的单容器设计,或使用经过审计的其他凭据注入机制。25
另外执行以下安全基线:
- Task、镜像与 Snapshot 中不放真实事故日志、客户数据和 Solution;
- Artifact 收集采用白名单,发布前扫描 token、密码、邮箱和访问日志;
no-network的验收同时测试 Agent 主服务和 Sidecar,确认内层服务无法外连;- Provider label 不包含用户输入、密钥或事件详情;
- 运行结束后核对 Sandbox、Snapshot、镜像与远程 Artifact 的保留策略;
- 删除失败、Artifact manifest 非
ok或账单异常时立即停止扩容。
警告:
delete: true是清理意图,不是已删除的证据。唯一可靠的闭环是 Harbor 日志无清理异常、Provider 控制台无遗留资源、账单资源秒停止增长三者一致。
18.11 验收与发布门
本章示例按以下顺序验收:
[ ] harbor --version == 0.18.0
[ ] JobConfig 解析,计划 Trial 数 == 4 Task × 3 attempts
[ ] Provider extra 可导入,Daytona preflight 通过
[ ] 单并发 Oracle 连续三次 Reward == 1
[ ] Sidecar collect 与 Artifact manifest 全部成功
[ ] Docker/Daytona 的 Reward map 与规范化 Artifact hash 相同
[ ] 429 故障只触发有限基础设施重试
[ ] Reward 0 不触发重试
[ ] Ctrl-C 后 resume 只补齐未完成/被筛选的 Trial
[ ] Provider 控制台无孤儿 Sandbox
[ ] 实际账单低于预先批准的上限
没有 Daytona 凭据时,可以完成前两项、支持矩阵探针、JobConfig 解析和 Harbor 单元测试;不得把这些静态结果描述为云端运行成功。正式出版的 Companion Repository 还必须在具备凭据的隔离账户中补齐 Oracle、真实 Agent、限流注入、恢复和账单核验。
18.12 本章小结
- v0.18.0 注册了 22 种内置 Environment type;能否用于某个 Task 取决于实现能力、SDK、凭据和资源语义。
- 全局 Trial 并发与 Agent 阶段并发是两道不同的门,后者不会自动限制同时存活的 Sandbox 数。
- Snapshot、镜像和构建缓存承担不同职责;Compose 的 DinD Snapshot 不会免除所有内层构建。
- Provider 内部重试、Job 重试和 Job resume 处理不同层级的失败,Reward 0 不应自动重跑。
- 成本预算必须包含计费状态、模型 token、重试和遗留资源,并用官方账单校准。
- 云迁移的成功标准是 Reward、Artifact 与 Task digest 的语义一致,不是运行时间相同。
18.13 练习
- 运行本章支持矩阵探针,从 v0.18.0 的
EnvironmentType与 Factory 注册表生成type → extraCSV,并断言两侧集合完全相等。 - 将批量配置改为两个 Agent,共享
concurrency_group: model-api,全局并发为 8、Agent 组并发为 3;构造一个不一致上限,观察 JobConfig 的失败信息。 - 在无云调用的
TrialQueue单元夹具中分别返回DaytonaRateLimitError、RewardFileNotFoundError和无异常 Reward 0,验证三者的重试次数。 - 用你账户的最新沙箱与模型价格重算 20 Task × 5 attempts、每 Trial 240 秒、一次额外重试的预算上界,并列出未计入项。
- 在隔离账户完成一次
n_concurrent_trials=1 → 2 → 4的阶梯实验;记录 Provider 限流头、峰值资源、Reward、Artifact 状态和孤儿资源数,不把墙钟差异解释为模型能力差异。