Skip to content

[Feature][CI/CD] Support agent runtime env auto deploy + 5-layer DinD + safe_start (v3) - #499

Closed
Huafubing wants to merge 36 commits into
AISBench:masterfrom
Huafubing:feature/v3-complete
Closed

[Feature][CI/CD] Support agent runtime env auto deploy + 5-layer DinD + safe_start (v3)#499
Huafubing wants to merge 36 commits into
AISBench:masterfrom
Huafubing:feature/v3-complete

Conversation

@Huafubing

@Huafubing Huafubing commented Aug 27, 2026

Copy link
Copy Markdown

AISBench Agent Runtime — 5 层 DinD 统一测评环境

为 AISBench Agent 测评(Harbor Terminal-Bench、SWE-bench、SWE-bench Pro)提供
镜像构建 + 容器启动 + 命令执行 的完整运行时方案。

本 PR 是 AISBench/benchmark 的社区运行时补充,不改动核心评测逻辑。

架构总览

宿主机用户
  │
  ├─ ais_bench_agent.sh build     ← 一键构建 (v3 H 批)
  │   └─ build_image_agent_runtime.sh [→ build_l2_baked_image.sh]
  │
  └─ ais_bench_agent.sh run       ← 拉起 + 自检 + 跑测试 (v3 H 批)
      │
      └─ bootstrap.sh → docker run 启动 runtime 容器
            │
            ├─ 模式 A (DinD): --privileged --net=host
            │     └─ 容器内 dockerd → harbor → trial 容器
            └─ 模式 B (Socket): -v /var/run/docker.sock
                  └─ 共享宿主 dockerd → harbor → trial 容器
            │
            ├─ datasets bind mount (宿主路径 = 容器内路径)
            ├─ case 镜像 tar 自动 docker load
            └─ 3 个隔离 venv: /opt/venvs/{harbor, swebench, swebench_pro}

容器内
  ├─ ais_bench_agent_doctor.sh <pack>    ← L1 自检
  ├─ agent_env <pack>                    ← 激活 venv
  ├─ ais_bench_agent_run.sh              ← harbor jobs start 包装
  ├─ ais_bench_agent_watch.sh            ← 阻塞等 results.json
  ├─ ais_bench_agent_summarize.sh        ← 聚合 → md/csv/json
  └─ ais_bench_agent_orchestrator_status.sh ← 5 段状态查询

分层职责

脚本/文件 职责
入口 ais_bench_agent.sh 统一 facade:build / run / status / watch / summarize / doctor
L4 镜像 Dockerfile.agent-runtime + build_image_agent_runtime.sh 在 aisbench_benchmark 基镜上追加 3 个隔离 venv
L5 启动 ais_bench_agent_bootstrap.sh + safe_start_... 一键起 runtime 容器 (DinD/Socket + 挂数据集 + 加载 case tar)
L3 调度 ais_bench_agent_{run,watch,summarize,orchestrator_status}.sh 在容器内调 harbor jobs / 等结果 / 汇总
L2 加速 build_l2_baked_image.sh + 5 个 Jinja2 模板 预烤 agent 到 case 镜像,跳过 trial 内装包
L1 自检 doctor.sh 静态校验 docker / venv / pack / 资源(秒级)

快速拉起流程

1. 构建 runtime 镜像(宿主机)

# 标准构建(用默认 harbor==0.6.1)
bash docker/agent_runtime/ais_bench_agent.sh build \
    --base-tag v3.1-20260522-master --push

# 用自定义 harbor wheel(如 harbor-offline,替换默认 harbor)
bash docker/agent_runtime/ais_bench_agent.sh build \
    --base-tag v3.1-20260522-master \
    --harbor-wheel /path/to/harbor-offline.whl --push

# 同时构建 L2 baked images
bash docker/agent_runtime/ais_bench_agent.sh build \
    --base-tag v3.1-20260522-master --l2

2. 跑测评(宿主机)

# Harbor Terminal-Bench(自动启容器 + doctor 自检 + 执行测试)
bash docker/agent_runtime/ais_bench_agent.sh run \
    --pack harbor \
    --datasets /data/harbor/mini-0.10/terminal-bench-2-offline-selected_0.10 \
    --matrix-yaml /opt/config/matrix.yaml \
    --api-key-file /opt/config/api_key.env

# SWE-bench verified mini(--split 自动派生 config 文件)
bash docker/agent_runtime/ais_bench_agent.sh run \
    --pack swebench --split verified_mini \
    --datasets /data/swebench/verified \
    --matrix-yaml /opt/config/matrix.yaml

# 自定义命令(透传到容器内,不自动推导)
bash docker/agent_runtime/ais_bench_agent.sh run \
    --pack harbor \
    --datasets /data/harbor/mini \
    --command "harbor jobs start -c /opt/swebench/config/my_matrix.yaml -n 2"

# 离线模式(内网 / 无 ghcr.io 访问)
bash docker/agent_runtime/ais_bench_agent.sh run \
    --pack harbor \
    --datasets /data/harbor \
    --runtime-tar /opt/aisbench/agent-runtime.tar.gz \
    --case-tar /opt/aisbench/case-images.tar.gz

3. 查询 / 等待 / 汇总(宿主机)

bash docker/agent_runtime/ais_bench_agent.sh status
bash docker/agent_runtime/ais_bench_agent.sh watch <job-name>
bash docker/agent_runtime/ais_bench_agent.sh summarize <job-name>

Pack 智能化:--pack + --split 自动派生 config

用户只需传 --pack--split,wrapper 自动选择正确的 ais_bench config 文件:

--pack --split 自动推导 config 路径
harbor (不需要) ais_bench/configs/agent_example/harbor_terminal_bench_2_task.py
swebench (默认) / lite .../swe_bench_examples/mini_swe_agent_swe_bench_lite.py
swebench verified .../swe_bench_examples/mini_swe_agent_swe_bench_verified.py
swebench verified_mini .../swe_bench_examples/mini_swe_agent_swe_bench_verified_mini.py
swebench full .../swe_bench_examples/mini_swe_agent_swe_bench_full.py
swebench multilingual .../swe_bench_examples/mini_swe_agent_swe_bench_multilingual.py
swebench_pro (默认) / mini .../swe_bench_pro_examples/mini_swe_agent_swe_bench_pro_mini.py
swebench_pro full .../swe_bench_pro_examples/mini_swe_agent_swe_bench_pro_full.py

非法 --split 会明确报错并提示合法值列表。

命令透传设计

ais_bench_agent.sh run --command "..." 将用户在宿主机交付的任意命令原样透传到容器内执行:

用户 CLI (宿主机)
  │  ais_bench_agent.sh run --command "harbor jobs start ..."
  ▼
wrapper 自动完成: 派生 config → 检查/启容器 → bootstrap → doctor → docker exec
  │
  ▼
容器内: bash -c "harbor jobs start ..."
  │
  ▼
harbor → DinD → trial 容器 → /opt/swebench/jobs/<job>/result.json

目录结构(核心文件)

docker/agent_runtime/
├── ais_bench_agent.sh                  ← 统一入口 (NEW)
├── Dockerfile.agent-runtime             ← L4 runtime 镜像
├── build_image_agent_runtime.sh         ← 镜像构建脚本
├── build_l2_baked_image.sh              ← L2 baked image 构建
├── ais_bench_agent_bootstrap.sh         ← L5 一键起容器
├── ais_bench_agent_entrypoint.sh        ← 容器 ENTRYPOINT
├── ais_bench_agent_run.sh               ← L3 harbor jobs 包装
├── ais_bench_agent_watch.sh             ← L3 等结果
├── ais_bench_agent_summarize.sh         ← L3 汇总
├── ais_bench_agent_orchestrator_status.sh ← L3 状态查询
├── safe_start_ais_bench_agent_bootstrap.sh ← watchdog 包装
├── doctor.sh                            ← L1 自检
├── scripts/
│   ├── filter_matrix.py                 ← matrix 过滤
│   └── summarize.py                     ← 汇总逻辑
├── packs/
│   ├── harbor.yaml
│   ├── swebench.yaml
│   └── swebench_pro.yaml
├── patches/
│   └── harbor_compose_patch.py
└── dockerfiles/
    ├── Dockerfile.l1-base.j2
    ├── Dockerfile.l2-agent-aider.j2
    ├── Dockerfile.l2-agent-msa.j2
    ├── Dockerfile.l2-agent-oh.j2
    ├── Dockerfile.l2-agent-qwen.j2
    └── README.md

所有提交概览

PR410 baseline (12 commits)

b92b18c25af9e0: 基镜像 docker 安装、ais_bench configs 更新、Dockerfile + build 脚本 + bootstrap.sh + doctor.sh + packs + patches + 文档

v3 A 批 — L4 镜像改造 (5 commits)

7875023 A1: ARM64 host 适配 · abb9024 A2: harbor 0.6.1 → 0.20.0 · c2663f9 A3: DinD registry-mirrors 参数化 · 848ec98 A4: ENTRYPOINT + binfmt + daemon.json · ce8a1f9 A5: /opt/swebench 预创建

v3 B 批 — L5 launcher 改造 (6 commits)

21200a4 B1: bootstrap.sh 新增 8 个参数 · 7f36db1 B2: --data-image · 3f89ec6 B3: bind mount + env 接入 · ead9f51 B4: --production --restart unless-stopped · c6fe5ad B5: DOCKER_DEFAULT_PLATFORM · 3a6ded5 B6: 推荐 harbor jobs start

v3 C 批 — L3 调度接入 (4 commits)

05e6221 C1: run.sh + filter_matrix.py · 6b17bcd C2: watch.sh · 08e3f0f C3: summarize.sh + summarize.py · 638d17b C4: orchestrator_status.sh

v3 D 批 — L2 baked image (3 commits)

d65f44d D1: build_l2_baked_image.sh · f0d0871 D2: 5 个 Jinja2 模板 · c59c4f8 D3: bootstrap.sh --l2-image

v3 E/F/G/H 批 (4 commits)

6cf5edb E: bootstrap.sh --qemu · 43ebb27 F: 移除 --cgroupns=host · 085a826 G: safe_start watchdog · 1e523aa H: ais_bench_agent.sh 统一入口 + harbor wheel 可替换


🤖 Generated with Claude Code

SJTUyh added 30 commits August 27, 2026 16:23
v3 Batch A 第一步:让 PR AISBench#410 agent-runtime 镜像在 ARM64 host 上
也能跑 SWE-bench / terminal-bench 2 等需要 x86_64 trial 镜像的 case。

- 加 ARG TARGETARCH
- TARGETARCH=arm64 时 build-time 装 qemu-user-static(apt 包)
- amd64 用户零开销(条件 RUN 不执行)

binfmt 注册放在 runtime entrypoint(A4 批次),不在 build-time 因为
build context 没有 /proc/sys/fs/binfmt_misc。
v3 Batch A 第二步:升级 harbor 到 0.20.0。

为什么升 0.20.0:
- 0.6.1 没有 harbor jobs CLI(仅 harbor run / harbor dataset / harbor task)
- mini_matrix 全套基于 harbor 0.20.x 的 harbor jobs start -c matrix.yaml
- 升 0.20.0 后 L3 调度才能真正跑起来(PR AISBench#410 当前 0.6.1 调不起来)

兼容性:
- harbor 0.20.0 同样会升 datasets 4.0+,所以 venv 隔离逻辑不变
- harbor 0.20.0 仍走 python3.12 site-packages,patch 路径不变
- PR AISBench#410 原生用户如仍用 0.6.1 路径跑 ais_bench 不受影响(本 PR 只换
  runtime image 内的 harbor 版本;源码安装路径由 docs 指引)
v3 Batch A 第三步:让 agent-runtime 镜像在 DinD 模式下能从环境变量注入
registry mirror,解决国内/海外/私有部署拉镜像的问题。

build-time ARG + runtime ENV 双覆盖:
- ARG AIS_BENCH_AGENT_REGISTRY_MIRROR (build-time 锁定)
- ENV AIS_BENCH_AGENT_REGISTRY_MIRROR  (runtime -e 覆盖,优先级高)
- 多 mirror 用英文逗号分隔
- 空值 → A4 entrypoint 不写 registry-mirrors 字段(用 DockerHub 默认)

image 层只声明 env + 建 /etc/docker 目录;
daemon.json 的实际生成逻辑在 A4 entrypoint(用 python 避免 jq 依赖)。
v3 Batch A 第四步:让 runtime container 启动时自动完成所有 ARM64 host
适配工作,让 bootstrap.sh 的 docker exec 启 dockerd 段可以正常接管。

新增 docker/agent_runtime/ais_bench_agent_entrypoint.sh (140 行):
  1. ARM64 检测 → mount binfmt_misc + 写 register 文件
  2. 写 /etc/docker/daemon.json(cgroupfs + vfs + 可选 registry-mirrors)
  3. ARM64 → export DOCKER_DEFAULT_PLATFORM=linux/amd64(避免 no matching manifest)
  4. /opt/swebench/agent-patches 注入 harbor installed agents(patched/skipped 计数)
  5. 打印 banner
  6. exec "$@"

Dockerfile.agent-runtime 变更:
  - COPY entrypoint 到 /usr/local/bin/ + chmod +x
  - ENTRYPOINT [ais_bench_agent_entrypoint.sh]
  - CMD ["bash"](默认开 shell,docker run 时可被覆盖)

幂等设计:
  - daemon.json 已存在且内容一致 → 不重写
  - binfmt 已注册 → 跳过
  - harbor installed dir 不存在 → warn 但不 fail
  - 不启 dockerd(bootstrap.sh 负责,避免双启冲突)

覆盖入口示例:
  docker run <image> harbor jobs --help
  → entrypoint 跑完 setup,exec harbor jobs --help
v3 Batch A 第五步:在镜像层预创建 5 层 DinD 容器内的 bind mount 目标路径,
bootstrap.sh 直接 docker run -v host_path:container_path 即可。

为什么要在镜像层建(而非 bootstrap.sh 临时建):
- 老 docker 版本 bind mount 到不存在路径会失败
- 即使成功 bind,权限会变成 root:root,host 普通用户写不进去
- 镜像层建好后,container 内路径已存在,bind mount 直接挂载,权限一致

5 个目录对应 mini_matrix L5 容器布局:
- jobs/          : harbor trial 输出(每个 trial 一个子目录 + result.json)
- tasks/         : SWE-bench / terminal-bench 任务定义(task.toml)
- config/        : harbor matrix.yaml / 全局配置
- logs/          : harbor dockerd / docker pull / trial 容器日志
- agent-patches/ : harbor agent 的 patched .py(A4 entrypoint 注入)

SWEBENCH_ROOT env var 给后续脚本引用,避免硬编码 /opt/swebench。
v3 Batch B 第一步:bootstrap.sh 增加 5 层 DinD L5 launch 所需的全部参数。
参数本身只为变量赋值 + 解析 + 探测日志打印,不影响 docker run(留给 B3)
和不影响 dockerd 启(留给 B5),保证 B1 独立 commit 可用。

新增参数(全部可选,J2 决策:用户主动指定 host 路径):

bind mount 类(host → 容器内 /opt/swebench/*):
  --matrix-yaml   <PATH>    harbor matrix.yaml
  --bind-jobs     <DIR>     trial 产物目录
  --bind-tasks    <DIR>     SWE-bench / terminal-bench task.toml
  --bind-config   <DIR>     harbor 配置目录
  --api-key-file  <FILE>    api_key.env(含 OPENAI_API_KEY/BASE)

env 注入类:
  --registry-mirror <URL>   -e AIS_BENCH_AGENT_REGISTRY_MIRROR(给 A3 daemon.json 用)

其他:
  --data-image <IMAGE[:TAG]>  B2 commit 使用,创建只读 data 容器
  --production               B4 commit 使用,加 --restart unless-stopped

参数解析严格性:
  - 路径类必须绝对路径(--bind-* / --api-key-file / --matrix-yaml)
  - 路径存在性检查(文件 -f / 目录 -d)
  - 错误信息中文,fail-fast(exit 1)

兼容性:
  - 现有 CLI 参数(--datasets / --runtime-tar / --case-tar / --mode / --container-name
    / --runtime-image / --host-path)全部保留
  - 现有参数行为不变
  - 不传新参数 → 行为退化到 PR AISBench#410 原版(用户自己 docker exec 跑 ais_bench)
v3 Batch B 第二步:在 [4/6] 启动容器 段加入 data container 处理逻辑。
仅在用户传 --data-image 时生效。

data container 概念(来自 mini_matrix start_orchestrator.sh):
  - `docker create --name X image` 创建空容器(不启动)
  - 容器自带 image 的所有数据卷
  - runtime 容器通过 `--volumes-from X:ro` 继承这些数据卷
  - runtime 容器只能读,不能改(ro)

用途:
  - 案例镜像预置场景:把 case 镜像 tar load 后打成一个 data image
  - 多 runtime 共享同一份基础数据集,无需重复 load
  - 数据集版本化:换 data image tag 即可换数据集

实现细节:
  - data container 名 = ${CONTAINER_NAME}-data(避免与 runtime 重名)
  - 已存在则复用(多次启动不需要重建)
  - 创建失败(镜像不存在)fail-fast 退出
  - DATA_VOLUMES_ARG 在 docker run 时插入(mode A 和 B 都生效)
  - 不传 --data-image → DATA_VOLUMES_ARG="" → 行为退化到 PR AISBench#410 原版

J 决策:J2 决策里 --bind-* 仍然是 host mount 主路径,
--data-image 是辅助(用于 image 内置数据集场景),二者可组合使用。
v3 Batch B 第三步:在 [4/6] 启动容器 段把 B1 新增的 6 个参数接到
docker run 命令上(mode A 和 mode B 都生效)。

新增拼装块(在 DATASET_ENV 之后、docker run 之前):
  SWEBENCH_BINDS / SWEBENCH_ENVS 两个变量

SWEBENCH_BINDS 拼装规则(每个 if 独立):
  --matrix-yaml      →  -v $MATRIX_YAML:/opt/swebench/config/matrix.yaml:ro
  --bind-jobs        →  -v $BIND_JOBS:/opt/swebench/jobs
  --bind-tasks       →  -v $BIND_TASKS:/opt/swebench/tasks
  --bind-config      →  -v $BIND_CONFIG:/opt/swebench/config
  --api-key-file     →  -v $API_KEY_FILE:/opt/swebench/api_key.env:ro

SWEBENCH_ENVS 拼装规则:
  --api-key-file     →  source 后提取 OPENAI_API_KEY / OPENAI_API_BASE 注入 -e
                        (set -a 让 source 变量自动 export 给后续 docker run)
  --registry-mirror  →  -e AIS_BENCH_AGENT_REGISTRY_MIRROR=$REGISTRY_MIRROR

docker run 接入(mode A + mode B 同样处理):
  原:  ${MOUNT_ARGS} \ ${DATASET_ENV} \
  新:  ${MOUNT_ARGS} \ ${DATA_VOLUMES_ARG} \ ${SWEBENCH_BINDS} \
      ${DATASET_ENV} \ ${SWEBENCH_ENVS} \

顺带补 B2 漏掉的 ${DATA_VOLUMES_ARG}(data container volumes-from)
mode B 原版 docker run 也没插,B2 留下的小洞这里一并补。

兼容性 / 退化路径:
  - 不传任何 B1 新参数 → MATRIX_YAML/BIND_* 都为空 → if 块全不进入 →
    SWEBENCH_BINDS="" SWEBENCH_ENVS="" → 行为完全等同 PR AISBench#410 原版
  - mode A 不变(仍然是 --privileged --cgroupns=host -w /benchmark)
  - mode B 不变(仍然挂 -v docker.sock + HOST_PATH)
  - 与 B1/B2 独立:本 commit 不修改 CLI 解析 / 不创建 data container,
    变量空引用也无害(set -e + 未定义 → 空字符串)

J 决策一致性:
  J2 host bind mount 是主路径(用户主动指定 host 路径),
  本 commit 是 J2 的实际接线点。
  --bind-jobs/tasks/config 缺失时容器内目录是空的(DIN A5 已预创建),
  harbor CLI 仍可读 matrix.yaml 中的 jobs.dir 字段自行创建。
v3 Batch B 第四步:J4 决策落地。仅当用户显式传 --production 时,
runtime 容器加 --restart unless-stopped;不传则不加(行为零变化)。

J4 决策回顾:
  - 候选 A:默认加 --restart unless-stopped(强制 production-ready)
  - 候选 B:仅 --production 启用(默认与 PR AISBench#410 行为一致)
  → 用户决定 B:避免 PR AISBench#410 用户行为变化,新行为 opt-in

实现:
  - 新增 PRODUCTION_CLI=0 暂存变量(line ~96)
  - 参数解析段新增 case --production(不消耗额外参数,幂等)
  - 应用默认值段 PRODUCTION="${PRODUCTION_CLI:-0}"
  - 计算 RESTART_ARG:PRODUCTION=1 → "--restart unless-stopped",否则 ""
  - mode A 和 mode B 两条 docker run 都插入 ${RESTART_ARG}(空时展开为空格,无副作用)
  - 顶部 help doc 加 --production 说明(含 J4 决策依据)

兼容性 / 退化路径:
  - 不传 --production → PRODUCTION=0 → RESTART_ARG="" → docker run 不加 --restart
    → 行为完全等同 PR AISBench#410 原版(默认 user 零感知)
  - 传 --production → 容器加 --restart → 物理机重启后自动拉起
  - mode A/B 都生效(两条 docker run 都加了)

独立 commit 设计:
  - 不依赖 B1/B2/B3 的任何变量(B4 用到的只有 PRODUCTION)
  - 不修改 mode A/B 的现有 docker run 选项(仅插入新行)
  - 单独 revert 不影响其它 B* commit

J 决策一致性:
  J2 host bind mount 是接入主路径(由 B1+B3 联合完成),
  J4 production flag 是接入副开关(仅本 commit)。
  两者无依赖关系,可独立 ship。
v3 Batch B 第五步:在 [5/6] 模式 A 的 docker exec heredoc 顶部加
export DOCKER_DEFAULT_PLATFORM=linux/amd64,作为 A4 entrypoint.sh
同一 export 的冗余兜底。

为什么需要冗余:
  - A4 entrypoint.sh 已经在容器启动时 export,但那是 PID 1 的 env
  - `docker exec` 启动新 bash 进程时是否继承 PID 1 的 env,取决于
    docker daemon 实现细节(理论上应该通过 /proc/1/environ 拿,但
    docker 27.x 在 cgroup v2 宿主上有过丢失 env 的 bug)
  - 显式再 export 一次,dockerd 100% 拿到,无副作用(重复 export 同值
    是幂等的)

用途:
  - dockerd 启动后所有 docker pull / docker run 命令挑 linux/amd64 manifest
  - ARM64 host 跑 x86_64 case 镜像:harbor trial 启动 case 容器时
    走 qemu-x86_64_static(由 L4 A1 装好的 binfmt_misc 注册)
  - 不设此变量时 dockerd 默认按 host arch(arm64)挑 manifest,
    x86_64 case 镜像拉不到 ARM64 manifest → trial 启动失败

实现细节:
  - 仅 mode A(mode B 走宿主 dockerd,宿主 DOCKER_DEFAULT_PLATFORM 不影响
    我们:用户在自己 host 上自行处理)
  - 加在 docker exec heredoc 的 set -e 之后,mkdir /etc/docker 之前
  - 一行 export + 4 行注释(共 +5 行)

兼容性:
  - mode A 行为变化:内层 dockerd 启动时一定设 DOCKER_DEFAULT_PLATFORM=linux/amd64
    (即使 A4 未生效也不会跑出问题:amd64 是 case 镜像主架构,
     只有在 ARM64 host 上跑 x86_64 case 才有意义,其他场景无影响)
  - mode B 行为不变
  - 不修改现有 daemon.json 内容(仍由 entrypoint.sh 写 registry-mirrors)
v3 Batch B 第六步:替换 [7/7] 自检 + 下一步提示中"激活 venv 跑原生 ais_bench"
的默认推荐路径,改为推荐"harbor jobs start -c matrix.yaml" 矩阵调度。

为什么换:
  - 5 层 DinD 架构下,矩阵调度(harbor CLI 多 trial)是真正的标准工作流
  - 原生 ais_bench 单 config 模式只跑一个 (case, agent) 对,不符合 mini_matrix
    的批量矩阵语义
  - harbor CLI 0.20.x(已由 A2 装)支持 `harbor jobs start -c matrix.yaml`
    是 5 层 DinD L3 的实际调度入口

修改内容([7/7] cat <<EOF 块):
  - step 4 加注释:用 harbor jobs start 时无需 vim 改 path/model_name/api_base
    (这些都从 matrix.yaml 读)
  - step 5 完全改写:从"激活 venv 跑 ais_bench" → "harbor jobs start -c matrix.yaml"
    + 推荐 ais_bench_agent_run.sh / watch.sh / summarize.sh(C 批封装脚本)
  - 新增 step 6 作为回退路径:明确写出"不推荐,仅在矩阵调度不适用时用"原生 ais_bench
  - 顶部脚本注释第 7 步说明:"原 ais_bench" → "harbor jobs start 矩阵调度"

引用 C 批脚本:
  - ais_bench_agent_run.sh / watch.sh / summarize.sh 还未创建(C 批将新增)
  - 此处引用是预告,C 批完成后即可在容器内调用
  - 即便 C 批脚本尚未创建,用户用 harbor jobs start 也能跑(C 批只是封装便利)

兼容性 / 退化路径:
  - 不传 --matrix-yaml(PR AISBench#410 老用户):matrix.yaml 不存在,harbor jobs start 会失败
    → 用户会自然回退到 step 6 原生 ais_bench 路径
  - 不会删除 step 6(PR AISBench#410 兼容)
  - 行为变化:默认推荐项变了,但 PR AISBench#410 用户的工作流仍能用(step 6)

与 Batch A 决策一致性:
  - A2 装 harbor 0.20.x → B6 推荐 harbor jobs start(决策落地闭环)
  - A5 预创建 /opt/swebench/{jobs,tasks,config} → B6 让用户把 matrix.yaml 放 /opt/swebench/config
    路径与 A5 完全对齐(--matrix-yaml bind 到这个路径)

独立 commit 设计:
  - 不修改任何 docker run / mode 选择 / 配置逻辑
  - 仅修改 [7/7] 末尾的 cat <<EOF 输出文本 + 顶部注释一行
  - 不依赖 B1-B5 任何变量(--matrix-yaml 概念由 B6 第一次在用户输出中提及)
v3 Batch C 第一步:实现 5 层 DinD L3 调度入口 — `ais_bench_agent_run.sh`,
简单包装 harbor CLI 0.20.x 的 `harbor jobs start -c matrix.yaml`,加上
默认参数(容器名 / matrix 路径 / jobs-dir)以及子集过滤。

新增文件 2 个:
  docker/agent_runtime/ais_bench_agent_run.sh    165 行
  docker/agent_runtime/scripts/filter_matrix.py   65 行(辅助脚本)

Dockerfile.agent-runtime 改动(+8 行):
  - COPY ais_bench_agent_run.sh  → /usr/local/bin/ais_bench_agent_run.sh
  - COPY scripts/filter_matrix.py → /usr/local/bin/ais_bench_agent_filter_matrix.py
  - 都 chmod +x

脚本设计要点:
  1. 默认参数对应当前 batch A+B 的 bootstrap 默认:
     - CONTAINER_NAME=ais_bench_agent
     - MATRIX_YAML=/opt/swebench/config/matrix.yaml(B1 --matrix-yaml bind)
     - JOBS_DIR=/opt/swebench/jobs(B1 --bind-jobs bind)
     - API_KEY_FILE=/opt/swebench/api_key.env(B1 --api-key-file bind)

  2. CLI 参数:
     --job-name <NAME>       自定义 job 名(默认 matrix-YYYYMMDD-HHMMSS)
     --datasets <LIST>       数据集子集过滤(逗号分隔;substring 匹配)
     --agents <LIST>         agent 子集过滤(逗号分隔;精确匹配)
     --container-name <NAME> 改 runtime 容器
     --matrix <PATH>         改 matrix.yaml 路径
     --dry-run               只打印 harbor 命令,不实际执行

  3. 前置检查(fail-fast):
     - matrix.yaml 存在
     - harbor 命令可用(A2 已装 harbor 0.20.x)
     - jobs-dir / log-dir 可写
     - api_key.env 存在(warn-only,不强制)

  4. api_key.env 自动 source:
     - set -a 让 source 的变量自动 export
     - harbor CLI 直接读 OPENAI_API_KEY / OPENAI_API_BASE

  5. filter tmp yaml 写到 /opt/swebench/logs/(host bind mount,可写):
     - harbor CLI 在容器内看 /opt/swebench/logs/_tmp_filtered_*.yaml
     - 用 filter_matrix.py 读 matrix.yaml 过滤后写 tmp yaml
     - trap EXIT 自动清理 tmp yaml

  6. harbor 调用:
     harbor jobs start -c $CMD_YAML --job-name $JOB_NAME \
       --jobs-dir /opt/swebench/jobs -n 2

  7. 提交成功后打印下一步提示:
     - harbor jobs view $JOB_NAME
     - ls -la $JOBS_DIR/$JOB_NAME/
     - ais_bench_agent_watch.sh / summarize.sh(C2/C3 将提供)

filter_matrix.py 设计要点:
  - 与 mini_matrix/scripts/filter_matrix.py 兼容(同样算法)
  - 改 input/output 帮助文字适配 PR AISBench#410 容器内场景
  - 不引入额外依赖(仅 pyyaml,runtime 容器基镜像已装)

与 batch A/B 决策一致性:
  - J2 host bind mount:A5 预创建 /opt/swebench/{jobs,tasks,config,logs},
    B1+B3 把 host 路径 bind 进容器;C1 假定这些点都存在
  - J1 harbor 0.20.x:A2 已装;C1 调 harbor CLI 0.20.x 专属接口
  - J4 production flag:与 C1 无关(C1 不启停容器,只在已起容器内跑)

独立 commit 设计:
  - C1 不引用 C2/C3/C4 脚本(仅在下一步提示中提及)
  - C1 不修改 bootstrap.sh(A+B 已固化)
  - filter_matrix.py 是 C1 内部辅助(C3 summarize.py 不会用这个)
  - C2/C3/C4 可独立 commit,集成时无冲突

与 PR AISBench#410 原生 ais_bench 路径兼容性:
  - 不传 --matrix-yaml bootstrap → 容器内 /opt/swebench/config/matrix.yaml 不存在
    → C1 fail-fast(matrix.yaml 不存在),提示用户 bootstrap --matrix-yaml
  - 原生 ais_bench 流程仍然可用(详见 bootstrap [7/7] step 6)
  - C1 是 5 层 DinD 完整工作流的入口,但不是唯一入口
v3 Batch C 第二步:实现 5 层 DinD L3 监控入口 — `ais_bench_agent_watch.sh`,
阻塞等 harbor job 完成(results.json 出现),然后解析并打印最终统计。

新增文件 1 个:
  docker/agent_runtime/ais_bench_agent_watch.sh  181 行

Dockerfile.agent-runtime 改动(+6 行):
  - COPY ais_bench_agent_watch.sh → /usr/local/bin/ais_bench_agent_watch.sh
  - chmod +x

脚本设计要点:
  1. 默认参数对应当前 batch A+B 的 bootstrap 默认:
     - JOBS_DIR=/opt/swebench/jobs(B1 --bind-jobs bind)

  2. CLI 参数:
     <job-name>                 必填,要 watch 的 job 名
     --interval <SECONDS>       轮询间隔(默认 15s)
     --timeout <SECONDS>        超时时间(默认 0=无限)
     --jobs-dir <PATH>          改 jobs 目录
     --no-poll                  不调 harbor jobs view(只等 result.json)
     -h|--help                  帮助

  3. 主循环:
     - 每 INTERVAL 秒检查 /opt/swebench/jobs/<job>/result.json
     - 出现即 break,调 python 解析统计
     - result.json 未出现但 harbor jobs view 显示 finished_at:
       → 多等 2s 再确认(harbor 写 result.json 之前的瞬态)
     - 任意 Ctrl-C (SIGINT/SIGTERM) trap 清理退出

  4. 进度提示:
     - 每 4 轮(即 INTERVAL*4 秒)打"等待中...(Xs,共 N 轮)"
     - 默认 15s * 4 = 60s 一次,避免日志 spam

  5. 完成统计(用 python3 解析 result.json):
     - started_at / finished_at / n_total / n_completed / n_errored
     - n_running / n_pending / n_pass / pass@1
     - 与 mini_matrix/scripts/monitor_3x3.sh 算法一致
       (evals.reward_stats.reward 中 reward=1.0 的 trial 数)

  6. 下一步提示:
     - cat result.json 看完整结果
     - ais_bench_agent_summarize.sh 聚合 md/csv/json
     - harbor jobs view 原生命令

  7. 超时处理(TIMEOUT > 0):
     - ELAPSED >= TIMEOUT → exit 2
     - 让 CI/批量调度脚本可检测超时

与 batch A+B 决策一致性:
  - J2 host bind mount:依赖 --bind-jobs 路径,不传则 jobs-dir 不存在 fail-fast
  - J1 harbor 0.20.x:依赖 harbor CLI 可用(无 harbor 命令则跳过 harbor jobs view 探测)
  - python3 解析:与 harbor 0.20.x result.json schema 对齐
    (stats.evals.reward_stats.reward → {reward_value: [trial_ids]} 桶)

独立 commit 设计:
  - C2 不引用 C1/C3/C4 脚本(仅在下一步提示中提及)
  - C2 不修改 bootstrap.sh(A+B 已固化)
  - C2 修改 Dockerfile.agent-runtime(COPY 顺序独立,合并时无冲突)
  - C1/C3/C4 可独立 commit,集成时每 commit 加自己的 COPY 即可

兼容性:
  - 不传 <job-name> → fail-fast 提示
  - jobs-dir 不存在 → fail-fast(提示 bootstrap --bind-jobs)
  - result.json 出现后即使 harbor jobs view 不可用也能完成统计
  - harbor 命令不可用 → 仅靠 result.json 文件检查(降级模式)
v3 Batch C 第三步:实现 5 层 DinD L3 聚合入口 — `ais_bench_agent_summarize.sh` +
配套 Python 聚合脚本,扫 jobs/*/result.json 输出 pass@1 by (bench, agent) 表。

新增文件 2 个:
  docker/agent_runtime/ais_bench_agent_summarize.sh   95 行(bash wrapper)
  docker/agent_runtime/scripts/summarize.py          169 行(Python 聚合)

Dockerfile.agent-runtime 改动(+7 行):
  - COPY ais_bench_agent_summarize.sh → /usr/local/bin/ais_bench_agent_summarize.sh
  - COPY summarize.py → /usr/local/bin/ais_bench_agent_summarize.py
  - 都 chmod +x

bash wrapper 设计要点:
  1. 默认参数对应当前 batch A+B 的 bootstrap 默认:
     - JOBS_DIR=/opt/swebench/jobs(B1 --bind-jobs bind)
     - OUTPUT_DIR=/opt/swebench/logs(B5 中 path 可能调整,此处用默认)

  2. CLI 参数:
     <job-name> [...]           要聚合的 job 名(substring 匹配,逗号分隔)
     --latest                  仅最新一个 job(ls -1dt 取最新)
     --jobs-dir <PATH>         改 jobs 目录
     --output-dir <PATH>       改输出目录

  3. 调用 summarize.py:
     python3 /usr/local/bin/ais_bench_agent_summarize.py \
       --jobs-dir $JOBS_DIR --output-dir $OUTPUT_DIR \
       [--include "<substring1,substring2>"]

  4. 完成后 ls 列出生成的 summary-*.{md,csv,json} 文件

Python summarize.py 设计要点:
  1. 算法与 mini_matrix/scripts/summarize.py 一致:
     - load_jobs: 扫 jobs_dir 子目录,过滤 substring
     - parse_job: 读 result.json + config.json,提取 stats/evals
     - 算 n_pass = sum(reward == 1.0),pass_at_1 = n_pass / len(rewards)
     - 按 (bench, agent) 分组输出

  2. 输出 3 份(同 timestamp):
     - summary-<ts>.md    Pass@1 by (Bench, Agent) 表 + All Jobs 详细行
     - summary-<ts>.csv   DictWriter 输出,Excel/pandas 可读
     - summary-<ts>.json  raw rows,default=str 让 datetime 可序列化

  3. 容错:
     - 缺 result.json 跳过
     - JSON 解析失败打印 warn 跳过(不中断全量聚合)
     - config.json 不存在时 bench="?"

  4. 与 harbor 0.20.x result.json schema 对齐:
     - stats.evals[eval_key].reward_stats.reward = {reward_value: [trial_ids]}
     - 与 C2 watch.sh 解析算法保持一致

与 batch A/B 决策一致性:
  - J2 host bind mount:依赖 --bind-jobs(/opt/swebench/jobs 存在)
  - J1 harbor 0.20.x:result.json schema 与 harbor CLI 0.20.x 输出对齐
  - J4 production flag:与 C3 无关(纯只读聚合)
  - 5 层 DinD L3:与 C1/C2 串联(run → watch → summarize)

独立 commit 设计:
  - C3 不引用 C1/C2/C4 脚本(纯聚合)
  - C3 不修改 bootstrap.sh(A+B 已固化)
  - C3 修改 Dockerfile.agent-runtime(COPY 顺序独立,合并时无冲突)
  - C1/C2/C4 可独立 commit

兼容性:
  - jobs-dir 不存在 → fail-fast(提示 bootstrap --bind-jobs)
  - summarize.py 缺失 → fail-fast(image 构建漏装)
  - jobs-dir 下无 result.json → "No jobs found" 退出 0(不报错)
  - 单个 result.json 损坏 → 跳过该 job 继续聚合其他
v3 Batch C 第四步:实现 5 层 DinD L3/L4/L5 健康检查入口 —
`ais_bench_agent_orchestrator_status.sh`,5 段查询输出容器状态。

新增文件 1 个:
  docker/agent_runtime/ais_bench_agent_orchestrator_status.sh  171 行

Dockerfile.agent-runtime 改动(+5 行):
  - COPY ais_bench_agent_orchestrator_status.sh → /usr/local/bin/ais_bench_agent_orchestrator_status.sh
  - chmod +x

脚本设计要点:
  1. 自动检测调用上下文:
     - 容器内视角(/var/run/docker.sock 可访问 + /opt/swebench 有内容)
       → 直接用内层 docker CLI
     - host 视角(否则)→ docker exec <container> 进容器查
     - host 视角下未传 --container-name 时自动找
       (grep -E 'ais_bench_agent|swebench-orchestrator' 取首个 running)

  2. 5 段输出:
     [1] 容器基础       hostname/uptime/arch (内) 或 state/image/restart policy (外)
     [2] 内层 docker    server-ver / storage-driver / cgroup-driver /
                        running containers / images / DOCKER_DEFAULT_PLATFORM
     [3] bind mount     /opt/swebench/{jobs,tasks,config,logs} 可见性 +
         可见性          api_key.env 存在 + OPENAI_API_KEY 行数
     [4] jobs 目录       total jobs / with result.json (completed) / running (估) /
         状态            latest 3 个 job 名
     [5] harbor CLI     --version + `harbor jobs list` 行数

  3. 容错设计:
     - 内层 docker 不可用 → "[错误] docker info 失败,daemon 可能未启动"
     - harbor CLI 不可用 → "[错误] harbor 命令不可用" + 提示 v3 A2 commit
     - bind mount 路径不存在 → 列出 ✗ 不报 exit
     - 所有 docker / docker exec 都加 2>/dev/null,失败不中断
     - jobs-dir 不存在 → 仅警告,继续后续段

  4. 收尾提示:
     - "如需追踪 trial" → watch.sh + summarize.sh
     - "如需启动新 trial" → run.sh
     - 与 C1/C2/C3 串联

  5. 适用场景:
     - bootstrap 完成后第一次验证(runtime 容器 / harbor daemon / bind mount 全通)
     - 跑 harbor jobs start 前的快速自检
     - trial 出错时排查(container crashed? daemon down? mount lost?)

与 batch A/B 决策一致性:
  - J2 host bind mount:[3] 段验证 --bind-{jobs,tasks,config} + --api-key-file 都生效
  - J1 harbor 0.20.x:[5] 段验证 harbor CLI 可用
  - J4 production flag:[1] 段显示 RestartPolicy.Name(--restart unless-stopped 已生效)
  - 5 层 DinD L3/L5:status 综合检查 L3 (harbor) + L4 (daemon) + L5 (mounts)

独立 commit 设计:
  - C4 不引用 C1/C2/C3(纯查询)
  - C4 不修改 bootstrap.sh(A+B 已固化)
  - C4 修改 Dockerfile.agent-runtime(COPY 顺序独立,合并时无冲突)
  - C1/C2/C3 可独立 commit

兼容性:
  - 容器内调用:不传 --container-name(自动用内层 docker)
  - host 调用:不传 --container-name → auto-detect 找名字含 ais_bench_agent
    或 swebench-orchestrator 的 running 容器
  - 找不到容器 → "[错误] 没找到 runtime 容器,显式传 --container-name"
  - mount 检查用 mount | grep(内层)或 docker exec du(外层),跨容器类型兼容
v3 Batch D 第一步:实现 L1 (case-base) + L2 (agent) baked image 一键构建脚本。

新增文件 1 个:
  docker/agent_runtime/build_l2_baked_image.sh  220+ 行

Dockerfile.agent-runtime 改动 (+3 行):
  - COPY build_l2_baked_image.sh → /usr/local/bin/ais_bench_agent_build_l2_baked_image.sh
  - chmod +x

脚本设计要点:
  1. 模板自动检测:
     - 容器内:/opt/swebench/dockerfiles/(D2 COPY 注入)
     - host  :./docker/agent_runtime/dockerfiles/(git checkout)
     - 都不是 → 报错退出

  2. 默认参数对齐 batch B 的 bind mount:
     - --tasks-dir 默认 /opt/swebench/tasks(B --bind-tasks 注入)
     - --dockerfiles-root 默认 /opt/swebench/dockerfiles(D2 COPY)
     - 模板内不直接用 tasks-dir(仅留作参考)

  3. Image tag 命名沿用 mini_matrix 规范:
     - L1:swebench/<dataset>-<case>-base:latest
     - L2:swebench/<dataset>-<case>-with-<agent>:latest

  4. 上游 prebuilt 解析:
     - 简单约定:<registry>/swebench/sweb.eval.x86_64.<dataset>_1776_<dataset>-<case>:latest
     - 默认 registry=docker.1ms.run(用户可 --registry 改)
     - 复杂 dataset id 映射留给用户在 matrix.yaml 里显式覆盖

  5. 并行 build:
     - L1 N 个 case 同时 docker build &
     - L2 (N case × M agent) 同时 docker build &
     - wait 全部完成
     - 已存在的 tag 自动 skip (用 docker images grep)

  6. ARM64 默认:
     - 默认 --platform linux/amd64(QEMU + binfmt 已在 A1 装)
     - --skip-arm64 标志:x86 host 关掉

  7. CLI 灵活性:
     - --case 11099 12308     单 case 全 agent
     - --agent aider msa      所有 case 同一 agent
     - --l1-only / --l2-only  分阶段
     - --dataset-prefix       改 dataset 名
     - --registry             改 prebuilt registry

  8. 容错:
     - 缺模板 → 报错并列出哪个 agent 缺
     - docker 不可用 → fail
     - L1 build 失败 → 打印最后 5 行 log + exit 1
     - L2 build 失败 → 继续跑其他 + 末段打印 fail 列表

与 batch A/B 决策一致性:
  - J3 L2 baked image:D1 + D2 落地 L1/L2 build,D3 bootstrap --l2-image 接入
  - A1 ARM64 QEMU:docker build --platform linux/amd64 依赖 A1
  - B --bind-tasks:tasks 目录由 B 注入(虽然 D1 不直接用,留 ref)

独立 commit 设计:
  - D1 不引用 D3 --l2-image(纯 build)
  - D1 可独立运行(用户手 docker run 也行)
  - D1 修改 Dockerfile.agent-runtime(COPY 顺序独立,与 D2 紧邻但 merge 时无冲突)

兼容性:
  - 与 mini_matrix build_baked_v2.sh 行为等价,但接口更通用
  - 不依赖 jinja2 CLI(模板里没有 jinja 语法,只是 bash + ARG)
  - 用户既可容器内也可 host 跑
  - 已 build 的 image 自动 skip,幂等
v3 Batch D 第二步:把 mini_matrix 5 层 DinD L1/L2 baked image 模板
搬进 PR AISBench#410 runtime 镜像,让 runtime 容器内用户可一键 build。

新增文件 6 个:
  docker/agent_runtime/dockerfiles/Dockerfile.l1-base.j2           (6 行)
  docker/agent_runtime/dockerfiles/Dockerfile.l2-agent-aider.j2    (12 行)
  docker/agent_runtime/dockerfiles/Dockerfile.l2-agent-msa.j2      (12 行)
  docker/agent_runtime/dockerfiles/Dockerfile.l2-agent-oh.j2       (15 行)
  docker/agent_runtime/dockerfiles/Dockerfile.l2-agent-qwen.j2     (17 行)
  docker/agent_runtime/dockerfiles/README.md                       (20 行)

Dockerfile.agent-runtime 改动 (+9 行):
  - ENV SWEBENCH_DOCKERFILES_ROOT=/opt/swebench/dockerfiles
  - RUN mkdir -p
  - COPY docker/agent_runtime/dockerfiles/ ${SWEBENCH_DOCKERFILES_ROOT}/

5 个模板设计:
  - l1-base:ARG BASE_IMAGE → FROM ${BASE_IMAGE} → WORKDIR /testbed + mkdir /logs
  - l2-agent-{aider,msa,oh,qwen}:ARG BASE_IMAGE + ARG AGENT → FROM ${BASE_IMAGE} → 装对应 agent
  - AGENT 校验:if 不匹配 agent 名 → exit 1(防呆)

模板来源:移植自 mini_matrix cli/swebench_dind/dockerfiles/,Jinja2 语法
兼容 docker buildx / docker build (无依赖 Jinja2 CLI,直接用模板原样即可)

与 batch A/B/C 决策一致性:
  - J3 L2 baked image:D2 提供模板,D1 build 脚本生成 L1/L2,D3 bootstrap --l2-image 接入
  - 与 v3 L4 image 协作:L4 镜像内置 docker engine,容器内用户直接 docker build 即可

独立 commit 设计:
  - D2 只增文件 + Dockerfile COPY/ENV
  - D2 不依赖 D1 build 脚本(用户可手 docker build)
  - D2 不依赖 D3 bootstrap --l2-image
  - D1/D3 可独立 commit

兼容性:
  - 模板复制自 mini_matrix,与原版字节级一致
  - 容器内路径:/opt/swebench/dockerfiles/(与 bootstrap --bind-config /opt/swebench/config 区分)
  - 模板内不引用 docker/agent_runtime 内部路径(全路径绝对)
v3 Batch D 第三步:bootstrap 加 --l2-image 参数,容器注入
-e AIS_BENCH_AGENT_L2_IMAGE=<tag>,harbor trial 容器直接用 baked image
启动,跳过 trial 内 pip/npm install (节省 ~10min/trial)。

Dockerfile.agent-runtime 无改动(脚本改动,不需 rebuild image)。

bootstrap.sh 改动 (+18 行):
  - help doc 加 --l2-image <IMAGE[:TAG]> 行(101-105)
  - 默认变量 L2_IMAGE=""(164-168)
  - CLI 参数解析 --l2-image,空值报错(283-288)
  - log 段:l2-image 设了就打印 tag,没设就提示默认行为(376-380)
  - SWEBENCH_ENVS 构造处:if L2_IMAGE 非空 → -e AIS_BENCH_AGENT_L2_IMAGE=<tag>(524-527)
  - log 段:env 注入提示加 AIS_BENCH_AGENT_L2_IMAGE (529)
  - [7/7] 下一步段:加 ais_bench_agent_build_l2_baked_image.sh 示例

设计要点:
  1. 不传 --l2-image → L2_IMAGE="" → 不注入 env → harbor 走默认行为
     (trial 内装 agent,行为完全退化到 PR AISBench#410 原版,J3 决策 opt-in)
  2. 不强校验 image 是否存在:
     - image 可能在 host 不存在(用户可能先 bootstrap 再 build baked image)
     - 真正报错留给 docker pull / harbor trial 启动时
  3. SWEBENCH_ENVS 注入:
     - 与 REGISTRY_MIRROR 同样的模式(if 非空则注入)
     - mode A/mode B 两条 docker run 都生效(SWEBENCH_ENVS 是共享变量)

与 batch D1+D2 协作:
  - D1 build_l2_baked_image.sh 产物 image tag 命名规范:
    swebench/<dataset>-<case>-with-<agent>:latest
  - D3 --l2-image <tag> 用户用 D1 产物的 tag
  - 完整工作流:
    bootstrap.sh --l2-image swebench/django-11099-with-aider:latest
    → 容器起 harbor → harbor trial 容器读 AIS_BENCH_AGENT_L2_IMAGE
    → 直接 docker run <tag> 启动 → 跳过 trial 内 pip install aider-chat

与 batch A/B/C 决策一致性:
  - J3 L2 baked image 必传 → opt-in(J4 一致),不传 = 原版行为
  - J2 host bind mount:不冲突(--l2-image 是 image tag,不是 bind 路径)
  - C 批脚本:不需改(C1 harbor jobs start 默认会读 container env)

独立 commit 设计:
  - D3 不依赖 D1/D2(纯参数注入)
  - D3 可独立合入
  - D1+D2+D3 merge 时可能 Dockerfile 有冲突(D1 加了 build script COPY,D2 加了 dockerfiles COPY,
    都是相邻 COPY 块),按 C 批经验解决:保留 HEAD 侧 + 追加 incoming + 更新注释

兼容性:
  - 默认行为不变(不传 --l2-image = PR AISBench#410 原版)
  - mode A/B 都生效(SWEBENCH_ENVS 共享)
  - 不与 B 批 --data-image 冲突(两个独立 flag,可叠加)
SJTUyh added 3 commits August 27, 2026 16:44
v3 E 批(增量):给 bootstrap.sh 加 --qemu <auto|yes|no> 参数,
解决 host arch vs runtime image target arch 不匹配时需装 qemu-user-static 的需求,
同时避免 host=image arch 时无谓的 x86→x86 翻译开销。

CLI:
  --qemu auto (默认):自动检测 host arch vs image target arch,不匹配才装
  --qemu yes         :强制装(debug / 跨架构确认用)
  --qemu no          :明确不装(用户已用其他方式确保 binfmt)

实现要点:
  1. help doc 加 4 行说明(--qemu 三种模式 + 用途)
  2. 默认变量 QEMU_INSTALL="auto"(B/D 参数下方)
  3. CLI 解析:--qemu) case,白名单 auto/yes/no,空值/非法值报错
  4. log 段:启动时打印 qemu:
  5. 新增 [6.5/7] 步骤(在 case-tar load 与 [7/7] 自检之间):
     - 探测 host arch:uname -m,归一化 aarch64/x86_64
     - 解析 image arch:从 --runtime-image <tag> 后缀推断
       (-x86_64 / -amd64 → x86_64;-arm64 / -aarch64 → aarch64)
     - 决策:yes 必装 / no 必不装 / auto 按 arch 匹配决定
     - 装时:docker exec <container> bash <<'EOF' 调 apt-get install qemu-user-static
     - 装前查重(已装则跳过,幂等)

设计动机:
  - 当前 A1 build-time 装 qemu(仅 TARGETARCH=arm64 时装)只能解决 build 时已知 arch 的场景
  - 但 bootstrap 启动时 host arch 可能与 image arch 不匹配(且 image 是预先 build 好的)
  - 给 bootstrap 加运行时按需装能力更灵活
  - 同时 auto 模式避免无谓 x86→x86 翻译(节省启动 + trial 时间)

实际启动验证(本 sandbox):
  [1] --qemu no  路径:host x86_64 + ubuntu x86_64 → 检测 arch 匹配 → SKIP ✓
  [2] --qemu yes 路径:force install qemu-user-static 1:8.2.2+ds-0ubuntu1.18
                      → qemu-x86_64-static 二进制 3.75MB,version 8.2.2 ✓
  [3] --qemu auto 路径:host x86_64 + image x86_64 → SKIP(避免 x86→x86 翻译) ✓

兼容性:
  - 默认行为:auto,host=image arch 时不装(避免开销)
  - 用户显式 --qemu no:明确不装(用于用户已配 binfmt)
  - 用户显式 --qemu yes:强制装(用于跨架构确认 / debug)
  - mode A/B 都生效(在 docker exec 阶段,与模式无关)
  - 不与 B/D 任何参数冲突(独立 flag)

独立 commit:
  - 仅改 bootstrap.sh
  - Dockerfile / 其他脚本不动
  - 不需 rebuild image
方案 A(arch 检测)由 A4 entrypoint.sh 已实现(本批不重复)。

方案 B(本批): 移除 docker run 的 --cgroupns=host
 - 原方案:--privileged + --cgroupns=host 让容器内 binfmt_misc mount 传播到 host
 - 风险:x86_64 host 上,容器内 qemu-x86_64-static 被泄漏到 host
        → host 的 ELF (如 /usr/bin/sleep) 被路由到 qemu → ELOOP
 - 新方案:依赖 daemon.json "exec-opts": ["native.cgroupdriver=cgroupfs"]
          + --net=host --ipc=host 让 dockerd 用 cgroupfs 文件直接管理 cgroup
          (不依赖 systemd,不依赖 host cgroup namespace 共享)
 - 配合 A4 entrypoint.sh arch 检测:x86_64 host 不装 qemu,binfmt 无泄漏源

参见 docs/research/31-v3-f-batch-ab-fix-2026-08.md
3 层安全网:
 ① 预检(docker / binfmt / sleep)
 ② setsid + nohup + disown watchdog 子进程
    ★ 关键顺序:sleep → 先清 host binfmt(写文件,不调 ELF)→ 再 docker rm -f
    解决"watchdog 自身也被 ELOOP"问题:binfmt 污染时 docker CLI 也会失败
 ③ trap cleanup(用户 Ctrl-C 自动停 watchdog)

解决 ELOOP 重现时"管理员介入"问题:即便 shell 完全卡死,
watchdog 在 3 分钟后自动 docker rm -f,把恢复时间从"不可控"压到"≤ 3 分钟"。

用法:
  bash safe_start_ais_bench_agent_bootstrap.sh --datasets /data/datasets
  bash safe_start_ais_bench_agent_bootstrap.sh --keep-alive --datasets ...
  bash safe_start_ais_bench_agent_bootstrap.sh --test  (只跑预检)

参见 docs/research/32-v3-g-batch-safe-launch-2026-08.md
@github-actions github-actions Bot added large_pr PR 新增代码超过 1000 行,建议拆分为更小的 PR no_review PR 未包含任何检视意见,合入前必须包含检视意见 labels Aug 27, 2026
@github-actions

Copy link
Copy Markdown

🚫 PR 合入质量检查未通过,PR 已被禁止合入。

未通过项:large_prno_review

  • 超大 PR:新增代码 4663 行,超过 1000 行上限,建议拆分。
  • 无检视意见:请至少邀请一位 reviewer 提交检视意见(Approve / Request changes / Comment 均可)。

请按上述提示整改后,重新推送(push)或评论本 PR 即可触发再次检查。

三个文件改动:
- 新增 ais_bench_agent.sh (483 行):顶层 facade 脚本,提供 build/run/status/watch/summarize/doctor 6 个子命令
- 修改 Dockerfile.agent-runtime (+27/-5):新增 ARG HARBOR_WHEEL_FILE + COPY 占位文件 + 条件安装(空文件回退 harbor==0.6.1)
- 修改 build_image_agent_runtime.sh (+24):新增 --harbor-wheel 参数 + wheel cp/trap 清理

核心能力:
- build 命令:一键构建 L4 runtime 镜像(可选 L2 baked image),支持 --harbor-wheel 替换自定义 harbor
- run 命令:6 步串联(校验→派生 config→启容器→doctor 自检→docker exec 透传命令),支持 --pack/--split 自动推导 config
- 向下兼容:现有 10 个脚本零改动

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🚫 PR 合入质量检查未通过,PR 已被禁止合入。

未通过项:large_prno_review

  • 超大 PR:新增代码 5192 行,超过 1000 行上限,建议拆分。
  • 无检视意见:请至少邀请一位 reviewer 提交检视意见(Approve / Request changes / Comment 均可)。

请按上述提示整改后,重新推送(push)或评论本 PR 即可触发再次检查。

Dockerfile.agent-runtime: 条件安装的 fallback 分支改为 harbor==0.20.0
build_image_agent_runtime.sh: 注释更新
ais_bench_agent.sh: usage 更新
PR499_DESCRIPTION.md: 示例命令更新

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the no_issue PR 未关联任何 Issue,合入前必须关联 Issue label Aug 28, 2026
@github-actions

Copy link
Copy Markdown

🚫 PR 合入质量检查未通过,PR 已被禁止合入。

未通过项:large_prno_issueno_review

  • 超大 PR:新增代码 5388 行,超过 1000 行上限,建议拆分。
  • 未关联 Issue:请在 PR 描述中关联 Issue,例如 Fixes #123Relates to #123
  • 无检视意见:请至少邀请一位 reviewer 提交检视意见(Approve / Request changes / Comment 均可)。

请按上述提示整改后,重新推送(push)或评论本 PR 即可触发再次检查。

从零端到端复现 PR AISBench#499 的 618 行操作手册,包含:
- 5 层 DinD runtime 镜像构建
- harbor offline 升级到 0.21.0 + --agent-deps 机制
- mini-swe-agent-ubuntu2204.tarball 注入 case 镜像
- msa-echo-test 自建 task + msa-base:ubuntu22 base image 重建
- 最终 trial 验证:msa-echo-test__aD8PXUd reward=1.0

诚实声明部分:
- 失败原因全部归因于环境基础设施 / 自加配置 / 自写 task 配置,与 PR 文档和 tarball 无关
- LLM 算力方案在 PR AISBench#499 中尚未确定(mock / 商业 API / 本地 vLLM / 跳板机 gateway 四选项待定)
- mini-swe-agent tarball 不绑定 LLM endpoint,必须在 harbor run 时通过 --ae 显式传入

新增的引导链接让 reviewer 能从 README.md / PR499_DESCRIPTION.md 直接跳到详细手册。

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🚫 PR 合入质量检查未通过,PR 已被禁止合入。

未通过项:large_prno_issueno_review

  • 超大 PR:新增代码 6010 行,超过 1000 行上限,建议拆分。
  • 未关联 Issue:请在 PR 描述中关联 Issue,例如 Fixes #123Relates to #123
  • 无检视意见:请至少邀请一位 reviewer 提交检视意见(Approve / Request changes / Comment 均可)。

请按上述提示整改后,重新推送(push)或评论本 PR 即可触发再次检查。

@Huafubing

Copy link
Copy Markdown
Author

此 PR 已由 #531 取代:基于 master 重新整理为单一 commit,聚焦 agent-runtime 镜像(harbor-only)与双模式拉起脚本,历史实验性方案不再保留。后续讨论请移步 #531

@Huafubing Huafubing closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

large_pr PR 新增代码超过 1000 行,建议拆分为更小的 PR no_issue PR 未关联任何 Issue,合入前必须关联 Issue no_review PR 未包含任何检视意见,合入前必须包含检视意见

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants