OpenClaw-RL 源码阅读笔记(4):系统架构拆解与 TaoToken 配置骨架
1. 从一次“跑不通”的架构阅读说起OpenClaw-RL 是一个面向智能体工具使用场景的在线强化学习框架它把策略服务、环境托管、奖励评判、策略训练四个异步解耦的循环拼在一起让模型一边给真实用户提供服务一边从刚刚发生的交互里在线学习。如果你正在读它的源码想搞清楚 Agentic RL、OPDOn-Policy Distillation在线策略蒸馏这些模块到底怎么分层、数据怎么流动那这篇笔记就是接着上一节往下拆的。我这次的目标很具体把系统架构的模块调用链核对清楚把 OPD 数据流跑通同时给出一份可以直接复制的config.toml/settings.json骨架以及用 TaoToken 统一 Key 接入推理与评判服务的配置。很多人第一次读 OpenClaw-RL 会卡在两个地方一是分不清openclaw-rl、openclaw-opd、openclaw-combine三个目录谁调用谁二是看到output_queue、_pending_records、teacher_log_probs这些字段就晕。其实它的核心设计只有一句话一套统一的 PPO 框架 三种不同的 advantage 注入方式。Binary RL 走 Slime 内置 GRPOOPD 靠teacher_log_probs字段Combine 用自定义 loss。把这句话记住再看代码就顺了。下面我按“原问题 → 前置准备 → 可复制配置 → 验证请求 → 错排查 → 接入”的顺序展开每一步都给出能直接跑的命令和参数。2. 原问题与架构分层四个异步循环到底怎么切2.1 四大组件与 GPU 分配OpenClaw-RL 的系统设计是四个异步解耦的循环——policy serving、environment hosting、reward judging、policy training 同时运行、互不阻塞。默认 8 卡节点的分配是这样的组件GPU实际进程核心代码Policy TrainingGPU 0-3Megatron ActorTP4slime/openclaw_rollout.pyPolicy ServingGPU 4-5SGLang Rollout FastAPI Proxyopenclaw_api_server.pyReward JudgingGPU 6-7SGLang PRM/Judge同一个openclaw_api_server.py中的评分逻辑Environment无 GPUOpenClaw App 用户openclaw/(TS app)这里有个容易忽略的点三个角色Actor / Rollout / Judge用的是同一个模型Qwen3-4B但只有 Actor 被训练更新。Rollout 是 Actor 权重的副本定期同步Judge 固定不变只做 forward pass。2.2 模块调用链核对读源码时我建议按这条链核对一遍确认自己没看错层级Slime Train Loop (train_async.py) - rollout_manager.generate.remote() - generate_rollout_openclaw() # openclaw_rollout.py - AsyncRolloutWorker.__init__() - OpenClawAPIServer(args, output_queue, submission_enabled) - sglang_chat_url fhttp://{args.sglang_router_ip}:{args.sglang_router_port}/...关键点在于generate_rollout_openclaw()不是主动生成数据而是resume_submission()打开闸门等真实用户对话把output_queue填满rollout_batch_size个样本再pause_submission()关闭。这就是 OpenClaw-RL 和标准 Slime 最大的区别——rollout 是被动等待不是主动采样。2.3 OPD 数据流全景OPD 的 advantage 计算是 per-token 的A_t teacher_log_probs[t] - rollout_log_probs[t]而 Binary RL 是标量广播A_t reward (broadcast 到所有 token)Combine 则把两者加权合并combined_adv w_opd * (teacher_lp - rollout_lp) w_rl * grpo_adv三种方法的注入路径不同Binary RL 用 Slime 内置 GRPOOPD 靠--advantage-estimator on_policy_distillation触发loss.py内部的分支Combine 是唯一需要--custom-loss-function-path的。3. TaoToken 前置统一 Key 接入推理与评判在复现或二次开发时你大概率不会一开始就上 8 卡。更现实的做法是先用云端 API 把 OPD 数据流跑通验证teacher_log_probs和rollout_log_probs的差值计算是否正确再迁移到本地 Megatron。这时候一个统一的 Key 能省掉很多切换成本。TaoToken 提供 OpenAI 兼容的接口模型对话、Coding Plan、API Keys 管理都在同一个控制台里。我实测下来把它作为 SGLang 的替代推理后端来验证数据流是可行的——你只需要把sglang_chat_url指向 TaoToken 的 API 地址其余的数据结构rollout_log_probs、teacher_log_probs保持不变。具体操作路径先在控制台创建 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite模型对话调试用https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite长期编码 / Agent 场景走 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteAPI 基础地址是https://taotoken.net/api不加 UTM。注意这里只是把它当作推理与评判服务的统一入口不涉及任何网络层操作。4. 可复制配置config.toml 与 settings.json 骨架4.1 config.toml 骨架下面这份config.toml是我按 OpenClaw-RL 的模块分层整理的字段名对齐源码里的args你可以直接改路径和端口[model] model_path Qwen/Qwen3-4B tokenizer_path Qwen/Qwen3-4B dtype bfloat16 [gpu] actor_gpus 4 rollout_gpus 2 prm_gpus 2 actor_tp 4 rollout_tp 2 prm_tp 2 [serving] # 对外暴露给 OpenClaw App 的端口 api_port 30000 # Slime 内部动态分配的 router 端口留空由 Slime 填充 sglang_router_ip sglang_router_port prm_router_ip prm_router_port [rollout] rollout_batch_size 16 # 被动等待模式禁用全局数据集 disable_rollout_global_dataset true rollout_function_path openclaw_rollout.generate_rollout_openclaw custom_generate_function_path openclaw_api_server.generate custom_rm_path openclaw_api_server.reward_func [advantage] # 三选一grpo / on_policy_distillation / combine estimator on_policy_distillation disable_rewards_normalization true [combine] # 仅 estimator combine 时生效 w_opd 1.0 w_rl 1.0 custom_loss_function_path combine_loss.combine_loss_function [ppo] lr 1e-5 beta1 0.9 beta2 0.98 clip_eps 0.2 clip_eps_high 0.28 kl_coef 0.001 entropy_coef 0.0 [taotoken] # 统一 Key 接入用于验证 OPD 数据流 base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} chat_model qwen3-4b judge_model qwen3-4b4.2 settings.json 骨架OpenClaw App 侧TypeScript的settings.json负责把用户请求转发到 FastAPI Proxy并带上会话标识{ api: { baseUrl: http://127.0.0.1:30000/v1, apiKey: ${SGLANG_API_KEY}, model: qwen3-4b }, session: { headerSessionId: X-Session-Id, headerTurnType: X-Turn-Type, headerSessionDone: X-Session-Done, defaultTurnType: main }, taotoken: { baseUrl: https://taotoken.net/api, apiKey: ${TAOTOKEN_API_KEY}, fallbackModel: qwen3-4b }, logging: { level: info, recordTurns: true } }两个文件里的${TAOTOKEN_API_KEY}从环境变量读取不要硬编码。X-Turn-Type设为main才会产生训练样本side会被完整转发但不截取。5. 验证请求模块调用链核对与 OPD 数据流跑通5.1 启动与调用链核对先按脚本启动观察 Slime 是否把动态端口写回argsexport TAOTOKEN_API_KEY你的Key export SGLANG_API_KEYlocal-dev-key ray job submit --addresshttp://127.0.0.1:8265 \ --runtime-env-json${RUNTIME_ENV_JSON} \ --python3 train_async.py \ --actor-num-nodes 1 \ --actor-num-gpus-per-node 4 \ --rollout-num-gpus 2 \ --rollout-function-path openclaw_rollout.generate_rollout_openclaw \ --custom-generate-function-path openclaw_api_server.generate \ --custom-rm-path openclaw_api_server.reward_func \ --advantage-estimator on_policy_distillation \ --disable-rollout-global-dataset启动后核对三件事args.sglang_router_port是否被填入、OpenClawAPIServer是否监听 30000、AsyncRolloutWorker是否进入resume_submission状态。这三步对应调用链的三个关键节点。5.2 发一条验证请求用 curl 模拟一次main类型的对话触发样本生成curl -X POST http://127.0.0.1:30000/v1/chat/completions \ -H Authorization: Bearer local-dev-key \ -H Content-Type: application/json \ -H X-Session-Id: sess_verify_001 \ -H X-Turn-Type: main \ -H X-Session-Done: false \ -d { model: qwen3-4b, messages: [ {role: user, content: 帮我写个快速排序算法} ], stream: false }成功的话你会看到响应里带response_text同时后台_pending_turn_data[session_id]里存下了prompt_ids、response_ids、rollout_log_probs。再发一条同 session 的请求messages[-1]作为 next_state就会触发_flush_pending_record和 PRM 异步评分。5.3 验证 OPD 的 teacher_log_probsOPD 的关键是 teacher 的 per-token log-probs。用 TaoToken 作为 teacher 后端时把max_new_tokens0做纯 forwardimport httpx resp httpx.post( https://taotoken.net/api/v1/chat/completions, headers{Authorization: fBearer {TAOTOKEN_API_KEY}}, json{ model: qwen3-4b, messages: enhanced_messages, # 含 hint 的增强消息 max_tokens: 0, logprobs: True, top_logprobs: 1, }, timeout60, ) teacher_log_probs resp.json()[choices][0][logprobs][content]拿到teacher_log_probs后和rollout_log_probs逐 token 相减得到 per-token advantage。如果所有 token 的差值都是正数说明 teacher 比 student 更自信符合 OPD 的预期方向。6. 本篇常见错排查6.1 报错KeyError: sglang_router_port这个报错通常出现在OpenClawAPIServer初始化时。原因是 Slime 还没完成_start_router的动态端口分配args里对应字段是空字符串。排查顺序先确认RolloutManager.__init__是否执行到_start_router再检查find_available_port()是否被防火墙拦截。解决方法是把config.toml里的sglang_router_port留空让 Slime 自己填不要手动指定。6.2output_queue一直不填满generate_rollout_openclaw()会阻塞等待rollout_batch_size个样本。如果队列一直不满先看X-Turn-Type是不是side——side请求不产生训练样本。再看submission_enabled是否被pause_submission()关掉了权重同步期间会临时关闭同步完成后要resume_submission()。6.3 OPD advantage 全为 0如果teacher_log_probs和rollout_log_probs完全相等advantage 就是 0。常见原因是 teacher 和 student 用了同一个权重副本或者 hint 没有正确注入到enhanced_messages。检查_select_best_hint是否返回了有效 hint长度大于 10 字符以及 hint 是否被追加到最后一条 user 消息末尾。6.4 Combine 的 loss 不下降Combine 是唯一需要--custom-loss-function-path的如果忘了加这个参数Slime 会走内置 policy losscombined_adv根本不会被使用。另外检查w_opd和w_rl的权重两个都是 1.0 时combined advantage 的量级会比纯 GRPO 大可能需要调小学习率。6.5 权重同步期间请求 503这是设计行为不是 bug。权重同步时submission_enabled被清除FastAPI Proxy 返回 503OpenClaw App 需要做重试。如果你在验证阶段频繁看到 503可以把save_interval调大减少同步次数。7. 语义一致接入把验证过的配置固化下来架构核对和数据流跑通之后下一步是把验证过的配置固化到实际开发流程里。如果你主要做排障和接入建议先把 API Keys 和接入文档过一遍确认 Key 的权限和配额API Keys 管理https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你要验证不同模型在 OPD 里的 teacher 效果用模型对话页面快速切换对比模型对话https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你打算长期跑编码类 Agent 的在线 RLCoding Plan 更适合作为稳定的推理后端Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewriteClaudeCodeAnthropic 相关的接入配置可以参考ClaudeCodeAnthropichttps://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite最后提醒一个我踩过的坑config.toml里的estimator字段和启动脚本里的--advantage-estimator必须一致否则 Slime 会按 CLI 参数走配置文件里的 Combine 权重不会生效。核对调用链时把这两个值打印出来对比一下能省掉很多排查时间。