Prhub

#49040 [Core][Frontend] Add weight version tagging for RL rollouts

原始 PR 作者 ShuoleiWang 合并时间 2026-07-28 14:35 文件变更 16 提交数 4 评论 15 代码增减 +142 / -18

执行摘要

为 RL rollout 添加权重版本标记 API

作为 RFC #48306(RL 信息检索)第 2.2 节的一部分,RL 训练框架需要标准化的权重版本机制来关联 rollout 与策略版本。此 PR 结合 #39212 的权重更新生命周期,为外部管理器(如 RL 训练器)提供轻量级版本标记能力,无需绑定到请求或输出。

此 PR 是 RL API 重要的基础设施,值得 RL 相关开发者和架构师精读。关键设计决策包括:版本仅在 finish_weight_update 成功时发布(失败不更新),版本绑定到 EngineCore 而非 request/output,API 按 dev-mode 方式提供。建议关注后续多副本身份标识和持久化方案。

讨论亮点
  • 请求绑定版本争议:reviewer aoshen02 在 vllm/v1/request.py 中质疑将版本绑定到 Request 的必要性,认为一个请求可能跨越多个模型版本。作者解释这是 RFC 的 admission time 绑定,但 reviewer 最终要求“remove it for now”,已执行移除。
  • 输出元数据移除:类似地,aoshen02 在 vllm/outputs.pyoutput_processor.py 中询问是否需要添加版本到输出,最终决定移除相关字段。
  • 测试策略讨论:aoshen02 指出 AI 生成的单元测试(如 test_weight_version.py)不符合要求,建议只保留真实引擎的 e2e 测试。作者移除 AI 测试,扩充现有集成测试套件。
  • 异步方法必要性:aoshen02 在 core_client.py 中询问为何需要 async 版本的 get/set。虽未明确结论,但最终保留了 async 方法以满足 AsyncLLM 调用需求。

实现拆解

  1. EngineCore 状态存储:在 vllm/v1/engine/core.pyEngineCore 中添加 _weight_version 属性(初始化为 "default"),并实现 set_weight_versionget_weight_version 方法,作为版本数据的单一真实来源。

  2. 协议层扩展:在 vllm/engine/protocol.pyEngineClient 协议中增加三个抽象方法——finish_weight_update(weight_version)(接受可选版本)、update_weight_version(new_version)get_weight_version()

  3. 客户端代理实现:在 vllm/v1/engine/core_client.py 中为所有客户端类(InprocClientMultiProcClientMultiProcessClient)实现同步和异步的版本设置/获取方法,通过 RPC 调用传递到 EngineCore。

  4. 公共 API 暴露:同步入口 vllm/entrypoints/llm.py 和异步入口 vllm/v1/engine/async_llm.py 均实现了 finish_weight_update(成功 RPC 后调用 update_weight_version)、update_weight_versionget_weight_version 方法。

  5. HTTP 端点:在 vllm/entrypoints/serve/dev/rlhf/api_router.py 中添加三个 dev-mode 端点(需 VLLM_SERVER_DEV_MODE=1):POST /finish_weight_update(接受可选 weight_version)、POST /update_weight_version(接受 new_version)、GET /weight_info(返回当前版本)。

  6. 文档与测试:更新 docs/training/async_rl.md 说明版本标记用法;修改 tests/distributed/test_weight_transfer.pytests/entrypoints/weight_transfer/test_weight_transfer_llm.py 集成测试,验证版本初始值、finish 后发布及手动更新流程;移除 reviewer 指出的 AI 生成单元测试。

文件 模块 状态 重要度
vllm/v1/engine/core.py 执行引擎 modified 6.83
vllm/v1/engine/core_client.py 客户端代理 modified 7.99
vllm/engine/protocol.py 协议定义 modified 7.32
vllm/v1/engine/async_llm.py 异步引擎 modified 7.06
vllm/entrypoints/llm.py 同步 LLM modified 6.7
vllm/entrypoints/serve/dev/rlhf/api_router.py API 路由 modified 7.69
tests/distributed/test_weight_transfer.py 分布式测试 modified 4.47
tests/entrypoints/weight_transfer/test_weight_transfer_llm.py 权重传输测试 modified 3.96
docs/training/async_rl.md 文档 modified 3.0

关键符号

set_weight_version get_weight_version set_weight_version_async get_weight_version_async finish_weight_update update_weight_version weight_info

关键源码片段

vllm/v1/engine/core.py core-logic

EngineCore 存储权重版本状态,是所有版本查询和更新的最终来源。

# vllm/v1/engine/core.py (partial)
class EngineCore:
    def __init__(self, vllm_config: VllmConfig, executor_class: type, log_stats: bool):
        # ... 其他初始化 ...
        # Opaque weight version supplied by the caller.
        self._weight_version = "default" # 初始版本字符串
        # ...
​
    def set_weight_version(self, weight_version: str) -> None:
        # 外部调用者通过此方法更新版本,通常由 finish_weight_update 触发
        self._weight_version = weight_version
​
    def get_weight_version(self) -> str:
        # 返回当前已提交的权重版本,供 LLM 和 HTTP 端点查询
        return self._weight_version
vllm/entrypoints/serve/dev/rlhf/api_router.py entrypoint

提供 HTTP 端点暴露 weight version API。

# vllm/entrypoints/serve/dev/rlhf/api_router.py (partial)
@router.post("/finish_weight_update")
async def finish_weight_update(
    raw_request: Request,
    weight_version: Annotated[str | None, Body(embed=True)] = None,
):
    # 完成权重更新,如果提供了版本则发布
    await engine_client(raw_request).finish_weight_update(weight_version)
    return JSONResponse(content={"message": "Weight update finished"})@router.post("/update_weight_version")
async def update_weight_version(
    raw_request: Request,
    new_version: Annotated[str, Body(embed=True)],
):
    # 直接更新版本元数据,不涉及权重传输
    await engine_client(raw_request).update_weight_version(new_version)
    return JSONResponse(content={"success": True, "new_version": new_version})@router.get("/weight_info")
async def weight_info(raw_request: Request):
    # 查询当前引擎权重版本
    weight_version = await engine_client(raw_request).get_weight_version()
    return JSONResponse(content={"weight_version": weight_version})

评论区精华

文档简化建议 documentation

aoshen02 在 docs/training/async_rl.md 上评论 'Can be simpler, no need to have a separate paragraph.'

结论:作者同意,简化了文档段落。 · 已解决

AI 生成测试不宜添加 测试

aoshen02 指出 AI 生成的单元测试(如 tests/v1/engine/test_weight_version.py)不符合质量要求,仅需 e2e 测试。

结论:作者移除了 AI 测试,保留了真实引擎的集成测试覆盖。 · 已解决

请求绑定版本设计争议 设计

aoshen02 质疑在 Request 上绑定 weight_version 的必要性,因一个请求可能跨越多个模型版本。作者解释为 admission time 锁定。

结论:reviewer 要求暂时移除该字段,作者执行移除。 · 已解决

输出元数据版本字段 设计

aoshen02 在 vllm/outputs.py 和 output_processor.py 中询问是否需要在输出中添加版本元数据。

结论:最终决定不添加,相关字段被移除。 · 已解决

异步方法必要性 设计

aoshen02 在 core_client.py 中询问为何需要异步版本的 get/set 方法。

结论:未明确结论,但保留了 async 方法以满足 AsyncLLM 调用需求。 · addressed

风险与影响

  • 状态非持久化:版本仅存在于单个 EngineCore 实例,重启后重置为 "default",不提供持久化或跨副本协调。多 DP 副本场景需外部协调,此 PR 未实现可能造成版本混淆。
  • Dev-Only API:HTTP 端点仅在 VLLM_SERVER_DEV_MODE=1 下暴露,未来若转为正式 API 需考虑兼容性设计。
  • 无输入校验:版本字符串完全由调用方管理,vLLM 不做校验,可能引入空字符串、重复值等语义冲突。
  • 低性能影响:仅存储和返回字符串,不参与推理路径,无额外性能开销。
  • 用户(RL 框架开发者):获得标准化的版本标记 API,可以追踪每个 rollout 对应的策略版本,支持 ALO 等在线 RL 场景。
  • 系统:改动集中在引擎层和 API 层,隔离良好,不影响模型执行、调度、attention 等核心路径。
  • 团队:为后续多副本版本选择、分布式回滚、响应元数据扩展奠定基础(RFC 第 2.2 节后续项)。
状态非持久化 单副本作用域 Dev-Only API 无输入校验

关联 Issue

#39212 [feat] Add explicit `/start_weight_update` and `/finish_weight_update` APIs for weight transfer
#48306 [RFC] RL Information Retrieval: APIs & Introspection

完整报告

参与讨论