作者:互联网 时间: 2026-07-23 08:50:09
0x00 概要
0x01 基础知识
0x02 OpenClaw-RL 的PRM
0x03 PRM 流程
0x04 显式PRM vs OPD teacher log-prob
0x05 显式PRM+OPD的理论结合方案
0xFF 参考
本系列的目的是:借着对 OpenClaw-RL 源码的学习,来梳理强化学习的一些相关概念和思想。所以,会有一些基础知识、扩展和发散,OpenClaw-RL 只是一个切入点。而且,因为整篇系列是一个整体,所以有些概念的解读/学习会在不同的文章中出现,还请大家谅解。
OpenClaw-RL 是一个用于在线强化学习(Online RL)的框架,专门针对智能体工具使用场景。它通过从环境反馈中提取过程奖励信号来训练语言模型,支持三种主要模式:
framework
OpenClaw-RL使用了 PRM,但它不是传统意义上的 "Process Reward Model"。OpenClaw 的代码中叫 "PRM",但实际是 "LLM Judge" 或更像是 "Outcome Reward Model (ORM)"。
外部环境→AgentServing→SGLang→用户响应↓RolloutCollection→样本构建↓PRM/JudgeEvaluation→奖励生成↓PolicyTraining←训练样本+奖励↓新策略模型→AgentServing(更新)
本篇我们来仔细解读下。
不用PRM时,我们来看看ORM(Outcome Reward Model)的效果,具体如下:
step1step2step3step4step5(终止)0000r=+1
当反向传播时:
模型完全不知道step1-4 做得好不好
PRM本质:一个函数r(s_t,a_t) → 当前步骤的即时质量分数。PRM的解决思路是:把稀疏信号变成密集信号引入PRM
step1step2step3step4step5(终止)r1r2r3r4r5
PRM在每一步评估:"这一步做得对不对?",于是 → episode内每步都有梯度信号 → dense reward
数学PRM(如Math-Shepherd),对推导过程每一步打分:
Web Agent PRM:
训练数据构建方式(以数学题为例):
方式1:Human Annotation
方式2:Monte Carlo采样(无监督):
理论依据:P_correct(step_t)= V*(s_t)=E[最终成功 I 到达s_t]
问题1:PRM本身需要训练数据
问题2:PRM的分布偏移
问题3:PRM可能被“黑"(Reward Hacking2.0)
ORM(稀疏):
R_episode=r_terminal(仅episode末端)advantage_t=E[R_episode|s_t,a_t]-V(s_t)=很难估计(需要大量rollout才能统计期望)
PRM(密集):
R_step=r_PRM(s_t,a_t)(每步即时分)
可以选择:
PRM VS 环境奖励:PRM是人工构造的评估机制,环境奖励是自然产生的反馈信号。
PRM(过程奖励模型)定义
环境奖励定义
在MDP 形式化中:Environment=(S,A,T,R,γ),其中 R 是 reward function,通常被视为 environment 的一部分。所以从纯理论角度,Reward Model也可以算 environment的组成部分。
但在现代LLM RL的工程实践中:Environment(信息来自外部)和Reward Model(独立计算的评估)在系统架构上被明确分离原因如下:
结论:在OpenClaw-RL 的架构中,PRM/Judge 明确属于Reward.Judging。它使用了Environment(用户)产生的 next_state 作为评估依据,但评分计算本身是一个独立的Reward Judging 过程。
OpenClaw 的代码中叫 "PRM",但实际是 "LLM Judge" 或 "Outcome Reward Model (ORM)"。
| 属性 | 传统PRM | OpenClaw "PRM" |
|---|---|---|
| 类型 | 训练好的 reward model | Zero-shot LLM judge |
| 粒度 | Step-level (每步) | Response-level(整体) |
| 输出 | 连续分数 [0,1] | 离散 {-1, 0, +1} |
| 训练 | 需要标注+训练 | 不需要训练 |
| 评分依据 | 学到的特征 | Prompt 指导 |
OpenClawRL不直接使用传统意义上的环境奖励,而是主要使用PRM,将环境反馈作为 PRM评估的输入。PRM/Judge 属于 Reward Judging, 不是 Environment。
这种设计体现了OpenClaw-RL的解耦思想:
通过PRM机制,OpenClaw-RL能够:
在 OpenClaw-RL 的四个阶段中,PRM/Judge 属于 Reward Judging, 不是 Environment
=========================================================================GPU4-5:PolicyServing(SGLang)←Environment→为真实用户提供对话服务Environment=用户提供的状态转移→接收用户消息,生成模型回复→捕获next_state(下一条用户消息)=========================================================================GPU6-7:RewardJudging(SGLangPRM)←RewardJudging→接收完成的对话turn独立计算reward,不参与状态转移→调用LLMJudge评分×3(多数投票)→输出+1/0/-1,设置loss_mask=========================================================================GPU0-3:PolicyTraining(MegatronActor)→接收带reward的sample,更新权重
传统 PRM (Process Reward Model):
收集人类偏好数据 (chosen/rejected pairs)
训练一个 reward model:R = f_Φ(prompt, response)
对推理过程的每一步打分 (step-level reward)
需要标注数据训练
固定Φ,用 R 指导 policy 训练
特点:
OpenClaw 的 "PRM":
本质是 LLM Judge,用同族模型 (Qwen3) 做 zero-shot 评分
不需要训练,用自然语言 prompt 描述评分标准,用 prompt 指导评分,对整个回答打分 (response-level reward)
输出boxed{1}/boxed{0}/boxed{-1}
majority vote (m=3)降噪
特点:
OpenClaw-RL采用“环境反馈 → PRM评估 → 奖励信号"的间接模式,虽然不直接使用环境奖励,但项目巧妙地利用环境反馈作为PRM评估的依据:
response=sglang_inference(prompt)#主策略推理next_state=get_next_user_input()#获取环境反馈prm_score=prm_evaluate(response,next_state)#PRM评估reward=prm_score#间接环境奖励
我们接下来一一分析。先来看看为何不用环境反馈。
这两种情况都被视为助手所处环境的反馈:
用户反馈:用户对助手响应的直接回应,如“谢谢”、“不对,我要的是.“、“再试一次“等
环境信号:用户的反应直接反映了助手行为的好坏,是评估助手表现的重要指标
工具返回值:当助手调用工具时,工具的执行结果成为“下一个状态”
关键说明:"This content was NOT available before the assistant's action -it exists BECA USE the assistant called the tool"
成功判断:"A successful,non-error tool output means the assistant's action worked correctly and should be scored positively"
环境信号:
关键区分: Environment 提供状态, Reward Judging 提供评分
OpenClaw 的 Environment(真实用户):
OpenClaw 的 Reward Judging (LLM Judge):
通用性优势
语义理解能力
实现复杂度平衡
这种“环境反馈→PRM评估→奖励信号“的间接模式,是OpenClaw-RL在通用性和实用性之间找到的最佳平衡点。
过程奖励模型(PRM)会根据这些“下一个状态“来判断助手的响应是否成功实现了用户意图。
在OpenClaw-RL框架中,“下一个状态“是指来自用户(user)或工具(tool)的反馈,它们共同构成了助手所处的“环境”。在_build_prm_judge_prompt()函数中,明确区分了两种类型的"下一个状态":
过程奖励模型(PRM)会根据这些“下一个状态“来判断助手的响应是否成功实现了用户意图:
#openclaw-rl/openclaw_api_server.py:_build_prm_judge_prompt#Judge的评分依据包含了next_state(下一条用户消息)#实际函数签名:_build_prm_judge_prompt(response_text,next_state_text,next_state_role)#它构造结构化systemprompt内含评分规则,不接收conversation_history参数。msgs=_build_prm_judge_prompt(response_text,ns_text,ns_role)#next_state是来自Environment的真实信号
但是,此处有一个容易混淆的地方:人们会误认为next_state 用作 reward 的来源
OpenClaw的PRM评分中,next_state起了关键作用:这造成了一定的混淆,但本质上:
类比:法庭判决用了犯罪现场的证据(来自“现实世界"),但做出判决的是法官(reward judging),而不是现实世界本身。
分支说明(PRM扮演的角色不同)
Binary RL 分支
OPD 分支
两条分支都通过 self._prm_url(同一个 SGLang Judge 服务;部署在GPU6-7,与actor/rollout解耦)调用PRM 模型。该模型本质是与策略同源的零样本LLM judge,不是单独训练的reward model。区别仅在调用方式。
具体参见如下:
10-分支说明
同一个 SGLang 服务(GPU 6-7),不同 prompt,不同信号路径。
如 1.4 小结所述,OpenClaw 的"PRM"名义上叫 Process Reward Model,实际语义是"Outcome Reward Model for each turn"——它对整条 response 的整体质量打分(+1/0/-1),并不对 response 内部哪个句子/词有贡献给分。
OPD 的 teacher log-prob 实际上是一种"隐式 PRM":OPD 的 per-token advantage = teacher_lp[t] - rollout_lp[t],相当于"在 hint 的指导下,teacher 对第 t 个 token 的评分"。这在 token 粒度上实现了——正确推导方向的 token → teacher 高概率 → advantage 大;错误 token → teacher 低概率 → advantage 小/负。
换句话说,teacher log-prob 是一种软版的 Process Reward,在 token 级别而非 step 级别。因此 OPD 绕过了"如何训练 PRM"的难题——teacher 的 log-prob 本身就是一个随时可用的过程级信号。
| 对比项 | Binary RL 用 PRM | OPD 用 PRM |
|---|---|---|
| Prompt | _build_prm_judge_prompt | _build_hint_judge_messages(hint 生成 prompt) |
| PRM 输出直接是训练信号? | ✅ 是(±1 即 reward) | ❌ 否(hint 是中间产物,要再过 teacher forward pass) |
| 输出格式 | boxed{±1/0} | boxed{1} + [HINT_START]...[HINT_END] |
| 信号进入训练的方式 | ±1 score → reward→ GRPO advantage | hint text → 注入 user message → teacher forward pass → log-probs差异 |
| 信号的信息量 | 1 bit (±1) per response | K bits per token (teacher 分布) |
| PRM 失效会怎样? | reward 全 0 → 没梯度 | hint 全废 → 没 OPD 样本,但 teacher 仍可工作(如果 hint 有效率太低则退化) |
| 同一句话能否同时被两路用? | ✅ 在 Combine 里 — 一次轮次并发跑两种 prompt,分别决定是否发 RL 样本 / OPD 样本 | 同左 |
| 对 PRM 能力要求 | 判别能力(好坏二分类) | 生成能力(要写出更好的回答) |
| 失败模式 | 误判 → 错误 reward 方向 | hint 偏离策略 → teacher-student gap 噪声大 |
PRM在两个分支扮演互补角色:
Combine把同一PRM的“判别力“和“生成力“同时榨干:用判别得到密集的 scalar 监督,用生成得到稀疏的token级方向监督。两条路径用到的 PRM 服务实例相同,但prompt、解析、聚合、入loss的方式完全不同。
项目中的 PRM 不是传统训练好的 reward 模型,而是用 LLM(同款 Qwen3)做 zero-shot 评判,通过 prompt 指导它输出 boxed{1} / boxed{0} / boxed{-1} 来评分。没有单独训练过 reward model。
代码里self._prm_url 是同一个 SGLang Judge 服务(GPU 6-7)。它同时承担两类调用:
| 调用类型 | 目的 | 服务于哪条路径 |
|---|---|---|
| Hint-judge (_query_judge_once,line) | 解析boxed{1}+[HINT_START]...[HINT_END] 提取更优回答提示 | OPD路径(决定是否发OPD样本+注入 hint) |
| PRM eval (_prm_evaluate → _majority_vote) | 给当前回答打分+1/0/-1 | RL路径(生成reward) |
两者共享同一个模型权重、同一个 router 端口,只是 prompt 模板不同(_build_hint_judge_messages vs _build_prm_judge_prompt),并发执行 m次投票。
在OpenClaw-RL中,OPD(On-Policy Distillation)使用了两个关键的服务器组件:
实现文件:openclaw-opd/openclaw_opd_api_server.py
功能:
启动方式:
实现方式:基于SGLangRouter的独立服务
功能:
技术架构:
协同工作:
扩展性:
这种架构设计使得OPD能够有效地利用延迟反馈(hindsight hints)来改进在线策略学习,同时保持系统的可扩展性和模块化。
10-总体流程
角色:评分员(Evaluator/Critic)
文件位置:openclaw-rl/openclaw_api_server.py
特点:
核心提示词:
Youareaprocessrewardmodel(PRM)evaluatinganAIassistant.Yourtask:decidewhethertheassistant'soutputsuccessfullyfulfilledtheuser'sintentatthatstep,usingthenextstateasevidence.##Scoringrules:-boxed{1}(good):#-boxed{-1}(bad):#-boxed{0}(neutral):##信息不足,无法判断Thinkstep-by-step,thengiveyourfinalscoreinsideboxed{}.
具体参见下表。
| 维度 | 内容 |
|---|---|
| 调用入口 | _build_prm_eval_prompt → _prm_eval_majority_vote |
| 输入 | (response, next_state) |
| Prompt 类型 | "你是 PRM,请评估 AI 回答的好坏" |
| 输出格式 | boxed{1} / boxed{0} / boxed{-1} |
| 聚合 | m 次独立投票多数决;平票 → 0 |
| 用途 | 直接进入训练损失 — 作为 GRPO 的 scalar reward |
| 信号粒度 | 序列级标量(整条 response 一个分数) |
| 信号性质 | 评估性(evaluative):"好/坏" |
| 数据密度 | 所有 ±1 分样本都进训练;0 分丢弃(除 "at-least-one" 保底) |
角色:教练/提示提取器(Hint Extractor)
文件位置:openclaw-opd/openclaw_opd_api_server.py
特点:双重功能,同时实现hint 提取(+1/-1Something wrong happened,please give me more information or retry)
PRM使用精心设计的提示模板确保评估一致性:
def_build_hint_judge_messages(response_text:str,next_state_text:str,next_state_role:str="user")->list[dict]:system=("Youareaprocessrewardmodelusedforhindsighthintextraction.n""Youaregiven:n""1)Theassistantresponseatturnt.n""2)Thenextstateatturnt+1,alongwithits**role**.nn""##Understandingthenextstate'srolen""-role='user':Areplyfromtheuser(follow-up,correction,newrequest,etc.).n""-role='tool':Thereturnvalueofatooltheassistantinvoked.""ThiscontentwasNOTavailablebeforetheassistant'saction—""itexistsBECAUSEtheassistantcalledthetool.""Asuccessful,non-errortooloutputgenerallymeanstheassistant's""actionwasappropriate;doNOTtreatitasinformationtheassistant""shouldhavealreadyknown.nn""Yourgoalistodecidewhetherthenextstaterevealsusefulhindsightinformationn""thatcouldhavehelpedimprovetheassistantresponseatturnt.nn""Outputformatrules(strict):n""-YouMUSTincludeexactlyonefinaldecisiontoken:boxed{1}orboxed{-1}.n""-Ifandonlyifdecisionisboxed{1},provideaconcise,information-densehintin1-3sentences,n""wrappedbetween[HINT_START]and[HINT_END].n""-Ifdecisionisboxed{-1},donotprovideahintblock.n""-Hintmustbeconcreteandactionableforimprovingthepreviousresponse.")user=(f"##Assistantresponse(turnt)n{response_text}nn"f"##Nextstate(turnt+1)[role:{next_state_role}]n{next_state_text}nn""Nowoutputyourdecisionand(ifpositive)thehintintherequiredformat.")return[{"role":"system","content":system},{"role":"user","content":user}]
具体参见下表。
| 维度 | 内容 |
|---|---|
| 调用入口 | _build_hint_judge_messages → _query_judge_once |
| 输入 | (response, next_state) |
| Prompt类型 | "若回答有问题,请给出更好的版本 [HINT_START]...[HINT_END]" |
| 输出格式 | boxed{1} + 文本 hint |
| 聚合 | m次投票,选出最长的有效正样本 hint(>10字符) |
| 用途 | 不直接进入损失;hint文本被注入到 user message,再喂给 teacher 做 forward pass,真正的训练信号是 teacher_log_probs - rollout_log_probs |
| 信号粒度 | token级向量(response中每个 token 一个 advantage 值) |
| 信号性质 | 方向性(directional):"应该这样回答" |
| 数据密度 | 只有 hint 通过的轮次才进训练(更稀疏,但更精细) |
关键点:OPD路径本身的“老师信号“不来自PRM±1分
OPD真正用于训练的方向性信号是teacher 模型对 hint-增强后,prompt 的 token级log-prob,再减去student(rollout_log_probs)。PRM评分只起两个作用:
所以:PRM·模型权重在OPD 路径里被调用(做hint 抽取),但PRM的±1分数本身不进入 OPD的损失函数一除非你跑的是Combine。
我们接下来看看评分流程,和其中一些技术细节。
PRM完整评分流程如下。
10-PRM完整评分流程
Majority Vote =3 等于对同一条response独立发起m=3次异步judge调用,取众数(most common score)。如果三票各不同(平局),返回0.0(中性/跳过)。
这3次Judge调用的特点为:
不足之处为:
Majority Vote 算法细节:
def_majority_vote(scores:list[int|None])->float:valid=[sforsinscoresifsisnotNone]#过滤失败的查询ifnotvalid:return0.0#全部失败→中性counter=Counter(valid)top=counter.most_common(1)[0]#关键:若有多个选项并列第一,返回0(保守策略)iflist(counter.values()).count(top[1])>1:return0.0returnfloat(top[0])
示例表格
| votes | 结果 | 原因 |
|---|---|---|
| [1, 1, 1] | +1 | 完全一致 |
| [1, 1, -1] | +1 | 多数票 |
| [1, -1, 0] | 0 | 三方平票 |
| [1, -1, None] | 0 | 2 有效,平票 |
| [None, None, 1] | +1 | 唯一有效票 |
#_submit_turn_sample()中的核心逻辑:exclude=nothas_next_stateorscore==0.0#正常情况:score=0→exclude=True→loss_mask=[0,0,...,0]#但是!特殊保障:ifexcludeandhas_next_stateandself._session_effective.get(session_id,0)==0:exclude=False#←强制参与训练!#"at-least-oneguarantee"
解决的问题场景如下:
用户发了 5 条消息,但每次都是中性反馈(score=0),导致:
保障机制如下:
第一个被 PRM 评过(has_next_state=True),但 score=0 的 turn
样本提交的异步状态机如下:
10-异步状态机
10-整体评分行为
我们接下来看看显式PRM和 OPD teacher log-prob 之间的比较。
显式PRM:“这一步(动作)对最终目标有多大贡献?
r(s_t,a_t)=P(最终成功|经过了这一步)
OPD teacher log-prob: “在看过hint后,teacher对这个token的认可程度?
adv(t)=logπ_T(a_t|a_1...a_{t-1},hint)-logπ_old(a_t|...)
这是两个不同的问题,只是恰好都能产生密集梯度信号。
显式PRM(理想情况):
OPD teacher log-prob:
因此:
场景:response在step5犯了错误,即 "the square root of 1764 is 43" ← 错误
显式PRM:
OPD teacher log-prob:
因此:
显式PRM:
OPD advantage:
adv(t)=teacher_lp-rollout_lp="相对于当前policy,teacher的偏好差"=若teacher和student在此token上意见一致→adv≈0=即使这个token"很重要",只要两者一致,梯度就是0
因此:
显式PRM(标准设计):
OPD teacher(hindsight):
我们在从理论角度看看,显式PRM+OPD的结合使用是否合理。
优劣
| 维度 | 显式 PRM | OPD teacher log-prob |
|---|---|---|
| 信号密度 | Dense (per step) | Dense (per token, 更细) |
| 反事实推理 | √ 可以定位错误步骤 | × 级联污染 |
| 绝对质量 | √ 绝对分数 | × 只有相对差 |
| 训练成本 | 高(需要标注数据) | 零(teacher 直接推理) |
| 分布偏移 | 有(PRM 本身需更新) | 无(每次实时计算) |
| 粒度 | Step 级(语义) | Token 级(sub-word) |
| Reward hacking 风险 | 中(model 游戏 PRM) | 低(teacher 足够大且变化慢) |
显式 PRM 回答“哪一步走错了”(诊断),OPD teacher log-prob 回答“每个词要往哪里改”(处方)。前者具备反事实定位能力,后者具备零训练成本的优势。两者测量的是不同问题,只是都能产生密集梯度这一性质让它们看起来相似。
因此:
#概念性代码defcombined_advantage(response_tokens,step_boundaries,prm_scores,teacher_lp,rollout_lp):adv=torch.zeros(len(response_tokens))forstep_idx,(start,end)inenumerate(step_boundaries):#PRM评估这一步是否正确prm_score=prm_scores[step_idx]#e.g.,+1/-1ifprm_score>0:#PRM:这步是对的→用OPD精细化内部tokenadv[start:end]=teacher_lp[start:end]-rollout_lp[start:end]else:#PRM:这步是错的→均匀惩罚(不用被污染的teacherLP)adv[start:end]=-1.0#←关键:从这步开始后面的OPD信号都丢弃(级联阻断)break#后续步骤advantage=0(不学习级联后的token)returnadv
这解决了 OPD 的核心问题:PRM 发现错误步骤后,后续步骤的 OPD 信号不再被计算,级联污染被切断。
advantage(t)=PRM_step(t)×(teacher_lp(t)-rollout_lp(t))↑↑步骤级"值不值得学习"Token级"往哪个方向学"
语义:PRM 决定"这一步的梯度权重",OPD 决定"梯度的方向"
正确步骤内的好token:PRM=+1×OPD_adv=+0.5=+0.5←适度强化正确步骤内的冗余token:PRM=+1×OPD_adv≈0≈0←不动错误步骤内的token:PRM=-1×OPD_adv=任意←全部反转为负
当前 Combine 实际上已经是一种弱版本:
Combine:advantage=w_rl*GRPO_reward+w_opd*OPD↑整条response一个标量(相当于sequence-levelPRM=GRPO)PRM+OPD:advantage=w_prm*PRM_step(t)+w_opd*OPD(t)↑每一步一个分数(更细粒度的PRM)
PRM+OPD 是 Combine 的自然升级:把 sequence-level reward 换成 step-level reward。
完整组合的advantage计算流程:
Response:[step_1][step_2][step_3]...[step_N]↓ PRM识别出step_3开始出错
Advantage 分配:
挑战1:PRM的训练数据
挑战2:步骤边界的定义
挑战3: PRM本身的分布偏移
挑战4:两个模型的推理成本。
显式PRM+OPD的结合是理论上最优的密集信号方案:PRM负责步骤级诊断和级联阻断,OPD负责步骤内的token 级精细处方。这正是Combine方法的自然延伸方向,但工程成本和PRM训练数据是主要障碍。
TransFormer-封面
Your Efficient RL Framework Secretly Brings You Off-Policy RL Training
zhuanlan.zhihu.com/p/200067022…
本文使用 markdown.com.cn 排版