1. 从一次 OPD 训练不收敛说起如果你正在做 Agentic RL 相关的工程实践大概率听过 OpenClaw-RL 这个框架。它把在线强化学习和 On-Policy DistillationOPD策略内蒸馏揉在一起专门解决智能体工具调用场景下的训练问题。但真正把仓库拉下来跑的时候很多人会卡在同一个地方配置文件写完了训练脚本也启动了loss 却像一条死鱼一样不动或者 advantage 全是零。我试过在本地复现 OpenClaw-RL 的 OPD 流程前后踩了不少坑。这篇笔记不打算重复讲 OPD 的数学推导而是聚焦一个更实际的问题On-Policy Distillation 的配置骨架到底长什么样以及怎么用最小的验证动作确认你的 OPD 链路是通的。适合已经读过 OPD 原理、准备动手跑 OpenClaw-RL 的强化学习工程实践者。OpenClaw-RL 支持三种模式openclaw-rl基于二元奖励的 GRPO、openclaw-opd基于后见之明提示的在线策略蒸馏、openclaw-combine联合方法。其中 OPD 是最有差别化的设计它的核心思路是学生模型先在原始上下文下生成动作环境返回 next state 后从中提取一个简洁的 hindsight hint把 hint 拼回原始上下文形成增强后的 teacher context再用同一个模型在这个增强上下文下对原响应做 forced scoring得到 teacher logprobs最后和 student 原来的 logprobs 做差作为 token 级 advantage。这个链路里涉及多个组件SGLang 推理引擎、PRM judge 服务、Megatron 训练引擎、经验回放缓冲区。任何一个环节的配置对不上OPD 的信号就断了。下面我把配置骨架和验证路径拆开讲。2. TaoToken 前置模型服务与 API Key 准备在跑 OPD 之前你需要一个能稳定提供模型推理服务的端点。OpenClaw-RL 的 OPD 分支里judge 评分和 teacher logprobs 查询共享同一个_prm_url端点也就是说你不需要部署两个独立服务但需要保证这个端点能同时处理打分请求和 logprobs 查询。如果你本地没有现成的推理集群可以用 TaoToken 提供的模型服务作为 PRM judge 和 teacher scoring 的后端。它的 API 兼容 OpenAI 格式接入成本低。具体操作先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后在控制台创建一个 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完 Key 之后到 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 复制你的密钥。API 的基础地址是https://taotoken.net/api注意这个地址不加 UTM 参数。你可以在模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 先手动测试一下模型是否能正常返回确认 Key 有效。如果你打算长期跑编码类 Agent 的训练任务可以了解一下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它在高频调用场景下更划算。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的请求示例和参数说明。注意OPD 的 teacher scoring 需要对每个 token 返回 logprobs不是所有模型服务都默认开启这个能力。在配置_prm_url之前先用一个简单的 curl 请求确认你的端点支持logprobs参数。3. 可复制的 config.toml 与 settings.json 配置骨架OpenClaw-RL 的配置分两层config.toml管训练框架级别的参数settings.json管 OPD 模块特有的行为。下面是我实测能跑通的骨架你可以直接复制后按自己的环境改。3.1 config.toml 训练框架配置[model] student_model_path /path/to/your/student-model tokenizer_path /path/to/your/tokenizer dtype bfloat16 max_seq_len 8192 [rollout] engine sglang sglang_host 127.0.0.1 sglang_port 30000 rollout_batch_size 32 max_new_tokens 2048 temperature 0.7 top_p 0.95 [training] engine megatron train_batch_size 16 micro_batch_size 2 learning_rate 1e-6 ppo_clip_epsilon 0.2 kl_coef 0.0 num_epochs 1 save_interval 100 [advantage] estimator on_policy_distillation gamma 1.0 lam 1.0 [prm] prm_url https://taotoken.net/api prm_api_key sk-your-key-here judge_model your-judge-model-name teacher_model your-teacher-model-name hint_max_tokens 128 hint_temperature 0.3几个关键点解释一下。kl_coef 0.0是因为 OPD 模式下 KL 正则被禁用advantage 本身已经包含了 teacher 和 student 的分布差异再加 KL 惩罚会干扰信号。advantage.estimator必须设为on_policy_distillation这是触发 OPD 分支的开关。prm_url和prm_api_key指向你的 judge 和 teacher 共享端点。3.2 settings.json OPD 模块配置{ opd: { enable_hint_extraction: true, hint_format: \n\n[users hint / instruction]\n{hint}, min_hint_length: 10, max_hint_length: 256, judge_vote_count: 3, judge_temperature: 0.0, teacher_scoring_mode: forced, advantage_clip: 5.0, drop_sample_without_hint: true, log_teacher_logprobs: true }, rollout: { passive_mode: true, session_timeout_sec: 300, max_turns_per_session: 10 }, training: { use_custom_loss: false, custom_loss_path: null } }hint_format这个字段很重要它决定了 hint 怎么拼回原始上下文。OpenClaw-RL 默认是把 hint 追加到最后一条用户消息的末尾格式就是上面写的\n\n[users hint / instruction]\n{hint}。如果你改了格式teacher scoring 的结果会完全不一样。drop_sample_without_hint设为true表示如果 judge 抽不出清晰的 hint这条样本就不进入 OPD 训练。这个设计是为了保证只有高质量的 directive signal 才被用来更新策略。teacher_scoring_mode设为forced表示用 teacher context 对原响应做强制打分而不是重新生成。这是 OPD 和普通蒸馏的关键区别。3.3 两种 OPD 变体的配置差异OpenClaw-RL 实现了两种 OPD 变体。变体 A 是标准 OPDadvantage-based走 PPO 的 advantage 分支配置就是上面那套。变体 B 是 Top-K OPDreverse KL loss需要额外设置[advantage] estimator grpo custom_loss_function_path openclaw_opd/losses/topk_reverse_kl.py [topk_opd] top_k 64 use_teacher_topk true变体 B 不使用 PPO clip 保护而是直接在 K1 个类别上计算 reverse KL。它的信息传输效率更高每个样本大约 200KB而标准 OPD 每个样本约 300MB。但变体 B 没有 clip 保护训练稳定性需要额外关注。4. 验证请求与成功结果确认配置写完之后不要直接启动完整训练。先用一个最小验证脚本确认 OPD 链路的每个环节都能跑通。4.1 验证 PRM judge 端点import requests import json url https://taotoken.net/api/v1/chat/completions headers { Authorization: Bearer sk-your-key-here, Content-Type: application/json } payload { model: your-judge-model-name, messages: [ {role: system, content: You are a judge. Evaluate the assistants last action and extract a hint if there is corrective information.}, {role: user, content: The assistant tried to edit a file but forgot to check if it exists first. The tool returned FileNotFoundError.} ], temperature: 0.0, max_tokens: 128 } resp requests.post(url, headersheaders, jsonpayload) print(resp.status_code) print(resp.json()[choices][0][message][content])如果返回的 hint 类似“先检查文件是否存在再编辑”说明 judge 端点工作正常。4.2 验证 teacher logprobs 查询payload { model: your-teacher-model-name, messages: [ {role: user, content: Write a function to read a file.\n\n[users hint / instruction]\n先检查文件是否存在再编辑} ], logprobs: True, top_logprobs: 5, max_tokens: 1, echo: True } resp requests.post(url, headersheaders, jsonpayload) logprobs resp.json()[choices][0][logprobs] print(json.dumps(logprobs, indent2)[:500])确认返回结构里有token_logprobs字段并且每个 token 都有对应的对数概率值。如果返回的是空或者报错说明你的端点不支持echologprobs组合需要换一个支持 forced scoring 的模型服务。4.3 验证 advantage 计算在 OpenClaw-RL 的compute_advantages_and_returns函数里OPD 分支的核心逻辑是elif args.advantage_estimator on_policy_distillation: student_log_probs log_probs teacher_log_probs rollout_data.get(teacher_log_probs) response_lengths rollout_data.get(response_lengths) device student_log_probs[0].device teacher_log_probs [t_log_prob.to(devicedevice) for t_log_prob in teacher_log_probs] teacher_log_probs [ t_log_prob[-response_length:] for t_log_prob, response_length in zip(teacher_log_probs, response_lengths, strictFalse) ] advantages [ teacher_log_prob - student_log_prob for teacher_log_prob, student_log_prob in zip(teacher_log_probs, student_log_probs, strictFalse) ] returns advantages你可以在本地用两个小的 logprobs 数组模拟一下import torch student_lp torch.tensor([-1.2, -0.8, -2.1, -0.5]) teacher_lp torch.tensor([-0.9, -1.5, -1.8, -0.3]) advantages teacher_lp - student_lp print(advantages) # tensor([ 0.3000, -0.7000, 0.3000, 0.2000])正 advantage 表示 teacher 比 student 更认可这个 token训练会推动 student 提高它的概率负 advantage 则相反。如果所有 advantage 都是零说明 teacher 和 student 的 logprobs 完全一样这通常意味着 hint 没有生效或者 teacher scoring 用的上下文和 student 一样。4.4 成功结果的判断标准一次成功的 OPD 验证应该看到第一judge 能稳定输出 1-3 句可执行的 hint而不是空字符串或泛泛而谈的“做得不好”。第二teacher logprobs 和 student logprobs 在关键 token 上有明显差异。比如在“检查文件”这个动作对应的 token 上teacher 的概率应该显著高于 student。第三advantage 的分布不是全零也不是全部同号。正常情况应该有正有负均值接近零但方差不为零。第四训练启动后 loss 在头几十步内有下降趋势而不是一条水平线。5. 本篇常见错误排查5.1 advantage 全为零最常见的原因有三个。一是prm_url配置的端点不支持logprobs参数返回的 teacher logprobs 全是默认值。二是 hint 提取失败drop_sample_without_hint设为false导致没有 hint 的样本也进入训练teacher context 和 student context 完全一样。三是teacher_scoring_mode设成了generate而不是forced导致 teacher 重新生成了响应和原响应的 token 对不上。排查方法在compute_advantages_and_returns里加一行日志打印teacher_log_probs[0][:10]和student_log_probs[0][:10]看两者是否完全相同。5.2 hint 提取质量差judge 模型如果太小或者 prompt 设计不好抽出来的 hint 可能是“你需要改进”这种没有方向性的废话。解决办法是调整 judge 的 system prompt明确要求输出可执行的指令比如“先做 X 再做 Y”或“不要包含 Z”。另外judge_vote_count设为 3 可以通过多数投票过滤掉低质量 hint。5.3 teacher logprobs 长度对不上OPD 要求 teacher logprobs 和 student logprobs 在 response 部分逐 token 对齐。如果 teacher scoring 时 max_tokens 设得太小或者 response_length 计算有误就会出现长度不匹配。代码里用t_log_prob[-response_length:]做截断但如果 teacher 返回的序列比 response 短截断后长度仍然不对。排查方法打印len(teacher_log_prob)和response_length确认 teacher 返回的 logprobs 长度大于等于 response_length。5.4 训练启动后 OOMOPD 需要同时维护 student 和 teacher 两份 logprobs显存占用比普通 RL 高。如果max_seq_len设为 8192 且rollout_batch_size设为 32在单卡上很容易 OOM。建议先把rollout_batch_size降到 8max_seq_len降到 4096跑通后再逐步调大。5.5 Top-K OPD 的 custom loss 报错变体 B 需要指定custom_loss_function_path如果路径不对或者文件里没有正确导出 loss 函数训练会直接报ImportError。确认你的topk_reverse_kl.py里定义了compute_loss函数并且签名和框架期望的一致。6. 接入文档与后续调试路径OPD 的配置骨架搭好之后下一步是把它接到真实的 Agent 交互循环里。OpenClaw-RL 的 passive rollout 模式会拦截 SGLang 的 API 请求把用户对话和工具返回结果作为 next-state signal 收集起来。你需要确保session_timeout_sec和max_turns_per_session设置合理否则长会话会被截断hint 提取拿不到完整的上下文。如果你在接入过程中遇到 API 报错或者 logprobs 返回格式不对可以先到接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 核对请求参数。模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 可以用来快速测试 judge prompt 的效果不用每次都跑完整训练脚本。对于需要长期跑 OPD 训练的任务建议把 API Key 和端点配置放在环境变量里不要硬编码在 config.toml 中。这样切换环境时只需要改环境变量不用动配置文件。最后说一个实际调试中的小技巧在 OPD 训练的前 100 步把log_teacher_logprobs设为true把每个样本的 teacher 和 student logprobs 差值保存下来。跑完之后统计一下 advantage 的分布如果发现某些 token 的 advantage 绝对值特别大比如超过 10说明 teacher 和 student 在这些 token 上的分歧过大可能需要调低学习率或者加 advantage clip。这个统计比看 loss 曲线更能反映 OPD 信号的质量。
