# PR #2015 完整报告

- 仓库：`radixark/miles`
- 标题：scripts, examples: stop forcing the deprecated Miles router in launchers
- 合并时间：2026-08-04 03:57
- 原文链接：http://prhub.com.cn/radixark/miles/pull/2015

---

# 执行摘要

- 一句话：移除 14 个启动器强制 --use-miles-router，默认走 sgl-router
- 推荐动作：值得快速浏览：它是理解 miles 路由演进与“如何用一个最小 diff 改变默认行为”的干净样例。重点可看作者的范围决策——不顺手删参数、不顺手删测试，而是把“移除标志”与“退役参数”两项决策解耦，避免一个看似无害的清理 PR 吞掉大量旧路径覆盖。若想进一步行动，可后续跟进 `arguments.py` 中 `--use-miles-router` 的正式退役，以及 3 个 e2e 测试对该标志的依赖清理。

# 功能与动机

PR body 明确指出：Miles router 已废弃、sgl-router 为默认，但 14 个 launcher 配方无条件传入 --use-miles-router；该标志声明为 action="store_true" 且没有否定形式，Python launcher 也没有暴露 ScriptArgs 字段，因此调用方除改写脚本外没有任何办法从外部关闭它，等于每次运行都被钉在废弃的文本路由路径上。本 PR 的目的就是摘除这个强制标志，让 router_manager 自然回退到 sgl-router 分支。

# 实现拆解

实现按如下 4 步拆解：

1. **修改 3 个 Python 启动器的参数拼接**：在 `scripts/run_glm5_1_744b_a40b_lora.py` 与 `scripts/run_glm5_2_744b_a40b_lora.py` 的 `_train` 函数中，从 `misc_args` 字符串里删除 `--use-miles-router`；在 `examples/multi_lora/run_multi_lora.py` 的 `topology_args` 中删除同一标志。这些字符串随后被拼进 `train_args` 并交给 `U.execute_train`，删除后 `router_manager` 在启动时不再进入 Miles router 分支，而走默认的 sgl-router 路径。

2. **清理 12 个 shell 配方**：`examples/lora/` 下的 `dev.sh`、`run-kimi-k25-megatron-lora.sh`、`run-qwen2.5-0.5B-megatron-lora.sh`、`run-qwen2.5-3B-megatron-lora-disaggregated.sh`、`run-qwen2.5-3B-megatron-lora-disaggregated-multi-node.sh`、`run-qwen3-4b-megatron-lora-result.sh`，`examples/infra_features/low_precision/` 下的 `run-kimi-k2-Thinking-int4.sh`、`run-qwen3-235B-A22B-int4.sh`、`run-qwen3-30b-a3b-fp8-two-nodes.sh`、`run-qwen3-30B-A3B-int4.sh`，以及 `examples/experimental/reproducibility/run-qwen2.5-0.5B-gsm8k.sh` 均删除参数数组或命令行中的 `--use-miles-router` 一行。`examples/infra_features/p2p_weight_transfer/run-glm4.7-flash-2node-profile.sh` 删除了一个已注释的死选项，避免该 flags 被误读为值得重新启用的选项。

3. **明确范围外（不做的事）**：`miles/utils/arguments.py` 中的参数定义、`router_manager.py`、`sglang_engine.py`、`dashboard/` 与 `router/` 全部不动；文档页 `/advanced/miles-router` 也不动，因为其内容是关于 R3 路由回放而非本标志。测试刻意保留：`tests/e2e/sglang/test_r3_router_equivalence.py` 用于对比 Miles router 与 sgl-router，删除标志会直接删掉覆盖而非现代化覆盖，因此 3 个 e2e 测试（`test_lora_qwen2.5_0.5B.py`、`test_quick_start_glm4_9B.py`、`test_glm5_2_744b_a40b_5layer_nvfp4.py`）仍会顺带传递该标志，作者表示可后续单独清理。

4. **验证配套**：作者用 `grep -rn -- "use-miles-router" scripts/ examples/` 确认无残留，`bash -n` 通过全部 12 个 shell 配方，并对 GLM-5.2 gsm8k 示例在 4x H200 上做了端到端前后对比：删标志后 Megatron 参数 dump 显示 `use_miles_router ... False`，`router_manager` 日志来自 sgl-router 分支的 `Router launched at <host>:<port>`，训练 /rollout logprob 最大绝对差 2.2e-4（均值 -10.2），与改动前一致。其余 13 个配方是同样的一行删除，未逐一实跑。

关键文件：
- `scripts/run_glm5_1_744b_a40b_lora.py`（模块 启动脚本；类别 source；类型 core-logic；符号 _train）: GLM-5.1 LoRA 启动器：此前在 misc_args 中写死 --use-miles-router，删除后该大规模 LoRA 训练默认走 sgl-router，是本 PR 两个核心启动器之一。
- `scripts/run_glm5_2_744b_a40b_lora.py`（模块 启动脚本；类别 source；类型 core-logic；符号 _train）: GLM-5.2 LoRA 启动器，与 5.1 同为本次端到端验证对象（4x H200 gsm8k）；删除后 Megatron 参数 dump 确认 use_miles_router=False。
- `examples/multi_lora/run_multi_lora.py`（模块 多 LoRA；类别 source；类型 core-logic；符号 _train）: 多 LoRA 训练的驱动启动器：topology_args 中原先强制 --use-miles-router，删除后多 LoRA rollout 统一走 sgl-router 默认路径，与 async 多 LoRA 训练配合。
- `examples/lora/dev.sh`（模块 LoRA 配方；类别 other；类型 configuration）: 最常用的 LoRA 开发配方，删除硬编码 --use-miles-router 后默认走 sgl-router，覆盖最广的开发路径。
- `examples/lora/run-kimi-k25-megatron-lora.sh`（模块 LoRA 配方；类别 other；类型 configuration）: Kimi K2 Megatron LoRA 配方，同属 lora 主配方集，统一移除强制旧路由标志。
- `examples/infra_features/low_precision/run-kimi-k2-Thinking-int4.sh`（模块 低精度配方；类别 other；类型 configuration）: Kimi K2 int4 低精度配方：此前在 SGLANG_ARGS 数组里强制 --use-miles-router，删除后与其它 low_precision 配方一致走 sgl-router。
- `examples/experimental/reproducibility/run-qwen2.5-0.5B-gsm8k.sh`（模块 复现配方；类别 other；类型 configuration）: 可复现性基准配方，删除强制旧路由标志以保证基准行为与默认路径一致。
- `examples/infra_features/p2p_weight_transfer/run-glm4.7-flash-2node-profile.sh`（模块 p2p 配置；类别 other；类型 configuration）: p2p 剖面对脚本：仅删除一行已注释的死选项，避免把 --use-miles-router 读成值得重新启用的选项。

关键符号：_train (scripts/run_glm5_1_744b_a40b_lora.py), _train (scripts/run_glm5_2_744b_a40b_lora.py), _train (examples/multi_lora/run_multi_lora.py)

## 关键源码片段

### `scripts/run_glm5_1_744b_a40b_lora.py`

GLM-5.1 LoRA 启动器：此前在 misc_args 中写死 --use-miles-router，删除后该大规模 LoRA 训练默认走 sgl-router，是本 PR 两个核心启动器之一。

```python
# GLM-5.1 LoRA 启动器的参数组装（变更后）。
# misc_args 是拼接进 train_args 的字符串段；此前这里写死了
# --use-miles-router，把每次运行都钉死在已弃用的文本路由路径上。
# 该标志为 store_true 且无反形式，启动器也没有暴露对应字段，
# 调用方无法从命令行关闭它，只能改脚本本身。
# 删除后 router_manager 会回退到默认的 sgl-router 分支。
misc_args = (
    f"--attention-dropout 0.0 --hidden-dropout 0.0 "
    "--accumulate-allreduce-grads-in-fp32 --attention-softmax-in-fp32 "
    "--attention-backend flash --calculate-per-token-loss "
    f"--actor-num-nodes 1 --actor-num-gpus-per-node {args.num_gpus_per_node} "
    f"--num-gpus-per-node {args.num_gpus_per_node} --colocate "
)
# 顺带保留：--calculate-per-token-loss 与 --colocate 仍由后续
# 逻辑消费，本次变更不触碰这些参数。
train_args = f"{ckpt_args} {lora_args} {rollout_args} {seq_args} {optimizer_args} {grpo_args} {r3_args} {wandb_args} {perf_args} {sglang_args} {save_args} {misc_args} {args.extra_args} "

```

### `scripts/run_glm5_2_744b_a40b_lora.py`

GLM-5.2 LoRA 启动器，与 5.1 同为本次端到端验证对象（4x H200 gsm8k）；删除后 Megatron 参数 dump 确认 use_miles_router=False。

```python
# GLM-5.2 LoRA 启动器（变更后）: fp8 rollout 场景需要额外写一个
# sglang 配置文件，让 update_weights 保持开启以同步每步 LoRA 权重。
if args.fp8_rollout:
    sglang_config_path = f"{load_save_path}/sglang_fp8_rollout.yaml"
    os.makedirs(load_save_path, exist_ok=True)
    with open(sglang_config_path, "w") as f:
        f.write(
            "sglang:\n"
            "  - name: default\n"
            f"    model_path: {args.fp8_rollout_checkpoint}\n"
            "    update_weights: true\n"
            "    server_groups:\n"
            "      - worker_type: regular\n"
            f"        num_gpus: {args.num_gpus_per_node}\n"
        )
    sglang_args += f"--sglang-config {sglang_config_path} "

# 变更前 misc_args 里混入了 --use-miles-router，把每次运行钉死在
# 已弃用的文本路由路径上；删除后 router_manager 走 sgl-router 默认。
misc_args = (
    f"--attention-dropout 0.0 --hidden-dropout 0.0 "
    "--accumulate-allreduce-grads-in-fp32 --attention-softmax-in-fp32 "
    "--attention-backend flash --calculate-per-token-loss "
    f"--actor-num-nodes 1 --actor-num-gpus-per-node {args.num_gpus_per_node} "
    f"--num-gpus-per-node {args.num_gpus_per_node} --colocate "
)

```

### `examples/multi_lora/run_multi_lora.py`

多 LoRA 训练的驱动启动器：topology_args 中原先强制 --use-miles-router，删除后多 LoRA rollout 统一走 sgl-router 默认路径，与 async 多 LoRA 训练配合。

```python
# 多 LoRA 驱动 : topology_args 此前强制 --use-miles-router，
# 把整套 multi-lora 训练固定到已弃用的文本路由路径。
# 该标志为 store_true 且无 CLI 反形式，配方复制者无法覆盖。
topology_args = (
    f"--actor-num-nodes 1 --actor-num-gpus-per-node {args.actor_num_gpus} "
    f"--rollout-num-gpus {args.rollout_num_gpus} "
)
# 结果 : router_manager 回退到 sgl-router 默认分支，
# rollouts 通过 sgl-router 而非 Miles 文本路由下发。

```

# 评论区精华

本 PR 没有实质性的 review 讨论。唯一的审核来自 yueming-yuan，状态为 APPROVED，评论主体为空。值得记录的决策都写在 PR body 里，作者主动划定了三类范围外事项：① 参数本身（arguments.py 与 router 管线）不在本次移除范围，退役标志是独立决策；② 测试刻意保留，以免删除对 Miles router 与 sgl-router 的等价性覆盖；③ 文档页与标志无关。作者还邀请 reviewer 决定是否把 3 个顺带传递该标志的 e2e 测试纳入清理，但最终未纳入。

- 暂无高价值评论线程

# 风险与影响

- 风险：主要风险是默认路由行为的事实切换：改动前所有配方固定走 Miles router（文本路由），改动后统一回退到 sgl-router，两者在 dp-aware 场景下的引擎寻址、错误处理、日志格式和并发语义存在差异。尤其需要关注 dp-attention 下 sgl-router 的 base URL 寻址（PR #2041 刚修复过 abort 无法触达引擎的问题），本次删除使这些配方实际进入该路径。其次，验证只覆盖了 GLM-5.2 gsm8k 一个配方，其余 13 个配方未单独运行，存在未暴露的兼容性回归可能；不过这些配方共用同一条 router_manager 回退逻辑，且均为单行删除，风险等级不高。测试侧并无变更，3 个 e2e 测试仍覆盖旧的 Miles router 路径，反而形成了新默认路径覆盖不足的偏差。
- 影响：影响范围集中在启动层：使用 `scripts/` 中 GLM-5.1/5.2 LoRA 启动器、`examples/lora/`、`examples/infra_features/low_precision/`、`examples/multi_lora/` 与 reproducibility 配方的用户，将从已弃用的文本路由自动迁移到 sgl-router，行为上更接近仓库其他脚本的默认路径，也有利于后续统一维护。对核心库无影响，因为 flags 的实际消费逻辑（router_manager 的分支选择）未改动，只是不再有调用方强制走进旧分支。对团队而言，这是一次低成本的默认值对齐，降低了“按示例跑却走上废弃路径”的认知负担。
- 风险标记：默认路由行为切换 , 剩余配方未逐一实测 , 测试仍覆盖旧路径

# 关联脉络

- PR #2012 router: enable dp-aware routing under dp-attention: 开启 dp-attention 时自动启用 dp-aware 路由，与本 PR 删除 --use-miles-router 后实际生效的 sgl-router 默认路径直接相关。
- PR #2041 Address engines by base URL when the router is dp-aware: 修复 dp-aware 路由下 abort 无法触达引擎的问题，是 sgl-router 默认路径上的关键配套修复，本 PR 让更多配方实际进入该路径。
- PR #1795 Bump sglang to v0.5.16: sglang 升级与路由、权重更新同步路径相关，sgl-router 分支的行为依赖该版本栈。