关于 run-experiment
面向机器学习研究者的实验提交技能,可为本地、SLURM 或 RunAI/Kubernetes 环境生成可复现作业脚本,并提供提交命令。
主要功能
- 01支持本地、SLURM 与 RunAI 环境
- 02按环境配置生成作业脚本
- 03记录提交命令与 Git 提交哈希
- 04提供脚本语法和格式检查
- 05支持 SLURM 作业数组
适用场景
- 01向 HPC 集群提交训练任务
- 02在本地生成实验运行脚本
- 03为超参数扫描配置 SLURM 作业数组
- 04记录可复现的实验运行配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ a-green-hand-jack/ ml-research-skills --skill run-experiment安装后在 Claude Code 中输入/run-experiment确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
environments.yamltemplates/local_run.shrunai_job.shslurm_job.sh详细介绍
运行实验
将机器学习实验提交到计算环境——本地机器、SLURM HPC(Ibex、UW 等)或 RunAI/Kubernetes(EPFL)。
在 jobs/ 中生成一份可复现的作业脚本,并将其与代码一起提交,然后提供准确的提交命令供你运行。
如果启动的作业需要关联到计划中的实验、证据条目、工作树或项目操作,请将此技能与 research-project-memory 配合使用。
如果生成的作业脚本需要在启动前进行检查,请将此技能与项目工具链门禁配合使用。如果可以使用 shellcheck 和 shfmt,请使用它们,但不要仅为了生成脚本草稿就要求用户安装它们。
术语:
local:Agent 运行所在的机器,通常是用户的 MacGit remote:用于同步代码的 GitHub/GitLab 远程仓库,例如originserver:SSH/HPC/RunAI 执行环境,例如quest、ibex-vscode或epfl-haas
在 SLURM 或 RunAI 上启动作业时,称其为服务器运行,而非远程运行;只有在指 Git 远程仓库时才使用远程运行一词。
技能目录结构
<installed-skill-dir>/
├── SKILL.md
├── environments.yaml # Environment profiles (extend for new clusters)
└── templates/
├── slurm_job.sh # SLURM template (Ibex, UW, any SLURM cluster)
├── runai_job.sh # RunAI/Kubernetes template (EPFL)
└── local_run.sh # Local tmux/nohup template
操作步骤
1. 读取环境注册表
将 <installed-skill-dir> 解析为包含此 SKILL.md 的目录,然后读取 <installed-skill-dir>/environments.yaml。
向用户列出可用环境,并为每个环境提供一行说明。
2. 询问实验详情
在一条消息中询问用户:
- 环境:使用哪个计算环境?(显示
environments.yaml中的可用选项,以及“其他”) - 脚本 / 命令:要运行什么命令?(例如,
uv run python train.py --lr 1e-3) - 作业名称:简短标识符(例如,
baseline-cifar10、ablation-no-attn)。默认值:脚本文件名 + 日期。 - GPU 数量:需要多少个 GPU?(默认值取自环境配置,纯 CPU 任务为 0)
- 运行时间 / 时间限制:(仅限 SLURM)需要多长时间?(默认值取自环境配置)
- Conda 环境或 venv:Conda 环境名称,或
.venv路径(如适用) - 输出目录:检查点 / 结果保存位置?(默认值:
outputs/<job-name>/) - 其他特殊要求?:额外环境变量、数组作业、指定 GPU 类型、PVC 挂载(RunAI)等。
如果传入了 --env、--script、--name 或 --gpus 参数,请预填对应答案。
3. 定位项目根目录
git rev-parse --show-toplevel 2>/dev/null || pwd
同时记录简短的 Git 提交哈希:
git rev-parse --short HEAD 2>/dev/null || echo "no-git"
4. 生成作业脚本
根据环境类型执行:
type: slurm(ibex、uw 或任何 SLURM 集群)
从 <installed-skill-dir>/templates/slurm_job.sh 读取 SLURM 模板。
填写所有 {PLACEHOLDER} 变量:
| 占位符 | 值 |
|---|---|
{PROJECT} | 项目目录名称 |
{ENV_NAME} | 环境键(例如,ibex) |
{ENV_DISPLAY} | 配置文件中的显示名称 |
{DATE} | 今天的日期 YYYY-MM-DD |
{COMMIT} | 简短的 Git SHA |
{JOB_NAME} | 用户提供的作业名称 |
{SCRIPT_NAME} | 生成的脚本的文件名 |
{PARTITION} | 环境配置默认值(或用户指定值) |
{CPUS} | 配置文件中的 cpus_per_task(或用户指定值) |
{GPUS} | 用户提供的 GPU 数量 |
{MEM} | 配置文件中的默认值(或用户指定值) |
{WALLTIME} | 用户指定值或配置文件默认值 |
{LOG_DIR} | outputs/logs/<job-name> |
{OUTPUT_DIR} | outputs/<job-name> |
{PROJECT_ROOT} | 项目根目录的绝对路径 |
{CONDA_ENV} | 用户提供的环境名称 |
{RUN_COMMAND} | 用户提供的命令 |
{SCRATCH} | 环境配置文件中的暂存路径 |
根据环境配置文件中的 common_modules,取消相关 module load 行的注释。
根据用户的回答,取消 conda activate 或 source .venv/activate 行的注释。
如果环境配置文件中包含暂存路径,请取消 TMPDIR 代码块的注释。
输出路径:jobs/<job-name>.sh
类型:runai(runai 配置)
读取 RunAI 模板:<installed-skill-dir>/templates/runai_job.sh。
填写占位符:
| 占位符 | 值 |
|---|---|
{PROJECT} | 项目目录名称 |
{DATE} | 今天的日期 |
{COMMIT} | Git 短 SHA |
{JOB_NAME} | 用户提供的作业名称 |
{SCRIPT_NAME} | 生成脚本的文件名 |
{IMAGE} | 来自环境配置文件中的 default_image(请用户确认) |
{RUNAI_PROJECT} | 来自环境配置文件中的 project |
{GPUS} | GPU 数量 |
{CPUS} | 配置文件默认值中的 CPU 数量 |
{MEM} | 配置文件默认值中的内存 |
{PVC_FLAGS} | 根据配置文件中的 pvc_mounts 生成:每个挂载点对应 --pvc claim:path \ |
{RUN_COMMAND} | 用户提供的命令 |
输出路径:jobs/<job-name>-runai.sh
类型:local
读取本地模板:<installed-skill-dir>/templates/local_run.sh。
以类似方式填写占位符。根据需要取消 conda/venv 激活行的注释。
输出路径:jobs/<job-name>-local.sh
类型:其他 / 未知
如果用户指定的环境不在 environments.yaml 中:
- 询问:“它使用什么调度器?(slurm / runai / other)”
- 如果兼容 SLURM:使用 SLURM 模板,并填入用户提供的信息。
- 如果确实是全新环境:生成一个最简通用包装脚本,并说明需要填写哪些内容。
- 建议:“要我把这个环境添加到
environments.yaml,以便今后使用吗?”
5. 编写作业脚本并预览
在预览或提交之前,创建作业脚本目录、日志目录和输出目录:
mkdir -p <project-root>/jobs
mkdir -p <project-root>/outputs/logs/<job-name>
mkdir -p <output-dir>
将填好的脚本写入 jobs/<job-name>.sh(或 -runai.sh / -local.sh)。
向用户展示生成脚本的完整内容以供审核。
5.1 检查生成的 Shell 脚本门禁
写入作业脚本后、提供启动选项前,若工具可用,则运行不会修改文件的 Shell 检查:
command -v shellcheck >/dev/null && shellcheck jobs/<job-name>.sh || true
command -v shfmt >/dev/null && shfmt -d jobs/<job-name>.sh || true
bash -n jobs/<job-name>.sh
报告每项检查是通过、失败,还是因未安装相应工具而跳过。将 bash -n 检查失败视为启动阻断项。将 shellcheck 和 shfmt -d 检查失败视为警告,除非项目策略将其标记为必需项。
不要静默运行 shfmt -w。如果用户要求格式化,或项目策略要求格式化,则运行 shfmt -w jobs/<job-name>.sh,并在提交前检查差异。
6. 显示提交命令并询问是否启动
打印适用于相应环境的准确提交命令:
SLURM(Ibex / UW / 等)
# If you're already on the login node:
sbatch jobs/<job-name>.sh
# If submitting from your local machine to a server (requires ssh access):
scp jobs/<job-name>.sh <ssh-alias>:<project-root>/jobs/
ssh <ssh-alias> "cd <project-root> && mkdir -p outputs/logs/<job-name> <output-dir> jobs && sbatch jobs/<job-name>.sh"
# Monitor:
squeue -u $USER
sacct -j <jobid> --format=JobID,State,Elapsed,AllocGRES
tail -f outputs/logs/<job-name>/slurm-<jobid>.out
RunAI
bash jobs/<job-name>-runai.sh
# Monitor:
runai list
runai logs <job-name> -f
本地
# Attached (output in terminal):
bash jobs/<job-name>-local.sh
# Detached in tmux:
tmux new-session -d -s <job-name> "bash jobs/<job-name>-local.sh"
tmux attach -t <job-name>
# Background with nohup:
nohup bash jobs/<job-name>-local.sh &
询问:“要我现在运行提交命令吗?”
- 如果是且为本地环境:直接运行。
- 如果是且为服务器环境:运行
scp+ssh sbatch命令(需要预先配置 SSH 密钥认证)。 - 如果否:提醒用户脚本已保存在
jobs/中,可以提交了。
7. 提议将作业添加到索引(可选)
如果存在 jobs/README.md 或 jobs/index.md,提议追加一行条目:
| {DATE} | {JOB_NAME} | {ENV_NAME} | {COMMIT} | {RUN_COMMAND_BRIEF} |
如果仓库采用 init-python-project 中的代码证据布局,也提议在以下位置创建或更新简短的运行指引:
docs/runs/<DATE>-<job-name>.md
此文件应包含命令、配置、提交、输出路径、预期指标和监控命令。不得包含原始日志。
8. 存在项目记忆时更新
如果代码库中有 memory/ 或工作树 .agent/worktree-status.md,则仅更新已核实的运行指针:
memory/evidence-board.md:添加或更新关联的EXP-###,包含作业脚本路径、提交、命令、输出目录,以及仅在已核实时使用的状态planned、submitted或runningmemory/provenance-board.md:仅为运行指针添加计划中或已可用的来源信息;在检查输出前,不要将最终指标标记为已核实docs/runs/:如果代码库采用此惯例,则编写简短的运行指针memory/action-board.md:将启动操作标记为doing,或创建监控操作memory/handoff-board.md:仅当预期其他模块会使用运行输出时,才创建监控/获取/报告交接memory/current-status.md:记录最新已知作业,以及接下来必须检查的内容<worktree>/.agent/worktree-status.md:将运行关联到工作树的用途和退出条件
除非在本次会话中已核实,否则不要将队列状态、作业成功情况或最终指标值保存为持久事实。监控任务请使用 needs-verification。
环境参考
所有环境均在 environments.yaml 中定义。当前已知环境:
| Key | 类型 | 集群 | 备注 |
|---|---|---|---|
local | 本地 | — | 当前机器,tmux/nohup |
ibex | slurm | KAUST Ibex | ilogin.ibex.kaust.edu.sa;gpu/batch/himem 分区 |
uw | slurm | UW HPC | 占位项 — 请在 environments.yaml 中更新实际详情 |
runai | runai | EPFL RunAI | Kubernetes;请在 environments.yaml 中更新项目/镜像 |
添加新环境
编辑 <installed-skill-dir>/environments.yaml 并添加一个区块:
my-cluster:
type: slurm # or runai / local
display_name: "My University HPC"
login_node: "login.cluster.edu"
ssh_alias: mycluster
scheduler: slurm
partitions:
gpu:
name: gpu
flag: "--partition=gpu"
gpu_flag: "--gres=gpu:{count}"
max_gpus_per_job: 4
defaults:
partition: gpu
gpus: 1
cpus_per_task: 4
mem: "32G"
walltime: "12:00:00"
max_walltime: "48:00:00"
storage:
home: "/home/{user}"
scratch: "/scratch/{user}"
module_system: lmod
common_modules:
- "cuda/12.1"
- "python/3.11"
notes: "..."
可复现性约定
每个生成的作业脚本都包含:
- 头部中的 Git 提交哈希,并将其作为环境变量 (
GIT_COMMIT) - 结构化输出目录:检查点位于
outputs/<job-name>/,日志位于outputs/logs/<job-name>/ - 带时间戳的日志文件,避免重新运行时覆盖文件
- 退出代码传递,以便作业数组和下游脚本检测故障
jobs/ 目录应提交到 git(脚本都是小型文本文件)。实际输出放在 outputs/ 中,该目录通常会被 .gitignore 忽略。
调用示例
/run-experiment # interactive wizard
/run-experiment --env ibex --script train.py --gpus 2
/run-experiment --env local --script eval.py --name eval-baseline
/run-experiment --env runai --gpus 4 --name big-run
/run-experiment --env ibex --script sweep.py --name sweep --gpus 1
常见模式
作业数组 (SLURM) — 超参数扫描
当用户说“我想扫描 N 个配置”时:
- 询问扫描配置或配置文件(例如,包含 N 个条目的
configs/sweep.yaml)。 - 在脚本中添加
#SBATCH --array=0-{N-1}%{max_concurrent}。 - 在运行命令中添加:
--config configs/sweep.yaml --config-idx $SLURM_ARRAY_TASK_ID - 输出目录:
outputs/<job-name>/$SLURM_ARRAY_TASK_ID/
多 GPU (DDP)
当 GPU 数量大于 1 且环境为 SLURM 时:
- 添加
--ntasks-per-node={GPUS}指令 - 使用
torchrun --nproc_per_node={GPUS}或srun python -m torch.distributed.launch包装命令 - 询问用户使用哪种分布式启动器
交互式会话(调试)
当用户想要进行交互式调试(而不是提交批处理作业)时:
Ibex:
srun --partition=gpu --gres=gpu:1 --cpus-per-task=4 --mem=32G --time=2:00:00 --pty bash
RunAI:
runai submit <name> --image <image> --gpu 1 --interactive --stdin -- bash
runai bash <name>
直接生成此命令,无需创建脚本文件。