【SkillRL】强化学习详解

WebShop GRPO 训练代码架构分析

本文档梳理 run_webshop_skills.sh 启动后,代码从入口到最终策略更新的完整执行流程,

涵盖动作模拟、环境交互、奖励计算、技能注入和动态更新等关键环节。


目录

  1. 整体架构概览
  2. 训练入口与初始化
  3. 核心训练循环 (fit)
  4. [多轮交互 Rollout 流程](#多轮交互 Rollout 流程)
  5. [WebShop 环境实现](#WebShop 环境实现)
  6. 奖励计算
  7. [GRPO 优势估计](#GRPO 优势估计)
  8. 策略更新 (Actor Update)
  9. 技能库系统
  10. 动态技能更新(教师模型)
  11. 验证流程与技能触发
  12. 端到端数据流汇总

1. 整体架构概览

复制代码
run_webshop_skills.sh
    |
    v
main_ppo.py (Hydra 入口)
    |
    v
TaskRunner.run() (Ray 远程 actor)
    |   ├── make_envs()          → WebshopEnvironmentManager (训练/验证环境)
    |   ├── EpisodeRewardManager → 奖励计算器
    |   ├── TrajectoryCollector  → 多轮交互循环
    |   └── RayPPOTrainer        → 核心训练器
    |       └── fit()            → 训练循环
    |           ├── 多轮 rollout  → 收集轨迹
    |           ├── 奖励计算      → 稀疏 episode 奖励
    |           ├── GRPO 优势     → 组内相对优势
    |           ├── Actor 更新    → PPO clipped loss + KL loss
    |           ├── 验证          → 评估 + 动态技能更新
    |           └── 保存 checkpoint

关键设计决策

决策 说明
GRPO 分组方式 标准 verl 用 rollout.n 重复生成;SkillRL 用 env.rollout.n 启动多个环境 worker,同一任务分配 n 个 worker 并行交互
单步独立 chat 每个环境步骤,模型看到的是一个全新的单轮对话,历史信息由环境管理器嵌入在文本观察中
稀疏奖励 只有 episode 结束时才有奖励(win=10, lose=0),放在最后 token 上
技能注入 通过模板模式,按任务类型关键词匹配检索技能,注入到 prompt 顶部

2. 训练入口与初始化

2.1 文件:verl/trainer/main_ppo.py

调用链

复制代码
main()  (Hydra 装饰器加载 ppo_trainer.yaml)
  → run_ppo(config)
    → ray.init(...)
    → TaskRunner.remote()          # Ray 远程 actor,运行在专用 CPU 上
    → TaskRunner.run.remote(config)

TaskRunner.run() 内部初始化顺序

步骤 操作 对应代码位置
1 copy_to_local() 下载模型 checkpoint main_ppo.py:90
2 make_envs(config) 创建训练/验证环境 main_ppo.py:95
3 hf_tokenizer() 实例化分词器 main_ppo.py:100
4 选择 worker 策略 (FSDP/Megatron) main_ppo.py:120
5 创建 EpisodeRewardManager 作为 reward_fn main_ppo.py:152
6 创建 TrajectoryCollector main_ppo.py:162
7 创建 RayPPOTrainer main_ppo.py:169
8 trainer.init_workers() 初始化所有 worker main_ppo.py:183
9 trainer.fit() 开始训练 main_ppo.py:186

2.2 GRPO 与 PPO 的关系

文件verl/trainer/ppo/ray_trainer.py:445-458

python 复制代码
if self.config.algorithm.adv_estimator == AdvantageEstimator.GAE:
    self.use_critic = True
elif self.config.algorithm.adv_estimator in [
    AdvantageEstimator.GRPO, AdvantageEstimator.RLOO, ...
]:
    self.use_critic = False  # GRPO 不需要 Critic 网络

GRPO 是 PPO 框架下的一个变体。main_ppo.py 是通用入口,通过 algorithm.adv_estimator=grpo 切换到 GRPO 模式,跳过 Critic 网络的创建和更新。


3. 核心训练循环 (fit)

文件:verl/trainer/ppo/ray_trainer.py:1209-1506

每个 training step 的完整执行序列:

复制代码
for epoch in total_epochs:
  for batch in train_dataloader:
    |
    |  ① Pop generation keys → gen_batch
    |  ② 多轮 Rollout (multi_turn_loop)
    |  ③ Post-processing (pad, response_mask)
    |  ④ 奖励计算 (EpisodeRewardManager)
    |  ⑤ 重算旧策略 log_prob
    |  ⑥ 参考策略 log_prob (KL loss 用)
    |  ⑦ 优势估计 (compute_grpo_outcome_advantage)
    |  ⑧ Actor 策略更新 (PPO clipped loss + KL loss)
    |  ⑨ 日志记录 & checkpoint
    |  ⑩ 每 test_freq 步执行验证 + 动态技能更新
    v

各阶段详解

阶段 输入 输出 说明
① Pop keys batch (DataProto) gen_batch + batch (剩余) input_idsraw_prompt 等弹出生成所需字段
② Rollout gen_batch gen_batch_output 多轮环境交互,生成完整轨迹
③ Post-process gen_batch_output + batch 合并的 batch 计算 response_mask、平衡 batch
④ Reward batch reward_tensor 在最后 token 位置放置 episode 奖励
⑤ Old log prob batch old_log_prob 用当前 actor 权重重算 log prob
⑥ Ref log prob batch ref_log_prob 用初始(冻结的)策略算 log prob
⑦ Advantage batch advantages, returns GRPO 组内标准化
⑧ Actor update batch loss metrics PPO dual-clip + KL loss + entropy
⑨ Logging metrics wandb/console 汇报训练指标

4. 多轮交互 Rollout 流程

文件:agent_system/multi_turn_rollout/rollout_loop.py

4.1 入口:multi_turn_loop()

python 复制代码
def multi_turn_loop(self, gen_batch, actor_rollout_wg, envs, is_train=True):
    # 训练时:将每个任务复制 n 份(GRPO 分组)
    if is_train:
        gen_batch = gen_batch.repeat(repeat_times=self.config.env.rollout.n, interleave=True)

    if self.config.algorithm.filter_groups.enable and is_train:
        result = self.dynamic_multi_turn_loop(...)   # DAPO 动态采样
    else:
        result = self.vanilla_multi_turn_loop(...)    # 标准采样

    gen_batch_output = self.gather_rollout_data(...)
    return gen_batch_output

GRPO 分组原理env.rollout.n=8 时,parquet 中的 16 个条目被复制为 128 个,每连续 8 个共享相同的 uid(任务标识),对应 WebShop 中的同一个购物目标。训练时 8 个环境 worker 对同一任务各自独立探索,产生 8 条不同轨迹,GRPO 用这些轨迹的相对好坏来计算优势。

4.2 核心循环:vanilla_multi_turn_loop()

python 复制代码
def vanilla_multi_turn_loop(self, gen_batch, actor_rollout_wg, envs, ...):
    # === 阶段 A:环境初始化 ===
    obs, infos = envs.reset()  # 每个 worker 得到初始观察(含任务描述)
    batch_size = len(obs['text'])

    # 为 GRPO 分组分配 uid
    for i in range(batch_size):
        if i % self.config.env.rollout.n == 0:
            uid = str(uuid.uuid4())      # 新任务 → 新 uid
        uid_batch.append(uid)

    # === 阶段 B:逐步交互循环 ===
    for _step in range(self.config.env.max_steps):  # WebShop: 15步
        active_masks = np.logical_not(is_done)

        # B1: 将观察转为 token(应用 chat template)
        batch = self.preprocess_batch(gen_batch=gen_batch, obs=obs)

        # B2: 模型生成动作
        batch_output = actor_rollout_wg.generate_sequences(batch_input)
        text_actions = tokenizer.batch_decode(batch_output.batch['responses'],
                                               skip_special_tokens=True)

        # B3: 环境执行动作
        next_obs, rewards, dones, infos = envs.step(text_actions)

        # B4: 累积奖励
        episode_rewards[active_masks] += rewards[active_masks]
        episode_lengths[active_masks] += 1

        # B5: 存储轨迹数据
        batch.non_tensor_batch['rewards'] = rewards
        batch.non_tensor_batch['active_masks'] = active_masks
        for i in range(batch_size):
            total_batch_list[i].append(batch_list[i])

        # B6: 更新终止状态
        is_done = np.logical_or(is_done, dones)
        obs = next_obs

        if is_done.all():
            break

    # === 阶段 C:评估和汇总 ===
    success = envs.success_evaluator(...)
    return total_batch_list, episode_rewards, episode_lengths, success, ...

4.3 观察预处理:preprocess_single_sample()

每个环境步骤,观察被转化为模型输入:

复制代码
环境文本观察 (含任务、历史、可用动作)
    |
    v
构建单轮 chat: [{"role": "user", "content": obs_text}]
    |
    v
应用 chat template: tokenizer.apply_chat_template()
    |   示例 (Qwen): <|im_start|>user\n{obs_text}<|im_start|>assistant\n
    v
Tokenize → input_ids, attention_mask, position_ids
    |
    v
存入 DataProto (同时存 raw_prompt 供训练时重算 log_prob)

关键点 :模型在每个步骤看到的是一个全新的 user 消息 ,不包含之前自己的回复。

所有历史信息(之前的观察和动作)已经由 WebshopEnvironmentManager.build_text_obs() 编排进了 obs_text 中。

4.4 模型推理

文件verl/workers/rollout/vllm_rollout/vllm_rollout.py

通过 vLLM 推理引擎批量生成:

python 复制代码
output = self.inference_engine.generate(
    prompt_token_ids=idx_list,
    sampling_params=self.sampling_params,  # temperature=1.0 for train
)
response = output[0]    # (bs, response_length) 生成的 token IDs
log_probs = output[1]   # (bs, response_length) 每个 token 的 log 概率

生成的文本格式要求:

复制代码
<think...>推理过程...</think...><action>search[blue running shoe]</action>

5. WebShop 环境实现

5.1 环境创建

文件agent_system/environments/env_manager.py:865-888

python 复制代码
elif "webshop" in config.env.env_name.lower():
    from agent_system.environments.env_package.webshop import build_webshop_envs, webshop_projection
    # env_num = train_batch_size (16)
    # group_n = env.rollout.n (8)
    # 总 worker 数 = 16 × 8 = 128 个 Ray actor
    _envs = build_webshop_envs(env_num=..., group_n=..., is_train=True, ...)
    envs = WebshopEnvironmentManager(_envs, webshop_projection, config)

5.2 向量化环境:WebshopMultiProcessEnv

文件agent_system/environments/env_package/webshop/envs.py

组件 说明
WebshopWorker Ray 远程 actor,每个 worker 持有一个 WebAgentTextEnv 实例
WebshopMultiProcessEnv 向量化封装,管理 env_num × group_n 个 worker

训练/验证数据划分

  • 训练集:goal 索引 [500, len(goals))
  • 验证集:goal 索引 [0, 500)

Reset(分配任务)

python 复制代码
def reset(self):
    # 从训练集中随机采样 env_num 个不重复的 goal
    # 每个 goal 分配给 group_n 个 worker(同一任务,不同探索)
    indices = random.sample(train_indices, env_num)  # 不放回
    repeated_indices = [idx for idx in indices for _ in range(group_n)]  # 复制
    # 每个 worker reset 到各自的 goal
    obs, info = worker.reset(session=repeated_indices[i])

Step(执行动作)

python 复制代码
def step(self, action):
    obs, reward, done, info = self.env.step(action)
    info['available_actions'] = self.env.get_available_actions()
    info['task_score'] = reward  # 保存原始分数

    # 二值化奖励
    if done and reward == 1.0:
        info['won'] = True
        reward = 10.0     # 成功购买正确商品
    else:
        info['won'] = False
        reward = 0.0      # 其他所有情况
    return obs, reward, done, info

5.3 动作投影:webshop_projection()

文件agent_system/environments/env_package/webshop/projection.py

将模型文本输出解析为环境可执行的动作,同时验证格式:

python 复制代码
def webshop_projection(actions: List[str]):
    valids = [0] * len(actions)
    for i, action in enumerate(actions):
        # 1. 提取 <action>...</action> 之间的内容
        start = action.lower().find("<action>")
        end = action.lower().find("</action>")
        if start == -1 or end == -1:
            actions[i] = action[-20:]   # 回退:取最后20字符
            continue  # valids[i] 保持为 0

        extracted = action[start+8:end].strip().lower()
        actions[i] = extracted
        valids[i] = 1

        # 2. 检查 <think...></think...> 推理标签
        if "<think" not in action or "</think" not in action:
            valids[i] = 0

        # 3. 检查中文字符
        if re.search(r'[\u4e00-\u9fff]', action):
            valids[i] = 0

    return actions, valids

验证规则

  • 必须包含 <action>...</action> 标签
  • 必须包含 <think...></think...> 推理标签
  • 不能包含中文字符
  • 无效动作仍会发送到环境(使用截断的回退字符串),但 is_action_valid=0 会触发惩罚

5.4 环境管理器:WebshopEnvironmentManager

文件agent_system/environments/env_manager.py:538-718

这是环境系统的核心协调器,负责:

Reset 流程
复制代码
envs.reset() → 原始 WebShop 观察文本
    |
    v
extract_task(obs) → 从 "[SEP] Instruction: [SEP] Find me a..." 中提取任务描述
    |
    v
format_obs(obs) → 重新格式化观察文本
    |
    v
retrieval_memory.retrieve(task) → 检索相关技能(如启用)
    |
    v
build_text_obs(obs, infos, init=True) → 组装完整 prompt
    |
    v
memory.reset() → 重置动作历史
    |
    v
返回 {'text': [prompt_1, ...], 'image': None, 'anchor': [...]}
Step 流程
复制代码
text_actions (模型输出文本)
    |
    v
projection_f(text_actions) → (actions, valids)  # 解析动作
    |
    v
envs.step(actions) → (obs, rewards, dones, infos)
    |
    v
format_obs(obs) → 格式化
    |
    v
memory.store({text_obs: 上一观察, action: 执行的动作})  # 存入历史
    |
    v
build_text_obs(obs, infos) → 用模板组装下一步 prompt(含历史)
    |
    v
返回 (next_observations, rewards, dones, infos)
Prompt 模板

文件agent_system/environments/prompts/webshop.py

模板 使用时机 关键字段
WEBSHOP_TEMPLATE_NO_HIS 首步 / 观察过长时回退 task_description, current_observation, available_actions
WEBSHOP_TEMPLATE 后续步骤(有历史) + step_count, action_history, current_step
WEBSHOP_TEMPLATE_WITH_MEMORY 启用技能检索时 + retrieved_memories

带历史的模板示例

复制代码
You are an expert autonomous agent operating in the WebShop e-commerce environment.
Your task is to: Find me a blue running shoe under $50.

## Retrieved Relevant Experience
### General Principles
- **Prioritize Core Keywords**: Include product type, 1-2 key functional attributes...
### Mistakes to Avoid
- **Don't**: Buying without verifying all constraints.

## Current Progress
Prior to this step, you have already taken 3 step(s).
Below are the most recent 4 observations and the corresponding actions you took:
[Observation 1: '...', Action 1: 'search[blue running shoe]']
[Observation 2: '...', Action 2: 'click[Nike Air Max 90]']

You are now at step 4 and your current observation is: '[WebShop page content]'.
Your admissible actions of the current situation are: [
  search[<your query>]
  click[Back to Search]
  click[Buy Now]
].
Now it's your turn to take one action for the current step.
You should first reason step-by-step within <think ...> tags.
Once you've finished your reasoning, present your action within <action> </action> tags.

安全机制 :如果组装后的 prompt 超过 13000 字符,自动回退到 WEBSHOP_TEMPLATE_NO_HIS(丢弃历史)。

可用动作格式化
python 复制代码
def format_avail_actions(self, avail):
    actions = []
    if avail["has_search_bar"]:
        actions.append("search[<your query>]")
    for txt in avail["clickables"]:
        actions.append(f"click[{txt}]")
    return actions

6. 奖励计算

6.1 环境级奖励(二值化)

文件agent_system/environments/env_package/webshop/envs.py:40-55

情况 reward 说明
成功购买正确商品 10.0 done=True 且原始 reward=1.0
其他所有情况 0.0 错误商品、超时、导航错误等

原始 WebShop 的连续评分(属性匹配度)保存在 info['task_score'] 中,但不用于 RL 训练。

6.2 Episode 级奖励管理器

文件agent_system/reward_manager/episode.py

python 复制代码
class EpisodeRewardManager:
    def __call__(self, data_item):
        episode_rewards = data_item.non_tensor_batch['episode_rewards']
        episode_lengths = data_item.non_tensor_batch['episode_lengths']

        # 默认不按长度归一化
        score = episode_rewards  # (batch_size,) 每个 episode 的总奖励

        # 将奖励放置在 response 的最后一个有效 token 上(稀疏奖励)
        reward_tensor[i, valid_response_length - 1] = score[i]
        return reward_tensor

设计要点

  • 奖励是稀疏的------只有 episode 最后一个 token 有值
  • 中间步骤全部为 0
  • 对于 WebShop,episode_reward 只能是 0 或 10(单步成功即 10,否则累计为 0)

6.3 无效动作惩罚

文件verl/trainer/ppo/ray_trainer.py:200-224

use_invalid_action_penalty=True 时:

python 复制代码
# 在 GRPO 优势计算之前应用
apply_invalid_action_penalty(batch, penalty_coef=0.1)
# → 对于 is_action_valid=0 的步骤,从 token_level_scores 中减去 penalty

7. GRPO 优势估计

文件:verl/trainer/ppo/core_algos.py:113-174

算法步骤

复制代码
输入:
  token_level_rewards: (bs, response_length) --- 每个 token 的奖励
  response_mask:       (bs, response_length) --- 有效 token 掩码
  index:              (bs,) --- 组标识 uid(同一任务的轨迹共享)
  traj_index:         (bs,) --- 轨迹标识 traj_uid(区分组内不同轨迹)

Step 1: 归约为 outcome 分数
  scores = token_level_rewards.sum(dim=-1)  # (bs,) 每条轨迹的总奖励

Step 2: 按 uid 分组
  id2score[uid] = [score_1, score_2, ..., score_n]  # 同一任务的 n 条轨迹

Step 3: 计算组统计量
  group_mean = mean(id2score[uid])
  group_std  = std(id2score[uid])
  # 单条轨迹的组:mean=0, std=1(避免除零)

Step 4: 标准化
  advantage[i] = (scores[i] - group_mean[uid[i]]) / (group_std[uid[i]] + ε)

Step 5: 广播到 token 级别
  advantages = advantage.unsqueeze(-1) * response_mask  # (bs, response_length)

两种模式

模式 公式 配置
原始 GRPO A = (R - mean) / std norm_adv_by_std_in_grpo=True (默认)
Dr.GRPO A = R - mean (不除以 std) norm_adv_by_std_in_grpo=False

WebShop 示例(8 条轨迹,1 条成功):

复制代码
轨迹奖励: [0, 0, 0, 10, 0, 0, 0, 0]
mean = 10/8 = 1.25
std = 3.24

优势: [-0.39, -0.39, -0.39, 2.70, -0.39, -0.39, -0.39, -0.39]
       ↑ 抑制失败动作    ↑ 强化成功动作    ↑ 抑制失败动作

8. 策略更新 (Actor Update)

文件:verl/workers/actor/dp_actor.py:317-447

8.1 更新流程

复制代码
对于每个 ppo_epoch (默认 1):
  对于每个 mini_batch (64):
    对于每个 micro_batch (每 GPU 4):
      ① _forward_micro_batch() → new_log_prob, [entropy]
      ② 计算 PPO clipped loss
      ③ 计算 KL loss (如启用)
      ④ 计算 entropy loss
      ⑤ loss.backward()
    optimizer_step() → grad_clip + step()

8.2 损失函数

PPO Dual-Clip Policy Loss

python 复制代码
ratio = exp(new_log_prob - old_log_prob)
surr1 = -advantages * ratio
surr2 = -advantages * clip(ratio, 1-ε, 1+ε)  # ε = 0.2
policy_loss = max(surr1, surr2)
# 下界约束 (当优势为负时)
policy_loss = min(policy_loss, -advantages * c)  # c 默认为负的较大值

KL Lossuse_kl_loss=True 时):

python 复制代码
# kl_loss_type = "low_var_kl"
kl = kl_penalty(new_log_prob, ref_log_prob, type="low_var_kl")
total_loss = policy_loss + kl_loss_coef * kl

Entropy Lossentropy_coeff=0.001 时):

python 复制代码
total_loss = total_loss - entropy_coeff * entropy_loss

8.3 完整损失

复制代码
L = L_policy + 0.01 * L_kl - 0.001 * H

其中:

  • L_policy:PPO dual-clip 策略损失
  • L_kl:与初始策略的 KL 散度(防止策略偏移过大)
  • H:策略熵(防止策略坍塌)

9. 技能库系统

文件:agent_system/memory/skills_only_memory.py

9.1 技能库 JSON 结构

json 复制代码
{
    "general_skills": [
        {
            "skill_id": "gen_001",
            "title": "Short Title",
            "principle": "Core insight described in 1-2 sentences.",
            "when_to_apply": "Trigger condition description."
        }
    ],
    "task_specific_skills": {
        "apparel": [...],
        "footwear": [...],
        "electronics": [...],
        "home_decor": [...],
        "accessories": [...],
        "beauty_health": [...],
        "other": [...]
    },
    "common_mistakes": [
        {
            "mistake_id": "err_001",
            "description": "What went wrong",
            "why_it_happens": "Root cause",
            "how_to_avoid": "How to prevent it"
        }
    ]
}

9.2 任务类型检测

通过关键词匹配判断购物任务的类别:

类别 关键词
apparel shirt, dress, jacket, pant, coat, sweater, blouse, t-shirt
footwear shoe, boot, sneaker, sandal, heel, slipper
electronics laptop, phone, computer, tablet, charger, headphone, camera
accessories necklace, ring, bracelet, earring, watch, jewelry, bag, wallet
home_decor furniture, lamp, curtain, pillow, bedding, decor, vase, rug
beauty_health cream, lotion, shampoo, makeup, vitamin, supplement
other 以上均不匹配时的回退类别

9.3 Template 模式检索(默认)

python 复制代码
def retrieve(self, task_description, top_k=6):
    task_type = self._detect_task_type(task_description)

    # 分离动态技能和静态技能
    dynamic_skills = [s for s in general_skills if s['skill_id'].startswith('dyn_')]
    static_skills = [s for s in general_skills if not s['skill_id'].startswith('dyn_')]

    # 动态技能始终注入,静态技能填充剩余 top_k 配额
    n_static = max(0, top_k - len(dynamic_skills))
    result = dynamic_skills + static_skills[:n_static]

    # 全量添加该类别的 task-specific 技能
    result += task_specific_skills[task_type]

    # 添加前 5 条常见错误
    result += common_mistakes[:5]

    return result

动态技能优先级dyn_ 前缀的技能(教师模型生成)始终被注入,不受 top_k 限制。

9.4 技能注入格式

复制代码
### General Principles
- **Prioritize Core Keywords**: Include product type, 1-2 key functional attributes...
- **Verify Price Early**: Always check the product price before proceeding...

### Footwear Skills
- **Check Size Chart First**: Always verify size availability before selecting options...
  _Apply when: The task involves footwear with specific size requirements_

### Mistakes to Avoid
- **Don't**: Repeating the same action after it fails.
  **Instead**: Check the admissible actions list and try an alternative.

10. 动态技能更新(教师模型)

文件:agent_system/memory/skill_updater.py

10.1 触发条件

在每次验证(每 test_freq=5 个 epoch)后检查:

复制代码
对于每个任务类别的 success_rate:
  如果 ANY category_success_rate < update_threshold (0.4):
    触发动态更新

10.2 执行流程

复制代码
① 收集失败轨迹
   - 从验证数据中筛选 score <= 0 的轨迹
   - 提取任务描述、任务类型、对话步骤
   - 限制最多 10 条失败轨迹

② 调用 Azure OpenAI o3 模型
   - 发送 prompt 包含:失败轨迹示例 + 已有技能标题
   - 要求生成 1-3 条新技能
   - 返回 JSON 格式的技能列表

③ 添加到训练环境
   - 新技能使用 dyn_NNN 编号
   - 仅添加到训练环境(train_envs),不添加到验证环境(避免数据泄漏)

④ 保存到磁盘
   - 路径: {default_local_dir}/updated_skills_step{N}.json

10.3 教师 Prompt 示例

复制代码
Analyze these failed agent trajectories and suggest NEW skills.

FAILED TRAJECTORIES:
Example 1:
Task: Find me a blue running shoe under $50
Task Type: footwear
Trajectory (last 5 steps):
  Action: search[blue running shoe]
  Observation: [first 200 chars truncated...]

EXISTING SKILL TITLES (avoid duplicating):
- Prioritize Core Keywords
- Verify Price Early
- Check Size Chart First
...

Generate 1-3 NEW actionable skills. Each must have:
  skill_id, title (3-5 words), principle (1-2 sentences), when_to_apply.
Use skill_ids: "dyn_001", "dyn_002", "dyn_003".
Return ONLY a JSON array.

10.4 动态技能的持久化

复制代码
初始化时加载:
  skills.json → SkillsOnlyMemory
    ↓
  验证触发更新:
  o3 生成新技能 → 添加 dyn_NNN → 仅训练环境
    ↓
  保存到磁盘:
  updated_skills_step5.json (step 5 验证后)
  updated_skills_step10.json (step 10 验证后)

11. 验证流程与技能触发

文件:verl/trainer/ppo/ray_trainer.py:689-918

11.1 验证执行序列

复制代码
_validate():
  for test_batch in val_dataloader:

    ① 多轮 Rollout (is_train=False, val_envs)
       - 使用验证环境(goal 索引 [0, 500))
       - group_n=1(每个任务只跑 1 次)

    ② 评分 (val_reward_fn)

    ③ 聚合指标
       - test_score: 平均 episode 奖励
       - success_rate: 各类别成功率
       - tool_call_count: 工具调用统计

  ④ 动态技能更新(如启用且满足阈值条件)
     - _update_skills_from_validation()

11.2 成功率计算

文件agent_system/environments/env_manager.py:709-718

python 复制代码
def _process_batch(self, ...):
    # 找到最后一个活跃步骤
    for i in reversed(range(len(batch_list))):
        if active_masks[i]:
            won = infos[i]['won']
            score = infos[i]['task_score']
            success['success_rate'].append(won)
            success['webshop_task_score (not success_rate)'].append(score)
            break

12. 端到端数据流汇总

单个训练 Step 的完整数据流

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    1. BATCH LOADING                                 │
│  train_dataloader → batch_dict → gen_batch (16 条)                 │
│  gen_batch.repeat(n=8) → 128 条 (每任务 8 份)                      │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    2. ENVIRONMENT RESET                              │
│  128 个 WebShop Worker 各自 reset 到 16 个不同购物任务              │
│  提取任务 → 检索技能 → 组装 prompt (WEBSHOP_TEMPLATE_WITH_MEMORY)   │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    3. MULTI-TURN LOOP (最多 15 步)                   │
│                                                                     │
│  ┌── Step N ────────────────────────────────────────────────────┐   │
│  │  a. obs → chat template → tokenize → input_ids             │   │
│  │  b. vLLM generate → response tokens → decode → text_action  │   │
│  │  c. webshop_projection → parse <action> → validate tags     │   │
│  │  d. env.step(action) → next_obs, reward(0/10), done, info  │   │
│  │  e. 存储历史 → 组装下一步 prompt                            │   │
│  │  f. done? → is_done[i] = True → 该 worker 不再参与          │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  输出: 128 条轨迹,每条包含最多 15 个 step 的数据                   │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    4. REWARD COMPUTATION                            │
│  EpisodeRewardManager:                                             │
│    episode_reward ∈ {0, 10} 放在最后有效 token 上                   │
│  无效动作惩罚: token_score -= 0.1 * (1 - is_action_valid)           │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    5. GRPO ADVANTAGE                                │
│  按 uid 分组 (同任务 8 条轨迹):                                     │
│    A[i] = (R[i] - mean(R_group)) / std(R_group)                   │
│  成功轨迹 advantage > 0 → 强化                                      │
│  失败轨迹 advantage < 0 → 抑制                                      │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    6. ACTOR UPDATE                                  │
│  L = L_ppo_clip + 0.01 * L_kl(ref_policy) - 0.001 * H(entropy)    │
│  FSDP 分布式更新 → 梯度裁剪 → 优化器 step                           │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────────┐
│                    7. VALIDATION (每 5 个 step)                      │
│  64 个验证 Worker,每任务 1 次探索                                   │
│  计算各类别 success_rate                                             │
│  如果 success_rate < 0.4:                                           │
│    收集失败轨迹 → Azure o3 分析 → 生成新技能 → 注入训练环境          │
│    保存 updated_skills_step{N}.json                                  │
└─────────────────────────────────────────────────────────────────────┘

轨迹数据结构

每个 step 的单条数据 (DataProto 中的一个元素):

字段 类型 说明
input_ids Tensor prompt 的 token IDs
responses Tensor 模型生成的 action token IDs
rollout_log_probs Tensor 生成时每个 token 的 log 概率
attention_mask Tensor 注意力掩码
position_ids Tensor 位置编码
episode_rewards scalar 该 episode 的总奖励 (0 或 10)
episode_lengths scalar 该 episode 的实际步数
uid str GRPO 组标识(同任务共享)
traj_uid str 轨迹唯一标识(组内区分)
is_action_valid int 动作格式是否合法 (0/1)
rewards scalar 该步的环境奖励
active_masks bool 该步是否还在活跃状态

关键文件速查

模块 文件 核心内容
训练入口 verl/trainer/main_ppo.py Hydra 启动、环境/Worker 创建
训练循环 verl/trainer/ppo/ray_trainer.py fit(), _validate(), GRPO 分发
GRPO 算法 verl/trainer/ppo/core_algos.py compute_grpo_outcome_advantage()
策略更新 verl/workers/actor/dp_actor.py update_policy(), PPO loss
模型推理 verl/workers/rollout/vllm_rollout/vllm_rollout.py generate_sequences()
多轮循环 agent_system/multi_turn_rollout/rollout_loop.py vanilla_multi_turn_loop()
环境管理 agent_system/environments/env_manager.py WebshopEnvironmentManager
WebShop 环境 agent_system/environments/env_package/webshop/envs.py WebshopWorker, WebshopMultiProcessEnv
动作解析 agent_system/environments/env_package/webshop/projection.py webshop_projection()
Prompt 模板 agent_system/environments/prompts/webshop.py 三套 prompt 模板
奖励管理 agent_system/reward_manager/episode.py EpisodeRewardManager
技能检索 agent_system/memory/skills_only_memory.py SkillsOnlyMemory, 任务分类, 模板检索
动态更新 agent_system/memory/skill_updater.py SkillUpdater, Azure o3 调用
动作历史 agent_system/memory/memory.py SimpleMemory
默认配置 verl/trainer/config/ppo_trainer.yaml 所有超参数默认值
WebShop 脚本 examples/grpo_trainer/run_webshop_skills.sh 完整启动命令
相关推荐
吃好睡好便好1 小时前
在Matlab中绘制抛物三维曲面图
开发语言·人工智能·学习·算法·matlab·信息可视化
伯远医学2 小时前
Nat. Methods | 邻近标记技术:活细胞中捕捉分子互作的新利器
java·开发语言·前端·javascript·人工智能·算法·eclipse
嘉琪0012 小时前
本地项目从0到1(AI协作实操指南)
人工智能
IC_157796114762 小时前
LVDS 转 MIPI CSI/MIPI DSI,支持图像 90° /270° 旋转 处理图像处理芯片
图像处理·人工智能
能源革命2 小时前
解读《关于促进人工智能与能源双向赋能的行动方案》通知
人工智能·能源
SeatuneWrite2 小时前
动态漫软件2026推荐,助力高效创作体验
人工智能·python
大模型推理2 小时前
Nano-vLLM 源码解读 - 5. Prefix Cache
人工智能
AC赳赳老秦2 小时前
文案策划提效:OpenClaw批量生成活动文案、宣传海报配文,适配不同渠道调性
java·大数据·服务器·人工智能·python·deepseek·openclaw
闵孚龙2 小时前
Claude Code 状态恢复机制全解析:自动压缩后文件、技能、计划与 Agent 上下文如何不断片?
人工智能·架构·claude