1. 从一次训练卡死说起:OpenClaw-RL 架构到底怎么读
如果你正在复现 OpenClaw-RL 的训练流程,大概率会遇到一个很迷惑的现象:脚本跑起来了,SGLang 在服务,Megatron 在等数据,PRM 在打分,但训练循环就是不动。日志里没有报错,GPU 占用也正常,可rollout_batch_size迟迟凑不满。这个问题我第一次读源码时也卡了很久,最后发现根因不在算法,而在架构理解——OpenClaw-RL 把 Slime 原本"主动生成 rollout"的假设,改成了"被动等待真实用户对话产生样本"。
OpenClaw-RL 是一个面向在线强化学习(Online RL)的框架,专门针对智能体工具使用场景。它从环境反馈中提取过程奖励信号来训练语言模型,支持三种主要模式:openclaw-rl(基于二元奖励的 GRPO)、openclaw-opd(基于反思之明提示的在线策略蒸馏)、openclaw-combine(在同一 PPO 更新中同时利用 RL reward 和 OPD teacher signal)。适合谁?适合想把真实用户交互变成训练数据、又不想改 Slime/Megatron 核心代码的开发者。
这篇笔记聚焦架构层源码阅读,围绕 Slime 与 PPO 的模块划分、调用链与配置入口展开。我会给出可复制的config.toml骨架、TaoToken 统一 Key/API 通道接入 AI 工具的settings.json片段,以及逐步验证动作,帮你确认架构理解与配置生效。读完之后,你应该能自己画出数据从用户请求到梯度更新的完整路径。
2. 前置准备:TaoToken 统一 Key 与 API 通道
在动手读源码之前,先把 AI 工具的接入通道理顺。OpenClaw-RL 的调试过程需要频繁调用模型做对比验证,如果每个工具都单独配 Key,切换成本很高。TaoToken 提供统一的 Key/API 通道,官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api (不加 UTM)。
你需要先拿到 API Key。登录后进入控制台,在 API Keys 页面创建一个新 Key,复制保存。这个 Key 会同时用于模型对话验证、Coding Plan 以及后续的接入文档对照。
对于长期编码和 Agent 场景,建议直接开通 Coding Plan,这样在调试 OpenClaw-RL 的 rollout 逻辑时不会因为额度问题中断。模型对话入口可以用来快速验证 Key 是否生效,接入文档则提供了不同工具链的配置模板。
拿到 Key 之后,在项目根目录创建或修改settings.json,把统一通道写进去。下面是我实测可用的片段:
{ "ai_provider": { "base_url": "https://taotoken.net/api", "api_key": "sk-your-taotoken-key", "default_model": "claude-sonnet-4-20250514", "timeout_seconds": 120, "max_retries": 3 }, "tools": { "model_chat": { "endpoint": "https://taotoken.net/api/v1/chat/completions", "stream": true }, "coding_plan": { "enabled": true, "workspace": "./openclaw-rl-workspace" } } }注意base_url不要带末尾斜杠,否则部分 HTTP 客户端会拼出双斜杠导致 404。api_key用你刚创建的那串,不要提交到 git,建议放进.env再用环境变量注入。
3. 架构拆解:Slime 与 PPO 的模块划分
3.1 统一 PPO 框架 + 三种 advantage 注入
OpenClaw-RL 的 RL 训练本质是"一套统一的 PPO 框架 + 三种不同的 advantage 注入方式"。这个设计原则很关键,理解了它,后面读代码就不会迷路。
| 方法 | Advantage 来源 | 适用场景 |
|---|---|---|
| Binary RL | A = R(raw broadcast) | 简单场景,只有 ±1 reward |
| OPD | A_t = teacher_lp_t - old_lp_t | 有 teacher model 提供 per-token 信号 |
| Combine | A_t = w_rl·R + w_opd·(teacher_lp_t - old_lp_t) | 同时需要 reward 和 teacher 信号 |
三条路径共享同一套 ratio-based clipped loss,区别只在 advantage 怎么算出来。Binary RL 走 Slime 内置 GRPO,reward 广播到全序列;OPD 靠字段劫持,API Server 把teacher_log_probs塞进 sample,Slime 的loss.py读到后自动算 per-token advantage;Combine 是唯一需要自定义 loss 的,用combine_loss.py::combine_loss_function读batch["advantages"]和batch["teacher_log_probs"],加权合成后再进 PPO clip。
3.2 文件结构与模块职责
OpenClaw-RL 的目录划分很清晰,核心 RL 框架在slime/,个性化 Agent 优化在openclaw-rl/、openclaw-opd/、openclaw-combine/,通用 Agent RL 在gui-rl/、swe-rl/、terminal-rl/、toolcall-rl/。
模块职责划分如下:
OpenClaw-RL/ ├── openclaw-rl/ │ ├── openclaw_api_server.py ← FastAPI 代理 + PRM 评分 + 样本提交 │ └── openclaw_rollout.py ← AsyncRolloutWorker: 桥接 API Server ↔ Slime ├── openclaw-opd/ ← OPD 变体(hint 提取 + teacher log-probs) ├── openclaw-combine/ ← Combined 变体(RL + OPD 并行) ├── slime/ │ └── train_async.py ← 基础 RL 框架(Megatron + SGLang) └── terminal-rl/ gui-rl/ swe-rl/ toolcall-rl/ ← Track 2 通用智能体 RLopenclaw_api_server.py是整个数据采集层的核心,它同时承担了 FastAPI 代理、PRM 评分、样本提交三件事。openclaw_rollout.py里的AsyncRolloutWorker是桥接层,负责管理 API Server 实例并收集样本。slime/train_async.py是训练主循环入口。
3.3 四大组件的异步解耦
OpenClaw-RL 的系统设计是四个异步解耦的循环——policy serving、environment hosting、reward judging、policy training 同时运行、互不阻塞。模型可以一边持续服务,一边从刚刚发生的真实交互中在线学习。
GPU 分配(8 卡节点,run_qwen3_4b_openclaw_rl.sh):
GPU 0-3: Megatron Actor (ACTOR_GPUS=4, TP=4) <- Policy Training GPU 4-5: SGLang Rollout (ROLLOUT_GPUS=2, TP=2) <- Policy Serving GPU 6-7: SGLang PRM/Judge (PRM_GPUS=2, TP=2) <- Reward Judging Environment: 无 GPU,OpenClaw App + 用户三个角色(Actor/Rollout/Judge)用的都是同一个 Qwen3-4B,但只有 Actor 被训练更新。Rollout 是 Actor 的权重副本,定期同步;PRM Judge 是固定不变的 judge/teacher。
3.4 Slime 的插件化扩展点
Slime 设计了一套插件化的钩子系统,OpenClaw-RL 通过 shell 脚本中的参数注入,不修改 Slime 核心即可接管整个训练流程。四个扩展点:
# run_qwen3_4b_openclaw_rl.sh 中的关键参数: --rollout-function-path openclaw_rollout.generate_rollout_openclaw # 扩展点1 --custom-generate-function-path openclaw_api_server.generate # 扩展点2 --custom-rm-path openclaw_api_server.reward_func # 扩展点3 # 无需 --custom-loss-function-path(RL 用标准 GRPO) # run_qwen3_4b_openclaw_combine.sh --custom-loss-function-path combine_loss.combine_loss_function # 扩展点4扩展点 1 是最核心的接管。Slime 框架原本假设 rollout 是主动的(给模型一个 prompt,模型生成 response),OpenClaw-RL 把它改成被动等待(等真实用户对话产生样本)。generate_rollout_openclaw()被 Slime 的RolloutManager调用,负责回调 OpenClawAPIServer 收集训练数据。
# openclaw_rollout.py def generate_rollout_openclaw(args, rollout_id, data_buffer, evaluation=False): """ Slime 框架期望: 调用这个函数 -> 返回 rollout_batch_size 个 Sample OpenClaw 实现: 不主动生成! 而是等待真实用户对话产生样本 """ worker = get_global_worker(args, data_buffer) if evaluation: eval_output, _ = run(eval_rollout(args, rollout_id)) return eval_output worker.resume_submission() # 开放 API 接受新会话的样本提交 completed_samples = run( _drain_output_queue(args, worker) # 阻塞等待,直到收集到 rollout_batch_size 个样本 ) worker.pause_submission() # 关闭提交(权重更新期间,503 所有请求) return RolloutFnTrainOutput(samples=completed_samples, metrics=...)标准 Slime 模式是"训练器 → 给我生成 rollout_batch_size 个样本 → rollout 引擎主动采样"。OpenClaw-RL 模式是"训练器 → 给我生成 rollout_batch_size 个样本 → 打开闸门等待 → 用户正常使用 OpenClaw(同时 API Server 收集并评分)→ output_queue 积满 → 关闭闸门,返回给训练器"。
4. 可复制配置:config.toml 骨架与 settings.json
4.1 config.toml 骨架
下面是我整理的可复制config.toml骨架,覆盖 Slime 与 PPO 的关键配置入口。你可以直接放到项目根目录,按需改路径和 GPU 数。
[model] path = "Qwen/Qwen3-4B" actor_gpus = 4 rollout_gpus = 2 prm_gpus = 2 tensor_parallel = 4 [slime] train_async_entry = "slime/train_async.py" rollout_function_path = "openclaw_rollout.generate_rollout_openclaw" custom_generate_function_path = "openclaw_api_server.generate" custom_rm_path = "openclaw_api_server.reward_func" # combine 模式才需要下面这行 # custom_loss_function_path = "combine_loss.combine_loss_function" [ppo] advantage_estimator = "grpo" # 可选: grpo / on_policy_distillation clip_range = 0.2 kl_coef = 0.01 rollout_batch_size = 32 max_new_tokens = 512 temperature = 0.7 [prm] judge_samples = 3 # 多数投票次数 m=3 score_range = [-1, 0, 1] async_eval = true [server] api_port = 30000 sglang_router_port = 0 # 0 表示由 Slime 动态分配 prm_router_port = 0 [taotoken] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY"几个容易踩坑的点:advantage_estimator选on_policy_distillation时,Slime 的loss.py内部已有 OPD 分支,不需要再配custom_loss_function_path;sglang_router_port和prm_router_port填 0 让 Slime 动态分配,避免端口冲突;rollout_batch_size要和实际并发用户量匹配,太小会频繁触发权重同步,太大则训练延迟高。
4.2 settings.json 接入片段
前面第 2 节已经给了settings.json的基础片段,这里补充一个针对 OpenClaw-RL 调试场景的增强版,把模型对话和 Coding Plan 都挂到统一通道:
{ "ai_provider": { "base_url": "https://taotoken.net/api", "api_key": "sk-your-taotoken-key", "default_model": "claude-sonnet-4-20250514" }, "openclaw_rl": { "debug_chat_endpoint": "https://taotoken.net/api/v1/chat/completions", "coding_plan_enabled": true, "log_level": "debug" } }debug_chat_endpoint用于在训练卡住时,单独发一条请求验证模型通道是否正常,排除是网络问题还是架构问题。
5. 验证请求与成功结果
配置写完之后,不要直接跑完整训练,先做三步验证。
第一步,验证 TaoToken 通道。用 curl 发一条最小请求:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 16 }'成功结果:返回 JSON 里choices[0].message.content有内容,usage.total_tokens大于 0。如果返回 401,检查 Key;返回 404,检查base_url是否多了斜杠。
第二步,验证 Slime 扩展点加载。启动训练脚本后,观察日志里是否出现rollout_function_path和custom_rm_path的加载记录。成功标志是看到generate_rollout_openclaw被调用,且OpenClawAPIServer在:30000端口监听。
# 另开终端验证 API Server 是否起来 curl -s http://127.0.0.1:30000/v1/models | head -c 200成功结果:返回模型列表 JSON,说明 FastAPI 代理已就绪。
第三步,验证样本收集。发一条模拟用户对话,观察output_queue是否收到样本:
curl -X POST http://127.0.0.1:30000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "X-Session-Id: test-session-001" \ -H "X-Turn-Type: main" \ -d '{ "model": "qwen3-4b", "messages": [{"role": "user", "content": "帮我写一个快速排序"}], "logprobs": true }'成功结果:返回带logprobs的响应,同时训练日志里出现sample submitted to output_queue或类似记录。如果rollout_batch_size设为 1,此时训练循环应该开始消费样本并进入 forward/backward。
6. 本篇常见错排查
6.1 训练循环卡在_drain_output_queue
现象:日志停在waiting for rollout_batch_size samples,GPU 占用正常但无进展。
原因:rollout_batch_size设得比实际并发用户量大,或者resume_submission()没被调用。检查generate_rollout_openclaw里worker.resume_submission()是否执行,以及 API Server 的submission_enabled事件是否被 set。
排查动作:把rollout_batch_size临时改成 1,发一条对话看是否触发训练。如果触发,说明是批量大小问题;如果不触发,检查_drain_output_queue的阻塞条件。
6.2 PRM 评分一直返回 0
现象:sample.reward["score"]始终是 0,GRPO advantage 全为 0,梯度不更新。
原因:PRM Judge 的 prompt 构造有问题,或者judge_samples多数投票逻辑没生效。检查_build_prm_judge_prompt()的输出是否符合预期,以及_majority_vote()是否收到 m=3 个独立结果。
排查动作:单独调用_query_prm_once(),打印原始 judge 输出。如果 judge 返回格式不匹配解析逻辑,score 会 fallback 到 0。
6.3 Combine 模式 loss 报维度不匹配
现象:combine_loss_function里combined_adv和logits维度对不上。
原因:batch["teacher_log_probs"]和batch["rollout_log_probs"]的 token 长度不一致,或者w_opd/w_rl权重没配。检查 API Server 提交样本时teacher_log_probs是否按max_new_tokens=0的 forward 结果正确填充。
排查动作:在combine_loss_function入口打印batch["advantages"].shape和batch["teacher_log_probs"].shape,确认两者在 token 维度对齐。
6.4 权重同步期间请求 503
现象:用户对话在权重更新期间收到 503。
原因:这是设计行为,pause_submission()会关闭提交,purge_record_files()清理状态。如果 503 持续时间过长,说明权重同步(mbridge)卡住。
排查动作:检查 Megatron → SGLang 的 mbridge 同步日志,确认save_interval和同步耗时。如果同步超过预期,考虑减小模型或增加同步带宽。
7. 继续深入:从架构理解到实操验证
读到这里,你应该能画出 OpenClaw-RL 的完整数据流:用户请求 → OpenClawAPIServer(生产样本)→ output_queue → AsyncRolloutWorker(收集样本)→ generate_rollout_openclaw() → RolloutManager → Slime Train Loop → Model Training。评价数据走generate()函数,奖励计算走reward_func(),两者都是模块级函数,不是 OpenClawAPIServer 的方法。
下一步建议你按这个顺序动手:先用模型对话入口验证 TaoToken 通道,再对照接入文档把settings.json配好,然后跑rollout_batch_size=1的最小训练循环,确认样本能进能出。长期编码和 Agent 调试场景,直接开 Coding Plan,避免额度中断打断你的源码阅读节奏。
架构理解到位之后,Slime 的四个扩展点就是你的操作面板。改rollout_function_path换数据来源,改custom_rm_path换奖励逻辑,改custom_loss_function_path换 advantage 合成方式。OpenClaw-RL 的工作量集中在数据采集层,Slime/Megatron 核心代码一行不用动——这也是它最值得学的地方。