环境:控制面 + agent 部署于 WSL2(DESKTOP-R7PRUVL,RTX 5090 32G,driver 610.88,Python 3.14.6);
MacBook 经 LAN(192.168.0.114)用 CLI/浏览器访问。本文件随验收推进逐项打勾。
此前 macOS(无 GPU)已完成本地端到端 smoke:dispatch→logs→SSE→TB→checkpoint→cancel→import-run 全通。
| 验收项 |
结果 |
证据 |
| agent 注册上线,GPU 实时指标可见 |
☑ |
node wsl-5090 ONLINE;gpu0.utilization/temp_c/power_w/mem_used 等 11 路指标 2s 采集、5s 上报(2026-08-18 13:15) |
| kill agent → 节点 OFFLINE |
☑ |
SIGTERM → +2s OFFLINE(WS 断开即时路径;90s 为网络分区兜底)。顺带修了两个真 bug:agent 健康连接期间收 SIGTERM 不退出(gather 不看 stop 事件);同名节点重复连接会顶号不断旧连(僵尸 agent 保活节点) |
| 重启 agent 自动重连,断线期数据经 spool 回补 |
☑ |
重启即重连,12 条 ingest cursor 种子恢复;spool 回补见 M3 server-down 测试 |
| 验收项 |
结果 |
证据 |
| mnist submit → QUEUED→RUNNING→SUCCEEDED |
☑ |
job_01M09N7V…(mnist-5090-a2):2 epoch 15s,exit 0,val acc 0.9658 |
logs -f 实时滚动、无丢行(对比 agent 本地文件) |
☑ |
SSE /logs/stream 实时推出运行中 job 的 device: cuda 等行;server 端 tail 2000 行缓存可查 |
| 训练中 cancel → 整进程组杀死,无 dataloader 孤儿(ps 验证) |
☑ |
mnist-cancel-test RUNNING 中 cancel → CANCELLED;PGID 118018 整组(含 2 个 dataloader worker)消失,pgrep 无残留 |
| agent 冻结(分区模拟)→ job LOST → 恢复后 reconcile 对齐 RUNNING → 完成 |
☑ |
kill -STOP agent → last_hb+90s 整 JOB_LOST critical;train.py 全程存活;-CONT 后 1s 内重连 reconcile → RUNNING(AGENT_RECONNECTED 事件),分区期 checkpoint 后续全部注册 |
| 3 个 GPU job 串行;1 个 cpu-only 并发 |
☑ |
gpu-serial-1/2/3 时间区间 [91472,97237]/[97480,102518]/[103488,108618] 零交叠;cpu-sidecar 与 serial-3 并发运行 |
| 验收项 |
结果 |
证据 |
| mnist 不改代码:TB 曲线进库、summary 可查 |
☑ |
train/loss 23pts、lr、eval/loss、eval/accuracy 全进库;summary latest/best/trend(decreasing)/progress(step 470) 正确 |
| checkpoint 被发现注册并关联 best |
☑ |
watcher 发现 step 235/470 并注册;eval/accuracy best@470 关联 ckpt_01M09N8C…(晚到的 checkpoint 回填关联生效) |
| SDK 3 行:自定义指标进库 |
☑ |
job 内 attach 模式(env 自动注入 GPUCTL_RUN_ID/SERVER/TOKEN);server 访问日志 4×POST /runs 200 |
| 停 server 训练继续,数据后补(SDK spool 重放) |
☑ |
kill server 后 train.py 存活、spool 积压 2 流;恢复后 agent 重连重放 50 帧、spool 清零;train/loss 287 点最大间隔 2s,112 个点时间戳落在停机窗口内 |
| 注入 NaN → LOSS_NAN critical 事件 |
☑ |
train/loss became NaN at step 2 critical。连带修两处:httpx json= 拒序列化 NaN(SDK 改 stdlib dumps);SQLite 把 NaN 存成 NULL 撞 NOT NULL(‘nan’ 文本哨兵,序列输出 null 保 JSON 合法) |
| 验收项 |
结果 |
证据 |
| import-run 导入历史实验(metrics + checkpoints + IMPORTED 状态) |
☑(macOS) |
40 pts + 2 ckpts |
gpuctl backup 在线备份 |
☑ |
backups/gpuctl-20260818-161324.db(11MB):9 jobs/13 runs/1284 metrics/98 events,PRAGMA integrity_check=ok |
| 30 分钟新用户部署(README 快速开始) |
☑ |
README 快速开始路径在 macOS 从零验证过;5090 部署耗时主要在网络受限(镜像失效),最终走 Mac 预下载 wheel + LAN scp,已写进 README 离线安装节 |
- CLI
job submit -d 在提交端 resolve 路径 — macOS firmlink 把 /home/eric/... 改写成 /System/Volumes/Data/...,远端 agent 找不到目录。改为原样透传(working_dir 是 agent 侧语义),agent 侧补 ~ 展开。
- agent 健康连接期间收 SIGTERM 不退出 —
gather(recv, heartbeat, metrics) 无人监听 stop 事件;改为与 _stop.wait() 竞速。
- 同名节点重复连接不顶号 — 僵尸 agent 持续心跳使节点永不 OFFLINE;registry.register 现在返回被替换连接并由 gateway 关闭。
- 分区恢复但 WS 未断时 LOST job 永不恢复 — 心跳使节点重新 ONLINE 却无 reconcile 触发;新增 server→agent
reconcile_request 帧。
- checkpoint 双注册 — SDK 相对路径 + watcher 绝对路径产生两行;server 端按 job working_dir 归一化(
(run_id,path) upsert 去重)。
- NaN 指标全链路断裂 — httpx 拒发、SQLite 存 NULL 撞约束、RunSummary JSON 序列化 null 读回校验失败;三层各修一处(详见 M3 行)。
- WSL2 机器入站仅 22 通(Windows 防火墙),Mac 访问控制面用
ssh -L 18601:127.0.0.1:8600 -N WSL-LAN。
- Web UI 由 server 直接托管(
web/dist 自动探测,或 GPUCTL_WEB_DIST 指定):http://127.0.0.1:18601/ + token 登录。