背景与目标
uv run eval 的目标语义是:任务即使没有目标后端的 owner YAML,也能回放任意后端训练出的 checkpoint(train 仍必须显式配置 owner YAML)。PR #1469 已解决第一层(CLI owner YAML 缺失时回退到同任务同 profile 的兄弟 owner + training.sim_backend override),但实测 uv run eval --algo ppo --task microduck_velocity_flat --sim motrix --render-mode interactive --load-run <mjwarp run> 仍被后两层挡住:
- Registry 层:
registry.make(src/unilab/base/registry.py:270)要求 env 为目标后端注册了 factory。MicroduckVelocityFlat 只注册了 mujoco/mjwarp(src/unilab/tasks/locomotion/microduck/__init__.py:19-28),报 does not support simulation backend 'motrix'。
- 后端能力层:就算注册,motrix 不支持 base.yaml 中
randomize_armature event 需要的 dof_armature reset payload,env 构建期 NotImplementedError(src/unilab/base/reset_state.py:768)。仓库现有惯例是后端 owner YAML 显式 events.<term>: null 禁用不支持的 term(如 go2_joystick_flat/motrix.yaml、go2_footstand/drake.yaml)——这又回到"每后端一份 yaml"的前提,与本 roadmap 目标矛盾。
目标:把 eval 提升为一等模式——策略 I/O 契约(sim2sim DENYLIST)严格校验不变,物理保真项(DR、scene 等 ALLOWLIST 字段)在目标后端能力不足时可降级,从而缺配置也能 sim2sim 回放。
参考:mjlab 的做法(~/ws/simulator/mjlab)
mjlab 只有一个物理后端,但其 play 机制可借鉴:
- 注册表为每个任务同时持有
env_cfg 与 play_env_cfg,play 裁剪(关 obs 噪声、pop 扰动 event、清 curriculum)是注册时的一等公民(src/mjlab/tasks/registry.py:10-15、tasks/velocity/config/go1/env_cfgs.py:237-257);startup 型 DR 在 play 中保留。
- checkpoint 只载 actor 权重(
load_cfg={"actor": True}, strict=True),环境配置永远由代码 factory 重建;训练 dump 的 params yaml 仅供审计,play 不读。
- 能力不匹配在 env 构建期 fail-fast(
@requires_model_fields + expand_model_fields),无运行时降级。
- deploy 方向:部署元数据(joint 顺序、obs scale、action_scale)内嵌进 ONNX metadata,部署侧完全免训练配置。
UniLab 已有的对应物:play_profile(owner YAML 内 play 覆写,config_adapter.py:50-115,目前仅视觉覆写)、sim2sim ALLOWLIST 已含 env.domain_rand / env.scene(即仓库已认定 DR/scene 差异不影响策略 I/O 契约)、后端能力检查本身是冷路径声明式的(_capability_error)。
交付边界(候选方案,按长期成本排序)
- 方案 A:eval 模式的能力感知裁剪。仅
play_only 路径:EventManager 绑定到目标后端不支持的 reset payload term 时 warning + 跳过该项;train 保持 NotImplementedError fail-closed。契约依据:DR 已在 sim2sim ALLOWLIST,DENYLIST(obs/actions/action_scale/网络结构)不受影响。效果等价于自动生成那份不存在的后端 owner YAML 中的 events.<term>: null。
- 方案 B:registry 的 eval 语义放宽。ManagerBased 任务的 factory 本是后端无关的 generic factory(如
make_microduck_velocity_env 仅包一层资产解析);eval 模式可允许"已注册 config 的 ManagerBased 任务"在目标后端直接用 generic factory 建 env,不再要求 per-task 逐后端枚举注册。注意:这会改变 support 声明语义(support matrix 由 registry 扫描生成),属于 support 等级的长期责任变更。
- 方案 C(独立、长期):一键部署 bundle。训练/导出时把策略 I/O 元数据物化进导出物(mjlab ONNX metadata 做法),deploy 场景彻底免配置。与 A/B 正交,可另立 roadmap。
A+B 组合即可让上述实测命令跑通,且都只作用于 eval/play 路径,train 语义不变。
预计规模
- 方案 A:EventManager/reset_state 的 play 模式降级分支 + 测试,约 2-4 个文件,~150-300 LOC(含测试)。
- 方案 B:registry eval 路径 + support matrix 生成逻辑/文档同步,约 3-5 个文件,~200-400 LOC(含测试与 support matrix 重生成)。
- 方案 C:导出管线 + artifact 元数据 schema,规模另估,建议独立 roadmap。
永久维护成本
- 方案 A:新增"play 模式降级"语义,需长期保持与 sim2sim DENYLIST/ALLOWLIST 的一致性;降级项变化时需更新文档与审计脚本(
scripts/audit_sim2sim_contracts.py)。
- 方案 B:support 声明从 per-task 枚举变为 runtime 能力声明,support matrix 语义与文档需长期同步;维护者需明确"注册即支持"的新含义。
- 方案 C:新增 artifact 元数据 schema 的版本化责任。
现状
暂不实施,保持现状。PR #1469(CLI 层 owner YAML 回退)已独立交付,是后续任何方案的前提。
背景与目标
uv run eval的目标语义是:任务即使没有目标后端的 owner YAML,也能回放任意后端训练出的 checkpoint(train 仍必须显式配置 owner YAML)。PR #1469 已解决第一层(CLI owner YAML 缺失时回退到同任务同 profile 的兄弟 owner +training.sim_backendoverride),但实测uv run eval --algo ppo --task microduck_velocity_flat --sim motrix --render-mode interactive --load-run <mjwarp run>仍被后两层挡住:registry.make(src/unilab/base/registry.py:270)要求 env 为目标后端注册了 factory。MicroduckVelocityFlat只注册了 mujoco/mjwarp(src/unilab/tasks/locomotion/microduck/__init__.py:19-28),报does not support simulation backend 'motrix'。randomize_armatureevent 需要的dof_armaturereset payload,env 构建期NotImplementedError(src/unilab/base/reset_state.py:768)。仓库现有惯例是后端 owner YAML 显式events.<term>: null禁用不支持的 term(如go2_joystick_flat/motrix.yaml、go2_footstand/drake.yaml)——这又回到"每后端一份 yaml"的前提,与本 roadmap 目标矛盾。目标:把 eval 提升为一等模式——策略 I/O 契约(sim2sim DENYLIST)严格校验不变,物理保真项(DR、scene 等 ALLOWLIST 字段)在目标后端能力不足时可降级,从而缺配置也能 sim2sim 回放。
参考:mjlab 的做法(~/ws/simulator/mjlab)
mjlab 只有一个物理后端,但其 play 机制可借鉴:
env_cfg与play_env_cfg,play 裁剪(关 obs 噪声、pop 扰动 event、清 curriculum)是注册时的一等公民(src/mjlab/tasks/registry.py:10-15、tasks/velocity/config/go1/env_cfgs.py:237-257);startup 型 DR 在 play 中保留。load_cfg={"actor": True}, strict=True),环境配置永远由代码 factory 重建;训练 dump 的 params yaml 仅供审计,play 不读。@requires_model_fields+expand_model_fields),无运行时降级。UniLab 已有的对应物:
play_profile(owner YAML 内 play 覆写,config_adapter.py:50-115,目前仅视觉覆写)、sim2sim ALLOWLIST 已含env.domain_rand/env.scene(即仓库已认定 DR/scene 差异不影响策略 I/O 契约)、后端能力检查本身是冷路径声明式的(_capability_error)。交付边界(候选方案,按长期成本排序)
play_only路径:EventManager 绑定到目标后端不支持的 reset payload term 时 warning + 跳过该项;train 保持NotImplementedErrorfail-closed。契约依据:DR 已在 sim2sim ALLOWLIST,DENYLIST(obs/actions/action_scale/网络结构)不受影响。效果等价于自动生成那份不存在的后端 owner YAML 中的events.<term>: null。make_microduck_velocity_env仅包一层资产解析);eval 模式可允许"已注册 config 的 ManagerBased 任务"在目标后端直接用 generic factory 建 env,不再要求 per-task 逐后端枚举注册。注意:这会改变 support 声明语义(support matrix 由 registry 扫描生成),属于 support 等级的长期责任变更。A+B 组合即可让上述实测命令跑通,且都只作用于 eval/play 路径,train 语义不变。
预计规模
永久维护成本
scripts/audit_sim2sim_contracts.py)。现状
暂不实施,保持现状。PR #1469(CLI 层 owner YAML 回退)已独立交付,是后续任何方案的前提。