Skip to main content
Glama
README.md
# GPUPlane

[![文档](https://img.shields.io/badge/文档-ericyuan2007.github.io%2FGPUPlane-4cc38a)](https://ericyuan2007.github.io/GPUPlane/)

**Agent-native training control plane for personal & small-scale GPU environments.**
个人 GPU 训练的控制面:任务排队与调度、实时指标/日志、checkpoint 登记、事件诊断 —— 为 Agent(MCP)与浏览器而生,不再依赖 SSH + tmux + 肉眼盯 loss。

> 产品文档站:<https://ericyuan2007.github.io/GPUPlane/>(快速开始 / 指南 / MCP 工具参考);
> 设计文档见 [`docs/`](docs/README.md)(产品调研 / 产品设计 / 系统设计 / MVP 路线图),
> 在文档站的[设计文档归档](https://ericyuan2007.github.io/GPUPlane/archive/)中也可阅读。

## 特性(v0.1)

- **训练代码零侵入**:`python train.py` 永远是独立可跑的;GPUPlane 只做进程托管与观测,SDK 完全可选且永不向训练进程抛异常。
- **GPU 槽位调度**:按卡独占分配(不靠利用率猜空闲),优先级队列、失败自动重试(OOM 除外)、`CUDA_VISIBLE_DEVICES` 自动注入。
- **三级指标接入**:TensorBoard 目录实时 tail(L1,零改动)、SDK 直报(L2,`from gpuctl import run`)、系统指标 NVML(L3)。
- **日志**:agent 全量落盘 + server 尾部 2000 行 + SSE 实时流,`gpuctl logs -f` 直接追。
- **Checkpoint 发现**:watch 目录防抖扫描,按 step 关联 best 指标。
- **事件语义层**:LOSS_NAN / OOM / DISK_LOW / 生命周期事件,规则引擎确定性判定。
- **SQLite 单文件**:无 Kafka/Redis/Postgres,WAL 模式,一条命令在线备份。
- **离线韧性**:agent 本地 jsonl spool,断连重放;server 端游标幂等去重,agent 重启不丢不重。

## 特性(v0.2,已验收)

- **实验管理**:Project/Experiment CRUD 与 run 归组;Web UI Experiments 页 + run 对比视图(多 run 指标叠图、按 best 排序)。
- **评估闭环**:`POST /checkpoints/{id}/evaluations` 排队 EVALUATE job(继承训练 job 的 working_dir/资源)→ 结果回挂 → `GET /checkpoints:recommend` 按 primary metric 推荐 checkpoint。
- **完整事件规则**:LOSS_SPIKE / OVERFITTING_SUSPECTED / GPU_UNDERUTILIZED / DISK_LOW,进程内滚动窗口 + 去抖。
- **失败分类重试**:OOM / EXIT_CODE / DISPATCH_FAILED 不自动重试,其余按 `max_attempts` 重排。
- **DockerRunner**:与 ProcessRunner 同语义(日志/退出码/槽位),`--gpus` 注入、`docker kill` 取消。
- **事件外推**:`server.yaml` 配置 ntfy/Bark webhook(severity/type 过滤,best-effort);`gpuctl event-hook` 用事件流唤醒本地脚本。
- **框架 callbacks**:`gpuctl.callbacks` 提供 Lightning / HF Trainer 薄封装,可选导入零依赖。
- **MetricDefinition UI**:direction 与 global/project/experiment primary 可视化编辑;解析顺序 experiment → project → global,驱动 checkpoint 推荐。

## 特性(v0.3,已验收)

- **Agent-native(MCP)**:`gpuctl-mcp` 独立进程/包(fastmcp 3.4.7 pinned)暴露 **21 个工具**——11 observe + 5 control + 5 semantic;streamable HTTP stateless 核心。坏了不影响 server(design §15)。
- **语义层(无 LLM)**:`diagnose_run` / `compare_runs` / `compare_checkpoints` / `get_best_checkpoint` / `explain_failure` 五个纯 Python 函数(规则 + 统计),REST 与 MCP 都是薄暴露层;每个返回带 `next_actions` 引导 Agent 下一步,无需轮询。
- **3 个 Skill**:`run-experiment` / `monitor-experiment` / `analyze-results`(`.claude/skills/gpu-training/`),6 字段 frontmatter,`allowed-tools` 预授权 MCP 工具,编码"提交→监控→对比→推荐"实验循环。
- **插件打包**:`.claude-plugin/plugin.json`(stdio MCP + skills + hooks 可分发);`.mcp.json`(http,gitignored)。
- **AGENTS.md**:仓库级 agent 规则(canonical,CLAUDE.md 镜像)——裸进程+SQLite 红线、实验循环、metric/job/ckpt 约定、OOM runbook。
- **读写域分级**:`GET /auth/whoami` 暴露 `{name, scope, can_write}`;写操作(submit/cancel/retry/evaluate)需 write-scope token,只读 token 返回 `WriteScopeError`。

> 验收:Agent 仅凭自然语言 + MCP,无任何人工平台交互,完成"提交训练 → 监控 → 对比 → 推荐"全循环。详见 [`docs/08-v0.3-acceptance.md`](docs/08-v0.3-acceptance.md)。

## 特性(v0.4)

- **Experiments 工作区重设计**(W&B 式):实验列表带 run 状态计数 / best primary / 最近活跃;详情页是多指标面板工作区——★ primary 置顶、面板增删、runs 勾选入图、step/relative/wall 三种 x 轴,视图状态全部 URL 深链(`?metrics=&runs=&smooth=&x=`)可直接分享。
- **专业图表工具链**:TensorBoard 风格 EMA 平滑(原始线淡化垫底)、LTTB 降采样、crosshair 精确读数 tooltip(step + 相对时间 + 墙钟时间、平滑值附原始值)、拖拽缩放 / 双击重置、legend 点击显隐 / ctrl-点击隔离,run 配色跨 Dashboard/Experiments/Compare/RunDetail 一致。
- **指标实时推送**:`POST /runs/metrics:query` 批量取数 + `GET /runs/{id}/metrics/stream` SSE 逐点推流 + 5s 水位轮询兜底修丢点;未缩放时曲线自动跟随新数据生长,用户缩放后保持视野不动。
- **工作流导向控制台**:Dashboard active runs 带进度条 + 实时 sparkline + 队列卡;RunDetail 对 RUNNING run live 优先(实时曲线/最近事件/日志折叠),FAILED/LOST 显示失败诊断卡(likely cause + evidence + suggested fixes);Jobs 页写操作按 token scope 隐藏,cancel 一律二次确认。

## 快速开始(30 分钟部署标准)

环境:Python ≥3.12,[uv](https://docs.astral.sh/uv/)。

```bash
git clone <repo> && cd GPUPlane
uv sync                          # 安装全部组件(server/agent/cli/sdk)

# 1. 启动 server(GPU 机器上;首启自动生成 admin token 写入 ~/.gpuctl/server.yaml)
uv run gpuctl-server

# 2. 启动 agent(同机;token 从 server.yaml 复制到 ~/.gpuctl/agent.yaml)
uv run gpuctl-agent

# 3. 提交训练(cpu_only 示例先跑通,再上 GPU)
uv run gpuctl job submit -n mnist -g 1 \
  -d "$PWD/examples/mnist" --watch checkpoints \
  -- python train.py --epochs 3

# 注意:-d/--working-dir 按【agent 所在机器】解释,CLI 不做本地改写;
# 从笔记本向远端提交时传远端绝对路径。

# 4. 观测
uv run gpuctl status             # 节点/队列总览
uv run gpuctl job list           # 任务状态
uv run gpuctl logs -f <job-id>   # 实时日志
uv run gpuctl run show <run-id>  # 指标摘要(latest/best/trend)

# 5. Web UI(server 自动托管 web/dist;也可用 GPUCTL_WEB_DIST 指定)
open http://<gpu-host>:8600      # 输入 token 登录
```

导入历史实验(TB events + checkpoints → IMPORTED run):

```bash
uv run gpuctl import-run ~/experiments/old-run --project legacy
```

## 受限/离线网络安装

依赖只有约 50 个小 wheel(torch 不属于平台依赖,用训练机现有环境即可)。
网络受限时在能上网的机器预下载,再拷到目标机离线安装:

```bash
# 在能上网的机器(如 Mac)上,为 Linux x86_64 + py3.12 下载
uv export --format requirements-txt --locked --no-hashes --no-dev -o /tmp/reqs.txt
grep -v '^-e ' /tmp/reqs.txt > /tmp/reqs-clean.txt
uv run --python 3.12 --with pip python -m pip download -r /tmp/reqs-clean.txt hatchling editables \
  --python-version 312 --only-binary=:all: \
  --platform manylinux_2_28_x86_64 --platform manylinux_2_17_x86_64 \
  --platform manylinux2014_x86_64 -d ./wheels
rsync -az ./ ./wheels/ gpu-host:~/GPUPlane-wheels/   # 含仓库本体

# 目标机(离线)
cd ~/GPUPlane && uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python --no-index --find-links ~/GPUPlane-wheels \
  -r ~/GPUPlane-wheels/reqs-clean.txt hatchling editables
uv pip install --python .venv/bin/python --no-index --no-build-isolation \
  -e ./packages/common -e ./packages/tbreader -e ./packages/sdk \
  -e ./packages/server -e ./packages/agent -e ./packages/cli
```

## 两种部署拓扑

**同机(推荐起步)**:server + agent 都跑在 GPU 机器上,笔记本浏览器/CLI 经 LAN 访问
(`server.yaml` 设 `host: 0.0.0.0`,token 认证)。

**分体**:server 跑在常开的轻量机器(甚至 Mac mini/NAS),agent 跑在各 GPU 机器,
`agent.yaml` 的 `server_url` 指向 server 的 `ws://<ip>:8600`。agent 单向出站长连接,
GPU 机器无需任何入站端口,断线自动重连 + spool 重放。

## 事件推送到手机 / 本地钩子(v0.2)

`~/.gpuctl/server.yaml` 加 webhook(ntfy 示例;Bark 用 `kind: bark` + 设备 URL):

```yaml
webhooks:
  - url: "https://ntfy.sh/my-gpu-topic"   # 手机装 ntfy 订阅同一 topic
    kind: ntfy
    min_severity: warning                  # info|warning|critical,低于此不推
    # types: ["OOM", "LOSS_NAN"]           # 可选:只推这些事件类型
```

本地自动化(事件到达即在当前机器跑一个命令,如唤醒本地 agent):

```bash
gpuctl event-hook --severity critical -- /path/to/on-event.sh
# 事件经 GPUCTL_EVENT_TYPE/SEVERITY/MESSAGE/RUN_ID/... 环境变量 + stdin JSON 传入
```

## Agent-native:用自然语言驱动实验循环(v0.3)

`gpuctl-mcp` 是独立 adapter 进程(不依赖 server/agent),把控制面暴露成 21 个 MCP 工具。
配好后,Claude Code(或任何 MCP 客户端)用自然语言完成"提交训练 → 监控异常 → 对比 checkpoint → 给推荐",全程不碰 Web/CLI:

```bash
# 1. 起 adapter(独立进程;指向 server,带 write-scope token)
GPUCTL_SERVER_URL=http://127.0.0.1:8600 GPUCTL_MCP_TOKEN=<write-token> \
  gpuctl-mcp serve --port 18602            # streamable HTTP, stateless
# stdio 形态(插件用):gpuctl-mcp stdio

# 2. 让 Claude Code 发现它(仓库根 .mcp.json,已 gitignore)
cat > .mcp.json <<'JSON'
{ "mcpServers": { "gpuctl": { "type": "http",
  "url": "http://127.0.0.1:18602/mcp",
  "headers": { "Authorization": "Bearer <write-token>" } } } }
JSON

# 3. 自然语言驱动(skill 自动加载,无需手点工具)
claude -p "提交一个 mnist 训练,跑完告诉我结果,再对比最近两次 run 给我最好的 checkpoint"
```

读写域分级:只读 token 下 `submit_job`/`cancel_job`/`retry_job`/`evaluate_checkpoint`/`set_primary_metric` 返回 `WriteScopeError`。
三个 skill(`.claude/skills/gpu-training/`)编码实验循环并预授权 MCP 工具;详见
[`docs/08-v0.3-acceptance.md`](docs/08-v0.3-acceptance.md)。

## 训练侧 SDK(可选)

```python
from gpuctl import run

run.init(project="qwen-sft", experiment="lr-2e5", config={...})   # 平台 job 内自动 attach,可省略
run.log({"train/loss": loss.item()}, step=step)                   # 有界队列,绝不阻塞/抛错
run.log_checkpoint(path, step=step)                               # 只登记,不搬运文件
run.finish()
```

平台 dispatch 的 job 内零配置(env 自动注入);平台外裸跑自动注册 `source=sdk` 的 run;
无 server 时静默降级为本地 jsonl(`~/.gpuctl/spool/`),训练脚本行为完全不变。

## 架构

```
┌────────────┐   WS (出站)   ┌──────────────┐    REST/SSE    ┌──────────┐
│  Agent(s)  │ ───────────► │    Server    │ ◄────────────  │ CLI/Web  │
│ monitor/   │  heartbeat   │  scheduler   │                │ (同源)   │
│ runner/tb  │  metrics/logs│  SQLite(WAL) │ ◄──── HTTP ─── │ SDK      │
└────────────┘              └──────────────┘                └──────────┘
```

monorepo:`packages/{common,server,agent,sdk,cli,mcp,tbreader}` + `web/` + `examples/mnist` + `.claude/skills/`(agent skills)+ `.claude-plugin/`。`packages/mcp` 是独立 adapter 进程,不依赖 server/agent。

## 常用运维

```bash
uv run gpuctl backup             # 在线备份 SQLite 到 <data_dir>/backups/
uv run gpuctl backup-agent       # 在每台 Agent 主机归档完整 job 日志/runtime/spool
# 常驻运行见 deploy/systemd/(user unit + enable-linger)
uv run gpuctl job retry <id>     # 失败任务重新排队
uv run gpuctl job cancel <id>    # SIGTERM → 5s → SIGKILL(整进程组)
uv run pytest tests/ -q          # Python 测试(当前 145 个用例)
cd web && pnpm test:e2e          # 浏览器 smoke(2 个用例)
uv run ruff check . && uv run mypy
```

Docker runner 是可选能力;Process runner 仍是默认。使用 GPU 容器前,Agent 主机必须先按
[NVIDIA 官方安装指南](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)
安装 Container Toolkit,并把 runtime 写入 Docker 配置:

```bash
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker       # 先确认没有运行中的容器
docker run --rm --gpus all <cuda-image> nvidia-smi
```

在 WSL2 上还应先确认宿主透传正常(`nvidia-smi` 可见 GPU);Toolkit 只负责把已透传的
GPU 暴露给容器,不能替代 Windows/WSL 驱动。

## 设计红线(贡献前必读)

1. 训练代码独立性:`python train.py` 脱离平台必须始终可用。
2. best-effort telemetry:任何上报失败只落本地缓冲,绝不向训练进程抛异常。
3. Process runner 是一等公民,不强制 Docker/Git。
4. GPU 调度按槽位独占,永不依据利用率判断空闲。
5. Job ≠ Run:Job 是调度单元,Run 是训练语义单元(一次 attempt 一个 Run)。
6. SQLite + 无消息队列;server 单 worker(内存 pub/sub 是刻意的)。

## License

GPUPlane is licensed under the [Apache License 2.0](LICENSE). Attribution information is in [NOTICE](NOTICE).