# PR #1897 完整报告

- 仓库：`radixark/miles`
- 标题：Fix various launch scripts errors about missing line concatenations or paths
- 合并时间：2026-08-09 18:22
- 原文链接：http://prhub.com.cn/radixark/miles/pull/1897

---

# 执行摘要

- 一句话：修复启动脚本路径与续行符错误并补 AMD 硬件配置
- 推荐动作：该 PR 属于小范围修复，可直接阅读；重点关注 command_utils.py 中 rsync_simple 的参数转发模式，以及后续 #1898-#1911 系列 PR 对启动脚本的测试保护，理解本 PR 如何为快照测试铺路。

# 功能与动机

PR body 仅标注 Part of #1837（重构与增强跟踪 issue）。实际动因是部分启动脚本因路径硬编码和缺失续行符无法运行，例如 examples/infra_features/low_precision 下多个脚本 source 相对路径错误，scripts/run-kimi-k2-*.sh 缺少续行符导致后续参数被吞，另外 NUM_GPUS_OF_HARDWARE 缺少 AMD 硬件声明会导致使用新硬件的启动脚本抛 KeyError。

# 实现拆解

1. **修正示例脚本中的模型配置路径**：对 examples/infra_features/low_precision 下的 4 个 int4 示例，将未生效的 `source "${SCRIPT_DIR}/../../models/xxx.sh"` 改为 `"${SCRIPT_DIR}/../../../scripts/models/xxx.sh"`，使模型脚本能被正确加载；对 examples/lora/run-qwen3-4B-megatron-lora.sh，修正 `REPO_ROOT` 向上级目录的层数（由 `../../..` 改为 `../..`），并去掉 source 路径中的多余 `miles/` 前缀，同时将 eval 配置路径中的 `miles/` 前缀移除。
2. **修复续行符缺失**：在 scripts/run-kimi-k2-Instruct.sh 与 scripts/run-kimi-k2-Thinking.sh 中，为 `--update-weight-buffer-size $(( 4 * 512 * 1024 * 1024))` 这一行补上末尾的 `\`，否则 shell 会把下一行的 `${MODEL_ARGS[@]}` 当作普通参数，导致模型参数被静默吞掉。
3. **扩展命令工具能力**：在 miles/utils/external_utils/command_utils.py 中，`rsync_simple` 新增 `num_nodes` 可选参数并转发给 `exec_command_all_ray_node`，使调用方可限制 rsync 只在指定节点上执行；同时为 `NUM_GPUS_OF_HARDWARE` 增加 `MI350X` 与 `MI355X`（各 8 GPU），避免新硬件启动脚本因 KeyError 失败。
4. **补充单元测试**：在 tests/fast/utils/test_command_utils.py 中新增两个测试：`test_every_supported_hardware_declares_its_gpus_per_node` 参数化覆盖所有已声明硬件并断言 GPU 数量大于 0；`test_rsync_simple_limits_itself_to_the_requested_node_count` 验证 `num_nodes` 参数确实被传递给 `exec_command_all_ray_node`。

关键文件：
- `miles/utils/external_utils/command_utils.py`（模块 命令工具；类别 source；类型 core-logic；符号 rsync_simple）: 核心命令工具修改：rsync_simple 增加 num_nodes 转发，NUM_GPUS_OF_HARDWARE 新增 AMD 硬件声明，影响多个启动脚本。
- `tests/fast/utils/test_command_utils.py`（模块 单元测试；类别 test；类型 test-coverage；符号 test_every_supported_hardware_declares_its_gpus_per_node, test_rsync_simple_limits_itself_to_the_requested_node_count）: 新增两个单元测试，分别覆盖硬件声明完整性和 rsync_simple 的 num_nodes 转发，确保回归不复发。
- `examples/lora/run-qwen3-4B-megatron-lora.sh`（模块 示例脚本；类别 other；类型 core-logic）: 修复 REPO_ROOT 层级计算和 source 路径，是路径类修复的典型代表，关系 LoRA 示例能否运行。
- `scripts/run-kimi-k2-Instruct.sh`（模块 启动脚本；类别 other；类型 core-logic）: 修复缺失续行符导致后续参数被吞的问题，直接影响 Kimi-K2 训练任务的启动。

关键符号：rsync_simple, test_every_supported_hardware_declares_its_gpus_per_node, test_rsync_simple_limits_itself_to_the_requested_node_count

## 关键源码片段

### `miles/utils/external_utils/command_utils.py`

核心命令工具修改：rsync_simple 增加 num_nodes 转发，NUM_GPUS_OF_HARDWARE 新增 AMD 硬件声明，影响多个启动脚本。

```python
# miles/utils/external_utils/command_utils.py

def rsync_simple(path_src: str, path_dst: str, num_nodes: int | None = None):
    # 新增 num_nodes 参数，转发给 exec_command_all_ray_node，
    # 使调用方可以把 rsync 限制在指定数量的节点上，避免全集群广播
    exec_command_all_ray_node(
        f"mkdir -p {path_dst} && rsync -a --info=progress2 {path_src}/ {path_dst}",
        num_nodes=num_nodes,
    )


NUM_GPUS_OF_HARDWARE = {
    "H100": 8,
    "GB200": 4,
    "GB300": 4,
    # 新增 AMD 硬件声明，避免使用这些硬件的启动脚本因 KeyError 失败
    "MI350X": 8,
    "MI355X": 8,
}

```

### `tests/fast/utils/test_command_utils.py`

新增两个单元测试，分别覆盖硬件声明完整性和 rsync_simple 的 num_nodes 转发，确保回归不复发。

```python
# tests/fast/utils/test_command_utils.py

import pytest
from miles.utils.external_utils import command_utils


@pytest.mark.parametrize("hardware", ["H100", "GB200", "GB300", "MI350X", "MI355X"])
def test_every_supported_hardware_declares_its_gpus_per_node(hardware):
    """缺少声明的硬件会在启动脚本运行时抛 KeyError，因此这里必须覆盖所有已支持硬件。"""
    assert command_utils.NUM_GPUS_OF_HARDWARE[hardware] > 0


def test_rsync_simple_limits_itself_to_the_requested_node_count(monkeypatch):
    """prepare_cp 依赖节点数限制，确认转发逻辑没有被破坏。"""
    calls = []
    monkeypatch.setattr(command_utils, "exec_command_all_ray_node", lambda cmd, **kwargs: calls.append(kwargs))

    command_utils.rsync_simple("/src", "/dst", num_nodes=4)

    # 必须原样把 num_nodes 传给底层执行器
    assert calls == [{"num_nodes": 4}]

```

# 评论区精华

本 PR 无 review 评论，仅有一条 issue 评论为 Gemini Code Assist 停用通知；唯一审核 yueming-yuan 的 APPROVED 也未附文字，因而没有实质交锋。

- 无实质 review 讨论 (other): 无争议，直接合并。

# 风险与影响

- 风险：
 1. **rsync_simple 签名变更**：新增可选参数 num_nodes，默认 None，已保持向后兼容，但若其他调用方未适配或 exec_command_all_ray_node 对 None 处理不当，可能影响 rsync 行为，需要调用方同步确认。
 2. **脚本路径修复依赖相对位置**：示例脚本的 source 路径修正依赖仓库目录结构保持稳定，若未来重新组织 scripts/models 目录，这些硬编码相对路径可能再次失效。
 3. **硬件字典新增条目**：NUM_GPUS_OF_HARDWARE 仅新增 MI350X / MI355X，若其他 AMD 硬件（如 MI300）被使用，仍会触发 KeyError，需持续补充声明。
 - 影响：直接影响是让多个 low_precision 示例和 Kimi-K2 启动脚本可以正确执行，避免无提示的失败；间接影响是为后续 #1898-#1911 系列启动脚本测试保护提供了前置修复，保证快照测试能稳定运行。对用户而言，使用这些脚本进行模型训练的工程师不再遇到路径或续行符错误；对团队而言，该 PR 清除了重构链条上的一些阻塞问题。
 - 风险标记：rsync_simple 签名扩展需调用方确认 , 示例脚本依赖相对路径稳定性 , 硬件字典仅补充两种 AMD 型号

# 关联脉络

- PR #1898 Derive the miles checkout location instead of hardcoding it in launch scripts: 同为 #1837 系列，解决启动脚本硬编码路径问题的不同方面，本 PR 修复路径错误，该 PR 改为推导路径。
- PR #1899 Snapshot the external commands of every shell launch script: 系列中为 shell 启动脚本建立快照测试，本 PR 修复的脚本是快照测试的对象。
- PR #1901 Snapshot the commands and generated configs of every python launch script: 系列中通过快照测试保护启动脚本命令，与本 PR 的修复形成前后配合。
- PR #1902 Cover the public surface of command_utils with unit tests: 为 command_utils 补齐测试，与本 PR 新增的 rsync_simple 测试同属对命令工具的测试加固。