环境:控制面 + agent 部署于 WSL2(RTX 5090 32G);MacBook 经 SSH tunnel(127.0.0.1:18601)驱动验收。
日期:2026-08-18。验收标准(roadmap §2):训练结束 → 自动对 N 个 checkpoint 排队评估 → 按 primary metric 给出推荐 checkpoint → 事件推送到手机。
| 步骤 |
结果 |
证据 |
| Project / Experiment 创建 |
☑ |
prj_01M0AJWX… / exp_01M0AJWX…(POST /projects, /experiments) |
| primary metric 定义 |
☑ |
PUT /metric-definitions/val_loss {direction: minimize, is_primary: true} |
| 训练 job(cpu_only,watch ./checkpoints) |
☑ |
job_01M0AJWX… QUEUED→SUCCEEDED exit 0;SDK 上报 train_loss/val_loss |
| run 自动归组 |
☑ |
run 的 project_id/experiment_id 与训练 job 一致;GET /experiments/{id}/runs 含该 run |
| watcher 发现 3 个 checkpoint |
☑ |
steps [100, 200, 300] 全部注册 |
| 逐 checkpoint 排队 EVALUATE job |
☑ |
POST /checkpoints/{id}/evaluations ×3,各自生成 EVALUATE job,env 带 GPUCTL_EVAL_CHECKPOINT/EVAL_ID |
| 评估结果回挂 |
☑ |
eval.py 写 GPUCTL_EVAL_OUTPUT JSON → runner 读取 → server 回挂:step100→0.58, step200→0.31, step300→0.47 |
| 按 primary metric 推荐 |
☑ |
GET /checkpoints:recommend?run_id=… → step-200 checkpoint, value 0.31, direction minimize(不是最新 step-300) |
| 事件 webhook 推送 |
☑ |
ntfy 捕获端点收到 38 条推送:EVALUATION_FINISHED ×3、CRITICAL 自定义事件(Priority 5)、CHECKPOINT_CREATED、GPU_UNDERUTILIZED 等;Title/Priority/Tags 头正确 |
gpuctl event-hook 本地钩子 |
☑ |
Mac 侧经 tunnel 订阅 --severity critical,POST 自定义事件后钩子触发:`CUSTOM |
webhook 推送样例(捕获端点原始记录):
5 [CRITICAL] CUSTOM | v0.2 acceptance ping
3 [INFO] EVALUATION_FINISHED | Evaluation finished for checkpoint step 200
4 [WARNING] GPU_UNDERUTILIZED | GPU 0 under 15% util for 60s
| # |
现象 |
根因 |
修复 |
| 1 |
评估 job 启动即 FAILED exit=2 |
EVALUATE job 未继承训练 job 的 working_dir(eval.py 与 train.py 同目录却找不到) |
create_evaluation 默认继承训练 job 的 working_dir/resources,可被显式覆盖 |
| 2 |
评估永远停在 QUEUED |
EVALUATE job 失败/Cancel 时无人更新 evaluations 行 |
_fail_evaluation:终态非成功时标记 FAILED/CANCELLED + 发 EVALUATION_FINISHED(warning);终态评估不被迟到帧重复标记 |
| 3 |
PUT /metric-definitions/train%2Floss 404 |
{name} 路径参数不匹配斜杠(编码与否都一样) |
改 {name:path};回归测试覆盖 |
| 模块 |
状态 |
交付 |
| Project/Experiment 实体 |
☑ |
CRUD + run 归组 + run_count 汇总;非空删除 409 |
| Evaluation Job |
☑ |
evaluations 表 + EVALUATE job 类型 + 结果回挂 checkpoint metadata + :recommend(评估优先、run metrics 兜底) |
| EventDetector 完整规则集 |
☑ |
LOSS_SPIKE(3×median) / OVERFITTING_SUSPECTED(train 降 + val 偏离 best 1.5×) / GPU_UNDERUTILIZED(持续窗口) / DISK_LOW / LOSS_NAN / OOM;进程内滚动窗 + 去抖 |
| Retry 失败分类 |
☑ |
OOM/EXIT_CODE/DISPATCH_FAILED 不自动重试;其余(LOST/SIGNAL…)按 max_attempts 重排 |
| DockerRunner |
☑ |
docker run --rm --name gpuctl-<job>-a<n> --gpus device=N -e/-w/-v;cancel 走 docker kill TERM→KILL;runner=docker 缺 image → 422 |
| Webhook + event-hook |
☑ |
server.yaml webhooks 配置(ntfy/Bark,min_severity/types 过滤,5s 超时 best-effort);gpuctl event-hook 逐事件拉起本地命令(GPUCTL_EVENT_* env + stdin JSON) |
| MetricDefinition 管理 UI |
☑ |
Experiments 页内 direction 下拉 + primary 单选(PUT 实时保存) |
| SDK extras |
☑ |
gpuctl.callbacks: GpuctlLightningCallback / GpuctlHFTrainerCallback;可选导入零依赖,缺框架时实例化报 ImportError |
/experiments:projects / experiments 双栏管理(内联新建、run_count、按 project 过滤)+ metric definitions 编辑器。
/experiments/:id:run 对比视图 —— 多 run 指标叠图(uPlot 多序列按 step 对齐、可勾选)、runs 表按 best 值排序(遵循 direction)、最优 run 标记。
- 验收截图路径:浏览器打开
http://127.0.0.1:18601/#/experiments 可见上述 project/experiment/run 数据。
uv run pytest:83 passed(v0.1 的 52 → 83;新增 detector 规则、评估 e2e、docker argv、webhook 过滤/线形/容错、event-hook、callbacks、层级指标名等)
uv run ruff check .:All checks passed
uv run mypy:49 source files 零错误
cd web && pnpm build:tsc + vite 构建通过
- webhook 为 best-effort:推送失败只记 server 日志,不重试(事件已落库,可轮询补齐)。
- detector 状态规则的历史窗口是进程内的:server 重启后需重新积累窗口(设计取舍,见 detector.py 头注)。
- DockerRunner 的容器内 GPU 分配依赖 nvidia-container-toolkit(宿主机责任)。