【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 完整启动命令
相关推荐
IT_陈寒1 小时前
Vue这个坑我跳了两次,原来问题出在这
前端·人工智能·后端
新新技术迷1 小时前
Node给AI接口做SSE代理与鉴权
人工智能
redreamSo2 小时前
大模型是不是到顶了?瓶颈到底在哪
人工智能·openai
Oo9202 小时前
Tool Use 背后的技术逻辑
人工智能
姗姗来迟了2 小时前
Vue3封装AI流式对话组件踩坑实录
人工智能
码上天下3 小时前
用Pinia管理AI多会话状态
人工智能
用户054324329703 小时前
Next.js接大模型流式SSE实操踩坑
人工智能
Assby3 小时前
从 Function Calling 到 MCP:理解 Agent 工具调用的底层通信机制
人工智能·后端
小星AI4 小时前
Claude Code 从入门到精通,一步到位
人工智能
后端小肥肠4 小时前
Codex + Obsidian 做人生副本视频:输入主题文案,直通剪映草稿
人工智能·aigc·agent