OpenClaw-RL 实战 08|PRM定制化训练:如何为终端、GUI、SWE场景训练专属评判器?

3 阅读9分钟

当评判器的“审美”与你的场景对齐,AI才真正懂得什么是“好”

引言:为什么需要专属PRM?

在上一篇中,我们用同一套RL代码同时跑通了终端、GUI、SWE、工具调用四大场景。但一个根本问题浮出水面:PRM(过程奖励模型)的评判标准,应该对所有场景一视同仁吗?

显然不行:

  • 终端场景:退出码为0就是成功,报错就是失败——评判标准客观且明确
  • GUI场景:界面状态变化是否符合预期?可能需要视觉理解
  • SWE场景:代码修改后测试是否通过?需要理解代码语义
  • 个人对话:用户满意与否?需要理解情感和意图

OpenClaw-RL的论文明确指出,PRM的定制化是提升强化学习效果的关键 。一个在数学推理上训练出来的PRM,无法理解GUI界面的“美观”与否;一个在终端命令上训练出来的PRM,也判断不了对话的“自然度”。

本文将带你完成:

  • 理解PRM的三重角色:学生、教师、裁判的职责分工
  • PRM评判器架构:从单次评判到多数投票
  • 各场景的定制策略:终端用规则、GUI用VLM、SWE用代码理解
  • 数据集构建:如何为特定场景采集训练数据
  • 训练与部署:从微调基座模型到集成到RL流水线

一、PRM的三重角色:学生、教师与裁判

在OpenClaw-RL中,PRM实际上扮演了三个截然不同的角色 :

角色英文职责对应方法
学生Student执行任务的策略模型,需要被优化主模型(如Qwen3-4B)
教师Teacher提供优化方向,告诉学生“应该怎么做”OPD中的增强上下文教师
裁判Judge/PRM给出评分,告诉学生“做得好不好”Binary RL中的PRM评判器

本文聚焦裁判角色——即专门负责给交互打分的PRM评判器。

1.1 裁判的职责

裁判PRM的输入输出非常简单 :

输入:动作 a_t(智能体的回复)+ 下一个状态 s_{t+1}(用户反馈/工具输出)
输出:评分 ∈ {+1(好), -1(差), 0(中性)}

但这个简单的函数背后,需要深刻理解特定场景的“好坏”标准。

1.2 裁判与教师的分工

维度裁判(PRM Judge)教师(Teacher)
输入a_t + s_{t+1}s_t + hint(从s_{t+1}提取)
输出标量{-1,0,1}Token级优势向量
覆盖所有交互仅含明确指导的交互
对应方法Binary RLOPD
本篇目标✅ 定制化训练❌ 下一篇

二、PRM评判器的核心架构

2.1 单次评判与多数投票

根据OpenClaw-RL论文,PRM的评判采用多数投票机制来提升鲁棒性 :

# prm_judge.py
class PRMJudge:
    """过程奖励模型评判器"""
    
    def __init__(self, model, num_votes=3):
        self.model = model  # 可以是LLM API或本地模型
        self.num_votes = num_votes
        
    def _single_judge(self, action: str, next_state: str) -> int:
        """单次评判,返回-1/0/1"""
        prompt = self._build_prompt(action, next_state)
        response = self.model.generate(prompt)
        return self._parse_score(response)
    
    def judge(self, action: str, next_state: str) -> int:
        """多数投票最终评分"""
        votes = [self._single_judge(action, next_state) 
                 for _ in range(self.num_votes)]
        # 多数投票
        final_score = max(set(votes), key=votes.count)
        return final_score

2.2 评判Prompt模板设计

评判Prompt的质量直接影响PRM性能。以下是论文中使用的通用模板 :

def _build_prompt(self, action, next_state):
    return f"""你是一个AI交互质量评判器。请根据用户的反馈,判断AI的上一次回复质量。

AI的回复:{action}
用户的反馈:{next_state}

请从以下选项中选择一个:
+1:用户满意/任务成功(如用户说“谢谢”“很好”、工具执行成功)
-1:用户不满意/任务失败(如用户重复提问、工具报错)
0:中性(无法判断好坏)

请只返回数字:+1、-1或0"""

但对于不同场景,这个模板需要深度定制。

三、各场景的PRM定制策略

3.1 终端场景:规则优先

终端场景的特点是信号客观明确

信号含义PRM评分
退出码=0命令执行成功+1
退出码≠0命令执行失败-1
输出为空不确定0

定制策略:规则优先,LLM辅助

# terminal_prm.py
class TerminalPRM:
    """终端场景专属PRM"""
    
    def judge(self, action: str, next_state: dict) -> int:
        # 1. 规则判断
        exit_code = next_state.get('exit_code')
        if exit_code == 0:
            return 1
        elif exit_code is not None and exit_code != 0:
            return -1
        
        # 2. LLM辅助判断(处理复杂情况)
        prompt = f"""你是终端命令执行评判器。判断以下命令执行是否成功。

执行的命令:{action}
命令输出:{next_state.get('stdout', '')}
错误输出:{next_state.get('stderr', '')}

请判断执行状态:
+1:成功(正常输出结果)
-1:失败(有明显错误信息)
0:不确定"""
        
        response = self.llm.generate(prompt)
        return self._parse_score(response)

3.2 GUI场景:视觉语言模型(VLM)介入

GUI场景的核心是视觉状态变化 。论文中使用Qwen3VL-8B-Thinking作为GUI场景的基座模型,PRM也需要具备视觉理解能力。

# gui_prm.py
import base64
from PIL import Image

class GUIPRM:
    """GUI场景专属PRM(基于VLM)"""
    
    def __init__(self, vlm_model):
        self.vlm = vlm_model  # 视觉语言模型
        
    def judge(self, action: dict, next_state: dict) -> int:
        """
        action: {"type": "click", "x": 100, "y": 200}
        next_state: {"screenshot": base64, "diff": {...}}
        """
        # 将截图转为图像
        screenshot = Image.open(
            io.BytesIO(base64.b64decode(next_state['screenshot']))
        )
        
        # 构建多模态Prompt
        prompt = f"""你是GUI操作评判器。判断以下操作是否成功。

执行的操作:{action['type']} at ({action.get('x')}, {action.get('y')})
操作前的期望:{action.get('expected', '')}

请观察操作后的截图,判断:
+1:操作成功(界面按预期变化)
-1:操作失败(无变化或出现异常)
0:无法判断

请只返回数字。"""
        
        # VLM同时接收图像和文本
        response = self.vlm.generate(prompt, images=[screenshot])
        return self._parse_score(response)

3.3 SWE场景:代码理解优先

SWE场景的核心是代码修改后测试是否通过 。PRM需要理解代码语义和测试输出。

# swe_prm.py
class SWEPRM:
    """SWE场景专属PRM"""
    
    def judge(self, action: str, next_state: dict) -> int:
        """
        action: 修改后的代码
        next_state: {"test_output": "...", "passed": bool, "error": "..."}
        """
        # 1. 测试结果优先
        if next_state.get('passed'):
            return 1
        
        # 2. 错误信息分析
        error = next_state.get('error', '')
        if 'AssertionError' in error or 'SyntaxError' in error:
            return -1
        
        # 3. 代码质量评估(需要LLM理解)
        prompt = f"""你是代码修改评判器。判断以下代码修改的质量。

修改后的代码:{action[:500]}...(截断)
测试输出:{next_state.get('test_output', '')}

请判断:
+1:代码正确,测试通过
-1:代码有明显错误
0:无法确定"""
        
        response = self.llm.generate(prompt)
        return self._parse_score(response)

3.4 工具调用场景:返回值分析

工具调用场景的评判相对简单,主要看返回值 :

# tool_prm.py
class ToolPRM:
    """工具调用场景专属PRM"""
    
    def judge(self, action: dict, next_state: dict) -> int:
        """
        action: {"name": "calculator", "params": {...}}
        next_state: {"status": "success", "result": "...", "error": None}
        """
        status = next_state.get('status')
        
        if status == 'success':
            return 1
        elif status == 'error':
            return -1
        else:
            return 0

四、训练专属PRM的数据集构建

4.1 数据来源

训练专属PRM需要三类数据 :

数据来源示例获取方式
历史交互日志真实用户的对话记录OpenClaw日志系统
人工标注对历史交互的评分少量人工标注 + 模型扩展
规则生成终端退出码、测试结果自动生成

4.2 数据集格式

每条训练样本的格式如下:

{
  "action": "AI的回复或操作",
  "next_state": "用户反馈或工具输出",
  "scene": "terminal/gui/swe/tool",
  "score": 1,
  "reason": "退出码为0,命令执行成功"
}

4.3 数据增强策略

针对样本稀疏的场景,可以采用数据增强

# data_augmentation.py
class PRMDataAugmentor:
    """PRM训练数据增强"""
    
    def augment_terminal(self, samples):
        """终端场景增强:通过规则生成变体"""
        augmented = []
        for sample in samples:
            # 原始样本
            augmented.append(sample)
            
            # 变体1:修改退出码
            if sample['next_state']['exit_code'] == 0:
                # 成功→失败的变体
                neg = deepcopy(sample)
                neg['next_state']['exit_code'] = 1
                neg['score'] = -1
                augmented.append(neg)
            
            # 变体2:修改输出内容
            # ...
        return augmented
    
    def augment_dialogue(self, samples, llm):
        """对话场景增强:通过LLM生成语义相似的变体"""
        # 略

4.4 训练代码示例

# train_prm.py
from transformers import AutoModelForCausalLM, AutoTokenizer
from datasets import load_dataset

def train_prm(scene="terminal"):
    """训练场景专属PRM"""
    
    # 1. 加载数据集
    dataset = load_dataset(f"openclaw/prm-{scene}-2026")
    
    # 2. 加载基座模型
    model = AutoModelForCausalLM.from_pretrained("Qwen2.5-7B")
    tokenizer = AutoTokenizer.from_pretrained("Qwen2.5-7B")
    
    # 3. 构建训练数据(转为文本生成任务)
    def format_sample(sample):
        prompt = f"""你是{scene}场景的交互质量评判器。
        
动作:{sample['action']}
下一状态:{sample['next_state']}

请输出评分(+1/-1/0):"""
        
        target = str(sample['score'])
        return {"prompt": prompt, "target": target}
    
    # 4. 微调
    trainer = PRMTrainer(
        model=model,
        train_dataset=dataset['train'].map(format_sample),
        learning_rate=1e-5,
        epochs=3
    )
    trainer.train()
    
    # 5. 保存
    model.save_pretrained(f"prm-{scene}-v1")

五、PRM集成到RL流水线

5.1 配置文件示例

# config_prm.yaml
prm:
  scenes:
    terminal:
      type: "rule_based"  # 终端可用规则
      fallback_model: "prm-terminal-v1"  # 复杂情况用模型
    gui:
      type: "vlm"
      model: "prm-gui-v1"  # 视觉语言模型
      num_votes: 3
    swe:
      type: "llm"
      model: "prm-swe-v1"
      temperature: 0.1
    tool:
      type: "rule_based"  # 工具调用规则简单
      fallback: null

5.2 统一调用接口

# unified_prm.py
class UnifiedPRM:
    """统一PRM调用接口"""
    
    def __init__(self, config):
        self.prms = {}
        for scene, cfg in config['prm']['scenes'].items():
            if cfg['type'] == 'rule_based':
                self.prms[scene] = RuleBasedPRM(scene, cfg)
            elif cfg['type'] == 'vlm':
                self.prms[scene] = VLMBasePRM(cfg['model'])
            else:
                self.prms[scene] = LLMBasePRM(cfg['model'])
    
    def judge(self, scene: str, action, next_state) -> int:
        """统一评判接口"""
        prm = self.prms.get(scene)
        if not prm:
            raise ValueError(f"Unsupported scene: {scene}")
        return prm.judge(action, next_state)

六、实验验证:定制PRM的效果

6.1 论文数据

根据OpenClaw-RL论文,集成定制化PRM后,各场景的提升效果 :

场景通用PRM定制PRM提升
工具调用0.170.30+76%
GUI0.310.33+6%
SWE0.150.26+73%

GUI场景提升较小,是因为其本身信号就较丰富;而工具调用和SWE场景依赖过程奖励,定制PRM的效果显著。

6.2 消融实验

# ablation.py
def run_ablation():
    """对比有无定制PRM的效果"""
    results = {}
    
    for scene in ['terminal', 'gui', 'swe', 'tool']:
        # 用通用PRM
        generic_prm = GenericPRM()
        acc_generic = evaluate(scene, generic_prm)
        
        # 用定制PRM
        custom_prm = load_custom_prm(scene)
        acc_custom = evaluate(scene, custom_prm)
        
        results[scene] = {
            'generic': acc_generic,
            'custom': acc_custom,
            'improvement': (acc_custom - acc_generic) / acc_generic
        }
    
    return results

七、下一步预告

恭喜!你已经掌握了PRM定制化的完整流程——从理解裁判角色,到各场景定制策略,再到数据集构建和训练部署。现在,你的RL系统拥有了真正“懂行”的评判器。

下一篇文章,我们将聚焦OPD教师模型的训练——如何训练一个能够从用户反馈中提取高质量指导信号、并提供Token级监督的教师模型,实现“后悔引导的同策略蒸馏”。

敬请期待:《OpenClaw-RL 实战 09|OPD教师模型训练:如何让AI从“后悔”中学会“聪明”?》

附录:核心命令速查

# 训练终端场景PRM
python train_prm.py --scene terminal --data data/terminal_prm.jsonl

# 训练GUI场景PRM(需要VLM)
python train_prm_vlm.py --scene gui --data data/gui_prm.jsonl

# 集成测试
python test_prm.py --scene swe --model prm-swe-v1

# 查看各场景PRM效果对比
python ablation.py

文章发布于稀土掘金

(本文为「OpenClaw-RL实战」系列第八篇,共12篇。欢迎关注、收藏、转发,与更多开发者一起探索AI的“边用边学”新范式!)