跳到主要内容

第 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、同一网络与资源意图。运行时间不属于不变量;本地和云端的调度、镜像缓存、区域与网络链路不同,要求毫秒级相同既不现实,也会掩盖真正的语义差异。

本章采用三道发布门:

  1. 结构门:JobConfig、Task、Provider 能力和凭据预检通过;
  2. 可解门:单并发 Oracle 在云端连续通过;
  3. 一致性门:同一批 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本地/HPCSingularity 运行时
daytona托管沙箱daytona单容器、Compose、快照
e2b托管沙箱e2b单容器
modal托管沙箱modal单容器与 Compose,能力随模式变化
runloop托管沙箱runloop单容器/Blueprint
langsmith托管沙箱langsmith单容器与 Compose
islo托管沙箱islo单容器与 Compose
novita托管沙箱novita单容器与 Compose
tensorlake托管沙箱tensorlake单容器/镜像与快照
cwsandbox托管沙箱cwsandboxCoreWeave Sandbox
wandb托管沙箱wandbW&B Sandbox
blaxel托管沙箱blaxel单容器与 Compose
opensandbox沙箱服务opensandbox可连接自建服务
beam托管沙箱beam单容器与 Compose
ec2云基础设施ec2VM 与 Compose
gke云基础设施gkeKubernetes
ack云基础设施阿里云 ACK
openshift集群运行时OpenShift
use-computer远程桌面/CUAuse-computer桌面任务专用
cua-cloud远程桌面/CUAcuaCUA 云池

harbor[cloud] 是多个 extra 的集合,但没有包含 ackopenshiftapple-containersingularity;不要把它解释为“安装后所有表中类型都立即可用”。固定版本还给各 SDK 设置了最低版本,例如 Daytona 为 >=0.192.03

选型时还要检查能力,而不只是名称。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 buildup -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_TOKENDAYTONA_ORGANIZATION_ID 的组合;该检查只证明凭据变量存在,不会证明余额、区域容量或远端授权有效。11

在仓库根目录保存下面的 configs/ch18-cloud.yamldatasets/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_attemptsn_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

实操时采用阶梯升压,而不是一步跳到目标值:

  1. n_concurrent_trials=1 跑 Oracle;
  2. 改为 2,记录 Sandbox 创建错误、模型 429、峰值资源和清理结果;
  3. 再尝试 4、8,每一级至少完成一整批;
  4. 任何一级出现持续 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 构建失败、缺文件、错误 digestTask/镜像确定性失败停止批量,修复并重新版本化
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.jsonexception_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.jsonresult.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 会加入不同的容器名、时间戳和路径。应比较下面四层:

  1. lock.json 中 Task digest、Harbor 版本、Agent、Environment 以外的实验字段;
  2. 确定性 Reward 和各维度 reward.json
  3. 规范化 Artifact,例如对排序后的数据库行、Nginx 指令和结构化报告计算 SHA-256;
  4. 失败类型分布,尤其是是否出现只在云端发生的 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 练习

  1. 运行本章支持矩阵探针,从 v0.18.0 的 EnvironmentType 与 Factory 注册表生成 type → extra CSV,并断言两侧集合完全相等。
  2. 将批量配置改为两个 Agent,共享 concurrency_group: model-api,全局并发为 8、Agent 组并发为 3;构造一个不一致上限,观察 JobConfig 的失败信息。
  3. 在无云调用的 TrialQueue 单元夹具中分别返回 DaytonaRateLimitErrorRewardFileNotFoundError 和无异常 Reward 0,验证三者的重试次数。
  4. 用你账户的最新沙箱与模型价格重算 20 Task × 5 attempts、每 Trial 240 秒、一次额外重试的预算上界,并列出未计入项。
  5. 在隔离账户完成一次 n_concurrent_trials=1 → 2 → 4 的阶梯实验;记录 Provider 限流头、峰值资源、Reward、Artifact 状态和孤儿资源数,不把墙钟差异解释为模型能力差异。

参考资料

Footnotes

  1. Harbor Framework Team,src/harbor/models/environment_type.py,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/environment_type.py#L4-L26,访问于 2026-07-16。

  2. Harbor Framework Team,src/harbor/environments/factory.py,内置 Environment 注册表、惰性导入与 extra 提示,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/factory.py#L25-L162,访问于 2026-07-16。

  3. Harbor Framework Team,pyproject.toml,Environment 可选依赖与 cloud extra,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/pyproject.toml#L57-L85,访问于 2026-07-16。

  4. Harbor Framework Team,src/harbor/environments/capabilities.py,Environment 与资源能力模型,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/capabilities.py#L8-L76,访问于 2026-07-16。

  5. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Daytona Provider 能力与资源能力,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L1032-L1066,访问于 2026-07-16。

  6. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Compose 自动检测与 DinD 策略选择,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L944-L947https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L1023-L1027,访问于 2026-07-16。

  7. Harbor Framework Team,Sandboxes,https://www.harborframework.com/docs/run-jobs/cloud-sandboxes,访问于 2026-07-16。该在线页可能随 Harbor 后续版本更新,本章只用它辅助解释,版本语义以固定提交为准。

  8. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Daytona Compose 覆盖文件与内部网络隔离参数,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L523-L539;DinD 启动、上传、构建与 Compose 启动链,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L671-L765,访问于 2026-07-16。

  9. Harbor Framework Team,src/harbor/environments/daytona/snapshots.py,内容哈希 Snapshot 命名、锁、状态与创建,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/snapshots.py#L71-L209,访问于 2026-07-16。

  10. Daytona,Snapshots:从镜像/Dockerfile 创建 Snapshot 与在 Sandbox 中运行 Docker Compose,https://www.daytona.io/docs/en/snapshots/,访问于 2026-07-16。

  11. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Daytona 凭据预检,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L139-L153https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L827-L831,访问于 2026-07-16。

  12. Harbor Framework Team,src/harbor/models/job/config.pysrc/harbor/models/trial/config.py,全局/Agent 并发字段及一致性校验,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/config.py#L329-L338https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/config.py#L387-L432https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/trial/config.py#L61-L80,访问于 2026-07-16。

  13. Harbor Framework Team,src/harbor/trial/queue.py,Trial 与 Agent semaphore,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/trial/queue.py#L25-L45https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/trial/queue.py#L116-L192,访问于 2026-07-16。

  14. Daytona,Limits:资源池、Sandbox 创建/生命周期限额、Rate Limit headers 与 429,https://www.daytona.io/docs/en/limits/,访问于 2026-07-16。

  15. Harbor Framework Team,src/harbor/environments/daytona/utils.py,Daytona 瞬态错误分类与等待策略,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/utils.py#L26-L134,访问于 2026-07-16。

  16. Harbor Framework Team,src/harbor/models/job/config.pyRetryConfig 默认值与 exclude 优先级,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/config.py#L280-L308,访问于 2026-07-16。

  17. Harbor Framework Team,src/harbor/trial/queue.py,Job 级异常匹配、Trial 重建与退避,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/trial/queue.py#L83-L110https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/trial/queue.py#L194-L235,访问于 2026-07-16。

  18. Harbor Framework Team,src/harbor/models/job/lock.py,Job/Trial Lock、Task digest 与 Harbor 版本信息,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/lock.py#L54-L72https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/lock.py#L125-L207https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/models/job/lock.py#L396-L429,访问于 2026-07-16。

  19. Harbor Framework Team,src/harbor/job.py,已有 Job 校验、结果加载与剩余 Trial 对账,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/job.py#L200-L315,访问于 2026-07-16。

  20. Harbor Framework Team,src/harbor/cli/jobs.pyharbor job resume 的错误类型过滤与恢复入口,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/cli/jobs.py#L1593-L1781,访问于 2026-07-16。

  21. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Compose 临时环境与删除失败回退,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L768-L788https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L1510-L1529,访问于 2026-07-16。

  22. Daytona,Billing:Sandbox 各生命周期状态的预留资源计费,https://www.daytona.io/docs/en/billing/,访问于 2026-07-16。

  23. Daytona,Pricing,https://www.daytona.io/pricing,访问于 2026-07-16。价格属于时变事实,实际实验应重新查询并保存价格快照。

  24. Daytona,Secrets:占位符、出口代理和 host Allowlist,https://www.daytona.io/docs/en/secrets/,访问于 2026-07-16。

  25. Harbor Framework Team,src/harbor/environments/daytona/environment.py,Compose/DinD 禁用 Daytona Secret 的约束,Harbor v0.18.0,https://github.com/harbor-framework/harbor/blob/527d50deb63a5d279e8c20593c18a2cbc7f61f9e/src/harbor/environments/daytona/environment.py#L984-L1000,访问于 2026-07-16。