首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >16-AgentLoop为什么RL训练需要token-in-token-out

16-AgentLoop为什么RL训练需要token-in-token-out

作者头像
anzhsoft
发布2026-07-06 14:22:59
发布2026-07-06 14:22:59
30
举报

上一篇说明了 rollout 不只是调用后端生成,而是在训推共卡时管理显存状态。本文继续往上看一个更容易被低估的变化:当 rollout 从单轮补全变成 agent loop,多轮工具调用、环境反馈和中间 observation 都必须进入训练轨迹。

本文的核心判断是:agent RL 不能只保存最终回答文本,而必须坚持 token-in/token-out。训练系统需要的是 prompt token、assistant token、tool observation token、response mask、attention mask、position ids、logprobs、reward 和 extra fields;只有这些字段完整,multi-turn rollout 才能复用 PPO/GRPO 的后续训练路径。

先看 agent loop 在 rollout 架构中的位置。读这张图时注意:AgentLoopWorker 不只是把 prompt 转发给 LLM server,它还负责在 LLM、工具/环境和 DataProto 之间维护一条 token 级轨迹。

这张图对应 trainer 初始化路径:`RayPPOTrainer` 创建 `LLMServerManager`,再把 `llm_server_manager.get_client()` 传给 `AgentLoopManager.create()`,让 agent loop worker 通过 `LLMServerClient` 调用推理服务(`verl/trainer/ppo/ray_trainer.py:842-868`)。也就是说,agent loop 不是绕过 rollout,而是 rollout 服务层上的一个多轮执行框架。

## 1. AgentLoopOutput 已经不是文本返回值

`verl.experimental.agent_loop` 的文件头明确把它定义为 multi-turn rollout 和 agentic reinforcement learning 的 agent framework,并列出 `AgentLoopBase`、`SingleTurnAgentLoop`、`ToolAgentLoop`、`AgentLoopWorker` 和 `AgentLoopManager`(`verl/experimental/agent_loop/agent_loop.py:14-28`)。它的核心输出 `AgentLoopOutput` 也不是字符串,而是一组 token 级字段:`prompt_ids`、`response_ids`、`response_mask`、`response_logprobs`、`multi_modal_data`、`reward_score`、`num_turns`、metrics 和 extra fields(`verl/experimental/agent_loop/agent_loop.py:88-145`)。

`SingleTurnAgentLoop` 也遵守这个合同。它从 `raw_prompt` 抽取多模态输入,apply chat template 得到 `prompt_ids`,通过 server manager 生成 `TokenOutput`,然后返回 `prompt_ids`、`response_ids`、`response_mask` 和 logprobs(`verl/experimental/agent_loop/single_turn_agent_loop.py:37-93`)。所以即便是单轮 agent,也已经是 token-in/token-out,而不是 text-in/text-out。

## 2. 多轮工具调用是一个状态机

下面这张图把 `ToolAgentLoop` 的运行过程压成状态机。看图时重点看循环:LLM 生成后如果解析出 tool call,就进入工具处理;工具 observation 被追加回上下文,然后再次生成。

源码里,`ToolAgentLoop.run()` 创建 `AgentData`,然后在 `PENDING -> GENERATING -> PROCESSING_TOOLS -> TERMINATED` 状态之间循环(`verl/experimental/agent_loop/tool_agent_loop.py:118-200`)。`_handle_pending_state()` 用当前 messages 和 tool schemas 生成 `prompt_ids`;`_handle_generating_state()` 调用 LLM server,追加 assistant token,把这些 token 的 `response_mask` 记为 1,并根据是否提取到 tool calls 决定继续还是结束(`verl/experimental/agent_loop/tool_agent_loop.py:202-271`)。

工具阶段则相反:`_handle_processing_tools_state()` 执行工具,把 tool response 追加到 messages,再把 tool response 经过 chat template 变成 token ids,追加到 `prompt_ids`,并把这些 observation token 的 `response_mask` 记为 0(`verl/experimental/agent_loop/tool_agent_loop.py:273-374`)。这就是 token 级轨迹不可省略的原因:同样在 response 区间里,有些 token 是模型生成的,有些 token 是环境返回的,它们对 loss 的意义不同。

## 3. response_mask 是 agent RL 的分界线

下面这张图把多轮轨迹拆成 token 段。它和状态机图互补:状态机解释“谁调用谁”,这张图解释“训练时哪些 token 被当成模型输出,哪些只是上下文 observation”。

`AgentLoopWorker.generate_sequences()` 的 docstring 已经把这个合同写成例子:multi-turn conversations 中,`responses` 包含 LLM generation 和 tool_calls,`response_mask` 对 LLM generated tokens 是 1,对 observation/padding tokens 是 0(`verl/experimental/agent_loop/agent_loop.py:450-470`)。

这条线直接影响 actor loss。工具 observation token 需要进入 `input_ids`、`attention_mask` 和 `position_ids`,因为后续 assistant turn 要把它当上下文;但它不应该像 assistant token 一样承担 policy gradient。没有 `response_mask`,训练系统就无法区分“模型说了什么”和“环境给了什么”。

## 4. agent output 如何回到 DataProto

最后看 agent loop 如何重新接上通用 RL 流水线。下面这张图把 `AgentLoopOutput` 到 `DataProto` 的归一化过程画出来。看图时注意:这里的关键不是 padding 本身,而是把变长多轮轨迹重新变成 downstream 能消费的固定字段。

`AgentLoopWorker.generate_sequences()` 会为 batch 中每个样本创建异步任务,执行 `_run_agent_loop()`,再把输出交给 `_postprocess()`(`verl/experimental/agent_loop/agent_loop.py:450-542`)。`_agent_loop_postprocess()` 会左 pad prompt、右 pad response,拼出 `input_ids` 和 `attention_mask`,计算 `position_ids`,处理多模态 inputs,并可计算 reward/teacher logprobs(`verl/experimental/agent_loop/agent_loop.py:579-729`)。

最终 `_postprocess()` 把所有样本 stack 成 DataProto:tensor batch 里有 `prompts`、`responses`、`response_mask`、`input_ids`、`attention_mask`、`position_ids`,可选 `rollout_log_probs`、`routed_experts`、teacher 字段和 `rm_scores`;non-tensor batch 里保留 `__num_turns__`、reward extra info、multi_modal_inputs、turn_scores、tool_rewards 等 extra fields(`verl/experimental/agent_loop/agent_loop.py:895-986`)。

这就是为什么 agent loop 可以接回第 6 篇建立的一轮 PPO/GRPO step:它把多轮轨迹规整成与普通 rollout 相同的数据合同,后续 reward、old/ref logprob、advantage 和 actor update 才能继续运行。

## 5. tokenization 一致性是多轮的硬约束

多轮还有一个隐藏问题:同一段对话逐轮 tokenization,必须和整体对话 tokenization 保持一致。rollout 配置里,`multi_turn.enable` 用于打开多轮工具任务,并提示这类任务应使用 SGLang;`use_inference_chat_template` 控制是否使用模型默认 chat template;`tokenization_sanity_check_mode` 则用于检查逐轮拼接 token 和整体 tokenization 是否一致(`verl/trainer/config/rollout/rollout.yaml:164-225`)。

`AsyncRolloutRequest` 也体现了同一约束。它保存 messages、tool schemas、多模态数据、`input_ids`、mask、position ids、prompt/response ids、reward scores 和长度上限,并在初始化时用 chat template 生成 tokenized prompt、attention mask、position ids 和 generation prompt 边界(`verl/workers/rollout/schemas.py:81-220`)。这说明 agent loop 的核心不是“循环调用 LLM”,而是在每一轮后仍能证明 token 边界没有漂移。

## 小结:agent RL 的最小单位是 token 轨迹

本文把 agent loop 从“多轮对话框架”重新放回 RL 训练系统。它真正提供的是这条合同:

```text

messages / tools / multimodal inputs

-> prompt_ids

-> LLM token_ids + logprobs

-> tool observation token_ids + mask=0

-> padded DataProto

-> reward / logprob / actor update

```

读到这里,第三组已经从 rollout 服务、backend 抽象、显存复用走到了多轮轨迹。下一篇会继续拆工具和 sandbox:当 tool response、tool reward、状态和环境反馈进入轨迹后,reward 为什么也不再只是模型输出后的一个分数函数。

## 本文源码索引

- `verl/trainer/ppo/ray_trainer.py:842-868`:trainer 创建 LLM server manager 和 AgentLoopManager。

- `verl/experimental/agent_loop/agent_loop.py:14-28`:agent loop framework 的定位。

- `verl/experimental/agent_loop/agent_loop.py:88-145`:`AgentLoopOutput` 的 token 级输出字段。

- `verl/experimental/agent_loop/single_turn_agent_loop.py:37-93`:单轮 agent loop 如何返回 token output。

- `verl/experimental/agent_loop/tool_agent_loop.py:118-200`:ToolAgentLoop 的主状态机循环和最终输出。

- `verl/experimental/agent_loop/tool_agent_loop.py:202-271`:pending/generating 阶段如何生成 prompt、调用 LLM、追加 assistant tokens。

- `verl/experimental/agent_loop/tool_agent_loop.py:273-374`:工具响应如何追加回 messages、prompt ids 和 `response_mask=0`。

- `verl/experimental/agent_loop/agent_loop.py:450-542`:AgentLoopWorker 如何并发运行 batch 内的 agent loops。

- `verl/experimental/agent_loop/agent_loop.py:579-729`:单条 agent output 如何 padding 成训练字段。

- `verl/experimental/agent_loop/agent_loop.py:895-986`:多个 agent outputs 如何合并成 DataProto。

- `verl/trainer/config/rollout/rollout.yaml:164-225`:multi-turn、tool、tokenization sanity 和 rollout logprob 配置。

- `verl/workers/rollout/schemas.py:81-220`:async rollout request 的消息、工具、多模态和 token 边界字段。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 训推工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档