verl Agentic RL 训练全指南基于异步 Rollout、多轮对话与工具调用的智能体强化学习实践【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl导读本文以 verlHybridFlow官方文档 docs/start/agentic_rl.rst 为主线系统讲解如何在 verl 中构建 Agentic RL面向智能体的强化学习训练流水线通过基于 Ray Actor 的 Server 端异步 Rollout 机制消除工具调用等待期间的 GPU 空转通过多轮对话与工具调用让模型学会使用外部工具并通过 LangGraph 等 Agent 框架扩展自定义智能体循环。读完本文你将掌握从数据集预处理、工具注册、训练脚本配置到 trace 调试的完整实操链路并理解 token 级 API 设计、负载均衡、delta 式分词等底层原理。一、Agentic RL 的三大能力支柱Agentic RL 的目标是用强化学习提升后端模型policy 模型作为 Agent 完成任务的能力。与单轮问答式 RL 不同Agent 需要与环境反复交互发起工具调用、等待工具结果、继续推理、再调用……这个过程引入了一个关键矛盾——如果按同步方式串行执行模型在等待工具返回结果时 GPU 会完全空闲训练吞吐被拖垮。为此verl 在训练过程中围绕三个方向构建了一整套能力这也是本仓库中 Agentic RL 的全部内涵基于 Server 的异步 RolloutServer-based asynchronous rollout用 asyncio 协程机制并发调度每个 rollout 请求避免 GPU 空转。多轮对话与工具调用Multi-turn conversations and tool calls让模型能在对话过程中按需调用工具并消化工具返回结果。基于 LangGraph 的 Agent 框架LangGraph-based Agent提供可插拔的 Agent 循环抽象支持用户自定义复杂 Agent 行为。下面依次深入每一块先讲系统原理再给可复现的实操步骤。二、Server 端异步 Rollout原理与架构2.1 为什么必须把推理引擎与 Agent 拆开Agent 通过各类工具调用与环境交互为了不让 GPU 在等待工具返回结果时空闲verl 引入基于 asyncio 的协程路由机制将每个 rollout 请求异步执行。异步 rollout 要求推理引擎Server与 AgentClient在架构上分离其设计目标有两点负载均衡在多 GPU 间平衡负载降低长尾请求long-tail requests对整体性能的影响。为此verl 以 recipe 的形式实现了流式调度能力recipe/stream_mode。隔离性防止 Agent 特有功能如 trace 追踪影响推理引擎本身的稳定与性能。2.2 系统组件整个异步 rollout 体系由三个核心组件构成职责划分如下组件角色AgentLoop客户端Client实现 Agent 功能即用户定义的智能体循环LLMServerClient推理网关Inference gateway为 AgentLoop 提供generate接口AsyncServer服务端Server每个实例连接推理引擎的一个 DP数据并行组其中AgentLoop的核心抽象位于 verl/experimental/agent_loop/agent_loop.py它定义了三个层次的类AgentLoopBase协程化的抽象基类用户只需实现run方法AgentLoopWorker并行运行 agent loop 协程的 worker 类AgentLoopManager并行管理多个 worker 的 manager 类。从源码注释可以看到AgentLoopManager是 verl 中一个可完全替换的 Agent 框架实现NVIDIA Nemo-Gym、AWS Bedrock AgentCore、SWE-agent 等外部框架都可以作为替代品接入。2.3 为什么用 token 级的 generate 接口而不是 Chat Completion APIClient 与 Server 之间基于 Ray Actor 使用generate函数通信而非标准的 Chat Completion API原因在于token 与文本之间的转换是不可逆的例如从think这串文本转换出的 token与 LLM 实际生成的 token 往往不是同一组训练阶段必须严格使用 LLM 推理产出的 token否则 advantage优势函数计算会失真进而影响模型性能由 Server 提供 token 级 API可以让 Client 维护工具调用产生的文本与LLM 返回的 token之间的对应关系从而为训练输出正确的 token。这套设计在 docs/advance/agent_loop.rst 中有更完整的论述文档指出几乎所有 Agent 框架LangGraph、CrewAI、LlamaIndex 等都通过 OpenAI Chat Completion API 调用 LLM 并保存消息历史但 verl 在 DAPO 单轮训练和 retool 多轮训练的实践中发现对最终消息应用 chat template 后得到的 token_ids与逐轮拼接 prompt_ids 和 response_ids 得到的 token_ids 并不相等。这种不一致对 serving/agent 系统无伤大雅但对 RL 训练是致命的——它会让轨迹偏离 policy 模型的真实分布甚至导致单轮 PPO 训练不收敛。工具解析器改写内容、decode-encode 往返损失是产生不一致的两大来源。2.4 推理引擎适配SGLang 与 vLLM 的差异AsyncServer向上层统一提供generate函数针对 SGLang 和 vLLM 各有一套实现以屏蔽底层差异引擎实现方式SGLang使用 SGLang 引擎的async_generate接口该接口位于每个 TP 组的首张 GPU 上因此AsyncServer需要通过 Ray Actor远程调用async_generatevLLM使用 vLLM 引擎的generate接口该接口通过ZMQ与 TP 组内的 GPU 通信因此可以在AsyncServer进程中直接调用2.5 AgentLoopWorker 的调度细节在 verl/experimental/agent_loop/agent_loop.py 的AgentLoopWorker.generate_sequences实现中可以看到完整的并发调度逻辑默认假设为单轮 Agent如果 batch 中没有agent_name字段则用config.agent.default_agent_loop填充为 batch 中每个样本asyncio.create_task一个_run_agent_loop协程再通过asyncio.gather并发执行采样参数temperature、top_p、top_k、repetition_penalty1.0、logprobs统一从 rollout 配置中提取验证validate阶段会覆盖为val_kwargs中的贪婪采样参数。一个值得注意的调度技巧来自官方文档的 tip如果AgentLoopWorker的数量等于 batch_size那么每个 worker 恰好负责一条 prompt多协程并发调度可以让 GPU 始终处于忙碌状态。2.6 一个 PPO step 的完整 rollout 时序综合 docs/advance/agent_loop.rst 的描述一个 PPO step 的 rollout 阶段分为以下 8 步PPOTrainer从数据集中采样一个 batch调用AgentLoopManager.generate_sequencesAgentLoopManagerwake_up所有异步 LLM Server 实例将推理引擎vLLM/SGLang与训练引擎FSDP/Megatron-LM之间的权重同步AgentLoopManager将 batch 切分为多个 chunk分发给AgentLoopWorkerAgentLoopWorker收到 chunk 后为每条 prompt 实例化一个用户自定义的AgentLoopBase实例运行run协程直至结束得到AgentLoopOutputAgent loop 中需要 LLM 生成时调用LLMServerClient.generate(prompt_ids)LLMServerClient第一轮选择请求数最少的 Server 实例发送请求后续轮次会发送到同一个 Server 实例即 sticky sessionAsyncLLMServer收到请求后通过 IPC/RPC 与 model_runner 交互并生成响应vLLM 与 SGLang 略有差异见 2.4 节所有 AgentLoopWorker 完成全部 prompt 后AgentLoopManager汇总结果返回PPOTrainer然后sleep所有 Server 实例——释放 KV cache 并把权重 offload 到 CPU 内存。2.7 AgentLoopOutput 与 response_maskAgentLoopOutput是 agent loop 的标准化输出见 agent_loop.py 中AgentLoopOutput类定义包含以下核心字段字段含义prompt_idsPrompt token idsresponse_ids响应 token ids同时包含 LLM 生成 token 与工具响应 tokenresponse_mask响应掩码1 表示 LLM 生成的 token0 表示工具响应 tokenresponse_logprobs响应 token 的 log 概率multi_modal_data多模态工具返回的图片/视频/音频数据reward_score轨迹的奖励分数num_turns对话轮数含 user、assistant、toolresponse_mask是多轮训练的关键它保证 loss 只覆盖 LLM 自己生成的 token工具返回的内容虽然参与 attention但不参与 loss 计算。在_agent_loop_postprocess的注释中给出了一个直观的例子当响应序列为[5,6,7,(tool start)8,9(tool end),10,11,12]时response_mask形如[1,1,1,(tool start),0,0(tool end),1,1]。另外多轮 agent loop 输出的数据还会额外附带num_turns、turn_scores、tool_rewards等字段由ToolAgentLoop.run写入extra_fields供奖励设计与调试使用。三、快速上手GSM8K 单轮 Agentic RL异步 Rollout3.1 准备数据集与模型按照 GSM8K 示例 准备数据集和模型 checkpoint 即可。核心要点数据集需预处理为 parquet 格式模型 checkpoint 需为 HuggingFace 格式本文示例为Qwen/Qwen3-8B。3.2 开启 Agent Loop 的两个必要配置使用 agent loop 必须设置两个选项data.return_raw_chat: True actor_rollout_ref.rollout.mode: asyncdata.return_raw_chatTrue让数据集保留原始 chat 消息结构[{role: ..., content: ...}]因为 agent loop 需要基于消息列表而非拼接文本进行交互actor_rollout_ref.rollout.modeasync开启异步 rollout 模式走 server-based 异步推理路径。3.3 启动训练示例脚本默认使用 SGLang 推理引擎也可以通过修改rollout_name切换到 vLLMbash examples/grpo_trainer/run_qwen3_8b_fsdp.sh以 examples/grpo_trainer/run_qwen3_8b_fsdp.sh 为例该脚本默认配置了 GRPO FSDP 训练 Qwen3-8B关键参数包括INFER_BACKEND默认vllmrollout 后端可选vllm | sglang | trtllmactor_rollout_ref.rollout.name对应推理后端名actor_rollout_ref.rollout.tensor_model_parallel_sizeTP 大小GPU 默认 2NPU 默认 4actor_rollout_ref.rollout.n5每个 prompt 采样 5 条轨迹GRPO 需要data.max_prompt_length/data.max_response_length输入输出长度上限data.filter_overlong_promptsTrue、data.truncationerror超长 prompt 的处理策略。该脚本同时支持 NVIDIA GPU 与 Ascend NPU通过torch_npu自动探测 DEVICENPU 下会自动启用参数/优化器 offload 与 sequence parallel。四、多轮对话与工具调用4.1 工具与配置文件准备多轮工具调用 Rollout 的准备工作参考 Multi-turn Rollout Support。启用多轮 rollout 的基础配置actor_rollout_ref: rollout: multi_turn: True name: sglang这会激活 SGLang 引擎用于 rollout 阶段的多轮交互。4.2 Tool Agent Loop 与 agent_name 字段Tool Agent Loop 相比单轮 agent 多了一个额外要求在数据集中添加agent_name字段。rollout 时会根据该字段选择使用tool_agent_loop还是single_turn_agent默认。在源码 agent_loop.py 的_agent_loop_registry中可以看到注册机制tool_agent与single_turn_agent都是注册名AgentLoopWorker._run_agent_loop会断言agent_name in _agent_loop_registry否则直接报错并列出所有已注册的 agent loop。用户也可以通过rollout.agent.agent_loop_config_path加载自定义 agent loop 配置见AgentLoopWorker.__init__中的OmegaConf.load逻辑。4.3 完整实操GSM8K 工具 MLflow trace官方文档给出了一条完整的端到端命令序列# 安装 mlflow 以查看 toolcall 和 llm trace pip install mlflow # 下载并预处理 GSM8K 数据集到 ~/data/gsm8k/并添加 agent_name 字段 python examples/data_preprocess/gsm8k_tool_agent_loop.py # 启动带工具调用并启用基于 mlflow 的 trace 的训练便于调试 rollout 细节 bash examples/sglang_multiturn/run_qwen2_5_3b_gsm8k_tool_agent_mlflow_fsdp.sh # 训练完成后启动 mlflow server 查看 trace mlflow ui -h 0.0.0.0 -p 5000 --backend-store-uri sqlite:////tmp/mlruns.db # 然后在浏览器中打开 http://your ip address:5000 查看 trace这里以 examples/data_preprocess/gsm8k_tool_agent_loop.py 为例解读数据预处理的关键逻辑从openai/gsm8k加载数据提取####后的最终答案作为 ground truth每条数据写入agent_name: tool_agent字段system prompt 要求模型逐步推理后再调用calc_gsm8k_reward工具在生成最终答案前至少调用一次并根据需要修正答案最终以#### answer格式输出extra_info.tools_kwargs中为calc_gsm8k_reward工具注入了create_kwargs: {ground_truth: solution}实现按样本注入工具参数verifed 工具在创建时需要知道该题的正确答案。注意训练过程中因为模型有时无法生成正确的 toolcall 标签控制台会输出Failed to decode tool call错误信息这不代表训练异常属于正常现象。关于 trace 功能的更多细节参见 Rollout trace。4.4 工具注册的两种方式方式一基于BaseTool的有状态工具基于 verl/tools/base_tool.py 的BaseTool实现自己的工具然后在 YAML 配置文件中指定tools: - class_name: config: type: native tool_schema:并通过 rollout 配置启用actor_rollout_ref: rollout: tool_kwargs: tools_config_file: path_to_tool_yaml_fileBaseTool工具拥有完整的create/execute/release生命周期。每个轨迹trajectory都会创建独立的工具实例适合需要按轨迹隔离状态的场景沙箱虚拟机、临时目录、数据库行等。它支持从数据集注入tools_kwargs如 4.3 节中的calc_gsm8k_reward例子。如果工具会产出多模态输入应在execute()实现中返回多模态输入列表。图像和视频需要在返回前完成处理例如 Qwen2.5-VL 场景from verl.utils.dataset.vision_utils import process_image, process_video async def execute(self, ...) - tuple[str | dict[str, Any], float, dict]: img1 process_image(img1) video1 process_video(video1) # 注意vLLM 中 (image | video) 的 key 是 (image | video) 而非 (images | videos) return ToolResponse(image[img1, ...], video[video1, ...], text...), 0, {}同时需要在数据集配置中设置return_multi_modal_inputs: False让 rollout 阶段动态处理多模态输入详见 4.7 节。方式二基于function_tool的无状态函数工具对于无状态工具定义一个完整的BaseTool子类加 YAML schema 显得过重。verl 提供了function_tool装饰器见 verl/tools/function_tool.py可以直接把普通 Python 函数注册为工具schema 推断交给transformers.utils.get_json_schema读取函数签名与 Google 风格 docstringactor_rollout_ref: rollout: mode: async multi_turn: enable: True format: hermes # 或你的模型 chat template 支持的其他格式 function_tool_path: path/to/your_tools.py agent: default_agent_loop: tool_agent工具定义示例来自 multiturn.rst 与 tests/experimental/agent_loop/function_tool_examples.pyfrom verl.tools.function_tool import function_tool function_tool def get_weather(city: str) - dict: Get the current weather for a city. Args: city: The city to look up, e.g. Tokyo or San Francisco. return {temperature_c: 17.3, condition: drizzle} function_tool(calculator) # 显式命名会覆盖函数名 def calculator(expression: str) - str: Evaluate a Python-style arithmetic expression. Args: expression: A Python-style arithmetic expression, e.g. (34)*5. return str(eval(expression, {__builtins__: {}}, {}))schema 推断规则参数类型来自函数的类型注解支持泛型与联合类型list[T]/dict[K, V]、Optional[X]/X | None生成nullable、int | float生成 JSON[integer, number]、Literal[a, b]生成enum每个参数的描述来自 docstring 的Args:部分没有默认值的参数标记为required函数可以是 sync 或 asyncsync 函数通过asyncio.to_thread派发不会阻塞事件循环*args/**kwargs无法在 JSON Schema 中表达注册时会直接报错可变长输入请改用param: list[T]也可以给装饰器传schema参数完全绕开推断直接提供自定义的OpenAIFunctionToolSchema或同构 dict。返回值会被统一归一化返回值归一化结果str包装为ToolResponse(text...)dictJSON 序列化进ToolResponse(text...)ToolResponse原样透传(response, reward)或(response, reward, metrics)元组解包Nonereward / metrics 视为0.0/{}function_tool_path与tool_config_path可以同时设置AgentLoopWorker启动时会将两者合并进同一个注册表两个路径下重名会直接报错。RLHFDataset复用同一加载器因此 prompt 长度过滤看到的工具 schema 与 rollout 实际使用的完全一致。4.5 ToolAgentLoop 的状态机verl/experimental/agent_loop/tool_agent_loop.py 中ToolAgentLoop用状态机驱动整个多轮循环四个状态依次流转PENDING - GENERATING - PROCESSING_TOOLS - GENERATING - ... - TERMINATEDPENDING通过 Continuous Token builder 构建初始 prompt token idsct_build_initial_tokens多模态输入在此阶段被渲染进 token 序列GENERATING调用server_manager.generate生成模型响应。此阶段会把工具解析器的 stop token 注入stop_token_ids让生成在每次工具调用后自动停止然后用ct_merge_assistant_token把新生成的 assistant token 合并进运行时 token 序列最后用tool_parser.extract_tool_calls解析是否包含工具调用——有工具调用则进入PROCESSING_TOOLS否则终止PROCESSING_TOOLS并发执行工具调用上限为max_parallel_calls把{role: tool, ...}消息追加进对话历史用ct_merge_context_msg合并工具响应 token随后回到GENERATINGTERMINATED触发条件包括response_mask长度达到response_length、assistant_turns达到max_assistant_turns、user_turns达到max_user_turns、或本轮没有工具调用。相关配置参数rollout.multi_turn下参数含义max_user_turns用户轮次上限max_assistant_turns助手轮次上限max_parallel_calls单轮最多并行工具调用数max_tool_response_length工具响应文本最大长度tool_response_truncate_side截断方向left/right/ 其他两侧截断format工具调用解析格式如hermes工具执行遵循两条契约见_call_toolFunctionTool无生命周期、直接调用BaseTool子类走create - execute - release完整生命周期。工具名不存在、参数 JSON 解析失败、执行抛异常时都会返回错误文本作为ToolResponse并记 warning而不会中断训练。4.6 多轮分词delta-based tokenization 与 tokenization sanity check多轮 rollout 的分词有一个天然难点对完整消息列表应用 chat template 并分词后得到的是一个扁平的 token 列表无法区分哪些 token 属于 assistant 消息也就无法正确构造 loss mask。verl 采用delta 分词策略每次 LLM 生成新消息时对历史消息messages[:i]应用 chat templateadd_generation_promptTrue排除 assistant 提示符以免计入 loss对包含最新消息的messages[:i1]再应用一次 chat template只对两次序列化字符串之间的delta做分词并给这段 token 打上loss_mask 1。# 使用 tokenizer 时 prev tokenizer.apply_chat_template(messages[:i], add_generation_promptTrue, tokenizeFalse) curr tokenizer.apply_chat_template(messages[:i1], add_generation_promptFalse, tokenizeFalse) token_ids tokenizer.encode(curr[len(prev):], add_special_tokensFalse) loss_mask [1] * len(token_ids) # 只掩码新增的 assistant token多模态场景使用 processor 时逻辑相同只是把tokenizer换成processor并带上 images/videosprev processor.apply_chat_template(messages[:i], add_generation_promptTrue, tokenizeFalse) prev_model_inputs processor(textprev, imagesimages, videosvideos, return_tensorspt)[0].tolist() curr processor.apply_chat_template(messages[:i1], add_generation_promptFalse, tokenizeFalse) curr_model_inputs processor(textcurr, imagesimages, videosvideos, return_tensorspt)[0].tolist() token_ids curr_model_inputs[input_ids][len(prev_model_inputs[input_ids]):] loss_mask [1] * len(token_ids)为了防御未来模型的 chat template 变化导致 delta 与完整分词结果悄然不一致verl 默认在每个 rollout 结束时对两种分词结果做对比校验tokenization sanity check通过actor_rollout_ref.rollout.multi_turn.tokenization_sanity_check_mode配置三种模式模式行为strict默认严格比较 delta 与完整分词结果任何差异都输出 warningignore_strippable忽略空白字符\n、\t、\r、空格差异仍检查有意义的文本差异disable完全关闭校验仅在确认差异无害时使用actor_rollout_ref: rollout: multi_turn: tokenization_sanity_check_mode: ignore_strippable # disable | ignore_strippable | strict如果看到如下 warning请去日志中核对不匹配的子串Inconsistent training and inference tokenization detected. This may lead to unexpected behavior during training. Please review your chat template to determine if this is intentional. For more information, refer to the multiturn README.md.特殊情形部分模型如 Qwen/QwQ-32B 与 Qwen3 系列在 chat template 渲染时会移除内部推理内容导致消息内容跨轮次变化delta 分词失效。此时 verl 退回到固定 base conversation只含一条 system 与一条 user 消息不含 assistant 与推理内容跨轮次保持一致BASE_CHAT_HISTORY [ {role: system, content: You are a helpful assistant.}, {role: user, content: I am a user.} ] prev tokenizer.apply_chat_template(BASE_CHAT_HISTORY, add_generation_promptTrue, tokenizeFalse) curr tokenizer.apply_chat_template([*BASE_CHAT_HISTORY, messages[i]], add_generation_promptFalse, tokenizeFalse) token_ids tokenizer.encode(curr[len(prev):], add_special_tokensFalse) loss_mask [1] * len(token_ids)该方法对 Qwen3 系列有效。Qwen/QwQ-32B 的 chat template 目前存在一个 bug修复 PR 尚未被采纳需要先下载修复版模型pip install huggingface_hub hf download Qwen/QwQ-32B --revision refs/pr/81训练与推理模板不一致即使修复了 delta 不匹配推理端 chat template 移除推理内容仍会带来新矛盾——训练使用完整推理内容而推理不使用。为规避不确定性verl 默认训练与 rollout 都使用完整响应含推理。但这样做的代价是长推理内容容易撑爆上下文窗口多轮场景尤其明显且 rollout 与生产环境默认 chat template 会丢弃历史推理不一致。如需缓解可开启actor_rollout_ref.rollout.multi_turn.use_inference_chat_template True4.7 数据集中的多模态输入处理RLHFDataset通过return_multi_modal_inputs标志控制多模态输入是否预处理进每个样本return_multi_modal_inputs: True默认数据集会预处理并在每个样本中附带multi_modal_inputs字典processor 产出的模型就绪表示如图像/视频张量。适合单轮或 SFT 式训练此时模型期望 batch 中所有模态齐全return_multi_modal_inputs: False数据集不包含multi_modal_inputs字段。推荐用于多轮 RL 或工具增强 rollout因为 rollout 过程中模型可能动态生成新的多模态输入避免 batch 中数据冲突或冗余。五、Agent 框架基于 LangGraph 的自定义 Agent5.1 系统架构与组件当内置的ToolAgentLoop不够用时verl 提供 Agent 框架扩展层。其设计哲学来自 docs/advance/agent_loop.rst是目标可插拔的用户自定义 agent loop为不同推理框架提供标准请求 generate API在多个推理 Server 之间提供请求级负载均衡非目标不规定工具如何定义、如何调用。核心组件如下组件角色ChatModelLangChain 的 LLM 对象用于适配LLMServerClient提供的 generate APIReactAgentLoopAgent 适配层默认支持一个朴素的 LangGraph Agentic可以派生新类来支持用户自定义 Agent需要实现run函数来完成 Agent 调用AsyncServerServer每个实例连接推理引擎的一个 DP 组5.2 AgentLoopBase用户唯一需要实现的接口AgentLoopBase是 agent loop 的抽象基类run是用户唯一需要实现的方法。给定 prompt 消息格式为[{role: user}, {content: ...}]和采样参数用户可以在run中做任何事调用 LLM generate API、调用工具web search、数据库查询、代码沙箱等、与环境交互、reflection……class AgentLoopBase(ABC): abstractmethod async def run(self, sampling_params: dict[str, Any], **kwargs) - AgentLoopOutput: Run agent loop to interact with LLM server and environment. Args: sampling_params (Dict[str, Any]): LLM sampling params. **kwargs: dataset fields from verl.utils.dataset.RLHFDataset. Returns: AgentLoopOutput: Agent loop output. raise NotImplementedError在AgentLoopBase.__init__中可以拿到LLMServerClient即server_manager用户随时可以通过LLMServerClient.generate与 LLM 交互。run最终返回AgentLoopOutput其中的prompt_ids、response_ids、response_mask会被用于 RL 训练。5.3 LLMServerClient负载均衡与 sticky sessionLLMServerClient充当多个AsyncLLMServer实例的代理提供两个关键能力负载均衡load balance第一轮选择请求数最少的 Server 实例发送请求粘性会话sticky session将request_id绑定到 Server 实例后续轮次同一request_id的请求始终发往同一 Server 实例从而保证同一轨迹的多轮生成落在同一份 KV cache / 权重副本上。LLMServerClient被传入AgentLoopBase.__init__用户通过LLMServerClient.generate(request_id, prompt_ids, sampling_params)获取 response_ids。5.4 AsyncServerBase接入新推理引擎的扩展点其他推理引擎可以通过实现AsyncServerBase轻松接入。抽象类定义了两个必须实现的 APIclass AsyncServerBase(ABC): abstractmethod async def chat_completion(self, raw_request: Request) - JSONResponse: OpenAI chat completion API. raise NotImplementedError abstractmethod async def generate(self, prompt_ids: list[int], sampling_params: dict[str, Any], request_id: str) - list[int]: Generate response ids given prompt ids. raise NotImplementedErrorvLLM 与 SGLang 的AsyncLLMServer都已官方支持并经过充分测试二者都实现了这两个 API。它们在进程模型上的差异来自 docs/advance/agent_loop.rstvLLMAsync LLM Engine 与 Server 同进程运行ModelRunner 与 FSDP/Megatron-LM worker 同进程运行二者通过ZeroMQ通信Server 收到请求后直接调用 engine 生成 response_idsSGLangAsync LLM Engine 与 FSDP/Megatron-LM 的 worker-0 同进程运行并 spawn 多个子进程作为 ModelRunner同样通过 ZeroMQ 通信Server 收到请求后远程调用 worker-0获取 response_ids。5.5 自定义 agent loop 的注册与加载新 agent loop 类通过register(agent_name)装饰器注册register(my_agent) class MyAgentLoop(AgentLoopBase): ...注册表_agent_loop_registry会记录{_target_: module.ClassName}AgentLoopWorker运行时通过hydra.utils.instantiate实例化。数据集中的agent_name字段如tool_agent就是查这张注册表的 key。更多细节可参考仓库中 LangGraph agent 的 recipe 文档recipe/langgraph_agent/example/README.md以及 tests/experimental/agent_loop/ 下的测试用例例如 test_basic_agent_loop.py、test_call_tool_on_cpu.py。六、工程落地要点与注意事项6.1 训练正确性token 一致性是生命线训练阶段必须使用 LLM 实际生成的 token 而非文本重编码结果否则 advantage 计算失真见 2.3 节response_mask决定 loss 范围工具 token 的 mask 必须为 0多轮场景务必关注 tokenization sanity check 的 warning出现Inconsistent training and inference tokenization detected时需检查 chat template。6.2 上下文长度与模板一致性长推理内容 多轮交互很容易超过上下文窗口必要时开启use_inference_chat_templateTrue让 rollout 与生产环境对齐Qwen/QwQ-32B 需使用refs/pr/81修订版模型见 4.6 节。6.3 工具状态管理需要按轨迹隔离状态沙箱、临时目录、DB 行或注入tools_kwargs→ 用BaseTooltool_config_path无状态工具 → 用function_toolfunction_tool_path零生命周期开销两套工具可同时使用但注意名称冲突会在启动时报错。6.4 性能与调试异步 rollout 让 GPU 在工具调用期间持续工作AgentLoopWorker数量等于 batch_size 时每个 worker 恰好负责一条 prompt见 2.5 节训练日志中出现Failed to decode tool call是正常现象不代表训练异常使用 mlflow tracemlflow ui -h 0.0.0.0 -p 5000 --backend-store-uri sqlite:////tmp/mlruns.db可视化多轮对话与工具调用细节trace 功能的完整机制见 Rollout trace。参考文档索引主题仓库路径Agentic RL 官方指南本文主文档docs/start/agentic_rl.rstAgent Loop 内部设计docs/advance/agent_loop.rst多轮 Rollout 支持docs/sglang_multiturn/multiturn.rstRollout tracedocs/advance/rollout_trace.rstGSM8K 示例docs/examples/gsm8k_example.rstAgent Loop 源码verl/experimental/agent_loop/agent_loop.pyTool Agent Loop 源码verl/experimental/agent_loop/tool_agent_loop.py函数工具定义verl/tools/function_tool.py工具基类verl/tools/base_tool.pyGSM8K 工具化数据预处理examples/data_preprocess/gsm8k_tool_agent_loop.pyGRPO 训练示例脚本examples/grpo_trainer/run_qwen3_8b_fsdp.shAgent Loop 测试用例tests/experimental/agent_loop/【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考