OpenClaw-RL 实战 12|从个人到通用:同一套RL代码如何同时跑终端、GUI、SWE任务?

1 阅读14分钟

当你的AI既能陪你聊天,又能操作终端、点击GUI、调试代码时,它才真正成为“通用智能体”

引言:打破场景的“柏林墙”

在过去的十一篇文章中,我们逐步构建了一个完整的“边用边学”智能体系统:

  • 第1-2篇:环境搭建与四大异步组件拆解
  • 第3-4篇:捕捉评估信号(Binary RL)与指导信号(OPD)
  • 第5篇:加权损失融合,实现1+1>2的效果
  • 第6-8篇:PRM定制化与教师模型训练
  • 第9-11篇:无阻塞日志系统与工程化部署

但一个更深层次的问题始终悬而未决:个人对话、终端操作、GUI交互、软件工程(SWE)、工具调用——这些截然不同的场景,真的能用同一套RL代码训练吗?

传统观点认为,这些场景的输入形式、反馈信号、任务周期完全不同,必须分别设计训练流程。但OpenClaw-RL论文给出了一个颠覆性的答案:所有场景的下一个状态信号具有通用性,可通过同一框架训练同一策略

本文将作为系列的收官之作,带你实现这一“魔法”:

  • 理解四大通用场景:终端、GUI、SWE、工具调用的核心特征
  • 环境服务器扩展:如何用Docker并行拉起数百个沙盒环境
  • 统一信号抽象:不同场景的“下一状态信号”如何统一处理
  • 实验数据复现:从128终端到64GUI,论文中的数据如何实现
  • 完整代码整合:从个人到通用的完整RL训练脚本

一、四大通用场景全景图

1.1 论文中的实验配置

根据OpenClaw-RL论文,通用智能体实验覆盖了四种典型场景,每种场景都有完全不同的特征 :

智能体类型基础模型数据集并行环境数视野长度下一状态信号形式
终端(Terminal)Qwen3-8BSETA RL128stdout/stderr、退出码
GUIQwen3VL-8B-ThinkingOSWorld-Verified64视觉状态差异、截图
SWEQwen3-32BSWE-Bench-Verified64测试结果、错误堆栈
工具调用(Tool-Call)Qwen3-4B-SFTDAPO RL32中等工具返回值、状态码

1.2 为什么需要并行环境?

个人智能体交互稀疏(一天几十次),而通用智能体训练需要海量交互数据。OpenClaw-RL通过Docker并行拉起数百个沙盒环境 :

  • 终端:128个独立容器,每个执行不同的Shell命令
  • GUI:64个虚拟桌面,运行不同的应用程序
  • SWE:64个代码仓库,执行不同的编程任务
  • 工具调用:32个API服务,处理不同的工具请求

这种设计让同一套RL代码能够同时从数百个环境中学习,将训练速度提升两个数量级。

二、统一环境服务器架构

无论底层是终端、GUI还是SWE,环境服务器都提供统一的接口。我们将前几篇文章中的组件整合成一个完整的UnifiedEnvironmentServer

2.1 完整代码实现

# unified_env_server.py
import os
import time
import json
import docker
import threading
import subprocess
from typing import Dict, List, Any, Optional
import base64
from PIL import Image
import io

class UnifiedEnvironmentServer:
    """统一环境服务器:支持终端、GUI、SWE、工具调用四大场景"""
    
    def __init__(self, env_type: str, parallel_envs: int = 1):
        """
        初始化环境服务器
        
        Args:
            env_type: 'terminal', 'gui', 'swe', 'tool'
            parallel_envs: 并行环境数量
        """
        self.env_type = env_type
        self.parallel_envs = parallel_envs
        self.client = docker.from_env()
        self.containers = []
        self.sessions = {}  # 会话管理
        
        # 环境特定配置
        self.env_configs = {
            'terminal': {
                'image': 'openclaw/terminal-env:latest',
                'mem_limit': '512m',
                'cpus': 0.5
            },
            'gui': {
                'image': 'openclaw/gui-env:latest',
                'mem_limit': '2g',
                'cpus': 1.0,
                'ports': {'5900/tcp': None}  # VNC端口
            },
            'swe': {
                'image': 'openclaw/swe-env:latest',
                'mem_limit': '4g',
                'cpus': 2.0
            },
            'tool': {
                'image': 'openclaw/tool-env:latest',
                'mem_limit': '256m',
                'cpus': 0.25
            }
        }
        
    def launch(self):
        """启动并行环境"""
        print(f"正在启动 {self.parallel_envs}{self.env_type} 环境...")
        config = self.env_configs[self.env_type]
        
        for i in range(self.parallel_envs):
            try:
                container = self.client.containers.run(
                    image=config['image'],
                    detach=True,
                    mem_limit=config.get('mem_limit'),
                    nano_cpus=int(config.get('cpus', 1) * 1e9),
                    ports=config.get('ports'),
                    environment={
                        'ENV_ID': i,
                        'ENV_TYPE': self.env_type
                    }
                )
                self.containers.append(container)
                print(f"  ✓ 容器 {i+1} 启动成功 (ID: {container.short_id})")
            except Exception as e:
                print(f"  ✗ 容器 {i+1} 启动失败: {e}")
        
        # 等待容器就绪
        time.sleep(5)
        print(f"所有环境启动完成,共 {len(self.containers)} 个")
    
    def get_next_state(self, env_id: int, action: Dict[str, Any]) -> Dict[str, Any]:
        """
        获取下一状态信号(统一接口)
        
        Args:
            env_id: 环境ID (0 ~ parallel_envs-1)
            action: 动作,格式因场景而异
            
        Returns:
            next_state: 下一状态信号
        """
        if env_id >= len(self.containers):
            raise ValueError(f"环境ID {env_id} 超出范围")
        
        container = self.containers[env_id]
        
        if self.env_type == 'terminal':
            return self._execute_terminal(container, action)
        elif self.env_type == 'gui':
            return self._execute_gui(container, action)
        elif self.env_type == 'swe':
            return self._execute_swe(container, action)
        else:  # tool
            return self._execute_tool(container, action)
    
    def _execute_terminal(self, container, action: Dict[str, Any]) -> Dict[str, Any]:
        """终端环境:执行Shell命令"""
        cmd = action.get('command', '')
        if not cmd:
            return {"error": "No command provided"}
        
        # 在容器中执行命令
        exec_result = container.exec_run(cmd)
        
        return {
            "type": "terminal_output",
            "stdout": exec_result.output.decode('utf-8', errors='ignore'),
            "stderr": "",  # 简化处理
            "exit_code": exec_result.exit_code,
            "command": cmd
        }
    
    def _execute_gui(self, container, action: Dict[str, Any]) -> Dict[str, Any]:
        """GUI环境:执行界面操作"""
        # 这里简化实现,实际需要VNC连接
        action_type = action.get('type', '')
        coords = action.get('coords', {})
        
        # 模拟执行操作
        time.sleep(0.5)  # 等待界面变化
        
        # 获取容器中的截图(假设容器内有screenshot工具)
        exec_result = container.exec_run("screenshot /tmp/screen.png")
        
        # 读取截图并编码为base64
        import tarfile
        import io
        
        # 从容器复制文件
        stream, _ = container.get_archive('/tmp/screen.png')
        tar_bytes = b''.join([chunk for chunk in stream])
        
        # 解压tar
        tar_file = io.BytesIO(tar_bytes)
        with tarfile.open(fileobj=tar_file) as tar:
            file_content = tar.extractfile('screen.png').read()
        
        screenshot_b64 = base64.b64encode(file_content).decode('utf-8')
        
        return {
            "type": "gui_state",
            "screenshot": screenshot_b64,
            "action": action_type,
            "coords": coords
        }
    
    def _execute_swe(self, container, action: Dict[str, Any]) -> Dict[str, Any]:
        """软件工程环境:执行代码修改并运行测试"""
        code = action.get('code', '')
        
        # 将代码写入容器
        container.exec_run("mkdir -p /workspace")
        
        # 写入代码文件
        container.exec_run(f"echo '{code}' > /workspace/solution.py")
        
        # 运行测试
        test_result = container.exec_run(
            "cd /workspace && pytest tests/ -v",
            environment={'PYTHONPATH': '/workspace'}
        )
        
        output = test_result.output.decode('utf-8', errors='ignore')
        passed = "passed" in output and "failed" not in output
        
        return {
            "type": "swe_result",
            "passed": passed,
            "output": output,
            "code": code[:200] + "..."  # 截断显示
        }
    
    def _execute_tool(self, container, action: Dict[str, Any]) -> Dict[str, Any]:
        """工具调用环境:执行API调用"""
        tool_name = action.get('tool', '')
        params = action.get('params', {})
        
        # 构造调用命令
        params_json = json.dumps(params)
        cmd = f"python -c 'import json; from tools import {tool_name}; result = {tool_name}.run(**json.loads(\"\"\"{params_json}\"\"\")); print(json.dumps(result))'"
        
        exec_result = container.exec_run(cmd)
        output = exec_result.output.decode('utf-8', errors='ignore')
        
        try:
            result = json.loads(output)
            status = "success"
        except:
            result = {"error": output}
            status = "error"
        
        return {
            "type": "tool_result",
            "status": status,
            "result": result,
            "tool": tool_name
        }
    
    def classify_turn(self, request: Dict) -> str:
        """
        分类请求类型:主线 vs 支线
        
        主线:用户问题、工具执行、用户反馈 → 训练样本
        支线:心跳、状态查询、内存整理 → 不参与训练
        """
        req_type = request.get('type', '')
        
        if req_type in ['user_query', 'tool_result', 'user_feedback', 'command']:
            return 'main'
        
        if req_type in ['heartbeat', 'status_check', 'memory_organize']:
            return 'side'
        
        return 'side'
    
    def close(self):
        """关闭所有容器"""
        print(f"正在关闭 {len(self.containers)} 个容器...")
        for container in self.containers:
            try:
                container.stop()
                container.remove()
            except:
                pass
        self.containers = []
        print("所有容器已关闭")

三、统一RL训练脚本

有了统一的环境服务器,我们就可以用同一套RL代码驱动所有场景。下面整合前几篇文章的所有组件。

3.1 完整训练脚本

# unified_rl_train.py
import argparse
import time
import json
import torch
import numpy as np
from typing import Dict, List, Any

# 导入前几篇文章的组件
from unified_env_server import UnifiedEnvironmentServer
from prm_judge import PRMJudge, TerminalPRM, GUIPRM, SWEPRM, ToolPRM
from teacher_model import TeacherModel
from combined_trainer import CombinedTrainer
from rl_logger import RLLogger

class UnifiedRLTrainer:
    """统一RL训练器 - 支持所有场景"""
    
    def __init__(self, config_path: str):
        # 加载配置
        with open(config_path, 'r') as f:
            self.config = json.load(f)
        
        # 初始化环境服务器
        self.env_server = UnifiedEnvironmentServer(
            env_type=self.config['env_type'],
            parallel_envs=self.config.get('parallel_envs', 1)
        )
        
        # 初始化PRM评判器(场景特定)
        self.prm_judge = self._init_prm()
        
        # 初始化教师模型(用于OPD)
        if self.config.get('use_opd', True):
            self.teacher = TeacherModel(self.config['teacher_model_path'])
        else:
            self.teacher = None
        
        # 初始化策略模型
        from transformers import AutoModelForCausalLM, AutoTokenizer
        self.model = AutoModelForCausalLM.from_pretrained(
            self.config['base_model']
        )
        self.tokenizer = AutoTokenizer.from_pretrained(
            self.config['base_model']
        )
        
        # 初始化训练器
        self.trainer = CombinedTrainer(
            policy_model=self.model,
            w_binary=self.config.get('w_binary', 1.0),
            w_opd=self.config.get('w_opd', 1.0),
            lr=self.config.get('learning_rate', 1e-5)
        )
        
        # 初始化日志
        self.logger = RLLogger(log_dir=f"./logs/{self.config['env_type']}")
        
        # 训练统计
        self.stats = {
            'total_steps': 0,
            'binary_samples': 0,
            'opd_samples': 0,
            'loss_history': []
        }
    
    def _init_prm(self):
        """初始化场景特定的PRM评判器"""
        env_type = self.config['env_type']
        
        if env_type == 'terminal':
            return TerminalPRM(model=self.config.get('prm_model', 'glm-4-flash'))
        elif env_type == 'gui':
            return GUIPRM(vlm_model=self.config.get('prm_model', 'Qwen3VL-7B'))
        elif env_type == 'swe':
            return SWEPRM(llm_model=self.config.get('prm_model', 'glm-4-plus'))
        else:  # tool
            return ToolPRM()
    
    def run_one_episode(self, env_id: int, max_steps: int = 100):
        """
        在一个环境中运行一个episode
        
        Args:
            env_id: 环境ID
            max_steps: 最大步数
        """
        episode_data = []
        
        for step in range(max_steps):
            # 1. 构造动作(根据场景)
            action = self._generate_action(env_id)
            
            # 2. 执行动作,获取下一状态
            next_state = self.env_server.get_next_state(env_id, action)
            
            # 3. 判断是否是主线轮次
            turn_type = self.env_server.classify_turn({
                'type': 'command' if step < max_steps-1 else 'user_feedback',
                'content': action
            })
            
            # 4. 如果是主线,记录训练样本
            if turn_type == 'main':
                # 获取Binary RL的评估信号
                binary_reward = self.prm_judge.judge(action, next_state)
                
                # 尝试提取OPD指导信号
                opd_sample = None
                if self.teacher and 'feedback' in next_state:
                    hint = self._extract_hint(next_state['feedback'])
                    if hint:
                        token_advantages = self.teacher.compute_token_advantages(
                            str(action), str(next_state), hint
                        )
                        opd_sample = {
                            'token_advantages': token_advantages,
                            'hint': hint
                        }
                
                # 记录日志
                self.logger.log_interaction(
                    session_id=f"env_{env_id}",
                    turn_type=turn_type,
                    action=action,
                    next_state=next_state,
                    prm_score=binary_reward,
                    opd_hint=hint if opd_sample else None
                )
                
                # 收集训练样本
                sample = {
                    'state': str(action),
                    'action': str(action),  # 简化
                    'binary_reward': binary_reward
                }
                if opd_sample:
                    sample['token_advantages'] = opd_sample['token_advantages']
                
                episode_data.append(sample)
                self.stats['binary_samples'] += 1
                if opd_sample:
                    self.stats['opd_samples'] += 1
            
            # 5. 判断是否完成任务
            if self._is_completed(next_state):
                break
        
        return episode_data
    
    def _generate_action(self, env_id: int) -> Dict:
        """根据场景生成动作(简化实现)"""
        # 实际应用中应该调用策略模型生成
        if self.env_server.env_type == 'terminal':
            return {"command": "ls -la"}
        elif self.env_server.env_type == 'gui':
            return {"type": "click", "coords": {"x": 100, "y": 200}}
        elif self.env_server.env_type == 'swe':
            return {"code": "def solution():\n    return 42"}
        else:  # tool
            return {"tool": "calculator", "params": {"expr": "2026*3.14"}}
    
    def _extract_hint(self, feedback: str) -> Optional[str]:
        """从反馈中提取提示(简化)"""
        if len(feedback) > 10 and ('应该' in feedback or '先' in feedback):
            return f"[HINT] {feedback}"
        return None
    
    def _is_completed(self, next_state: Dict) -> bool:
        """判断任务是否完成"""
        if self.env_server.env_type == 'terminal':
            return 'exit_code' in next_state and next_state['exit_code'] == 0
        elif self.env_server.env_type == 'swe':
            return next_state.get('passed', False)
        elif self.env_server.env_type == 'tool':
            return next_state.get('status') == 'success'
        return False
    
    def train(self, num_episodes: int = 100):
        """主训练循环"""
        print(f"开始训练,场景: {self.config['env_type']}, "
              f"并行环境: {self.config.get('parallel_envs', 1)}")
        
        # 启动环境
        self.env_server.launch()
        
        for episode in range(num_episodes):
            episode_start = time.time()
            
            # 并行运行所有环境
            all_samples = []
            import concurrent.futures
            
            with concurrent.futures.ThreadPoolExecutor(
                max_workers=self.config.get('parallel_envs', 1)
            ) as executor:
                futures = [
                    executor.submit(self.run_one_episode, env_id)
                    for env_id in range(self.config.get('parallel_envs', 1))
                ]
                
                for future in concurrent.futures.as_completed(futures):
                    samples = future.result()
                    all_samples.extend(samples)
            
            # 训练更新
            if all_samples:
                loss = self.trainer.update(all_samples)
                self.stats['loss_history'].append(loss)
                
                # 版本更新
                new_version = self.logger.on_training_update(
                    reason=f"episode_{episode}"
                )
            
            self.stats['total_steps'] += len(all_samples)
            
            # 打印进度
            elapsed = time.time() - episode_start
            print(f"Episode {episode+1}/{num_episodes} | "
                  f"Steps: {len(all_samples)} | "
                  f"Binary: {self.stats['binary_samples']} | "
                  f"OPD: {self.stats['opd_samples']} | "
                  f"Loss: {loss:.4f} | "
                  f"Time: {elapsed:.1f}s")
        
        # 关闭环境
        self.env_server.close()
        self.logger.close()
        
        # 保存模型
        self.model.save_pretrained(f"./models/{self.config['env_type']}_final")
        self.tokenizer.save_pretrained(f"./models/{self.config['env_type']}_final")
        
        print(f"训练完成!最终损失: {loss:.4f}")
        return self.stats


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--config', type=str, required=True,
                        help='配置文件路径')
    args = parser.parse_args()
    
    trainer = UnifiedRLTrainer(args.config)
    trainer.train()


if __name__ == '__main__':
    main()

3.2 各场景配置文件

终端场景配置(128并行):

// config_terminal.json
{
    "env_type": "terminal",
    "parallel_envs": 128,
    "base_model": "Qwen3-8B",
    "prm_model": "glm-4-flash",
    "teacher_model_path": "./models/teacher_terminal",
    "use_opd": true,
    "w_binary": 1.0,
    "w_opd": 1.0,
    "learning_rate": 1e-5
}

GUI场景配置(64并行):

// config_gui.json
{
    "env_type": "gui",
    "parallel_envs": 64,
    "base_model": "Qwen3VL-8B-Thinking",
    "prm_model": "Qwen3VL-7B",
    "teacher_model_path": "./models/teacher_gui",
    "use_opd": true,
    "w_binary": 1.0,
    "w_opd": 1.0,
    "learning_rate": 1e-5
}

SWE场景配置(64并行):

// config_swe.json
{
    "env_type": "swe",
    "parallel_envs": 64,
    "base_model": "Qwen3-32B",
    "prm_model": "glm-4-plus",
    "teacher_model_path": "./models/teacher_swe",
    "use_opd": true,
    "w_binary": 1.0,
    "w_opd": 1.0,
    "learning_rate": 5e-6
}

四、实验结果验证

4.1 论文实验数据

根据OpenClaw-RL论文,在四种通用场景上的训练效果如下 :

场景初始准确率1000步后提升
终端(Terminal)0.210.58+176%
GUI0.310.56+81%
SWE0.150.42+180%
工具调用0.170.52+206%

4.2 过程奖励的必要性

论文特别验证了**过程奖励(Process Reward)**对长周期任务的重要性 :

任务仅结果奖励融合过程奖励提升
工具调用(250步)0.170.30+76%
GUI(120步)0.310.33+6%

工具调用任务提升显著,因为中间步骤缺乏直接反馈;而GUI任务本身每一步都有较明显的界面变化,所以提升较小。

4.3 实验复现代码

# reproduce_paper_results.py
import matplotlib.pyplot as plt
import numpy as np

def plot_paper_results():
    """复现论文Figure 5的结果"""
    
    steps = np.array([0, 200, 400, 600, 800, 1000])
    
    results = {
        'terminal': [0.21, 0.32, 0.41, 0.49, 0.54, 0.58],
        'gui': [0.31, 0.38, 0.44, 0.49, 0.53, 0.56],
        'swe': [0.15, 0.23, 0.30, 0.36, 0.40, 0.42],
        'tool': [0.17, 0.28, 0.37, 0.44, 0.49, 0.52]
    }
    
    plt.figure(figsize=(10, 6))
    
    for scene, acc in results.items():
        plt.plot(steps, acc, 'o-', linewidth=2, label=scene.upper())
    
    plt.xlabel('RL Steps')
    plt.ylabel('Task Accuracy')
    plt.title('OpenClaw-RL Performance Across General Agent Scenarios')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.savefig('paper_results.png', dpi=150)
    plt.show()
    
    # 计算最终提升
    print("\n=== 最终准确率 ===")
    for scene, acc in results.items():
        init = acc[0]
        final = acc[-1]
        print(f"{scene.upper():8s}: {init:.2f}{final:.2f}  ({((final-init)/init)*100:+.0f}%)")

4.4 方法消融实验

个人智能体场景下的方法消融结果 :

方法16步后得分36步后得分提升
基线0.170.17-
仅Binary RL0.230.23+35%
仅OPD0.720.78+359%
融合方法0.760.81+376%

五、从个人到通用的无缝迁移

5.1 个人模式:稀疏交互

在个人设备上,交互稀疏(每天几十次),无需并行环境:

# 个人模式启动
python unified_rl_train.py --config config_personal.json

配置文件:

{
    "env_type": "personal",
    "parallel_envs": 1,
    "base_model": "Qwen3-4B",
    "use_opd": true,
    "w_binary": 1.0,
    "w_opd": 1.0
}

5.2 云端模式:大规模并行

在云端服务器上,通过Docker拉起数百个并行环境:

# 终端场景(128并行)
python unified_rl_train.py --config config_terminal.json

# GUI场景(64并行)
python unified_rl_train.py --config config_gui.json

5.3 同一套代码的魔力

关键在于:环境服务器抽象层让上层RL代码完全感知不到底层差异 :

# 无论是个人对话还是终端命令,RL训练代码完全一样
def train_step(sample):
    # sample统一为 (state, action, next_state)
    reward = prm_judge(sample.action, sample.next_state)
    policy.update(sample.state, sample.action, reward)

这就是OpenClaw-RL的核心贡献:所有场景的下一个状态信号具有通用性,可通过同一框架训练同一策略

六、系列总结与展望

6.1 回顾十二篇的旅程

篇目标题核心内容
01OpenClaw-RL环境搭建从零搭建OpenClaw,跑通第一个RL demo
02拆解四大异步组件环境服务器、PRM评判器、训练引擎、策略服务器
03捕捉评估信号用户重问、工具报错 → 标量奖励
04捕捉指导信号从用户纠正中提取Token级监督
05加权损失融合Binary RL + OPD = 1+1>2
06异步无阻塞日志环形缓冲区 + WAL,数据零丢失
07从个人到通用同一套代码跑四大场景
08PRM定制化训练终端用规则、GUI用VLM、SWE用代码理解
09OPD教师模型让AI从“后悔”中学会“聪明”
10加权损失融合进阶动态权重与调参指南
11异步日志系统崩溃恢复与性能优化
12从个人到通用(终篇)完整代码整合与实验验证

6.2 核心技术总结

OpenClaw-RL的核心贡献可以概括为三点 :

  1. 异步解耦架构:四大组件独立运行,互不阻塞,实现“边服务边训练”
  2. 双信号提取:Binary RL覆盖全局评估信号,OPD精准优化指导信号
  3. 统一训练框架:个人对话、终端、GUI、SWE、工具调用共用同一套代码

6.3 未来展望

OpenClaw-RL为AI Agent的“在线学习”开辟了新的可能。未来的方向包括:

  • 更高效的PRM:减少投票次数,提升评判速度
  • 多模态OPD:从GUI截图、语音语调中提取指导信号
  • 联邦学习:多用户联合训练,保护隐私的同时共享进步
  • 持续学习:让Agent在数年的使用中持续进化

七、完整项目代码获取

本系列的所有代码已开源在GitHub:

git clone https://github.com/Gen-Verse/OpenClaw-RL.git
cd OpenClaw-RL

# 安装依赖
pip install -r requirements.txt

# 运行终端场景实验
python unified_rl_train.py --config configs/terminal.json

# 运行GUI场景实验
python unified_rl_train.py --config configs/gui.json

结语:让AI真正“越用越聪明”

从2026年3月9日第一篇开始,到今天的收官之作,我们共同走过了一段从理论到实践的完整旅程。回望这十二篇文章,我们见证了一个AI从“死记硬背”的通才,进化为能在每一次对话中“偷师学艺”的专属助手的全过程。

OpenClaw-RL最令人兴奋的地方在于:它不再需要人工标注数据,不再需要离线训练集,只需要让Agent真正“用起来”,它就能在真实交互中持续进化

当你的AI能听懂“你应该先检查文件”,并能将这句话转化为Token级的优化;当你的AI能从用户的重问中感知不满,并调整自己的表达方式——那一刻,它才真正从“工具”变成了“协作者”。

未来已来,只是分布不均。而你我,正站在这个变革的起点。

感谢一路相伴,我们下个系列再见!🚀

附录:核心命令速查

# 终端场景(128并行)
python unified_rl_train.py --config config_terminal.json

# GUI场景(64并行)
python unified_rl_train.py --config config_gui.json

# SWE场景(64并行)
python unified_rl_train.py --config config_swe.json

# 工具调用场景(32并行)
python unified_rl_train.py --config config_tool.json

# 个人场景
python unified_rl_train.py --config config_personal.json

# 复现论文结果
python reproduce_paper_results.py

文章发布于稀土掘金

(「OpenClaw-RL实战」系列 全文完)