# MVP 计划（V1）

本文档目标：把当前“口述/实验记录”整理成**可复现、可验收**的 MVP 计划，并明确下一步最小闭环。

## 1. 背景与目标

我们要验证的最小闭环是：

1) 在“Head（CPU 容器）+ Worker（GPU 容器）”的 Ray Cluster 上，能够跑通一次 `verl` 的 PPO 训练。  
2) 训练所需的 **数据集 / 模型缓存 / 训练产物（checkpoint）/ 日志** 不落在容器临时文件系统里，而是落在**共享存储（NFS）**，容器重启后可继续使用。  
3) 所有步骤能写成一套**清晰命令/脚本**，新人可照着复现。

## 2. 环境与假设

- 机器：H20 机器（具体规格由算力平台提供）
- 访问方式：通过 `ssh h1a` 远程登录（进入算力平台/宿主访问入口）
- 容器：算力平台可申请 CPU 容器（对外暴露端口）与若干 GPU 容器（可 SSH 互通）
- 共享存储：所有容器可挂载同一套 NFS（在 `specs/hl_design_v2.md` 中假设为 `/mnt/shared`）

## 3. 已验证现状（现有实验）

目录 `ray_in_docker/` 已经做过一次可运行的实验（偏“本地/示例级别”）：

- 用 `docker-compose` 起了 2 个 `verl` 镜像容器：
  - `verl-head`：作为 Ray Head（Dashboard 端口 `8265`）
  - `verl-worker`：作为 Ray Worker
- 在容器中执行：
  - 下载 GSM8K 数据集（`examples/data_preprocess/gsm8k.py`）
  - 拉取 HuggingFace 模型（示例：`Qwen/Qwen2.5-0.5B-Instruct`）
  - `ray start --head` + `ray start --address=...`
  - 通过 `ray job submit ... python -m verl.trainer.main_ppo ...` 提交 PPO 训练任务（见 `ray_in_docker/ray_example/ppo_train.sh`）

结论：**训练脚本可以跑通**。

## 4. 当前主要问题（从实验到平台化 MVP 的差距）

1) **数据 / 模型 / 输出落在容器内**：容器重启/替换后不可复用；也不利于多人共享与审计。  
2) **缓存路径不规范**：HuggingFace cache、Ray 临时目录、Hydra 输出目录等可能分散在容器默认路径。  
3) **可复现不足**：缺少明确的目录规范、统一的启动/提交流程、验收口径。  
4) Ray 节点**打标签/亲和性调度**的方法未固化：需要明确是否统一用 `ray start --resources`，以及命名规范如何设计。

## 5. MVP V1：最小闭环定义

以 `specs/hl_design_v2.md` 的方向为准，但 V1 **只做最小可运行原型**，暂不做完整 Web/调度系统。

### 5.1 目录规范（统一落到 NFS）

约定所有容器统一挂载 NFS 到 `/mnt/shared`，并在其中固定目录结构：

- `/mnt/shared/code/`：代码（可选：按版本/分支隔离）
- `/mnt/shared/datasets/`：数据集（如 `gsm8k/`）
- `/mnt/shared/hf/`：HuggingFace 缓存（设置 `HF_HOME=/mnt/shared/hf`）
- `/mnt/shared/ray/`：Ray 运行期临时目录（可选：设置 `RAY_TMPDIR=/mnt/shared/ray/tmp`）
- `/mnt/shared/outputs/`：训练输出根目录（Hydra/日志/ckpt 统一落这里）
  - `/mnt/shared/outputs/logs/<job_id>/`
  - `/mnt/shared/outputs/checkpoints/<job_id>/`

### 5.2 最小集群形态

- 1 个 Head（CPU 容器）
  - 跑 `ray start --head --dashboard-host=0.0.0.0`
  - 暴露 `8265` 给 Desktop/用户查看 Job 状态
- 1~2 个 Worker（GPU 容器）
  - 跑 `ray start --address=<head_ip>:6379` 加入集群
  - （可选）通过 `--resources='{\"gpu_pool_a\": 1}'` 给节点打标签

### 5.3 最小训练任务

- 目标任务：跑通一次 `verl.trainer.main_ppo`（以 GSM8K 为例）
- 要求：
  - `data.train_files` / `data.val_files` 指向 `/mnt/shared/datasets/...`
  - HuggingFace 模型下载缓存落到 `/mnt/shared/hf`
  - 训练输出（Hydra outputs、checkpoint、stdout/stderr）落到 `/mnt/shared/outputs/...`

建议在提交命令里显式覆盖 Hydra 输出目录（示例，具体目录名按需调整）：

- `hydra.run.dir=/mnt/shared/outputs/logs/${JOB_TAG}`

## 6. 实施步骤（Checklist）

### 6.1 一次性准备

- [ ] 确认所有容器已挂载 NFS 到同一路径：`/mnt/shared`
- [ ] 在 `/mnt/shared/` 下创建目录：`datasets/ hf/ outputs/ ray/`
- [ ] 在所有容器中设置/注入环境变量（推荐写入统一脚本）：
  - `HF_HOME=/mnt/shared/hf`
  - `HF_ENDPOINT=https://hf-mirror.com`（如需）
  - `RAY_TMPDIR=/mnt/shared/ray/tmp`（可选）

### 6.2 启动集群（Head + Worker）

- [ ] 在 Head 容器启动 Ray Head（并记录 `head_ip:6379`）
- [ ] 在每个 Worker 容器执行 `ray start --address=...` 加入集群
- [ ] 在 Head 上通过 `ray status` / Dashboard 验证节点已注册

### 6.3 准备数据与模型

- [ ] 数据集下载到：`/mnt/shared/datasets/gsm8k/`
- [ ] 模型缓存落到：`/mnt/shared/hf/`（拉取一次即可多任务复用）

### 6.4 提交训练任务

- [ ] 用 `ray job submit --address=http://<head>:8265 ...` 提交 PPO 训练
- [ ] 训练日志与 checkpoint 在 `/mnt/shared/outputs/` 可见

## 7. 验收标准（V1）

- [ ] Ray Head/Worker 能稳定加入同一集群（Dashboard 可见）
- [ ] PPO 训练任务可提交并跑通（至少完成若干 step/epoch）
- [ ] 数据集、HF 缓存、训练输出均在 `/mnt/shared/` 下可复用（容器重启后仍在）
- [ ] 有一份“从零到跑通”的命令清单（或脚本）可复现

## 8. 未决问题（记录待补齐）

- [ ] Ray 节点标签/亲和性调度：是否统一用 `ray start --resources`，以及命名规范如何设计
- [ ] RL workload 的 Driver 放置策略：先按 `verl` 默认即可，后续再按 `specs/hl_design_v2.md` 收敛到“Driver-on-Head / Placement Group”等模式

## 9. 下一步（进入 V2）

当 V1 达到“可复现 + 产物可落盘”的验收标准后，下一阶段工作见：`specs/mvp_plan_v2.md`。
