在 verl + SGLang 中为多轮 RL 集成搜索工具(Search Tool Integration):从本地检索服务到端到端训练完整指南
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
verl(HybridFlow)为多轮强化学习(Multi-Turn RL)增加了搜索工具调用能力:模型可以在 Actor rollout 阶段主动发起检索请求,并将检索结果直接用于训练。本文基于仓库中的 search_tool_example.rst 官方指南,完整覆盖"本地稠密检索器(local dense retriever)搭建 → 检索服务启动 → 数据预处理 → 8×H20 端到端训练 → 自定义搜索配置"的全流程,并结合仓库源码(工具抽象基类、注册机制、奖励函数、数据预处理脚本)讲解底层实现,帮助你掌握在 verl 多轮 RL 管线中接入任意本地检索引擎的完整集成模式。
版本提示:本指南所描述的端到端配方(配置、检索服务、训练脚本)当前仍然有效,用于说明搜索工具与 verl 多轮 RL 的集成模式;但仓库中内置的参考实现
verl.tools.search_tool.SearchTool类已被移除,SearchTool现在需要由用户基于verl.tools.base_tool.BaseTool自行提供。
一、背景:为什么要在多轮 RL 中接入搜索工具
verl 为 Multi-Turn RL 新增了搜索工具调用功能,核心价值在于:
- 模型自主检索:在 Actor rollout 阶段,模型可以根据自身知识缺口主动发起检索请求(retrieval request),无需人工干预;
- 检索结果直接参与训练:检索返回的文档被拼入对话轨迹,作为后续轮次的上下文,直接参与策略梯度训练;
- 两种检索接入方式:既支持使用本地稠密检索器(local dense retriever)作为检索工具,也支持对接你自己的本地检索引擎(local retrieval engine)。
从整体架构看,一次完整的搜索式多轮 rollout 流程为:
- 模型在某个轮次判断"缺乏相关知识",通过工具调用协议(sglang tool-calling)发出搜索意图;
- verl 的工具执行层将查询参数(queries、topk 等)转发给检索服务;
- 检索服务返回 top-k 文档;
- 检索结果以
tool角色消息的形式回到对话历史,模型据此继续推理并最终给出答案; - 奖励函数(如 EM 精确匹配)对最终答案打分,驱动策略更新。
二、快速复现:从零搭建端到端搜索 RL 环境
官方文档给出了一套完整的快速复现流程。需要注意的是,训练进程与本地检索服务运行在两个独立的 Python 环境中:训练环境使用 uv(verl-multiturn-rollout),检索器环境使用 conda 安装faiss-gpu。
2.1 创建 Docker 容器
使用 SGLang 官方镜像启动容器:
docker run \ -it \ --shm-size 32g \ --gpus all \ -v {Huggingface-Cache-Path}:/root/.cache \ --ipc=host \ --network=host \ --privileged \ --name sglang_{your-name} \ lmsysorg/sglang:dev \ /bin/zsh退出容器后如需重启,执行:
docker start -i sglang_{your-name}2.2 安装 Python 3.10 并配置 uv 虚拟环境
apt update apt install -y python3.10 python3.10-venv # 创建虚拟环境 python3 -m venv ~/.python/verl-multiturn-rollout # 激活虚拟环境 source ~/.python/verl-multiturn-rollout/bin/activate # 安装 uv python3 -m pip install uv2.3 安装 verl 及其依赖
cd ~ git clone https://github.com/verl-project/verl.git cd verl # 安装 verl python3 -m uv pip install . python3 -m uv pip install -r ./requirements_sglang.txt # 手动安装 flash-attn python3 -m uv pip install wheel python3 -m uv pip install packaging python3 -m uv pip install flash-attn --no-build-isolation --no-deps三、搭建本地稠密检索引擎(Local Dense Retriever)
如果你已有自己的本地检索服务,可以跳过本小节。官方指南采用 search-R1 示例中提供的本地稠密检索器(详细说明见 search-R1 的 retriever 文档),要点如下:
- GPU 版本:精度和速度更高,每块 GPU 约占用 5–7 GB 显存;
- CPU 版本:可用于简单测试,但检索精度较低,会拉低训练效果;
- 安装建议:推荐使用 conda 安装
faiss-gpu=1.8.0;使用 venv 可能出现错误。
3.1 安装 Miniconda 并创建检索器环境
# 下载 Miniconda 安装脚本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh # 以批处理模式安装到 $HOME/miniconda3 bash ~/miniconda.sh -b -p $HOME/miniconda3 # 仅在当前 shell 激活 conda eval "$($HOME/miniconda3/bin/conda shell.bash hook)" # (可选)将 conda 加入默认 shell 启动项 conda init # 重新加载 shell 配置 source ~/.bashrc # 创建并激活 Python 3.10 的检索器环境 conda create -n retriever python=3.10 -y conda activate retriever # 安装 PyTorch(GPU 版)及相关库 conda install pytorch==2.4.0 torchvision==0.19.0 torchaudio==2.4.0 pytorch-cuda=12.1 -c pytorch -c nvidia -y # 安装其他 Python 包 pip install transformers datasets pyserini huggingface_hub # 安装 GPU 版 faiss conda install faiss-gpu=1.8.0 -c pytorch -c nvidia -y # 安装 API 服务框架 pip install uvicorn fastapi3.2 下载索引与语料库
本地检索文件较大,请预留充足磁盘空间:下载约 60–70 GB,解压后约 132 GB。
conda activate retriever save_path=/the/path/to/save python examples/sglang_multiturn/search_r1_like/local_dense_retriever/download.py --save_path $save_path cat $save_path/part_* > $save_path/e5_Flat.index gzip -d $save_path/wiki-18.jsonl.gz3.3 启动本地 flat e5 检索服务
启动注意事项:
- 首次启动会下载模型并加载索引;
- 除下载外,启动过程约需 1–2 分钟;
- 启动后每块 GPU 约占用 5–7 GB 显存,其余显存留给多轮 RL 训练。
conda activate retriever index_file=$save_path/e5_Flat.index corpus_file=$save_path/wiki-18.jsonl retriever_name=e5 retriever_path=intfloat/e5-base-v2 python examples/sglang_multiturn/search_r1_like/local_dense_retriever/retrieval_server.py \ --index_path $index_file \ --corpus_path $corpus_file \ --topk 3 \ --retriever_name $retriever_name \ --retriever_model $retriever_path \ --faiss_gpu路径说明:
examples/sglang_multiturn/...下的download.py、retrieval_server.py以及后文的训练脚本属于被移除的参考配方。在当前仓库树中,这些示例文件已不存在(与内置SearchTool一并移除),上述命令作为官方文档记录的集成配方保留;实现你自己的检索服务时,需提供与 5.3 节 I/O 契约兼容的 HTTP 接口。
四、数据预处理与端到端训练
4.1 设置 WANDB_API_KEY
export WANDB_API_KEY={YOUR_WANDB_API_KEY} # 定义时间戳函数 function now() { date '+%Y-%m-%d-%H-%M' }4.2 预处理数据集
注意:以下数据预处理与训练命令必须在verl-multiturn-rollout环境中运行。
python3 examples/data_preprocess/preprocess_search_r1_dataset.py该脚本(preprocess_search_r1_dataset.py)的核心逻辑是:从 HuggingFace 下载train.parquet/test.parquet(默认仓库PeterJinGo/nq_hotpotqa_train,支持通过--hf_repo_id、--local_dir、--hdfs_dir覆盖),逐行转换为 verl 训练所需的格式:
- 构建 system/user 两轮 prompt,system 提示词要求模型"先在
<think>中推理,缺少知识时通过<tool_call>发起搜索,搜索结果显示在<tool_response>之间,可直接给出<answer>格式的答案"; - 从
reward_model.ground_truth或golden_answers提取标准答案; - 为每个样本生成
extra_info.tools_kwargs,将ground_truth、question、data_source注入搜索工具的create_kwargs; - 可选将处理后的 parquet 拷贝到 HDFS(
--hdfs_dir)。
4.3 在 8 × H20 上测试
# 确保已定义 now() 函数 # 创建日志目录 mkdir -p logs # 设置 GPU 并使用合适的日志路径 export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 nohup bash examples/sglang_multiturn/search_r1_like/run_qwen2_5_3b_search_multiturn_fsdp.sh \ trainer.experiment_name=qwen2.5-3b-it_rm-searchR1-like-sgl-multiturn-$(now) \ > logs/searchR1-like$(now).log 2>&1 &该命令以 Qwen2.5-3B-Instruct 为策略模型、FSDP 为并行后端、SGLang 为 rollout 引擎,通过命令行覆盖trainer.experiment_name的方式启动一次带搜索的 search-R1 风格多轮 RL 训练,日志写入logs/目录。
五、自定义搜索配置:参数详解与 I/O 契约
5.1 开启多轮推理
在训练配置中设置以下字段以启用多轮 rollout(同时参见 multiturn.rst 中的multi_turn: True写法):
actor_rollout_ref: rollout: name: "sglang" multi_turn: enable: True5.2 声明搜索工具
你必须在工具配置文件中指定retrieval_service_url,并正确配置并发参数。并发配置的详细设计可参考 Sandbox Fusion 示例:
tools: - class_name: verl.tools.search_tool.SearchTool config: retrieval_service_url: http://127.0.0.1:8000/retrieve num_workers: 120 rate_limit: 120 timeout: 30各字段含义:
| 参数 | 说明 |
|---|---|
retrieval_service_url | 本地检索服务的 HTTP 端点,训练时工具通过该地址发起检索请求 |
num_workers | 每个 DP 上请求检索服务的并发 worker 数 |
rate_limit | 全局并发检索请求上限,用于保护检索服务、避免 429 等过载错误 |
timeout | 单次检索请求的超时秒数 |
在此基础上,还需要在 rollout 配置中通过tool_kwargs.tools_config_file指定该工具 YAML 文件的路径(详见 multiturn.rst 的 Custom Tool Configuration 小节):
actor_rollout_ref: rollout: tool_kwargs: tools_config_file: <path_to_tool_yaml_file>源码印证:工具 YAML 的解析与实例化在 tool_registry.py 的
initialize_tools_from_config中实现——它通过OmegaConf.load读取 YAML,按class_name动态导入工具类(get_tool_class),并将config字段转为 dict 后实例化;当前代码要求config.type为native(ToolType.NATIVE枚举),同时支持可选的tool_schema字段(构造OpenAIFunctionToolSchema)。也就是说,自定义工具的 YAML 需要包含type: native字段。
5.3 检索服务 I/O 契约
如果检索服务的输入输出与下列格式匹配,只需要修改retrieval_service_url;否则可以在search_r1_like_utils.py中自定义适配逻辑。
输入格式(POST JSON):
{ "queries": ["What is Python?", "Tell me about neural networks."], "topk": 3, "return_scores": true }输出格式(return_scores=True时返回相似度分数,result为每个 query 的结果列表):
{ "result": [ [ # 每个 query 的结果 { "document": doc, "score": score }, # ... 更多文档 ], # ... 其他 query 的结果 ] }六、源码级原理:工具抽象、注册机制与奖励函数
6.1 工具生命周期:BaseTool 抽象基类
所有接入 verl 多轮 RL 的工具(包括搜索工具)都需要继承 base_tool.py 中的BaseTool。该抽象类定义了完整的工具生命周期:
get_openai_tool_schema():返回 OpenAI 格式的工具 schema,用于向模型描述可调用工具;create(instance_id, **kwargs):为一条对话轨迹创建工具实例(默认生成 uuid);execute(instance_id, parameters, **kwargs):执行工具,返回(ToolResponse, tool_reward_score, tool_metrics)——ToolResponse支持文本、图片、视频等多模态内容;calc_reward(instance_id, **kwargs):根据工具状态计算奖励(默认返回 0.0);release(instance_id, **kwargs):rollout 结束后释放工具实例(默认空操作)。
对于搜索工具,execute即负责将模型发出的 query 转发到retrieval_service_url,并把返回的 top-k 文档封装为工具响应文本。由于仓库中的参考实现verl.tools.search_tool.SearchTool已移除,你需要按上述接口自己实现一个SearchTool(BaseTool)子类,并保持 YAML 中class_name指向你的实现。
6.2 无状态工具:@function_tool 装饰器
如果检索工具是无状态的(无需create/release生命周期),也可以使用 function_tool.py 提供的@function_tool装饰器,把一个普通 Python 函数注册为工具,schema 由transformers.utils.get_json_schema依据函数签名和 Google 风格 docstring 自动推断。两种方式(function_tool_path与tool_config_path)可以同时配置,AgentLoopWorker启动时会合并注册表,重名工具会报错。
6.3 奖励函数:Search-R1 风格 EM 打分
训练效果的奖励信号来自 search_r1_like_qa_em.py:
extract_solution:用正则从模型输出中提取<answer>...</answer>之间的最终答案(取最后一个匹配);normalize_answer:对答案做小写、去标点、去冠词、规整空白等标准化;em_check/subem_check:分别做精确匹配(EM)与子串包含匹配(Sub-EM);compute_score:提取不到答案得 0;匹配成功得满分score;若<answer>标签数量超过 10 个(防止模型刷标签),分数降为score/4;否则返回format_score。
该奖励函数由 reward_score/init.py 中的分发逻辑按data_source路由:searchR1_nq、searchR1_triviaqa、searchR1_popqa、searchR1_hotpotqa、searchR1_2wikimultihopqa、searchR1_musique、searchR1_bamboogle等数据集均走search_r1_like_qa_em.compute_score。这意味着你可以直接用上述数据源标识复用这套奖励机制。
七、注意事项与运维要点
- 训练耗时:该配方的总训练时间约 27 小时;
- 验证集开销:验证集非常大(51k 条),每次验证约需 6000 秒,因此默认
val_before_train=False,即训练前不做验证,避免在训练开始前就消耗大量验证时间; - 双环境隔离:训练(uv +
verl-multiturn-rollout)与检索服务(conda +retriever)必须分离,检索器环境的faiss-gpu建议用 conda 安装; - 磁盘与显存规划:语料下载 + 解压约需 200 GB 量级磁盘;检索服务每 GPU 占用 5–7 GB,需为多轮 RL 训练预留足够显存;
- 参考实现已移除:当前仓库不再内置
SearchTool类与examples/sglang_multiturn/示例脚本,请基于BaseTool自行实现,并确保与 5.3 节检索服务 I/O 契约兼容。
八、延伸阅读
- 多轮 rollout 总指南:多轮配置、
@function_tool、delta 分词策略、Qwen 系列推理内容裁剪特例处理 - Sandbox Fusion 工具集成:基于 Ray Global Actor + 信号量的分布式令牌桶限流设计(对应
num_workers/rate_limit参数的底层实现) - 搜索式 RL 数据预处理脚本:Search-R1 风格训练数据的完整构造逻辑
- Search-R1 风格 EM 奖励函数:精确匹配/子串匹配打分实现
- 工具注册与实例化:YAML 工具配置的解析、动态导入与名称冲突检查
- 工具抽象基类:
create/execute/calc_reward/release生命周期接口定义
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考