Qwen3-8B-OpenClaw-RL-tboverfit-iter311
把
Qwen/Qwen3-8B 用
HansBug/OpenClaw-RL 框架做
GRPO outcome-only RL,
直接把 terminal-bench v0.1.x 的 86 个 eval 任务当训练集(eval-as-train overfit 探针),跑出来的末态 checkpoint。对应 wandb run
fdhgc9j7 的 iter 311。
与同框架同算法的另一个 ckpt
HansBug/Qwen3-8B-OpenClaw-RL-iter215 仅有一个差别 ——
训练数据 pool:iter215 在
seta_env 1367 task 上训(OOD 评测 TB v0.1.x),本 ckpt 把 TB v0.1.x 自己当训练集(in-distribution 上限探针)。算法、超参、agent harness、scaffolding
完全 0 改动。
⚠️ 本 ckpt 不可作为 fair benchmark / 不可提交 TB leaderboard —— train set ≡ eval set = 数据泄漏。本 ckpt 是一个 capacity probe,用来回答一个明确的科学问题(见下)。
这个 ckpt 证明了什么
本 ckpt 是一个对照实验的 in-distribution 末态。同框架同算法只换数据 pool,得出以下 4 个结论,每条都是直接可读数字:
1. OOD gap 是真实存在的,但不是 8B 涨不动的全部原因
| ckpt | 训练数据 | TB v0.1.x pass@1 |
|---|
| Qwen3-8B base | — | 0.020 |
iter215 (sibling repo, OOD eval) | seta_env 1367 task | 0.056 |
本 ckpt tboverfit-iter311 (in-distribution) | TB v0.1.x 86 task (eval=train) | 0.092 |
- iter215 → tboverfit-iter311 涨幅 +3.6 pp / 1.65× = OOD gap 的真实量级。
- 但 0.092 远没到 17 % 或 15.5 %(见下表)。消除数据分布漂移之后,8B+default setup 还是只能到 9.2 %。iter215 的瓶颈不在数据分布,至少不主要在。
2. 8B + 默认 setup 的能力上界 ≈ 9–10 %;继续涨需要换 setup,不是换数据
同尺寸 / 同档位开源对照:
| 模型 + agent | TB pass@1 | 路线 |
|---|
| AfterQuery GPT-OSS-20B SFT+RL | 0.170 (TB Core 2.0) | SFT cold-start + RL,专门 agent-tuning |
| Qwen3-32B + TerminalAgent (同 family) | 0.155 (v0.1.x) | 4× 体量 + 更好 agent harness |
| 本 ckpt (eval=train) | 0.092 (v0.1.x) | 8B + default Terminus 2 + 无 SFT |
| Qwen3-235B-A22B (Terminus 1) | 0.066 | 29× 体量,但无 RL fine-tune |
要再涨需要:(a) base 模型放大、(b) agent scaffolding 升级、(c) SFT cold-start 流程。本实验已经证明:单纯在 RL 数据上做手脚(哪怕极端到 eval-as-train)也压不过 setup 维度的 gap。
3. "训练侧 infrastructure 健康" ≠ "模型在学到东西"
84 h 跑下来训练曲线全绿:
| 指标 | 全程范围 | 健康阈 | 状态 |
|---|
train/grad_norm | [0.000, 2.069],mean 0.564 | [0.05, 50] | ✅ |
train/kl_loss | [0.000, 0.237],mean 0.089 | <0.5 | ✅ |
rollout/response_len/mean | 50w 区间 [87, 283] | >30 | ✅ no mode collapse |
terminal/accuracy (50w) | 0.174 → 0.360 → 0.355 | 上升 | ✅ 训练 setup 没崩 |
但实际 reward 分布告诉另一个故事(37,236 trial):
| 区间 | 数量 | 占比 | 含义 |
|---|
accuracy < 0.05 | 19,933 | 53.5 % | 完全失败,GRPO advantage 接近 0 |
accuracy ∈ [0.05, 0.99) | 15,396 | 41.4 % | partial credit,看似有信号但碰不到通关 |
accuracy ≥ 0.99 | 1,907 | 5.1 % | 真正 strict success,但集中在 5–6 个 task 上 |
→ 训练曲线漂亮,只代表 setup 没崩;不代表模型学到 "86 task 的分布"。
4. eval-as-train 也没法救 hard task —— 集中 overfit 而非广度学习
整 84 h 训练,86 个 task 中 67 个一次 strict success 都没有(含全部 swe-bench-astropy-*、tmux-advanced-workflow、build-linux-kernel-qemu、chess-best-move 等)。
学到的只有这 6 个简单 task:
| Task | strict success / trials | pass rate | 备注 |
|---|
| swe-bench-fsspec | 467 / 467 | 100.0 % | ⚠️ outlier,verifier 可能有 shortcut |
| broken-python | 1 / 1 | 100.0 % | 单 trial 置信度低 |
| fix-permissions | 371 / 400 | 92.8 % | 单步 chmod |
| fibonacci-server | 210 / 378 | 55.6 % | flask 单 endpoint |
| hello-world | 216 / 478 | 45.2 % | 写文件 |
| vim-terminal-task | 197 / 480 | 41.0 % | vim 命令固定 pattern |
而 partial credit 是个陷阱 —— swe-bench-astropy-2 mean reward 0.889,但 strict success 0 / 472。模型能拿 89 % 的 partial 分数但永远到不了 100 %,GRPO 一直在反复优化这种 "差一口气" 的状态。
目录
- 快速开始
- 模型从哪来
- 训练设置
- 训练曲线
- leaderboard 对照
- 86 task per-task 表现
- 推理 on-the-wire 协议
- NFS ENOQUOTA 强停
- 已知限制
- 合法 / 不合法的用法
- 复现 / 推理工具
- 引用 / 致谢
快速开始
完全 drop-in 兼容:config.json / generation_config.json / tokenizer 与上游 Qwen/Qwen3-8B 字节级一致(与 iter215 也 diff 无差),任何能跑 Qwen3-8B 的工具链(HF transformers / vLLM / sglang / ollama / llama.cpp)一行不改即可用。
用 HF transformers
1from transformers import AutoTokenizer, AutoModelForCausalLM
2
3tok = AutoTokenizer.from_pretrained("HansBug/Qwen3-8B-OpenClaw-RL-tboverfit-iter311")
4model = AutoModelForCausalLM.from_pretrained(
5 "HansBug/Qwen3-8B-OpenClaw-RL-tboverfit-iter311",
6 torch_dtype="bfloat16",
7 device_map="auto",
8)
9
10messages = [
11 {"role": "user", "content": "Write a one-line bash command to recursively count *.py files under /usr/lib."}
12]
13text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
14inputs = tok(text, return_tensors="pt").to(model.device)
15out = model.generate(**inputs, max_new_tokens=256, temperature=0.2)
16print(tok.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
用 sglang(推荐,配 tool-call 支持)
1python -m sglang.launch_server \
2 --model-path HansBug/Qwen3-8B-OpenClaw-RL-tboverfit-iter311 \
3 --served-model-name qwen3-8b-rl-tboverfit-iter311 \
4 --tp 1 --port 30000 \
5 --tool-call-parser qwen
--tool-call-parser qwen / qwen25 把模型 emit 的 <tool_call>{...}</tool_call> 还原成 OpenAI 结构化 tool_calls,这正是训练时 rollout 使用的 parser。
用 vLLM
vllm serve HansBug/Qwen3-8B-OpenClaw-RL-tboverfit-iter311 --enable-auto-tool-choice --tool-call-parser hermes
用 oc-repl 端到端复刻训练分布
HansBug/oc-repl 是为这套 ckpt 量身写的 Codex 风格 REPL,默认
--protocol camel-terminal-toolkit 字节级对齐训练分布:
1pip install git+https://github.com/HansBug/oc-repl
2
3docker run -d --name oc-sandbox -w /app ubuntu:24.04 sleep infinity
4
5oc-repl --sandbox docker:oc-sandbox \
6 --api-base http://127.0.0.1:30000/v1 \
7 --model qwen3-8b-rl-tboverfit-iter311
模型从哪来
Qwen3-8B-OpenClaw-RL-tboverfit-iter311 =
Qwen3-8B +
311 个 RL iteration 的 GRPO outcome-only 训练。rollout 在
terminal-bench v0.1.x 的 86 个 task 上 —— 注意这 86 个 task 是 TB v0.1.x 的 eval set,
本 run 把它当训练集。每个 task 是真实 Linux shell 任务(修脚本 / 解 base64 / 起 server / 跑 pytest 等),在隔离 docker 容器里运行,agent 通过
camel.toolkits.TerminalToolkit 暴露的 4 个工具(
shell_exec /
shell_view /
shell_write_to_process /
shell_write_content_to_file)操作 shell。
与 iter215 sibling repo 的关系
iter215 = production ckpt(OOD 公平评测),本 ckpt = capacity probe(数据泄漏的上界探针)。两个 ckpt 是配对的对照实验:
| iter215 (sibling repo) | tboverfit-iter311(本 repo) |
|---|
| 训练数据 | seta_env 1367 task | TB v0.1.x 86 task |
| Wandb run | msp60ius | fdhgc9j7 |
| Step | 215 | 311 |
| TB v0.1.x pass@1 | 0.056(OOD eval) | 0.092(in-distribution,污染) |
| 启动脚本差异 | — | 仅 override ROLLOUT_PROMPT_DATA=tbench_test_convert/train.jsonl,算法侧 0 改动 |
| 用途 | production / 公平 benchmark | capacity probe,不可作 fair benchmark |
训练设置
整个 launch wrapper 就是 iter215 同款脚本前面加一行 env override:
1#!/usr/bin/env bash
2set -euo pipefail
3SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
4export ROLLOUT_PROMPT_DATA="${SCRIPT_DIR}/dataset/tbench_test_convert/train.jsonl" # ← 与 iter215 唯一区别
5export SAVE_CKPT="${SCRIPT_DIR}/ckpt/qwen3-8b-tboverfit"
6export WANDB_GROUP="qwen3-8b-tboverfit-probe"
7exec "${SCRIPT_DIR}/run_qwen3_8b_experiment.sh"
tbench_test_convert/train.jsonl = TB v0.1.x 86 个 task 转换后的 prompt-only jsonl(task 描述 + Dockerfile 路径)。
关键超参(与 iter215 字节一致):
1# GRPO 算法
2--advantage-estimator grpo
3--use-kl-loss --kl-loss-coef 0.01 --kl-loss-type k3
4--dynamic_history
5
6# Optimizer (low LR + Adam beta2=0.98)
7--optimizer adam --lr 1e-6 --lr-decay-style constant
8--weight-decay 0.1 --adam-beta1 0.9 --adam-beta2 0.98
9
10# Rollout
11--rollout-batch-size 16
12--n-samples-per-prompt 8 # 每 prompt 采 8 个 sample 算 GRPO group std
13--num-steps-per-rollout 2
14--rollout-temperature 1
15--rollout-max-response-len 8192
16--rollout-max-context-len 16384
17
18# Megatron
19--tensor-model-parallel-size 4
20--sequence-parallel
21--recompute-granularity full
22
23# Rollout agent + tool-call parser
24--custom-config-path configs/rollout_qwen3.yaml # tool_call_parser: qwen25
25 # max_iteration: 10
Reward signal —— dense pass-rate, outcome-only:每个 rollout 跑完后 task 自带的 run-tests.sh 在 sandbox 里跑 pytest,reward = passed / total_tests ∈ [0, 1],线性变换到 score = 2·reward − 1 ∈ [−1, +1] 喂 GRPO。没有 PRM / process reward,整个 episode 共用一个标量。
| 配置项 | iter215 | tboverfit-iter311(本 run) |
|---|
| 基座 | Qwen3-8B base | 同 |
| algorithm | GRPO outcome-only | 同 |
rollout_batch | 16 prompt × 8 sample = 128 traj | 同 |
lr | 1e-6 const | 同 |
kl_loss_coef | 0.01 (k3) | 同 |
save_interval | 8 round | 同 |
num_rollout target | 2000 | 同(实跑 320 因 ENOQUOTA) |
| 训练数据 | seta_env 1367 task | TB v0.1.x 86 task |
| Agent | Terminus 2 / camel ChatAgent | 同 |
| SFT warmup | 无 | 无 |
训练曲线
数据源:wandb
fdhgc9j7 的 1,616 个 step 完整 history。
training curves
per-50-rollout bucket(wandb canonical 字段)
| bucket | terminal/accuracy | terminal/reward_mean | terminal/non_trainable_ratio |
|---|
| 0–49 | 0.174 | −0.653 | 3.41 % |
| 50–99 | 0.222 | −0.556 | 5.50 % |
| 100–149 | 0.249 | −0.502 | 3.20 % |
| 150–199 | 0.281 | −0.438 | 5.33 % |
| 200–249 | 0.318 | −0.363 | 7.49 % |
| 250–299 | 0.355 | −0.291 | 22.65 % ⚠️ |
| 300–319 | 0.349 | −0.301 | 7.91 % |
三个核心观察(直接从曲线读出来)
(1) eval-as-train 的 train-side accuracy 反而显著低于 iter215
| start | peak | final |
|---|
| iter215 (seta_env, OOD-evaluable) | 0.318 | 0.528 | 0.517 |
| tboverfit-iter311 (eval-as-train) | 0.174 | 0.360 | 0.355 |
| gap | −0.144 | −0.168 | −0.162 |
直觉上"把答案集喂回去训练应该 trivially 飙到 90 %+"在 多步 task + GRPO 高方差 + partial credit 主导 的现实里完全不成立。86 个 TB v0.1.x task 大部分是 evaluation-grade hard task,模型很快被 dominate 在少数能解的 task 上(详见 §6)。
(2) non_trainable_ratio 后半段出现 22.65 % 大 spike
前 225 step non_trainable_ratio mean 3.36 %(≈ iter215 全程 < 3 %),但 rollout 250–299 这一段升到 22.65 %,含 7 次 single-step > 0.5 的 spike。意味着 GRPO group 内 8 个 sample reward 标准差变 0(全成功 or 全失败)→ 该 batch 对 policy 无任何梯度信号 → "训练在跑,但每 4 个 batch 有 1 个白跑"。
(3) infrastructure 全部健康
grad_norm ∈ [0, 2.07] mean 0.564、kl_loss ∈ [0, 0.237] mean 0.089、entropy_loss 稳在 0.04–0.08、response_len 50w 始终在 [87, 283] —— 没有任何标准训练监控会报警。这就是为什么 §1 的结论 "训练侧 healthy ≠ 在学" 重要:只看常规曲线会漏掉 reward 集中在 5 task 这个真问题。
leaderboard 对照
iter311 在 eval-as-train trajectory 上的 Codex pass@1(
Codex 论文 §2.1 公式,50-rollout sliding window):
| 锚点 | rollout | Codex pass@1 |
|---|
| Start (50w 第一个有效点) | 49 | 2.49 % |
| Peak | 318 | 9.27 % ⭐ |
| Final(iter319,因 ENOQUOTA 损坏) | 319 | 9.23 % |
| 本 ckpt(iter311,最后健康) | 311 | ~9.23 %(与 final 差 <0.05 pp) |
| All-time micro-avg | — | 5.12 % |
| Tasks-ever-solved (pass@∞) | — | 19 / 86 = 22.09 % |
Trajectory peak ckpt 应该是 iter_0000319,但写到 38 GB(vs 正常 104 GB)时 NFS ENOQUOTA,ckpt 损坏已删。iter_0000311 是仍能 load 的最后健康 ckpt,即本 repo 上传的内容;pass@1 与 peak 差 ≤ 0.05 pp。
leaderboard chart
读这张图的方式
- 本 ckpt 0.092(橙色实线)是 in-distribution upper bound,不能与 OOD 数字横向比较 —— 同条件 iter215 OOD 是 0.056(橙色虚线下方)。
- Qwen3-32B + TerminalAgent 0.155 / AfterQuery SFT+RL 0.170 在更高位 —— 同档量级 / 同 family,说明真实 capacity 上界在 0.15–0.20,不是 0.09。本 ckpt 数字之所以低于这两个,主要因素不是体量也不是数据,是 setup(缺 SFT cold-start + 用 default Terminus 2 而非更优 agent scaffolding)。
- 与 Qwen3-235B-A22B (Terminus 1) 0.066 同水位但远小于体量 —— 这是 RL fine-tune 在 8B 上的真实贡献(base 0.020 → 0.092,约 4.6×)。
数据来源(公开可验证)
tbench.ai/leaderboard/terminal-bench/1.0 —— 第三方所有数字
afterquery.com/blog/terminal-bench-improvement —— AfterQuery 0.170 (TB Core 2.0)
- 本 ckpt 0.092 —— wandb
fdhgc9j7 rollout 319 final(trajectory peak rollout 318 = 0.0927)
86 task per-task 表现
把 37,236 个 trial 按 task 聚合(log 解析 + Welford 在线方差),每个 task 算 (mean_reward, std_reward, strict_n),按 8 类分布上色:
per-task scatter
8 类含义
| 类别 | 含义 | 数量 |
|---|
| truly-cold-flat | mean ≈ 0 / std ≈ 0:320 rollout 从没采到任何 partial reward,GRPO 永远 0 梯度 | 多数 |
| truly-cold-noisy | mean 接近 0 / std 较高:偶尔有微小 partial credit 噪声 | 4 |
| weak-learnable | strict_n ∈ [1, 50]:偶有突破但非主流 | 5 |
| partial-noisy | mean ∈ [0.3, 0.9] / std 较高:partial credit 区间,可能可推到 strict | 18 |
| partial-stuck-flat | mean ∈ [0.5, 0.9] / std 极低 / strict_n < 50:靠 partial credit 拿 50–90 % 分但永远碰不到 1.0 阈值(典型:"swe-bench-astropy-2" mean 0.889 strict 0/472) | 7 |
| mid-learnable | 中 mean / 中等 strict_n | 2 |
| truly-learnable | strict_n > 100 + mean > 0.6:模型在这 4 个 task 上真实学到东西(fix-permissions / fibonacci-server / vim-terminal-task / hello-world) | 4 |
| strict-flat (verifier shortcut) | strict_n > 200 + std < 0.05:swe-bench-fsspec 异常 outlier(467/467 trial 全 strict pass),verifier 大概率有 shortcut,不应被解读为模型真的解决了 SWE 修 bug task | 1 |
takeaway
- 86 task 里只有 4 个真正学到(占 5 %);其余 82 个要么从未碰到任何 partial credit,要么卡在 partial credit 出不去。
- partial credit 比看起来危险:22 个 task 在 mean reward ∈ [0.3, 0.95] 区间,平均看 reward signal 没问题;但 swe-bench-astropy-1 (0.867) / swe-bench-astropy-2 (0.889) / tmux-advanced-workflow (0.774) / decommissioning-service-with-sensitive-data (0.678) 这些 mean 很高的 task strict success = 0,模型能改对大部分 test 但碰不到最后一关。这种 task 给 GRPO 的 advantage 是 "几乎成功 vs 完全成功" 的低信噪比信号,对 outcome-only RL 极不友好。
- eval-as-train 没有"广度学习":既没学到 86 task 的代表性子集,也没从简单 task 推广到难 task。集中 overfit 而非泛化。
推理 on-the-wire 协议
本 ckpt 训练时看到的是
camel-ai TerminalToolkit 的 4 工具 OpenAI function-calling 协议(与 iter215 完全一致),
不是 terminal-bench
terminus-2 的 JSON 协议。证据来自
HansBug/OpenClaw-RL 仓库三个文件:
terminal-rl/agent/camel_agent.py:rollout agent = camel.agents.ChatAgent 子类
terminal-rl/remote/terminal_env.py:env 在 reset 时把 camel.toolkits.TerminalToolkit 的 4 个工具封装成 OpenAI tool schema 喂给 rollout
terminal-rl/configs/rollout_qwen3.yaml:tool_call_parser: qwen25,sglang 把模型 <tool_call>{...}</tool_call> markup 转回 OpenAI tool_calls
System prompt = get_developer_agent_prompt(system='Linux (in Docker)', machine='x86_64', non_think_mode=True) 的整段输出,结尾加 /no_think(关闭 qwen3 think trace,让模型直接 emit tool_call)。
100 % 复刻训练分布做推理,用
HansBug/oc-repl 的默认
--protocol camel-terminal-toolkit。
NFS ENOQUOTA 强停
本 run 实际 plan 2000 rollout,因 NFS quota 爆盘提前 320 rollout 中断:
| UTC 时间 | 事件 |
|---|
| 2026-05-02 12:20 | 训练启动 |
| 2026-05-05 11:55:42 | step 319 完成训练,开始写 iter_0000319 ckpt |
| 2026-05-05 11:57:18 | filesystem_async.py:326 - [Errno 122] Disk quota exceeded |
| 2026-05-05 11:57+ | 8 个 Megatron rank 全部 exit,训练已死 |
| 2026-05-06 02:13 | 清理中间 ckpt + 损坏的 iter_0000319,iter_0000311 留作本 repo source |
根因:save_interval=8,从 iter_0000007 开始累积 → 40 个 ckpt × ~104 GB ≈ 4.0 TB;共享 NFS quota 25 TB → 写到 iter_0000319 时 ENOQUOTA,写出 38 GB(vs 正常 104 GB)即损坏。
为什么 iter311 即 final ckpt:iter_0000319 因损坏无法 load;iter_0000311 是 save_interval=8 的次新合法 ckpt(pass@1 9.23 % vs peak 9.27 %,差 ≤ 0.05 pp),即本 repo 内容。
后续训练防护(已 backport 到
OpenClaw-RL launch 脚本):增大
save_interval 控制磁盘占用 + 启动前
df -h 预检查。
已知限制
-
本 ckpt 是 capacity probe,不可作 fair benchmark / leaderboard 提交 —— train set ≡ eval set。任何报告的 pass@1(包括本 README 引用的 0.092)必须明示为 in-distribution upper bound。
-
集中 overfit 而非分散学习:strict pass 集中在 6 个简单 task;67 / 86 task 整 84 h 0 strict success。本 ckpt 不是 "对 TB v0.1.x 整体都强" 的模型,只是 "在 fix-permissions / fibonacci-server / vim-terminal-task / hello-world 这 4–5 个简单 task 上很强"。
-
swe-bench-fsspec 100 % strict pass 是 outlier,verifier 大概率有 shortcut,不应被解读为模型真的解决了 SWE 修 bug task。
-
模型不会主动停止 tool_call。训练时 agent loop 由 max_iteration: 10 硬截断 + outcome reward 兜底,模型没学到 "做完后不发 tool_call、写一段总结" 这个动作。Inference 时必须设 max-turns 兜底(oc-repl 默认 12 轮)。
-
task_complete 自报 ≠ 任务真完成。模型说 "完成" 不一定真完成,验证要做客观检查(diff / tests / verifier)。
-
terminus-2 / terminus-XML 协议是 OOD。模型没在这些协议上 RL 训练过,能不能跑通靠 qwen3 底座的通用指令跟随能力。要复刻训练分布请用 camel TerminalToolkit 协议。
-
不是 frontier 水平。in-distribution pass@1 0.092 与 Claude 4.5 (0.645) / GPT-5 (0.525) 差 6–7×。本 ckpt 适合做 RL capacity 探针 / case study,不适合直接当 production agent 用。
合法 / 不合法的用法
✅ 合法用途
- capacity 上界探针对照样本 —— 同 base / 同算法 / 同 setup 不同数据 pool 的 in-distribution 末态,与 iter215 OOD 末态形成 controlled comparison
- reward landscape / 学习行为分析 —— 37,236 trial 的 per-task reward 分布是公开数据
- SFT-then-RL / reward shaping 等后续实验的 baseline —— 与 iter215 共享算法和 agent harness,差异变量受控
- agent loop / tool-call parser 行为研究
- post-train distillation 实验的源 ckpt(可以从这个 in-distribution 上界蒸馏到学生模型)
❌ 不合法用途
- 提交 terminal-bench leaderboard / 写在论文里和 OOD 数字比较 —— train ≡ eval 数据泄漏
- 当 production agent 用 —— 86 task 里只有 4 个真学到,其余 82 个 0 或卡 partial
- 引用 0.092 时不加 "eval-as-train, in-distribution upper bound" 限定语 —— 这是误导
复现 / 推理工具
引用 / 致谢
License: Apache-2.0(继承自 Qwen3-8B)。
如果引用本 ckpt 做 paper / blog:(1) 必须明示
eval-as-train capacity probe,pass@1 是 in-distribution upper bound,不可与 OOD 数字横向比较;(2) 欢迎引用
HansBug/OpenClaw-RL + 本 model card。