# PR #1953 完整报告

- 仓库：`radixark/miles`
- 标题：refactor(examples): group examples into infra_features/ and experimental/
- 合并时间：2026-07-30 03:10
- 原文链接：http://prhub.com.cn/radixark/miles/pull/1953

---

# 执行摘要

- 一句话：重组 examples 目录并清理废弃示例
- 推荐动作：值得精读，这是大型目录重组的典型案例。重点关注三类引用形式的区分处理、fully_async 与并发 PR 的冲突规避、以及静态审计替代 CI 的验证方法。若不依赖示例目录，可快速浏览执行摘要与风险部分即可。

# 功能与动机

PR body 明确说明：`The top level of examples/ had grown to 24 entries with no signal about which are verified recipes, which are infrastructure plumbing, and which are unverified experiments. This sorts them so the top level mostly holds training recipes people are expected to run.` 即顶层目录因缺少分类信号而难以导航，需要按「已验证配方 / 基础设施管道 / 未验证实验」重新分层，同时清理已被替代或废弃的示例。

# 实现拆解

实现拆解共分 5 步：

1. **建立分类目录并迁移**：新增 `examples/infra_features/` 收纳运行时 / 基础设施类管道，包括 `low_precision`、`p2p_weight_transfer`、`train_infer_mismatch_helper`、`true_on_policy`、`random_async`；将 `DrGRPO`、`eval`、`eval_multi_task`、`formal_math`、`multi_agent`、`reproducibility`、`search-r1`、`strands_sglang`、`tau-bench` 迁入已有的 `examples/experimental/`（与 `openenv` 并列）。

2. **合并与删除**：将 `examples/geo3k_vlm_multi_turn/` 合并为 `examples/geo3k_vlm/multi_turn/`，使两个 geo3k 配方同处一个目录；删除被 `retool_v2` 完全取代的 `examples/retool/` 和废弃的 `examples/true_on_policy_vlm/`，并移除孤儿文档页 `docs/examples/retool.md`，同时删掉 `retool_v2/tool_sandbox.py` 中指向已不存在来源的 provenance docstring。

3. **三类引用分别处理**：PR body 梳理了三种引用形式——文件系统路径（移动即需改）、dotted module 路径（常藏在 `scripts/run_*.py` 启动器里，最容易漏）、Mintlify 文档路由（由 `docs/examples/*.md` 文件名派生，与目录布局无关，因此故意不动）。此项顺带修复了三个本来就失效的 dotted path：`examples.Dr.GRPO.custom_reducer` → `examples.experimental.DrGRPO.custom_reducer`、`examples.vlm_multi_turn.env_geo3k` → `examples.geo3k_vlm.multi_turn.env_geo3k`（后者是 `DEFAULT_ENV_MODULE` 运行时默认值）、`examples/full_async` → `examples/fully_async`（注释中的拼写错误）。

4. **路径深度修复**：最后一个提交 `4870aa3` 修正搬迁后层级加深导致的 `__file__`/`$SCRIPT_DIR` 向上路径失效，共 12 处，例如 `skills_server.py` 的 `parents[3]` 改为 `parents[4]`、`p2p_weight_transfer/run.py` 的 `parents[2]` 改为 `parents[3]`，以及 `eval/scripts/run-qwen3-{4B,32B}.sh` 等 shell 脚本补一个 `..`。

5. **文档与静态验证**：重写 `examples/README.md`，补录此前缺失的 `lora`、`multi_lora`、`p2p_weight_transfer`、`random_async`、`retool_v2` 条目，并修复失效的 `strands-agents` 链接；新增 `examples/infra_features/README.md` 落地页；更新 `docs/docs.json` 导航。由于 `examples/` 在 pytest 的 `norecursedirs` 中、CI 不 import 示例代码，验证改为静态审计：所有 `examples/...` 路径 token、所有 dotted `examples.*` 引用、`docs.json` 中 76 条页面 / 组根引用、9 条 `from examples.... import ...` 均逐一核对可解析。

关键文件：
- `examples/README.md`（模块 示例索引；类别 docs；类型 restructure）: 重构后顶层索引的权威入口，重写后补录 5 个此前缺失的示例条目并修复失效链接，直接反映新的目录分层意图。
- `docs/docs.json`（模块 文档导航；类别 docs；类型 configuration）: Mintlify 文档导航入口，需与目录重组保持同步；本次验证全部 76 条页面和组根引用可解析，并移除已删除页面对应的路由。
- `examples/experimental/eval/nemo_skills/skills_server.py`（模块 评测服务；类别 source；类型 rename-or-move；符号 REPO_ROOT, SkillsEvalEnvDatasetConfig）: 典型的重命名加固文件：目录层级加深后 REPO_ROOT 从 parents[3] 改为 parents[4]，同包 import 前缀改为 examples.experimental，可看出所有搬迁文件需要同步处理的两种路径写法。
- `examples/geo3k_vlm/multi_turn/rollout.py`（模块 多轮环境；类别 source；类型 rename-or-move；符号 DEFAULT_ENV_MODULE, BaseInteractionEnv）: 目录合并的代表文件，且顺带修复了 DEFAULT_ENV_MODULE 这个运行时默认值——旧值指向从未存在的模块，属于 PR 提到的“本已损坏、顺带修正”的关键例子。
- `miles/utils/arguments.py`（模块 参数解析；类别 source；类型 fix；符号 custom_reducer）: 非示例目录中的核心改动：修正 help text 里失效的 dotted path，说明示例重组对外部引用有连带影响，是三类引用中最容易遗漏的一类。
- `examples/retool/generate_with_retool.py`（模块 旧版示例；类别 source；类型 deletion；符号 format_conversation_with_tools, postprocess_predictions, postprocess_responses, execute_predictions）: 被整体删除的 retool 示例核心文件，代表本 PR 中“被替代即删除”的清理策略；其多轮生成与工具调用逻辑已被 retool_v2 取代。
- `examples/retool/tool_sandbox.py`（模块 旧版示例；类别 source；类型 deletion；符号 PythonSandbox, execute_code, _check_code_safety, get_memory_usage）: retool 示例的安全沙盒实现，随示例整体删除；其中 PythonSandbox 与内存清理逻辑不再保留在仓库中。
- `examples/infra_features/README.md`（模块 基础设施；类别 docs；类型 entrypoint）: 新增 infra_features 落地页，标记该目录定位为运行时 / 基础设施管道而非模型配方，是本次分层设计中新增的导航节点。
- `examples/true_on_policy_vlm/run_simple.py`（模块 示例脚本；类别 source；类型 deletion；符号 prepare, execute）: 被删除的废弃 VLM true-on-policy 启动脚本，体现本次对无人维护示例的清理。
- `docs/examples/retool.md`（模块 文档页面；类别 docs；类型 deletion；符号 PythonSandbox, execute_code, reward_func）: 随 retool 删除而移除的孤儿文档页，与 docs/docs.json 路由删除配套，避免文档指向不存在的示例。

关键符号：DEFAULT_ENV_MODULE, format_conversation_with_tools, postprocess_predictions, postprocess_responses, execute_predictions, PythonSandbox, execute_code, prepare, execute

## 关键源码片段

### `examples/experimental/eval/nemo_skills/skills_server.py`

典型的重命名加固文件：目录层级加深后 REPO_ROOT 从 parents[3] 改为 parents[4]，同包 import 前缀改为 examples.experimental，可看出所有搬迁文件需要同步处理的两种路径写法。

```python
# examples/experimental/eval/nemo_skills/skills_server.py
# 目录层级加深后，需要同步修正两类路径写法，否则脚本无法启动。

# 1) REPO_ROOT: 从 examples/eval/nemo_skills/ 向上到仓库根原来是 parents[3]；
# 搬进 examples/experimental/eval/nemo_skills/ 后多了一层，必须改成 parents[4]，
# 否则 sys.path 会注入到 examples/ 而不是仓库根。
REPO_ROOT = Path(__file__).resolve().parents[4]
if str(REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(REPO_ROOT))

# 2) 同包 import 一律改为 examples.experimental 前缀，
# 保持 dotted module path 与磁盘布局一致。
from examples.experimental.eval.nemo_skills.skills_config import SkillsEvalEnvDatasetConfig

```

### `examples/geo3k_vlm/multi_turn/rollout.py`

目录合并的代表文件，且顺带修复了 DEFAULT_ENV_MODULE 这个运行时默认值——旧值指向从未存在的模块，属于 PR 提到的“本已损坏、顺带修正”的关键例子。

```python
# examples/geo3k_vlm/multi_turn/rollout.py
# 该文件由 examples/geo3k_vlm_multi_turn/rollout.py 合并而来，
# 搬迁后模块路径和运行时默认值需要同步修正，否则按默认参数启动会直接 import 失败。

# 1) 模块内跨文件 import 跟随新目录
from examples.geo3k_vlm.multi_turn.base_env import BaseInteractionEnv

# 2) DEFAULT_ENV_MODULE 是训练启动时的运行时默认值。
# 旧值 examples.vlm_multi_turn.env_geo3k 是一个从未存在过的模块路径，
# 这次重组把它纠正为合并后的真实模块，避免 --custom-env-module 未指定时崩溃。
DEFAULT_ENV_MODULE = "examples.geo3k_vlm.multi_turn.env_geo3k"

```

# 评论区精华

该 PR 的 review 评论为空，仅有一条来自合入者 Zhichenzzz 的批准意见：`LGTM, and small nits according to the parents path fixed`，指向提交 4870aa3 对 12 处向上路径深度的修正。PR body 中另有两处值得关注的决策讨论：

- **fully_async 刻意保留**：作者说明有一个进行中的并发 PR 会迁移 `examples/fully_async/`，因此本 PR 不触碰它，并确保 `examples/fully_async/` 与两个 fully-async 文档页相对 main 完全一致、`swe-agent` 异步启动器保留原 `PYTHONPATH` 入口，避免与并发迁移冲突。
- **引用形式的三分类**：作者区分了文件系统路径、dotted module 路径与 Mintlify doc route 三种引用，明确定义哪些会随移动失效、哪些不会，并借此解释了为何 `docs.json` 中的 `/examples/search-r1` 路由不动而 `docs/examples/retool.md` 路由必须删除。

 - fully_async 是否纳入本次重组 (design): fully_async 保留在顶层，待并发 PR 再迁移；同时提醒后续迁移需同步更新 swe-agent 脚本与 docs/user-guide/fully-async.md。
 - parents 路径深度修正 (correctness): 已修复并合入，无遗留问题。
 - DEFAULT_ENV_MODULE 运行时默认路径修复 (correctness): 改为 examples.geo3k_vlm.multi_turn.env_geo3k 并合入，避免默认参数启动时的 import 崩溃。
 - 静态审计代替 CI 验证 (testing): 静态审计通过；但示例级 import 错误仍无自动化防护，风险由人工审计兜底。

# 风险与影响

- 风险：主要风险集中在路径引用与验证盲区：

1. **dotted module 路径回归**：这类引用大量存在于 `scripts/run_*.py` 启动器而非示例目录内部，静态审计已覆盖仓库内所有 token，但审计是一次性动作，后续新增引用仍可能踩中旧路径。
2. **向上路径深度依赖**：`REPO_ROOT` 等 `parents[n]` 计算对目录深度敏感，本次靠提交 4870aa3 修复 12 处，但未来继续向 `examples/experimental/` 下层嵌套时仍容易重犯，缺少运行时校验。
3. **CI 覆盖缺失**：`examples/` 在 pytest 的 `norecursedirs` 中，示例代码的 import 错误不会被 CI 捕获；本次依赖静态审计，后续示例级改动若无对应测试，同样存在回归风险。
4. **retool 删除影响**：删除 `examples/retool/` 后，任何未纳入审计的外部脚本或文档若仍引用该目录将直接中断；PR 声称仓库内已无残留引用，但对外部使用者有破坏性。
5. **fully_async 并发依赖**：`fully_async_rollout.generate_rollout_fully_async` 被 `examples/swe-agent/run-glm47-flash-agentic-async.py` 和 `docs/user-guide/fully-async.md` 依赖，并发 PR 落地时需要同步更新，否则会出现文档与代码脱节。
- 影响：对用户：所有依赖旧示例路径的 shell 脚本、启动命令和文档链接都需要更新，`examples/retool` 与 `examples/true_on_policy_vlm` 被直接删除，属于破坏性变更；对系统：核心训练 / 推理代码无功能改动，但 `miles/utils/arguments.py` 的 help text 和 `examples/geo3k_vlm/multi_turn/rollout.py` 的 `DEFAULT_ENV_MODULE` 属运行时可见修正；对团队：examples 目录从此有了「顶层为可运行配方、infra_features 为管道、experimental 为未验证研究」的清晰分层，降低导航与甄别成本，也减少了未来新示例乱放的可能。
- 风险标记：大型目录重组 , 引用路径易回归 , 无自动化测试覆盖 , 并发 PR 依赖 , CI 不校验 examples/import

# 关联脉络

- PR #1921 Add NeMo-Gym integration: mini_swe_agent_2 via the agent function: 该 PR 新增的 examples/experimental/nemo-gym 示例与本次将 eval、formal_math 等迁入 examples/experimental/ 的目录分层方向一致，共同确立 experimental/ 的组织约定。
- PR #1790 openenv/tbench2: score the shared-server leg natively; retire the adapter compensation: openenv 本就是 examples/experimental/ 下的既有示例，本次将其他研究型示例并入同一分组，二者共同定义了 experimental/ 的定位。
- PR #2009 [docs] Add Inkling-Small model page: 该 PR 同样修改 docs/docs.json 导航，与本次文档路由调整属于同一文档演进线。