:On-Policy Distillation 的配置骨架與驗(yàn)證路徑)
1. 從一次 OPD 訓(xùn)練不收斂說(shuō)起如果你正在做 Agentic RL 相關(guān)的工程實(shí)踐大概率聽(tīng)過(guò) OpenClaw-RL 這個(gè)框架。它把在線強(qiáng)化學(xué)習(xí)和 On-Policy DistillationOPD策略內(nèi)蒸餾揉在一起專門(mén)解決智能體工具調(diào)用場(chǎng)景下的訓(xùn)練問(wèn)題。但真正把倉(cāng)庫(kù)拉下來(lái)跑的時(shí)候很多人會(huì)卡在同一個(gè)地方配置文件寫(xiě)完了訓(xùn)練腳本也啟動(dòng)了loss 卻像一條死魚(yú)一樣不動(dòng)或者 advantage 全是零。我試過(guò)在本地復(fù)現(xiàn) OpenClaw-RL 的 OPD 流程前后踩了不少坑。這篇筆記不打算重復(fù)講 OPD 的數(shù)學(xué)推導(dǎo)而是聚焦一個(gè)更實(shí)際的問(wèn)題On-Policy Distillation 的配置骨架到底長(zhǎng)什么樣以及怎么用最小的驗(yàn)證動(dòng)作確認(rèn)你的 OPD 鏈路是通的。適合已經(jīng)讀過(guò) OPD 原理、準(zhǔn)備動(dòng)手跑 OpenClaw-RL 的強(qiáng)化學(xué)習(xí)工程實(shí)踐者。OpenClaw-RL 支持三種模式openclaw-rl基于二元獎(jiǎng)勵(lì)的 GRPO、openclaw-opd基于后見(jiàn)之明提示的在線策略蒸餾、openclaw-combine聯(lián)合方法。其中 OPD 是最有差別化的設(shè)計(jì)它的核心思路是學(xué)生模型先在原始上下文下生成動(dòng)作環(huán)境返回 next state 后從中提取一個(gè)簡(jiǎn)潔的 hindsight hint把 hint 拼回原始上下文形成增強(qiáng)后的 teacher context再用同一個(gè)模型在這個(gè)增強(qiáng)上下文下對(duì)原響應(yīng)做 forced scoring得到 teacher logprobs最后和 student 原來(lái)的 logprobs 做差作為 token 級(jí) advantage。這個(gè)鏈路里涉及多個(gè)組件SGLang 推理引擎、PRM judge 服務(wù)、Megatron 訓(xùn)練引擎、經(jīng)驗(yàn)回放緩沖區(qū)。任何一個(gè)環(huán)節(jié)的配置對(duì)不上OPD 的信號(hào)就斷了。下面我把配置骨架和驗(yàn)證路徑拆開(kāi)講。2. TaoToken 前置模型服務(wù)與 API Key 準(zhǔn)備在跑 OPD 之前你需要一個(gè)能穩(wěn)定提供模型推理服務(wù)的端點(diǎn)。OpenClaw-RL 的 OPD 分支里judge 評(píng)分和 teacher logprobs 查詢共享同一個(gè)_prm_url端點(diǎn)也就是說(shuō)你不需要部署兩個(gè)獨(dú)立服務(wù)但需要保證這個(gè)端點(diǎn)能同時(shí)處理打分請(qǐng)求和 logprobs 查詢。如果你本地沒(méi)有現(xiàn)成的推理集群可以用 TaoToken 提供的模型服務(wù)作為 PRM judge 和 teacher scoring 的后端。它的 API 兼容 OpenAI 格式接入成本低。具體操作先到官網(wǎng) https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注冊(cè)賬號(hào)然后在控制臺(tái)創(chuàng)建一個(gè) API Key。控制臺(tái)地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。創(chuàng)建完 Key 之后到 API Keys 頁(yè)面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 復(fù)制你的密鑰。API 的基礎(chǔ)地址是https://taotoken.net/api注意這個(gè)地址不加 UTM 參數(shù)。你可以在模型對(duì)話頁(yè)面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 先手動(dòng)測(cè)試一下模型是否能正常返回確認(rèn) Key 有效。如果你打算長(zhǎng)期跑編碼類 Agent 的訓(xùn)練任務(wù)可以了解一下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它在高頻調(diào)用場(chǎng)景下更劃算。接入文檔在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的請(qǐng)求示例和參數(shù)說(shuō)明。注意OPD 的 teacher scoring 需要對(duì)每個(gè) token 返回 logprobs不是所有模型服務(wù)都默認(rèn)開(kāi)啟這個(gè)能力。在配置_prm_url之前先用一個(gè)簡(jiǎn)單的 curl 請(qǐng)求確認(rèn)你的端點(diǎn)支持logprobs參數(shù)。3. 可復(fù)制的 config.toml 與 settings.json 配置骨架OpenClaw-RL 的配置分兩層config.toml管訓(xùn)練框架級(jí)別的參數(shù)settings.json管 OPD 模塊特有的行為。下面是我實(shí)測(cè)能跑通的骨架你可以直接復(fù)制后按自己的環(huán)境改。3.1 config.toml 訓(xùn)練框架配置[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幾個(gè)關(guān)鍵點(diǎn)解釋一下。kl_coef 0.0是因?yàn)?OPD 模式下 KL 正則被禁用advantage 本身已經(jīng)包含了 teacher 和 student 的分布差異再加 KL 懲罰會(huì)干擾信號(hào)。advantage.estimator必須設(shè)為on_policy_distillation這是觸發(fā) OPD 分支的開(kāi)關(guān)。prm_url和prm_api_key指向你的 judge 和 teacher 共享端點(diǎn)。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這個(gè)字段很重要它決定了 hint 怎么拼回原始上下文。OpenClaw-RL 默認(rèn)是把 hint 追加到最后一條用戶消息的末尾格式就是上面寫(xiě)的\n\n[users hint / instruction]\n{hint}。如果你改了格式teacher scoring 的結(jié)果會(huì)完全不一樣。drop_sample_without_hint設(shè)為true表示如果 judge 抽不出清晰的 hint這條樣本就不進(jìn)入 OPD 訓(xùn)練。這個(gè)設(shè)計(jì)是為了保證只有高質(zhì)量的 directive signal 才被用來(lái)更新策略。teacher_scoring_mode設(shè)為forced表示用 teacher context 對(duì)原響應(yīng)做強(qiáng)制打分而不是重新生成。這是 OPD 和普通蒸餾的關(guān)鍵區(qū)別。3.3 兩種 OPD 變體的配置差異OpenClaw-RL 實(shí)現(xiàn)了兩種 OPD 變體。變體 A 是標(biāo)準(zhǔn) OPDadvantage-based走 PPO 的 advantage 分支配置就是上面那套。變體 B 是 Top-K OPDreverse KL loss需要額外設(shè)置[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 保護(hù)而是直接在 K1 個(gè)類別上計(jì)算 reverse KL。它的信息傳輸效率更高每個(gè)樣本大約 200KB而標(biāo)準(zhǔn) OPD 每個(gè)樣本約 300MB。但變體 B 沒(méi)有 clip 保護(hù)訓(xùn)練穩(wěn)定性需要額外關(guān)注。4. 驗(yàn)證請(qǐng)求與成功結(jié)果確認(rèn)配置寫(xiě)完之后不要直接啟動(dòng)完整訓(xùn)練。先用一個(gè)最小驗(yàn)證腳本確認(rèn) OPD 鏈路的每個(gè)環(huán)節(jié)都能跑通。4.1 驗(yàn)證 PRM judge 端點(diǎn)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 類似“先檢查文件是否存在再編輯”說(shuō)明 judge 端點(diǎn)工作正常。4.2 驗(yàn)證 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])確認(rèn)返回結(jié)構(gòu)里有token_logprobs字段并且每個(gè) token 都有對(duì)應(yīng)的對(duì)數(shù)概率值。如果返回的是空或者報(bào)錯(cuò)說(shuō)明你的端點(diǎn)不支持echologprobs組合需要換一個(gè)支持 forced scoring 的模型服務(wù)。4.3 驗(yàn)證 advantage 計(jì)算在 OpenClaw-RL 的compute_advantages_and_returns函數(shù)里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你可以在本地用兩個(gè)小的 logprobs 數(shù)組模擬一下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 更認(rèn)可這個(gè) token訓(xùn)練會(huì)推動(dòng) student 提高它的概率負(fù) advantage 則相反。如果所有 advantage 都是零說(shuō)明 teacher 和 student 的 logprobs 完全一樣這通常意味著 hint 沒(méi)有生效或者 teacher scoring 用的上下文和 student 一樣。4.4 成功結(jié)果的判斷標(biāo)準(zhǔn)一次成功的 OPD 驗(yàn)證應(yīng)該看到第一judge 能穩(wěn)定輸出 1-3 句可執(zhí)行的 hint而不是空字符串或泛泛而談的“做得不好”。第二teacher logprobs 和 student logprobs 在關(guān)鍵 token 上有明顯差異。比如在“檢查文件”這個(gè)動(dòng)作對(duì)應(yīng)的 token 上teacher 的概率應(yīng)該顯著高于 student。第三advantage 的分布不是全零也不是全部同號(hào)。正常情況應(yīng)該有正有負(fù)均值接近零但方差不為零。第四訓(xùn)練啟動(dòng)后 loss 在頭幾十步內(nèi)有下降趨勢(shì)而不是一條水平線。5. 本篇常見(jiàn)錯(cuò)誤排查5.1 advantage 全為零最常見(jiàn)的原因有三個(gè)。一是prm_url配置的端點(diǎn)不支持logprobs參數(shù)返回的 teacher logprobs 全是默認(rèn)值。二是 hint 提取失敗drop_sample_without_hint設(shè)為false導(dǎo)致沒(méi)有 hint 的樣本也進(jìn)入訓(xùn)練teacher context 和 student context 完全一樣。三是teacher_scoring_mode設(shè)成了generate而不是forced導(dǎo)致 teacher 重新生成了響應(yīng)和原響應(yīng)的 token 對(duì)不上。排查方法在compute_advantages_and_returns里加一行日志打印teacher_log_probs[0][:10]和student_log_probs[0][:10]看兩者是否完全相同。5.2 hint 提取質(zhì)量差judge 模型如果太小或者 prompt 設(shè)計(jì)不好抽出來(lái)的 hint 可能是“你需要改進(jìn)”這種沒(méi)有方向性的廢話。解決辦法是調(diào)整 judge 的 system prompt明確要求輸出可執(zhí)行的指令比如“先做 X 再做 Y”或“不要包含 Z”。另外judge_vote_count設(shè)為 3 可以通過(guò)多數(shù)投票過(guò)濾掉低質(zhì)量 hint。5.3 teacher logprobs 長(zhǎng)度對(duì)不上OPD 要求 teacher logprobs 和 student logprobs 在 response 部分逐 token 對(duì)齊。如果 teacher scoring 時(shí) max_tokens 設(shè)得太小或者 response_length 計(jì)算有誤就會(huì)出現(xiàn)長(zhǎng)度不匹配。代碼里用t_log_prob[-response_length:]做截?cái)嗟绻?teacher 返回的序列比 response 短截?cái)嗪箝L(zhǎng)度仍然不對(duì)。排查方法打印len(teacher_log_prob)和response_length確認(rèn) teacher 返回的 logprobs 長(zhǎng)度大于等于 response_length。5.4 訓(xùn)練啟動(dòng)后 OOMOPD 需要同時(shí)維護(hù) student 和 teacher 兩份 logprobs顯存占用比普通 RL 高。如果max_seq_len設(shè)為 8192 且rollout_batch_size設(shè)為 32在單卡上很容易 OOM。建議先把rollout_batch_size降到 8max_seq_len降到 4096跑通后再逐步調(diào)大。5.5 Top-K OPD 的 custom loss 報(bào)錯(cuò)變體 B 需要指定custom_loss_function_path如果路徑不對(duì)或者文件里沒(méi)有正確導(dǎo)出 loss 函數(shù)訓(xùn)練會(huì)直接報(bào)ImportError。確認(rèn)你的topk_reverse_kl.py里定義了compute_loss函數(shù)并且簽名和框架期望的一致。6. 接入文檔與后續(xù)調(diào)試路徑OPD 的配置骨架搭好之后下一步是把它接到真實(shí)的 Agent 交互循環(huán)里。OpenClaw-RL 的 passive rollout 模式會(huì)攔截 SGLang 的 API 請(qǐng)求把用戶對(duì)話和工具返回結(jié)果作為 next-state signal 收集起來(lái)。你需要確保session_timeout_sec和max_turns_per_session設(shè)置合理否則長(zhǎng)會(huì)話會(huì)被截?cái)鄅int 提取拿不到完整的上下文。如果你在接入過(guò)程中遇到 API 報(bào)錯(cuò)或者 logprobs 返回格式不對(duì)可以先到接入文檔 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 核對(duì)請(qǐng)求參數(shù)。模型對(duì)話頁(yè)面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 可以用來(lái)快速測(cè)試 judge prompt 的效果不用每次都跑完整訓(xùn)練腳本。對(duì)于需要長(zhǎng)期跑 OPD 訓(xùn)練的任務(wù)建議把 API Key 和端點(diǎn)配置放在環(huán)境變量里不要硬編碼在 config.toml 中。這樣切換環(huán)境時(shí)只需要改環(huán)境變量不用動(dòng)配置文件。最后說(shuō)一個(gè)實(shí)際調(diào)試中的小技巧在 OPD 訓(xùn)練的前 100 步把log_teacher_logprobs設(shè)為true把每個(gè)樣本的 teacher 和 student logprobs 差值保存下來(lái)。跑完之后統(tǒng)計(jì)一下 advantage 的分布如果發(fā)現(xiàn)某些 token 的 advantage 絕對(duì)值特別大比如超過(guò) 10說(shuō)明 teacher 和 student 在這些 token 上的分歧過(guò)大可能需要調(diào)低學(xué)習(xí)率或者加 advantage clip。這個(gè)統(tǒng)計(jì)比看 loss 曲線更能反映 OPD 信號(hào)的質(zhì)量。