ADR-0016 RL Reintegration Into unilab.rl And Single Distribution¶
语言: 简体中文
Status: Proposed
Date: 2026-10-16
Update: 2026-10-19 —— 下文“保留空 extra”的过渡安排已执行完毕,
uni_rlextra 及--extra uni_rl安装入口已移除。Owners: RL / Runtime / Packaging maintainers
Supersedes: None(替代 roadmap #1476 确立的 unilab-rl 独立发行决策;该决策当时未记录为 ADR)
Superseded by: None
Context¶
UniLab 1.3.0 前后将 RL 算法与异步 runtime 拆分为独立仓库 unilabsim/unilab_rl
(distribution unilab-rl,import namespace uni_rl),UniLab 经可选 extra uni_rl
依赖它。拆分后同一契约分属两侧,靠复制或跨包委托维持一致,产生 #1623 记录的双实现
漂移:
UNILAB_DP_LOG_DIR、torchrun rank/local-rank/world-size reader、removed-device 配置 检查在unilab.rl.distributed与uni_rl.ipc.dp_launcher各有一份定义;resolve_collector_cpu_ids、launch_torchrun_workers在 UniLab 侧只是经_require_uni_rl_dp_launcher的懒加载转发,签名靠两个独立版本维持对齐;CUDA_VISIBLE_DEVICES解析在unilab.base.process_device与uni_rl.ipc.dp_launcher各有一份;训练 seed 逻辑在
unilab.training.seed与uni_rl.utils.seed平行重复,且对 NumPy RNG 的副作用不一致。
仅合并 Git 仓库、保留两个包和两套实现不能消除该问题;解决标准是唯一实现及其行为
测试。审计基线:UniLab 5b27e5d9(1.3.5)、unilab_rl 3654e4b(1.4.15),方案细节见
roadmap issue #2372。
Decision¶
采用一个仓库、一个 unilab distribution、一个 unilab.rl 算法与训练执行子系统。
unisim-core 继续作为独立物理层(ADR-0007 不变)。PPO 的 runner、algorithm 与
collective 继续由上游 rsl-rl 提供,不建立自研 algos/ppo。
目标分层与 owner¶
src/unilab/rl/ 按语义分五层:
rl/contracts/:env 注入契约(EnvProtocol/EnvFactory/capabilities)、obs/critic 与 final-observation 语义、冷路径环境契约探测。只依赖类型与通用 tensor 库,不导入具体 环境、registry、manager、backend 或算法。rl/adapters/rsl_rl/:第三方协议适配。vec_env.py持有唯一RslRlVecEnvAdapter;distributed.py持有 PPO 专属 rank seed/device/样本数/结束/回放拓扑助手。rl/algos/{appo,sac,flash_sac,common}/:自研算法 runner/learner/网络与算法专属 staging/replay 策略。可依赖 contracts、runtime、logging 与 rsl-rl,不构造具体 UniLab 环境。rl/runtime/:训练执行。distributed/{context,launcher,affinity,sync}、ipc/(仅进程间 数据与同步机制)、offpolicy/、seed.py、device.py、settings.py、placement.py、tensor.py、nan_guard.py。可依赖 contracts 与 RL logging;不依赖具体算法、training、任务注册或 simulator,算法经 learner/builder/callback 注入。rl/logging/:RL logger、metric schema、trace。
相邻层职责:training/ 管实验装配与落盘(含 Hydra rank 配置转换、removed-device 配置
拒绝、plain JSON 训练状态 checkpoint 序列化/复制),base/process_device.py 管
simulator 专属设备绑定。CUDA_VISIBLE_DEVICES 的
通用 token 解析下沉到 stdlib-only 的 unilab/process_context.py,供 base 与 RL 共用;
backend payload/device namespace 翻译留在 base。runtime 不反向 import training。
#1623 符号唯一属主¶
契约/行为 |
唯一属主 |
|---|---|
|
|
|
|
|
|
|
|
removed-device 配置拒绝、Hydra rank 配置转换 |
training 配置装配层 |
PPO rank seed、rsl device、每轮样本数、结束与回放拓扑 |
|
|
|
obs/可选 critic、terminal observation 规则 |
|
旧转发/复制路径删除;若迁移过渡期保留原 unilab.rl 导出面,只允许同一对象的
re-export,禁止带独立签名/默认值/转换逻辑的第二层实现。
包初始化与导入闭包¶
unilab、unilab.rl、rl.runtime、rl.runtime.distributed 的 __init__.py 必须轻量:
不聚合导入算法、Torch、TensorDict,导入 launcher/context 不得触发 CUDA 初始化或任务
注册。边界守卫由 tests/test_library_import_boundary.py 改为内部 DAG 检查:RL 核心不
依赖 base/envs/tasks/managers/training/scripts/unisim,runtime 不反向依赖 algos。
发行与兼容¶
最终只发布
unilab:标准与 ROCm pyproject、dev group、Makefile、CI 与锁文件移除unilab-rl依赖;依赖约束按合并需要合并(纳入wandb>=0.22.3),不取两边并集。unilab[uni_rl]保留为空 extra 作为旧安装命令的过渡兼容,文档标注其无必要并设定 移除版本;不承诺永久支持import uni_rl。若未来证实必须支持旧 import,另行设计有限期 compatibility distribution,禁止与旧实现同时提供两份类定义。外部扩展点保持有效:
runtime_resolver、class_name、actor_adapter_modules、actor registry 与 warmup hook 继续存在,内置路径更新到新 namespace,外部包路径保持原值;OffPolicyRuntime的 isinstance 校验要求任何兼容出口重导出同一个 class。保持日志 metric schema 与 checkpoint schema;新 provenance 记录统一 UniLab 版本与迁移 来源,历史
unilab_rl版本字段允许读取。历史 checkpoint 仅在旧路径解析边界做显式 映射,不全局重写历史 run config。本次开发不改动项目版本号:标准/ROCm pyproject、运行时版本声明与锁文件中的本项目 版本保持原值;不打 release tag、不发布 PyPI。发版是后续独立工作。
分支治理¶
最终 PR base:
main;集成分支dev/issue-2372-rl-reintegration。child 分支基于集成分支、PR 只合入集成分支;统一 head 验收后再向 main 提 PR。
源码统一与撤包作为同一可交付单元验收,避免 main 处于需同时安装两套实现的状态。
Stable Contracts¶
上表每个 #1623 契约只有一个定义;跨进程行为测试覆盖 parent 写 log-dir/rank → spawn child 读取、失败传播与清理,而非只比较常量字符串。
rank/visibility 测试覆盖单卡、多卡、UUID/MIG token 与 rank-local
cuda:0,不把 LOCAL_RANK 当作 CUDA 索引;CPU 分区测试覆盖单 rank、非连续 affinity、SMT、显式 重叠/空分区与非法 rank。observation adapter 用同一组输入验证 PPO 与 APPO 回放(actor/critic 维度、顺序、 dtype/device、terminated/truncated、time_outs、episode state、训练状态导入导出); 观测语义遵循 ADR-0005、ADR-0011/0012,迁移不得静默改变模型输入。
不安装
unilab-rl时,统一测试与隔离 wheel/sdist 安装仍可完成相关导入与最小训练 路径;py.typed与native_h2d_ext.cpp进入 wheel/sdist。两 GPU PPO smoke(启动、rank 0 落盘、失败传播/清理、训练后回放)是 #1623 关闭条件; 缺硬件证据时 issue 保留待验收状态。
RL CPU 单测在独立子进程验证,避免根 conftest 的 registry 注入掩盖环境独立性;合并 coverage 保留原 RL 覆盖强度。
Alternatives Considered¶
维持两个仓库、靠签名比对对齐接口:#1623 的根因(同一契约双实现)继续存在,拒绝。
仅合并 Git 仓库、保留
uni_rl包与第二份实现:namespace 与发行版本仍双轨,漂移源 未消除,拒绝。在
rl/algos/ppo建立自研 PPO:PPO runner/algorithm 属上游 rsl-rl 职责,自建会造成 新的双实现,拒绝;adapters/rsl_rl仅做协议适配。迁移期保留
unilab.rl.distributed转发层长期存在:转发层带独立签名与默认值即第二 实现,拒绝;只允许纯 re-export 且限期移除。
Consequences¶
ADR-0011 中”direct rsl-rl/PPO 无
uni_rl可运行、多卡启动才懒加载委托uni_rl.ipc” 与”uni_rl不得 import UniLab”两条随本 ADR 失效:合并后 RL 与 env 同属一仓,边界 改为unilab.rl内部 DAG(runtime 不依赖 algos、RL 核心不依赖 env/training 层)。 ADR-0011 其余 contract(Torch-only manager runtime、tensor 生命周期)不变。tests/test_library_import_boundary.py的”algos/ipc/logging 目录不得存在”与_MIGRATED_SYMBOLS禁重定义清单被内部 DAG 守卫替代。公开 API 路径变化(
uni_rl.*→unilab.rl.*)必须记录迁移说明;CLI 算法名、日志 路径、checkpoint 字段与 metric schema 不改名。旧
unilab_rl仓库停止独立维护与归档另行决定;其历史 commit/tag/release 保留,本仓 记录来源 commit3654e4b与版权,不重写 UniLab 历史。以后不应再出现:跨包的同契约副本、经懒加载转发维持的”可选”核心 runtime、为关闭 #1623 而恢复已删除的
resolve_dp_topology/validate_dp_launchable接口。
Evidence In Repo¶
src/unilab/rl/:合并后的 contracts/adapters/algos/runtime/logging 分层。src/unilab/process_context.py:stdlib-only 进程可见性解析。src/unilab/base/process_device.py、src/unilab/base/env_factory.py:backend 设备绑定 与可 pickle env factory。tests/test_library_import_boundary.py:内部 DAG 守卫。tests/rl/:迁移后的 RL 单测(contracts/adapters/algos/runtime/logging)。pyproject.toml、pyproject.rocm.toml、uv.lock、uv.rocm.lock:单一 distribution 与 空uni_rlextra。Roadmap issue #2372、#1623、#1586;来源仓库 unilab_rl @
3654e4b(1.4.15)。