201 lines
8.7 KiB
Markdown
201 lines
8.7 KiB
Markdown
# MVP v3.5(精简版)开发计划(TDD)
|
||
|
||
> 目标:在 v3.0 已有能力基础上,仅新增两项能力:
|
||
> 1) **Advanced TaskSpec(自定义 command)**
|
||
> 2) **Custom Reward(方式 A:用户自己在 command 里写 `custom_reward_function.*`)**
|
||
>
|
||
> 设计依据:`specs/mvp/v3.5/v3.5_design.md`(本计划不再扩展 scope)。
|
||
|
||
---
|
||
|
||
## 0. 范围与约束
|
||
|
||
### 0.1 In scope
|
||
- 新增 `kind: advanced` 的 TaskSpec:用户提供 `command`,平台做 `$HOME` 宏替换与 best-effort 校验,再提交 Ray Job。
|
||
- Custom Reward:平台仅做 **reward path 校验**(方式 A),不新增结构化字段。
|
||
- `$HOME/common/*` 路径语义支持(关键):用户在 SFTPGo/WebClient 看到的路径能被训练进程正确读取。
|
||
|
||
### 0.2 Out of scope(本轮不做)
|
||
- 自定义 verl 版本/代码路径(多版本共存)
|
||
- 断点续训(resume from checkpoint)
|
||
- IB/RoCEv2/NCCL 专项支持
|
||
- Model Serving
|
||
- Node management 改造(v3.0 的 stateless head/worker/watchdog/supervisor 机制保持不变)
|
||
|
||
### 0.3 关键路径映射(必须保持一致)
|
||
> 说明:SFTPGo 的 `$HOME/common/...` 是 **virtual folder**,训练进程看不到该虚拟路径。
|
||
|
||
提交 Advanced command 前必须展开/映射:
|
||
- `$HOME/common/datasets` → `/private/datasets`(只读语义)
|
||
- `$HOME/common/hf` → `/private/hf`(只读语义)
|
||
- 其余 `$HOME` → `/private/users/<user_id>`
|
||
|
||
并且为兼容历史用法(v3.0):
|
||
- Basic TaskSpec 仍接受 `/private/common/datasets/...`、`/private/common/hf/...`(不强制迁移)。
|
||
|
||
---
|
||
|
||
## 1. 测试策略(TDD)
|
||
|
||
### 1.1 单元测试优先级
|
||
1) **解析与兼容**:`kind: advanced` 能解析;无 `kind` 仍按 Basic 解析,旧用法不破坏。
|
||
2) **宏替换正确性**:`$HOME` / `$HOME/common/*` 映射严格按约定展开。
|
||
3) **best-effort 校验**:拒绝明显危险/跨用户路径;对 reward path 做 allowlist。
|
||
4) **提交链路**:Scheduler 能识别 Advanced spec 并调用对应的提交方法,确保 submission_id/目录规范不变。
|
||
5) **WebUI/API**:New Task 模板与 `/spec` 展示完整 resolved spec(包含展开后的 command)。
|
||
|
||
### 1.2 本地运行方式
|
||
- 复用已有 `.venv`,执行:`.venv/bin/python -m pytest`
|
||
- 若环境没有 pip,使用 uv 的方式参考 v3.0 约定(不在本计划重复)。
|
||
|
||
---
|
||
|
||
## 2. 里程碑划分(每个里程碑可独立验证)
|
||
|
||
> 约定:每个里程碑先写测试(失败),再实现代码使测试通过;里程碑结束跑一遍 `pytest`。
|
||
|
||
### M1 — TaskSpec 模型与解析(兼容优先)
|
||
**目标**
|
||
- 引入 AdvancedTaskSpec 数据结构与 union parser,同时保证 v3.0 Basic 行为不变。
|
||
|
||
**新增/修改(建议位置)**
|
||
- `src/mvp/py/argus/ray/models.py`
|
||
- 新增 `AdvancedTaskSpec`
|
||
- 新增 `parse_taskspec(obj: dict) -> JobSpec | AdvancedTaskSpec`
|
||
- 兼容策略:缺省 `kind` → 走 `JobSpec.from_dict`
|
||
|
||
**测试(先写)**
|
||
- `src/mvp/py/tests/test_models.py`
|
||
- `test_parse_taskspec_basic_no_kind_compat()`
|
||
- `test_parse_taskspec_advanced_smoke()`
|
||
- `test_parse_taskspec_advanced_requires_command_nnodes_gpus()`
|
||
|
||
**验收**
|
||
- `pytest -q` 通过;旧测试不修改或仅做最小必要更新。
|
||
|
||
---
|
||
|
||
### M2 — Advanced command 展开与校验(核心能力)
|
||
**目标**
|
||
- 实现 command 展开(含 `$HOME/common/*` 映射)与 best-effort 强约束校验。
|
||
|
||
**实现点(建议新增模块)**
|
||
- `src/mvp/py/argus/service/command_expand.py`(或放在 `argus/service/validation.py`)
|
||
- `expand_advanced_command(user_id: str, command: str) -> str`
|
||
- `validate_advanced_command(user_id: str, expanded_command: str) -> None`(失败抛 `ValueError`)
|
||
|
||
**强约束(与设计文档一致)**
|
||
- 必须包含 `python3` 且包含 `-m verl.trainer.`(否则 400)
|
||
- 禁止出现 `/private/users/<other>/...`(跨用户路径)
|
||
- 若检测到 `data.train_files=`/`data.val_files=`:
|
||
- 只允许 `/private/users/<me>/datasets/...` 或 `/private/datasets/...`
|
||
- (兼容)允许 `/private/common/datasets/...`(旧路径)
|
||
- 若检测到 `custom_reward_function.path=`:
|
||
- 只允许 `/private/users/<me>/code/...`(展开后校验)
|
||
|
||
**测试(先写)**
|
||
- 新增:`src/mvp/py/tests/test_advanced_command.py`
|
||
- `test_expand_maps_home_common_datasets_to_private_datasets()`
|
||
- `test_expand_maps_home_common_hf_to_private_hf()`
|
||
- `test_expand_maps_home_to_private_users()`
|
||
- `test_validate_rejects_cross_user_paths()`
|
||
- `test_validate_requires_verl_trainer_entry()`
|
||
- `test_validate_allows_reward_path_under_user_code()`
|
||
- `test_validate_rejects_reward_path_outside_user_code()`
|
||
|
||
**验收**
|
||
- 单测覆盖映射/校验的正反例;错误信息可读(用于 API 400 detail)。
|
||
|
||
---
|
||
|
||
### M3 — Ray 提交链路支持 Advanced(Builder/Tool/Scheduler)
|
||
**目标**
|
||
- Advanced spec 能进入 scheduler 队列并提交为 Ray job(driver 仍落 worker)。
|
||
|
||
**代码改动点(建议)**
|
||
- `src/mvp/py/argus/ray/builders.py`
|
||
- 新增 `build_advanced_argv(command: str)`:返回 `["bash","-lc", expanded_command]`
|
||
- `src/mvp/py/argus/ray/ray_job_tool.py`
|
||
- 新增 `submit_advanced(...)`(或统一成内部 submit plan)
|
||
- runtime_env:继续注入公共 verl code path(本轮不支持用户自定义 verl 代码)
|
||
- 可选:把 `/private/users/<user>/code` 加入 `PYTHONPATH`,提升 reward 代码 `import` 体验
|
||
- `src/mvp/py/argus/service/scheduler.py`
|
||
- 使用 `parse_taskspec` 分流 Basic/Advanced
|
||
- Advanced 调用 `tool.submit_advanced(...)`
|
||
|
||
**测试(先写)**
|
||
- `src/mvp/py/tests/test_builders.py`
|
||
- `test_build_advanced_argv_uses_bash_lc()`
|
||
- `src/mvp/py/tests/test_scheduler.py`
|
||
- 新增一个 `kind: advanced` 的任务,断言 scheduler 调用了 `submit_advanced`
|
||
- 断言 job_dir/submission_id 规则不变(仍按 `/private/users/<user>/jobs/<sid>`)
|
||
- `src/mvp/py/tests/test_ray_job_tool.py`
|
||
- 断言 advanced 提交时 entrypoint 是 driver_entrypoint + `bash -lc ...`
|
||
|
||
**验收**
|
||
- 单测跑通;Scheduler tick 能完成 Advanced 任务从 QUEUED → SUBMITTED(mock Ray)。
|
||
|
||
---
|
||
|
||
### M4 — API & WebUI(最小功能闭环)
|
||
**目标**
|
||
- WebUI/HTTP API 能提交 Advanced Task,并在详情页看到 resolved spec(含完整 command)。
|
||
|
||
**API 改动点**
|
||
- `src/mvp/py/argus/service/app.py`
|
||
- `POST /api/v2/tasks`:支持 `kind: advanced`
|
||
- 保存 raw YAML(保持与 Basic 一致)
|
||
- 对 Advanced:展开 command + 校验(失败返回 400)
|
||
- `GET /api/v2/tasks/{task_id}/spec`:
|
||
- 返回 resolved spec(建议同时返回 raw + expanded,或 YAML 中直接给 expanded)
|
||
|
||
**WebUI 改动点**
|
||
- `src/mvp/py/argus/service/ui.py`
|
||
- New Task 页面新增 Advanced 模板(含中文注释)
|
||
- 文案强调共享目录:`$HOME/common/datasets`、`$HOME/common/hf`
|
||
|
||
**测试(先写)**
|
||
- `src/mvp/py/tests/test_app.py`
|
||
- `test_create_task_advanced_ok()`(最小 valid command)
|
||
- `test_create_task_advanced_rejects_invalid_command()`
|
||
- `test_task_spec_endpoint_includes_expanded_command()`
|
||
- `src/mvp/py/tests/test_ui.py`
|
||
- 断言页面包含 Advanced 示例块
|
||
|
||
**验收**
|
||
- `pytest` 通过;浏览器可提交 Advanced YAML 并看到 expanded command。
|
||
|
||
---
|
||
|
||
### M5 — 端到端验证(远端 argus@h1)
|
||
**目标**
|
||
- 在真实 Ray cluster + VERL 环境下验证 Advanced 与 Custom Reward(方式 A)。
|
||
|
||
**步骤(手工验收脚本化可选)**
|
||
1) 启动 v3.0/v3.5 统一的 compose + API(沿用现有 `run_all` 脚本体系)
|
||
2) 用户(如 `alice`)通过 SFTP 上传 reward 代码到:
|
||
- `$HOME/code/reward.py`(真实路径 `/private/users/alice/code/reward.py`)
|
||
3) 通过 WebUI 或 curl 提交 Advanced task:
|
||
- `command` 中包含:
|
||
- `custom_reward_function.path=$HOME/code/reward.py`
|
||
- `custom_reward_function.name=compute_score`
|
||
- `data.train_files=$HOME/common/datasets/gsm8k/train.parquet`
|
||
- `data.val_files=$HOME/common/datasets/gsm8k/test.parquet`
|
||
4) 检查:
|
||
- 任务状态从 QUEUED → RUNNING → SUCCEEDED/FAILED(有日志)
|
||
- driver 不在 head 上跑(dashboard 验证)
|
||
- 日志出现 “custom reward” 生效的提示(按 VERL 实际日志关键字确认)
|
||
5) 回归:提交 Basic ppo/grpo/sft 任务仍可运行(确保兼容性)
|
||
|
||
**验收**
|
||
- Advanced task 能跑至少若干 step,且 reward 注入生效。
|
||
- Basic 任务兼容不回退。
|
||
|
||
---
|
||
|
||
## 3. 风险点与边界(明确写进 PR/变更说明)
|
||
- Advanced command 只做 best-effort 校验,不做完整 shell AST 解析;复杂命令可能存在漏检/误判(后续可扩展)。
|
||
- `$HOME/common/*` 是“用户侧语义”,服务层必须映射到真实路径,否则训练必然 FileNotFound。
|
||
- 校验策略(强约束)如果后续要允许非 VERL 命令,需要调整规则并补测试(本轮默认拒绝)。
|
||
|