diff --git a/knowledge/repos/afd-plugin/_index.md b/knowledge/repos/afd-plugin/_index.md index 162aeff5e..73587dd64 100644 --- a/knowledge/repos/afd-plugin/_index.md +++ b/knowledge/repos/afd-plugin/_index.md @@ -44,3 +44,7 @@ sources: | 核对 connector/graph/DBO 当前支持组合 | [execution-platforms](components/execution-platforms/architecture.md) | v0.26 CUDA/Ascend 平台现状 | 默认 briefing 只加载本入口和仓库规则;深层 owner 页面由 changed-file route 或 `doc_search` / `doc_read` 按需读取。 +- [AFD plugin 仓库规则](rules-init.md) +- [上游 API 漂移与兼容边界(afd-plugin)](rules-seed-vllm-omni-rebase-upstream-api-drift.md) +- [每次审查 AFD PR 都先套用的通用审查规则。AFD 特有的门禁再看本仓库的规则页。](../../general/review/rules.md) +- [以下情况先读这页,判断是否应该先改上游:新增或刷新 compat patch、从 vLLM/vLLM-Ascend 复制函数,或在插件里重写上游逻辑(MoE routing、DP metadata、graph capture、EngineC](../../general/review/guides/upstream-first-for-algorithm.md) diff --git a/knowledge/repos/afd-plugin/components/compatibility/rules.md b/knowledge/repos/afd-plugin/components/compatibility/rules.md index f9a2d0ff9..1f7171810 100644 --- a/knowledge/repos/afd-plugin/components/compatibility/rules.md +++ b/knowledge/repos/afd-plugin/components/compatibility/rules.md @@ -1,7 +1,7 @@ --- title: "AFD compatibility 规则" created: 2026-08-06 -updated: 2026-08-06 +updated: 2026-09-30 type: rule tags: [afd-plugin, components, compatibility, review] sources: @@ -33,3 +33,25 @@ confidence: high - 验收:当前 pinned signature 对齐,故意缺失字段不会被 fallback 吞掉,保存的 original 不会在 reload 时被 wrapper 覆盖。 patch 生命周期见 [architecture](architecture.md),版本支持声明见 [仓库规则](../../rules.md)。 + +## AFD-I5 — 升级 vLLM 或 vLLM-Ascend 基线时,需要同步修改多个文件并做完整审计 + +- vLLM 目标版本出现在三个地方:`pyproject.toml` 的 extra pin、`afd_plugin/compat/vllm.py` 的版本检查、README(Overview、Known gaps、GPU installation)。三处必须一致。 +- 插件注册用 `strict=False` 调版本检查,版本不符只告警。不能把“注册成功”当作新版本受支持的证据(`PATCH-INV-003`)。 + - `engine_core.py` 和 `npu/force_load_balance.py` 没有 patch 内的版本守卫,只能靠 review 把关。 + - `async_dp_engine.py` 里复制的 coordinator 只在 `0.26.0` 精确版本下生效,守卫要跟着更新。 + - `attention_model_runner.py` 里规避 DBO metadata cache 的代码:等 vLLM #48659 进入 pin 的版本后删除。 + - 连接器进程组用到的私有 PyTorch 和 vLLM 符号。 +- `pyproject.toml` 没有声明 vLLM-Ascend 依赖。刷新 Ascend 侧时,必须引用确切的 vLLM-Ascend 源码 commit(当前是 `80d8c194f`),同步 README 的 Ascend 基线表,并附上 NPU 单元测试和 E2E 证据。 + + + +## AFD-I6 — 临时替换上游符号的代码必须在 `finally` 里还原,并登记到兼容文档 + +- 这类临时替换包括:包装 `create_forward_context`、在 `Worker.init_device()` 期间替换 runner 符号、包装 V2 capture 的输入准备、安装 full-graph replay hook、给 DBO 临时关闭 metadata cache。它们都必须在 `finally` 里恢复符号和 runner 状态,异常路径也要恢复。 +- 需要有测试覆盖恢复逻辑和错误路径。参考 `test_attention_model_runner.py` 对 DBO 规避代码的覆盖。 +- 新增的这类替换要写进 `docs/design/module/compatibility_and_patches.md` 的 scoped adaptation 列表,否则升级审计会漏掉它。 +- 需要在整个进程生效的替换,应放到 `afd_plugin/compat/patches/`,并走 patch 审查流程。反过来,AFD 自己的功能不要借全局 hook 藏进 patch 模块。 +- 保存了原函数的 patch,在模块重载时不能用自己的 wrapper 覆盖已保存的原函数。 + + diff --git a/knowledge/repos/afd-plugin/rules-init.md b/knowledge/repos/afd-plugin/rules-init.md new file mode 100644 index 000000000..79672478b --- /dev/null +++ b/knowledge/repos/afd-plugin/rules-init.md @@ -0,0 +1,123 @@ +--- +title: "AFD plugin 仓库规则" +created: 2026-09-30 +updated: 2026-09-30 +type: rule +tags: [afd-plugin] +sources: [] +--- + +# AFD plugin 仓库规则 + +## AFD-I1 — 新增或重命名 connector 时,必须同时更新 factory 注册、配置白名单和 connector 自有 schema + +- 只在 `AFDConnectorFactory` 注册还不够。配置层另有一份硬编码白名单,它不是从注册表推导出来的。名字不在白名单里的 connector,会在配置校验阶段被拒绝。 +- 检查 PR 是否同时更新了以下内容:白名单、README 里 `connector` 可选值的说明、README 的 connector 支持表。 +- 新 connector 必须实现 `parse_extra_config()`,schema 是封闭的,未知字段直接报错。原因是 `compat/npu/feature_validation.py` 会通过 factory 复用同一个 schema 做前置校验,不能在别处另写一套校验。 +- 实现要放进对应的后端包:只用于 GPU 的放 `afd_plugin.connectors.gpu`,只用于 NPU 的放 `afd_plugin.connectors.npu`。 +- factory 的注册方法仍是 draft。不要在文档里把它写成公开扩展契约。 + + + +## AFD-I2 — 判断走控制面驱动还是 connector 驱动,只能看 `connector.control_plane` 是否为 `None` + +- 不能用 `isinstance` 或 connector 类名来选择执行路径(`CAP-INV-001`)。调用 `send_dp_metadata_list()`、`recv_dp_metadata_list()` 或 `update_state_from_dp_metadata()` 之前,必须先检查 `control_plane`。 +- GPU 只支持控制面驱动:`GPUFFNModelRunner` 构造时会断言 `control_plane` 不为 `None`;GPU daemon 遇到 `None` 会抛 `NotImplementedError`。所以任何新的 GPU connector 都必须在构造阶段装好 `AFDControlPlane`。 +- `control_plane is None` 的 connector(目前只有 `CAMAsyncAFDConnector`)不能进 FFN graph cache 路径,因为 cache key 依赖 DP metadata。这类 connector 必须保持 eager-only。Attention 在这种情况下会跳过 DP metadata 协调,所以路由和 token 元数据必须随 dispatch payload 一起发送。 +- 控制面对象属于 connector,没有独立的生命周期。不要给它单独加 close 或所有权逻辑。 + + + +## AFD-I3 — connector 构造只做轻量工作,跨角色的集合通信必须等权重加载完成后再做 + +- 构造函数和 `parse_extra_config()` 里不能创建进程组、communicator、算子注册,也不能加载 CANN 算子。这些都属于 `init_afd_connector()`。 +- Attention 端:`init_afd_connector()` 必须放在 `load_model()` 末尾,并且在 ubatch wrapper 安装之后。 +- FFN 端:`init_afd_connector()` 必须在 `initialize_from_config()` 里调用,位置在空 KV-cache 初始化之后、daemon 线程启动之前。 +- 如果把 rendezvous 提前到 `init_device()` 或构造阶段,两个角色的权重加载就不能再重叠。无论怎么改,rendezvous 都必须在 Attention 显存 profiling 和首次 forward 之前完成。 +- 对未初始化或只初始化了一部分的 connector,`close()` 必须安全,并且之后仍能再次关闭。connector 的改动要附带清理测试,以及重试或重新初始化测试。 +- 缺少原生算子时,要在 connector 初始化时报错,不能在包 import 时报错。 + + + +## AFD-I4 — 保持 CPU-safe import 边界:没有 CUDA、vLLM 或 torch_npu 时,默认测试也必须能跑 + +- `vllm` 是可选 extra,默认的 `uv run pytest` 要能在没有 CUDA 的环境里运行。 +- 以下模块在 import 时不能引入 torch、vLLM 或 torch_npu:顶层包、通用配置和校验、版本检查、graph policy helper(`v1/worker/cuda_graph.py` 里的 `validate_cuda_graph_mode()`)。 +- `AFDConnectorFactory` 只保存懒加载器。新增 connector 时,不能在 factory 模块顶层 import CUDA 或 Ascend 的具体实现。 +- 只针对 CUDA 的插件注册路径不能 import vLLM-Ascend。Ascend 的 patch 必须等 Ascend 插件完成平台初始化之后再应用。 +- `afd_plugin._C_ascend` 只能在 connector 初始化时由 `ensure_cam_p2p_ops_available()` 懒加载。 +- CUDA 和 Ascend 的 runtime 模块可以直接 import 设备依赖。依赖 NPU 的测试属于 Ascend 环境的测试套件,不属于默认 CPU 测试。 + + + +## AFD-I7 — `compute_gate_on_attention` 允许什么值,要同时看 connector、runner 版本和模型 + + - `P2pNcclAFDConnector`(CUDA V1):`true` 和 `false` 都可以。 + - `CAMP2pAFDConnector`:通用字段和 `connector_extra_config` 里的同名字段都必须是 `false`。 + - `CAMAsyncAFDConnector`:文档里的配置要求通用字段为 `true`,开启 `async_moe_ubatching` 时也要求 `true`。 + - 任何 ModelRunnerV2 配对都要求 `false`。 + - Qwen3 MoE 和 Qwen3.5/Qwen3.6 MoE 都拒绝 `true`。 +- 修改其中一个维度的校验时,要核对另外两个维度,不能只在 connector parser 或只在模型层做校验。V2 的限制由 Attention 和 FFN 两个角色共用同一个校验器。 +- 通用字段 `compute_gate_on_attention` 是模型路由的选择开关;CAMP2P 的同名局部字段是算子契约。两者不能互相替代。 +- 同步更新 README 的模型表、connector 表和 Known gaps。 + + + +## AFD-I8 — README 和设计文档对 ModelRunnerV2 的说法相互矛盾,改动 V2 时必须明确对齐 + + - 设计文档和 E2E 契约描述了受支持的 CUDA V2(`AFDAttentionModelRunnerV2`),CI 在 `l4_4` 上按节点 ID 运行 `afd-v2-*-dp2` 和 `afd-v2-*-tp2` 四个用例。 + - Ascend V2 只有单元测试证据。 +- 涉及 V2 的 PR 要说明以哪份文档为准,并同步修改另一份。 +- 不能依据 README 删除 V2 代码路径或 CI 用例,也不能宣称 Ascend V2 经过了硬件验证。 +- V2 的约束由 Attention 和 FFN 共用同一个校验器,只改一端属于缺陷。约束包括: + - `compute_gate_on_attention=false`; + - PP、PCP、DCP 均为 1; + - 角色 rank 数等于 DP x TP; + - 静态 EP; + - 不用 DBO 或 ubatching; +- 图模式有平台差异:CUDA V2 只支持 eager 和 `FULL_DECODE_ONLY`,Ascend V2 还支持 `FULL`。不要把 Ascend 的 `FULL` 放宽到 CUDA。另外 README 的 connector 表把 CAMP2P 写成只支持 `FULL_DECODE_ONLY`,修改时要一并对齐。 + + + +## AFD-I9 — AFD metadata 只能放在 `forward_context.additional_kwargs['afd_metadata']` + +- CUDA 和 Ascend、Attention 和 FFN、V1 和 V2,全部读写同一个键。不要重新引入 NPU 独立的 metadata 镜像,也不要新增其他 forward context 属性。 +- Ascend FFN 有两条路径:V1 用 `set_ascend_forward_context`;V2 配对用原生 `set_forward_context` 加 MRV2 profile override。修改 `compat/npu/forward_context.py` 时,两条路径都要测。 +- CUDA 空闲 DP rank 走的 dummy-batch 路径会绕过 `_model_forward()`,metadata 在这条路径上是惰性安装的。修改 metadata 的安装位置时,要检查这条路径。 +- `AFDForwardContextMetadata` 的结构和其中的 live connector 引用都不是稳定 API(#86、#88、#105 仍未解决)。不要在对外接口里承诺它们。 + + + +## AFD-I10 — 修改控制面 payload 的字段,发送端、接收端和编解码都要同步 + +- `AFDControlPayload` 只能携带插件自有的原始数据:整数 stage key、token count 列表、max count,以及 graph、warmup、profile 布尔标志。它经 JSON 编码后,先发长度再发 `uint8` tensor。不能序列化 vLLM 的 `DPMetadata` 或其他 vLLM 内部对象(`XFER-INV-001`)。 +- 新增字段时(`is_profile` 就是一个例子),以下内容都要一起更新: + - `P2pNcclAFDControlPlane`(NCCL)和 `CAMP2pAFDControlPlane`(Gloo); + - Attention 的所有发送点:非 ubatch、原生 ubatch、padded full-graph、V2 的 capture 和 replay hook; + - FFN daemon 的 profile、warmup、capture、replay 分支; + - 如果字段影响形状,还要改 `make_ffn_graph_key()` 或 ACL graph key。 +- 控制面必须先调用 `update_state_from_dp_metadata()` 更新本地 connector 状态,再发送。FFN 必须在进入 `torch.cuda.graph(...)` 或 `torch.npu.graph(...)` 之前完成状态更新,控制面的副作用不能被录进图里。 + + + +## AFD-I11 — worker 自动选择和显式指定 worker 路径必须同时可用 + +- `worker_cls="auto"` 在上游平台规范化之后,由 `compat/patches/config_validation.py` 映射到 CUDA 或标准 Ascend 的角色 worker。显式指定的 worker 路径和非 AFD 配置不能被改写。 +- 显式 AFD worker 路径仍然接受(兼容旧命令),但不是稳定接口。 +- 不要删掉 NPU worker 里非 SP 场景的 all-to-all 修正。auto 路径在配置规范化时已经做了同样的改写,但显式指定 worker 的旧式启动还依赖 worker 里这份修正作为兜底。 +- `config_validation` 和其他核心 patch 在同一个 best-effort `try` 块里导入,失败时可能只在 debug 日志里出现,映射就静默丢了。因此 worker 初始化遇到隐式或错误的 worker class 时,必须明确报错,不能猜。 +- 没有 `--afd-config` 这个参数。配置只能从 `additional_config["afd"]` 进入(`afd_role` 等兼容别名除外)。 + + + +## AFD-I12 — E2E:分清 PR 门禁、每周门禁和只在本地跑的用例 + +- PR 门禁用例最多使用 4 个设备,AFD 门禁用例必须是 2A2F。2A1F 用例(包括 README 本地冒烟示例里的 `afd-eager-2a1f`)不能加入 PR 门禁。 + - 一处说 2A1F 用例 “run outside CI”; + - 另一处的每周门禁包含 DeepSeek `afd-graph-dbo-2a1f`,以及 Qwen3 MoE 和 Qwen3.6 的用例,其中 Qwen3.6 套件是 2A1F。 + - 修改 CI 选择时,要同步更新 `docs/design/module/e2e_testing.md` 和 `tests/e2e/README.md`,不要让这处矛盾继续扩大。 +- 按通用规则,harness 升级到 `SIGKILL` 就必须判用例失败。通过 `/proc/*/environ` 标记扫描再发 `SIGKILL` 的例外,只适用于 NPU async CAM 场景(`afd-eager-async-cam`、`afd-dsv4-flash-async-cam-dp2tp4-ep8`)。新场景不能沿用这个例外。 +- CI 不能设置 `AFD_GSM8K_LIMIT`,也不能调低 `AFD_GSM8K_THRESHOLD`。DBO 场景需要 12 路并发、至少 24 个样本,并断言至少记录到一次实际运行的双 ubatch 步骤。 +- 门禁用例不能用 `skip` 或 `xfail`。新行为需要单元测试加真实硬件证据;Ascend V2 目前只有单元测试证据,不能当作先例。 + + diff --git a/knowledge/repos/afd-plugin/rules-seed-vllm-omni-rebase-upstream-api-drift.md b/knowledge/repos/afd-plugin/rules-seed-vllm-omni-rebase-upstream-api-drift.md new file mode 100644 index 000000000..89317e37f --- /dev/null +++ b/knowledge/repos/afd-plugin/rules-seed-vllm-omni-rebase-upstream-api-drift.md @@ -0,0 +1,90 @@ +--- +title: "上游 API 漂移与兼容边界(afd-plugin)" +created: 2026-09-30 +updated: 2026-09-30 +type: rule +tags: [afd-plugin] +sources: [] +--- + +# 上游 API 漂移与兼容边界(afd-plugin) + +## AFD-I13 — vLLM 目标版本写在多处,注册期只告警:改 pin 时所有守卫要一起改 + +- 目标版本 `0.26.0` 同时写在三类位置:`pyproject.toml` 的 `vllm` extra(`vllm==0.26.0`)、`afd_plugin/compat/vllm.py` 的版本检查、README 与设计文档里的支持声明。PR 改了其中一处,其余位置必须跟着改。版本守卫有变化时,还要补 package 兼容测试 `tests/unit/package/test_package.py`。 +- 插件注册调用版本检查时用的是 `strict=False`,版本不匹配只打 warning,然后继续运行。因此“新版本上插件能注册”或“单测能通过”都不算支持证据。在签名不同的上游上跑通的单测,也不算兼容证据。 + - `config_validation.py` 和 `async_dp_forward_context.py` 放行三种情况:目标版本、dev 版本、缺失版本元数据。 + - `async_dp_engine.py` 的一般 async 绑定沿用 target/dev 守卫,但其中复制的 coordinator 只在版本恰好是 `0.26.0` 时生效。更新的 dev 版本会保留原生 coordinator。 +- 所以 bump pin 的 PR 必须显式更新这个 exact 守卫及其测试。否则 async-DP Attention 的行为会悄悄变掉,而且不会报错。 + + + +## AFD-I14 — 没有版本守卫的复制型 patch 只靠 pin 和评审兜底:基线一变就要从新上游重新拷贝函数体 + +- 以下 patch 没有 patch-local 版本守卫: + - `afd_plugin/compat/patches/engine_core.py` 直接给类属性赋值,也没有 saved-original 哨兵。 + - `afd_plugin/compat/patches/npu/force_load_balance.py` 复制了上游函数体,同样没有版本守卫或重载守卫。 + - `npu/ascend_platform.py` 和 `npu/mla_graph.py` 也没有版本守卫。 +- 任何改变 vLLM 或 vLLM-Ascend 基线的 PR,都要按这个顺序处理: + - 在 PR 中写明上游文件、symbol、版本或 commit、签名。 +- 全局替换会影响每个加载了插件的进程,所以复制出来的非 AFD 分支也要有回归测试。例如:`tests/unit/compat/patches/test_engine_core.py` 里的非 FFN 路径,`test_force_load_balance.py` 里的 pass-through 路径。 + + + +## AFD-I16 — 靠重绑导入别名生效的 patch:上游新增的 import 点会绕过 AFD 分支 + + - `async_dp_forward_context.py` 替换 `vllm.forward_context.set_forward_context`,并重绑已知的、已经导入的 worker 别名,防止调用方继续持有旧函数。 + - `async_dp_engine.py` 同时替换 `vllm.v1.engine.utils.launch_core_engines` 和它被导入后的 client 别名。 +- 如果升级后上游在新的模块里导入这些 symbol,新别名不会被重绑。结果是 AFD async-DP 静默退回原生 `DPMetadata` 协调,或退回原生引擎启动流程。 + - 在新基线里 grep 这些 symbol 的全部导入点。 + - 同步更新 `test_async_dp_forward_context.py` 和 `test_async_dp_engine.py`。 +- `async_dp_engine.py` 会在 spawn 子进程里通过插件自有的进程 target 重新应用 loop。新增的重绑点也要覆盖 spawn 导入路径。 + + + +## AFD-I17 — 核心 patch 是 best-effort 分组导入:运行时 fail-fast 检查是唯一兜底,不能删 + +- `async_dp_engine`、`async_dp_forward_context`、`config_validation`、`engine_core` 在同一个 `try` 里导入。 +- 任一个失败只打 debug 日志,然后继续注册模型。中途失败会留下一个不完整的补丁集:前面的已经装上,后面的被跳过。 +- 部分应用能被发现,全靠运行时的 fail-fast 检查: + - Attention/FFN 初始化时遇到下列情况会直接失败:AFD config 缺失、角色不匹配、worker class 是隐式的(即 `auto` 没有被解析掉)或错误的。 + - FFN 收到 scheduler 驱动的 `execute_model()` 调用时会 fail fast。如果 `engine_core` 没有生效,这就是最后一道防线。 +- 不得把这些检查当成“不可达代码”删掉,也不得降级成 warning。 +- 往这个 best-effort 块里新增 patch 时,必须同时提供一个显式检查,让受影响的运行时在执行前就能发现问题(PATCH-INV-004),而不是静默产出错误结果。 + + + +## AFD-I18 — CPU-safe 与 Ascend 延迟导入:注册期和公共模块不得引入设备栈 + +- `vllm` 是可选 extra。CPU 或 macOS 环境必须能用默认的 `uv run pytest` 跑 import/config 测试。 +- 以下模块必须保持 CPU-safe:顶层包、公共配置和校验、版本检查、graph policy helper。其中 `validate_cuda_graph_mode()` 在模块导入时不能 import torch 或 vLLM。 +- `AFDConnectorFactory` 用的是 lazy loader,导入 factory 时不得带出 CUDA 或 Ascend 实现。 +- Ascend 原生算子要等 connector 初始化时才加载。算子缺失应该在 connector 初始化时报错,而不是在导入包时报错。 +- 纯 CUDA 的插件注册不得导入 vLLM-Ascend。Ascend patch 必须等 Ascend 插件完成平台初始化之后再应用,入口是 config facade、worker runtime facade,以及 FFN worker 构造时导入的 force-load-balance。 +- 把 Ascend patch 挪到 `register_afd` 或模块顶层的 PR 应该拒绝。 +- runtime facade 只有在 wrapper 真正装上之后才缓存“成功”,这样早期导入 vLLM-Ascend 失败时还能重试。修改 facade 时要保留这一点。 + + + +## AFD-I19 — vLLM-Ascend 基线只是一个源码 commit(`80d8c194f`),不是包依赖;v0.19.1rc1 的资产不能当 v0.26 的证据 + +- `pyproject.toml` 没有声明 vLLM-Ascend 依赖。兼容证据只有两样:源码 commit `80d8c194f`,以及对应的 NPU 验证记录。这个 commit 同时记在 README、`compatibility_and_patches.md`、`execution_platforms.md` 三处。 +- 如果 NPU patch 或 runtime 针对的 vLLM-Ascend 源码快照变了,这三处要一起更新,并附上精确的 commit 和受影响路径的 NPU 单元/E2E 证据。 +- 仓库没有已发布的 v0.26 vLLM-Ascend 容器 tag。不得在 PR 或文档里把某个 tag 或容器写成权威 pin,也不得复用旧的 v0.19.1rc1 镜像作为 v0.26 的运行环境。 +- `CAMAsyncAFDConnector` 的 legacy PCP8 recipe 要求 `release/v0.19.1rc1`。而在 v0.26 上,基于 PCP 的 model-runner-v1 部署不受支持,CAM async 也不支持 prefill/decode 上下文并行。所以引用这个 recipe 得到的结果,不能作为 v0.26 CAM async 的支持证据。 +- NPU 单元测试要在 Ascend 开发环境里跑。默认的 CPU `uv run pytest` 通过,不构成 NPU 证据。 + + + +## AFD-I23 — 显式 worker 路径仍被接受:自动映射和 worker 侧的兜底修正都要保留 + +- README 有两条承诺: + - 省略 `--worker-cls` 时,自动选择对应角色的 worker。 + - 显式指定 AFD worker 路径仍然被接受,但不是稳定接口。 +- 自动选择由 `config_validation.py` patch 完成:它在上游平台归一化之后,把初始的 `worker_cls="auto"` 映射成对应的 worker。显式路径和非 AFD 配置不会被重映射。 +- 因此,用显式 worker 路径启动时,会绕过配置期上游对默认 worker 的重写。 +- 对这类旧式启动来说,唯一的兜底是 NPU worker 在设备初始化时做的 non-SP all-to-all backend 修正(选择 `flashinfer_all2allv`,相关逻辑见 `afd_plugin/compat/npu/runtime_config.py`)。不能以“自动选择已经覆盖了”为理由删掉它。 +- 反过来,不得在文档或 recipe 里把显式 worker 类路径写成稳定的启动接口。 +- 新增或修改 worker 类时,要同时更新四种 role/platform 映射及其测试。 + +