argus-cluster/specs/mvp/v3.5/v3.5_design.md

16 KiB
Raw Blame History

MVP v3.5 详细设计方案(进一步精简版,基于 v3.0

背景v3.0 已具备 WebUI + API server + 用户/任务隔离 + SFTPGo 数据管理 + Stateless Ray clusterhead + worker node pool

v3.5 本轮 只做 2 件事

  1. Advanced Task支持用户提交自定义训练命令command
  2. Custom Reward支持用户通过 VERL 原生 custom_reward_function.* 方式注入 reward仅方式 A用户自己写命令

明确不做(从上一版设计中移除):(3) 自定义 verl 版本/代码路径、(4) 断点续训、(5) IB/RoCEv2 网络支持、(6) Model Serving。


0. 继承 v3.0 的不变点(重要约束)

  1. Node management 不变
  • v3.5 不新增/不修改 node management 机制;仍按 v3.0 现状运行head 写 discovery、worker watchdog 自动 join、自愈
  1. Head 不跑训练
  • 所有训练/Serving driver 通过 Ray entrypoint placement 强制落在 worker例如 entrypoint_resources={"worker_node": 1})。
  1. SFTPGo 的 “common” 目录约定变更
  • 不再使用 $COMMON 宏。
  • 在 SFTPGo 中,把共享只读资源映射到用户 home 下的固定目录(用户在 SFTP/WebClient 看到的是 $HOME/common/...
    • $HOME/common/datasets → 容器内真实路径 /private/datasets(只读)
    • $HOME/common/hf → 容器内真实路径 /private/hf(只读)

这里的 $HOME 指:/private/users/<user_id>(容器内路径)。


1. v3.5 需求范围(精简后)

1.1 In scope

A. Advanced TaskSpec自定义命令

  • 用户提交 command(多行 shell 或单行)
  • 平台做 $HOME 宏替换
  • 平台做 best-effort 安全检查(路径/关键参数),然后提交为 Ray job

B. Custom Reward仅方式 A

  • 用户在 command 里显式写 hydra overrides
    • custom_reward_function.path=...
    • custom_reward_function.name=...
    • custom_reward_function.reward_kwargs.*=...(可选)
  • 平台不提供结构化 reward 字段(不做方式 B只做检查校验 path 合法)

1.2 Out of scope本轮不做

  • 自定义 verl 版本/代码路径(仍使用平台内置/公共 verl 代码快照)
  • 断点续训resume from checkpoint
  • IB/RoCEv2 网络专门支持NCCL/RDMA env 先不引入平台)
  • Model Serving暂缓后续单独设计迭代

2. Advanced TaskSpec 设计

2.1 为什么需要 Advanced Task

v3.0 的 Basic TaskSpecppo/grpo/sft通过平台模板生成固定 overrides适合“快速跑通”。 但科研/调参场景需要更高自由度:用户希望直接写 python3 -m verl.trainer.main_ppo ... 并自行控制每个 override。

2.2 Advanced TaskSpec建议 schema

建议新增一种 TaskSpec 类型,通过 kind: advanced 区分:

kind: advanced

# 资源(平台调度与预检查用;仍需要)
nnodes: 2
n_gpus_per_node: 4

# 自定义命令(用户负责写对 VERL 的参数/路径)
# 平台会对 $HOME 做宏替换;其余保持原样
command: |
  PYTHONUNBUFFERED=1 python3 -m verl.trainer.main_ppo \
    data.train_files=$HOME/datasets/gsm8k/train.parquet \
    data.val_files=$HOME/datasets/gsm8k/test.parquet \
    actor_rollout_ref.model.path=Qwen/Qwen2.5-0.5B-Instruct \
    trainer.nnodes=2 \
    trainer.n_gpus_per_node=4 \
    trainer.total_epochs=1 \
    trainer.save_freq=10 \
    +ray_kwargs.ray_init.address=auto

2.3 $HOME 宏替换规则

仅支持 $HOMEv3.5 移除 $COMMON

  • $HOME/private/users/<user_id>

用户如果要用共享数据/缓存:

  • 共享数据:$HOME/common/datasets/...
  • 共享 HF 缓存:$HOME/common/hf/...(通常不需要写进 command但可用于 debug

2.3.1 重要说明SFTPGo “virtual folder” 与训练进程看到的“真实路径”

在 SFTPGo 中,$HOME/common/datasets / $HOME/common/hfSFTP 虚拟目录映射virtual folder它们映射到容器内真实路径

  • $HOME/common/datasets/private/datasets
  • $HOME/common/hf/private/hf

训练进程Ray worker 上的 python 进程)看到的是 容器内真实文件系统,它并不会理解 SFTPGo 的 virtual folder。

因此,为了让用户能沿用 WebClient 里看到的路径语义(写 $HOME/common/...),服务层在提交 Advanced command 前需要做 路径宏映射

  • "$HOME/common/datasets""/private/datasets"
  • "$HOME/common/hf""/private/hf"
  • 其余 "$HOME""/private/users/<user_id>"

这样用户写的 command 能在训练进程里正确读到文件。

2.4 服务层检查best-effort强约束 + 弱约束)

目标:在不“解析完整 shell”的前提下尽可能避免跨用户读文件与明显错误的任务。

强约束(必须通过,否则 400

  1. nnodesn_gpus_per_node 必须存在(用于队列/资源预检查/placement
  2. command 必须包含一个明确的 python entry
    • 建议最低要求:包含 python3 且包含 -m verl.trainer.(防止随意执行系统命令)
  3. 路径隔离校验(字符串/正则级别):
    • 展开 $HOME(含 $HOME/common/* 映射到 /private/*)后:
      • 禁止出现 /private/users/ 下 “非当前用户”的路径(例如 /private/users/bob/...
      • data.train_files=...data.val_files=...(若出现)做 allowlist
        • 允许(用户目录):/private/users/<me>/datasets/...
        • 允许(共享目录):/private/datasets/...
    • custom_reward_function.path=...(若出现)做 allowlist
      • 允许:/private/users/<me>/code/...(用户自行上传)

弱约束warning不阻塞

  • 未检测到 data.train_files=/data.val_files=(可能是用户写成了别的 key 或使用了 config file
  • 未检测到 +ray_kwargs.ray_init.address=autov3.0/v3.5 推荐加,但用户可自行负责)

说明Advanced command 本质上属于“内部可信用户”能力v3.5 不做强沙箱;安全检查以 best-effort 为主。


3. Custom Reward仅方式 A用户自己写

3.1 VERL 原生机制(本仓库 verl/ 已调研)

VERL PPO trainer 配置里支持:

  • custom_reward_function.path
  • custom_reward_function.name
  • custom_reward_function.reward_kwargs

对应实现位置:

  • 配置模板:verl/verl/trainer/config/ppo_trainer.yaml
  • 加载逻辑:verl/verl/trainer/ppo/reward.py:get_custom_reward_fn
  • 典型 reward managerverl/verl/workers/reward_manager/naive.py 会调用 compute_score(...)

3.2 用户写法(示例)

用户上传 $HOME/code/reward.py,在 command 里加:

custom_reward_function.path=$HOME/code/reward.py \
custom_reward_function.name=compute_score

函数签名建议(与 naive reward manager 参数对齐):

def compute_score(*, data_source: str, solution_str: str, ground_truth: str, extra_info=None, **kwargs):
    ...

3.3 平台侧只做检查(不做字段扩展)

v3.5 限定 reward 注入方式为 “用户写 command”平台只做

  • 展开 $HOME
  • 若检测到 custom_reward_function.path=,校验 path 在 $HOME/code/
  • 不尝试解析/合并 reward_kwargs用户自己写

4. 服务层与 SFTPGo 的映射修改(你提出的关键点)

v3.0 时代平台允许用户引用:

  • /private/common/datasets/...
  • /private/common/hf/...

但现在 common 以 SFTPGo virtual folder 的形式呈现给用户(用户看到 $HOME/common/...,真实路径是 /private/...),因此 v3.5 的服务层需要做两件事:

  1. 用户侧语义(写 TaskSpec/command
  • 共享 datasets只读$HOME/common/datasets/...
  • 共享 hf cache只读$HOME/common/hf/...
  1. 运行时真实路径(提交到 Ray 前展开)
  • $HOME/common/datasets/.../private/datasets/...
  • $HOME/common/hf/.../private/hf/...

同时保留用户自有目录:

  • 用户 datasets$HOME/datasets/...
  • 用户 models$HOME/models/...
  • 用户 codereward$HOME/code/...

这部分主要影响:

  • Advanced command 检查allowlist
  • WebUI/Data 页面文案(告诉用户共享数据在哪里)

兼容性建议:为了不影响 v3.0 期间已经习惯使用 /private/common/datasets/... 的用户/历史任务, v3.5 实现阶段建议 同时接受

  • /private/common/datasets/...(旧路径语义,仍可读)
  • /private/datasets/...(真实路径语义,推荐)
  • Advanced command 里写的 $HOME/common/datasets/... 会先映射到 /private/datasets/...

5. 验收标准(精简版)

5.1 Advanced command

  • 提交一个 Advanced PPO commandtrain/val 使用 $HOME/common/datasets/...$HOME/datasets/...
  • 确认:
    • 任务从 QUEUED → SUBMITTED/RUNNING
    • driver 在 worker 上head 不跑训练)
    • 训练能正常跑至少若干 step

5.2 Custom reward方式 A

  • 用户上传 $HOME/code/reward.py
  • 在 command 中设置 custom_reward_function.path=$HOME/code/reward.py
  • 确认训练日志出现 using customized reward function ...

6. 待确认问题(需要你拍板/补充)

  1. Advanced command 的“强约束”是否需要更严格?

    • 目前建议要求包含 python3 -m verl.trainer.,否则拒绝。
    • 你是否允许用户跑非 verl 的命令(例如自定义评估脚本)?
  2. $HOME/common/datasets$HOME/common/hf 两个映射目录在平台侧是否需要“强制只读”语义?

    • 例如TaskSpec 校验允许读取但禁止写入(目前设计是 best-effort 字符串级校验)。

7. 基于现有源码的改动点分析(实现清单)

本节按当前 v3.0 已上线的源码结构(src/mvp/py/argus/...)逐文件列出 v3.5 需要的具体改动点,并评估对现有能力的影响面。

7.1 TaskSpec/模型层(解析与兼容)

现状

  • Basic TaskSpec 由 argus.ray.models.JobSpec.from_dict() 解析:src/mvp/py/argus/ray/models.py
  • API /api/v2/tasks 直接 JobSpec.from_dict(obj),并基于字段做路径校验:src/mvp/py/argus/service/app.py
  • Scheduler 同样假定 jobspec_yaml 能解析为 JobSpecsrc/mvp/py/argus/service/scheduler.py

v3.5 需要新增

  1. 新增 AdvancedTaskSpec 数据结构(建议放在 src/mvp/py/argus/ray/models.py
    • 必填:kind: advancedworkload(建议仍要求 ppo/grpo/sft用于 task_id 命名与 UI 分类)、nnodesn_gpus_per_nodecommand
    • 可选:submission_id(由服务层 override
  2. 新增 “union 解析”:
    • 新增 parse_taskspec(obj: dict) -> Basic(JobSpec) | Advanced(AdvancedTaskSpec)
    • 兼容策略:如果没有 kind 字段,则 默认按 v3.0 Basic JobSpec 解析(保证老客户端无感)。

7.2 Builder 层(把 TaskSpec 转为可执行 argv

现状

  • src/mvp/py/argus/ray/builders.py:build_training_argv(spec: JobSpec, ...) 只支持模板化 PPO/GRPO/SFT。

v3.5 需要新增

  1. 新增 build_advanced_argv(command: str) -> list[str]
    • 推荐实现:返回 ["bash", "-lc", "<expanded_command>"]
    • 原因:用户 command 允许 ENV=... python3 ... \ 多行以及 shell 语法,bash -lc 兼容性最好。
  2. Driver entrypoint 复用:
    • 仍通过 argus.ray.driver_entrypoint 执行(统一 job_dir、日志与退出码

7.3 RayJobTool 层runtime_env 与提交)

现状

  • src/mvp/py/argus/ray/ray_job_tool.py:RayJobTool.submit(spec: JobSpec, ...)
    • runtime_env 的 PYTHONPATHspec.code_path 决定
    • entrypoint 固定为 driver_entrypoint + builder 生成 argv

v3.5 需要新增

  1. 扩展 submit 支持 AdvancedTaskSpec
    • 方案 A最小侵入新增 submit_advanced(...) 方法,参数为 command + job_dir + submission_id + nnodes/n_gpus...
    • 方案 B统一接口新增内部抽象 SubmitPlan(包含 runtime_env + entrypoint + artifactsBasic/Advanced 都生成 plan再走同一 submit 逻辑。
  2. runtime_env 的 code path
    • 因 v3.5 本轮不做“自定义 verl code_path”建议仍固定使用公共快照例如 /private/common/code/verl/verl_repo)。
    • 为减少散落常量,建议在 config 增加 ray.verl_code_path(或 service.verl_code_pathRayJobTool 统一读取。
  3. runtime_env 的用户代码目录(可选增强):
    • VERL 的自定义 reward 函数是通过 custom_reward_function.path 以“文件路径”动态 import 的,理论上不依赖 PYTHONPATH
    • 但用户的 reward.py 可能会 import 自己目录下的其他模块;为了提升易用性,可将 /private/users/<user>/code 追加到 job 的 PYTHONPATH
    • 这需要 RayJobTool.submit/submit_advanced 能感知 user_id(由 Scheduler 传入),属于小改动但要注意兼容性。

7.4 API Server提交校验、宏替换、spec 展示)

现状

  • POST /api/v2/tasks:只支持 Basic JobSpec 且强校验 code_path/train_file/val_file/model_id 前缀:src/mvp/py/argus/service/app.py
  • /api/v2/tasks/{task_id}/spec:返回 resolved 的 Basic JobSpec补默认值/补 submission_idsrc/mvp/py/argus/service/app.py

v3.5 需要新增/修改

  1. POST /api/v2/tasks 分流:
    • kind != advanced:走原 Basic 流程(兼容 v3.0
    • kind == advanced:走 Advanced 解析 + 校验
  2. Advanced command 宏替换与映射(核心):
    • 实现 expand_command(user_id, command)
      • 先把 $HOME/common/datasets/private/datasets
      • 再把 $HOME/common/hf/private/hf
      • 再把其余 $HOME/private/users/<user>
    • 校验使用 “展开后的 command”
  3. reward 注入检查(仅方式 A
    • 若发现 custom_reward_function.path=...
      • 校验展开后的 path 前缀必须是 /private/users/<me>/code/
  4. /api/v2/tasks/{task_id}/spec
    • 需要支持返回 AdvancedTaskSpec 的 resolved 版本:
      • 展示时可选择“原始 command”$HOME)或“展开后的 command”建议都展示raw + expanded

7.5 Scheduler队列与提交

现状

  • src/mvp/py/argus/service/scheduler.py 假定 jobspec_yaml 一定是 Basic JobSpec并调用 tool.submit(spec2, ...)

v3.5 需要新增

  1. Scheduler 的 _parse_jobspec 替换为 parse_taskspec(支持 Basic/Advanced
  2. _submit_one 根据 spec 类型调用:
    • Basic保持现状 tool.submit(JobSpec, ...)
    • Advanced调用 tool.submit_advanced(...)(或统一 SubmitPlan

7.6 WebUI最小改动

现状

  • src/mvp/py/argus/service/ui.py 的 New Task 页面只提供 Basic YAML 模板。

v3.5 需要新增

  • 增加 “Advanced Task” 模板按钮:
    • kind: advanced
    • workload: ppo|grpo|sft(用于 UI 分类与 task_id
    • nnodes/n_gpus_per_node
    • command: | ...(带中文注释)
  • Data 页面文案更新:
    • 明确共享目录在 $HOME/common/datasets$HOME/common/hf(并解释会映射到 /private/datasets/private/hf

8. 对现有功能的兼容性影响评估

8.1 API/TaskSpec 兼容

  • 兼容策略:没有 kind 字段的 YAML 一律按 v3.0 Basic JobSpec 解析
    • 现有脚本/客户端(提交 ppo/grpo/sft 的 YAML无需修改。
  • AdvancedTaskSpec 是新增能力,不影响既有任务状态机/DB。

8.2 路径策略变更的影响

风险点v3.0 的 Basic 任务/模板大量使用 /private/common/datasets/...

建议:

  • v3.5 实现阶段先保持 “双栈兼容”:
    • Basic 继续接受 /private/common/datasets/...(旧)
    • 同时接受 /private/datasets/...(新/真实路径)
  • Advanced command 允许用户写 $HOME/common/datasets/...,服务层展开为 /private/datasets/...(避免虚拟目录不可见问题)。

8.3 任务执行/调度兼容

  • Scheduler 队列/并发控制(max_running_tasks)保持不变。
  • 资源预检查仍只依赖 nnodes/n_gpus_per_nodeAdvancedTaskSpec 不改变资源模型。

8.4 安全边界变化

  • Advanced command 引入后,平台从“结构化参数”变成“执行用户命令”,安全边界变宽。
  • 缓解措施best-effort
    • 强约束要求命令包含 python3 -m verl.trainer.
    • 基础路径隔离校验(禁止跨用户路径)
    • reward 文件路径限制在 $HOME/code

8.5 数据库兼容

  • DB schema 不强制变更:仍复用 tasks.jobspec_yaml 存储原始 YAML。
  • 若后续需要更强查询/过滤,再考虑增加 tasks.kind 字段(可选增量迁移)。