执行摘要
- 一句话:新增 Harbor E2B 沙箱端到端测试,验证 agentic 路径完整链路,为 CI 自动化铺路。
- 推荐动作:本 PR 值得精读,尤其是测试脚本的设计和演进。它展示了如何为复杂的 agentic 路径构建端到端测试,包括环境预检、资源准备、执行和结果验证的清晰分离。其中关于解决 Qwen3 模型特定问题、处理陈旧状态和增强预检逻辑的迭代过程(通过提交历史可见)具有学习价值。最终的
--debug-rollout-only 方法巧妙地降低了测试的资源需求(仅需 2 GPU)。对于从事 agentic 系统、沙箱集成或端到端测试开发的工程师,此 PR 提供了实用的模式参考。
功能与动机
为了验证 Harbor 在真实 E2B 沙箱上的完整端到端流程,因为现有的沙箱 smoke 测试(scripts/sandbox_smoke)无法覆盖 launcher 接线到 rollout workers、session server + TITO 使用真实模型、terminus-2 驱动沙箱以及奖励回流的完整训练路径。PR body 中明确说明,此前的训练路径形状(一个 GRPO 步骤)因 Qwen3-0.6B 模型的退化输出需要严格门控的豁免,而当前变更通过仅运行 rollout 并使用更强的模型(GLM-4.7-Flash)解决了此问题。
实现拆解
- 新增端到端测试脚本:在
tests/e2e/agentic/test_harbor_e2b_rollout.py 创建了完整的测试脚本。它定义了 preflight()(快速检查环境变量、密钥和网络连接)、prepare()(准备模型、任务数据集和提示数据)、harbor_worker_env()(构建 rollout workers 的 Harbor 环境变量)、execute()(组装并执行训练参数,使用 --debug-rollout-only 仅运行 rollout)和 check_trials()(验证至少有一个 Harbor 试验成功完成并获得奖励)。该脚本被注册为禁用状态,需要手动运行。
- 注册 CI 标签:在
tests/ci/labels.py 的 KNOWN_LABELS 字典中添加了新的 agentic 标签,描述为“需要沙箱服务路由和密钥的 Agentic 沙箱端到端测试”,用于在 CI 系统中识别此类测试。
- 更新 CI 评论命令策略:在
.github/workflows/policies/comment-command-access.json 的 allowed_labels 列表中添加了 run-ci-agentic,允许通过评论命令触发带有该标签的 CI 测试。
关键文件:
tests/e2e/agentic/test_harbor_e2b_rollout.py(模块 端到端测试;类别 test;类型 test-coverage;符号 preflight, prepare, harbor_worker_env, execute): 核心测试文件,实现了 Harbor E2B 沙箱端到端测试的完整流程,是验证 agentic 路径的关键。
tests/ci/labels.py(模块 CI 标签;类别 test;类型 configuration): CI 标签注册表,新增 'agentic' 标签以在 CI 系统中识别此类测试。
.github/workflows/policies/comment-command-access.json(模块 CI 策略;类别 infra;类型 infrastructure): CI 评论命令策略文件,授权通过评论命令触发带有 'agentic' 标签的 CI 测试。
关键符号:preflight, prepare, harbor_worker_env, execute, check_trials
关键源码片段
tests/e2e/agentic/test_harbor_e2b_rollout.py
核心测试文件,实现了 Harbor E2B 沙箱端到端测试的完整流程,是验证 agentic 路径的关键。
# 端到端测试脚本的核心执行流程
if __name__ == "__main__":
preflight() # 1. 快速失败检查:环境变量、密钥文件、网络连通性、harbor 库
prepare() # 2. 准备资源:清理旧试验目录、下载模型、克隆任务仓库、生成提示数据
# 清除代理变量,确保 rollout workers 直接访问沙箱服务
for proxy_var in ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"):
os.environ.pop(proxy_var, None)
execute() # 3. 执行训练(仅 rollout):组装参数并调用 execute_train
check_trials() # 4. 验证结果:检查试验目录存在且至少有一个试验无异常
# preflight() 函数:关键预检逻辑
def preflight():
"""Fail fast with instructions instead of failing every trial later."""
api_url = os.environ.get("E2B_API_URL", "").strip()
if not api_url:
sys.exit("set E2B_API_URL (and E2B_SANDBOX_URL) to your E2B-compatible service; see the module docstring")
key_file = Path(os.environ.get("E2B_API_KEY_FILE", "~/.config/e2b/api_key")).expanduser()
# 镜像真实检查 (credentials.sandbox_key_supply):空占位符文件必须在此失败
file_has_key = key_file.is_file() and bool(key_file.read_text().strip())
if not os.environ.get("E2B_API_KEY", "").strip() and not file_has_key:
sys.exit(f"no e2b credential: set E2B_API_KEY or put a non-empty key at {key_file}")
try:
request = urllib.request.Request(f"{api_url}/nodes", headers={"X-API-Key": "preflight"})
urllib.request.urlopen(request, timeout=10).read()
except urllib.error.HTTPError:
pass # 401 仍然证明控制平面有响应
except OSError as e:
sys.exit(f"sandbox service unreachable at {api_url} ({e}); does this machine have a route to it?")
try:
import harbor # noqa: F401
except ImportError:
sys.exit("harbor is not importable; see the module docstring for the install line")
评论区精华
Review 讨论主要由 claude[bot] 发起,集中于测试脚本的健壮性和正确性。核心争议点包括:
- 陈旧试验目录:claude[bot] 指出
prepare() 未清理 TRIALS_DIR,可能导致旧的成功试验掩盖当前运行的失败。作者 nblintao 在后续提交中通过在 prepare() 开头添加 shutil.rmtree(TRIALS_DIR, ignore_errors=True) 修复了此问题。
- 预检密钥文件检查:claude[bot] 发现
preflight() 仅检查密钥文件是否存在(is_file()),但实际运行时会检查内容是否非空,这可能导致空占位文件通过预检但在训练深处失败。作者在后续提交中更新了检查逻辑为 key_file.is_file() and bool(key_file.read_text().strip())。
- TITO 严格门控豁免范围:claude[bot] 提出
--ci-disable-tito-strict-checker 标志会禁用所有三种不匹配类型的断言,而不仅仅是 Qwen3 的 special_token_count 误报,可能掩盖其他 bug。此评论指向的是被重写为仅 rollout 的旧版本测试(test_harbor_e2b_training.py),在最终合并的版本中,由于采用了 GLM-4.7-Flash 模型和正确的解析器配置,该标志已被移除,此问题不再存在。
- 试运行超时设置:claude[bot] 发现
harbor_worker_env() 中的 AGENT_TRIAL_TIMEOUT 设置可能因环境变量条件而不生效。作者在后续提交中将其改为无条件设置(使用 os.environ.setdefault)。
所有讨论均以作者修复代码或问题因架构重写而不再存在告终,无未解决疑虑。
- 陈旧试验目录可能掩盖测试失败 (correctness): 作者在后续提交中修复了此问题,在
prepare() 开头添加了 shutil.rmtree(TRIALS_DIR, ignore_errors=True)。
- 预检密钥文件检查不充分 (correctness): 作者在后续提交中更新了检查逻辑为
key_file.is_file() and bool(key_file.read_text().strip())。
- TITO 严格门控豁免范围过宽 (correctness): 此评论针对的是旧版本测试(包含训练步骤)。最终合并的版本采用仅 rollout 模式和 GLM-4.7-Flash 模型,移除了该标志,问题不再存在。
- 试运行超时设置可能不生效 (correctness): 作者在后续提交中将
AGENT_TRIAL_TIMEOUT 的设置改为无条件(使用 os.environ.setdefault)。
风险与影响
- 风险:
- 测试有效性风险:测试脚本注册为
disabled,依赖手动在特定开发机上运行,缺乏 CI 自动化,可能无法及时发现回归。虽然 PR 旨在为 CI 自动化铺路,但启用取决于 runner 配置就绪。
- 环境依赖风险:测试强依赖外部 E2B 兼容服务的网络路由和 API 密钥。
preflight() 函数已尽力提前验证,但网络环境变化仍可能导致测试失败。
- 模型特定风险:测试固定使用 GLM-4.7-Flash 模型和特定的 TITO 家族(
glm47),验证范围限于该组合。其他模型或配置的兼容性未覆盖。
- 代码演进风险:从提交历史看,该 PR 经历了从包含训练步骤到仅 rollout 的重大重写。最终版本移除了对 Qwen3-0.6B 的支持以及相关的严格门控豁免逻辑,这简化了代码,但也意味着针对特定模型问题的修复可能被后续引入。
- 影响:对用户的直接影响很小,因为这是一个测试变更。它增强了对 Harbor agentic 路径(特别是 E2B 沙箱集成)的端到端验证能力,为团队在集成和发布新功能时提供了更高的信心。引入的 agentic CI 标签为后续自动化测试奠定了基础。对系统的影响主要在于测试基础设施的扩展,增加了对 agentic 场景的覆盖。对团队的影响是提供了一个可复用的、结构清晰的手动测试模板,用于验证类似的 agentic 滚动更新路径。
- 风险标记:测试依赖外部服务, CI 自动化待启用, 模型特定验证, 手动运行易出错
关联脉络
- PR #2868 smoke: manual golden episodes on real sandbox APIs: 同为沙箱相关测试,本 PR 的测试覆盖了更完整的 rollout 路径,是其上层集成验证。
- PR #2837 examples: launcher for the in-process Harbor path: 本 PR 测试的 launcher 接线和环境变量构建逻辑来源于该 PR 引入的
launch_common 模块。
- PR #3113 fix: chunk true-on-policy logprob computation: PR body 中提及 Qwen3-0.6B 模型的退化 turn 问题与 #3113 的分析相关,本 PR 最终通过更换模型解决了该问题。
- PR #3167 docs(tito): explain abnormal Qwen3 endoftext output: 评论中 guapisolo 提及了 Qwen3 模型的异常 EOS 输出行为,本 PR 的演进过程(从 Qwen3 到 GLM)与此背景相关。
参与讨论