v0.3 验收记录(Agent-native Training)
环境:控制面 + agent 部署于 WSL2(RTX 5090 32G);MacBook 经 SSH tunnel(
127.0.0.1:18601)驱动验收。 日期:2026-08-19。验收标准(roadmap §3):Claude Code 配置 MCP,自然语言完成“提交训练 → 盯异常 → 对比 checkpoint → 给推荐”,全程无人工平台交互。
v0.3 把 GPUPlane 从“人点 Web/CLI”升级为“Agent 用自然语言驱动整个实验循环”。当前交付物 = MCP server(20 工具)+ 语义层(5 函数,纯规则无 LLM)+ 3 个 skill + 插件打包 + AGENTS.md。
端到端主链路(自然语言,全绿)
Section titled “端到端主链路(自然语言,全绿)”Agent(claude -p,仅给 .mcp.json + 仓库 skill/AGENTS.md,无任何人工平台操作)执行下述循环:
| 步骤 | Agent 动作 | 结果 | 证据 |
|---|---|---|---|
| 1. SUBMIT | submit_job(command=["python3","-c","…5 步 loss…"], working_dir="/tmp", gpus=0, cpu_only=True, project="accept") |
☑ QUEUED | 返回 job_01M0AY8X113V1T9ZEETZ76F8GG + TaskHandle(ttlMs=86400000, pollIntervalMs=15000, next_actions 指向 get_job/get_run_summary) |
| 2. MONITOR | 轮询 get_job(job_id) → tail_logs |
☑ SUCCEEDED exit 0 | 5 行日志:step 0 train/loss=1.000 … step 4 train/loss=0.200 |
| 3. COMPARE | compare_runs(两个带 val_loss eval 的最近 run) |
☑ winner run_01M0AJWY6TW6D1P9KS6FZ07BGE(is_best:true) |
两 run 训练曲线并列 best 0.35/latest 0.45;winner 因有完成的评估胜出 |
| 4. DIAGNOSE | diagnose_run(run_id=winner) |
☑ SUCCEEDED,无异常/OOM/NaN/health warning | best val_loss 0.35 @ step 200,latest 0.45 @ 300,improving:false |
| 5. BEST ckpt | get_best_checkpoint(run_id=winner) |
☑ ckpt_01M0AJXDRDW6BKSE1RXAT69B0X step 200 value 0.31 |
语义层优先用评估结果(0.31)而非训练兜底值(0.35) |
| 6. RECOMMEND | 自然语言给结论 | ☑ | “保留 ckpt_…69B0X(step 200,eval val_loss=0.31);step 300 回退到 0.45,应早停在 200” |
Agent 原话(focused compare→recommend run):
Recommendation: Keep
ckpt_01M0AJXDRDW6BKSE1RXAT69B0X(step 200, evaluatedval_loss=0.31) from runrun_01M0AJWY6TW6D1P9KS6FZ07BGE.compare_runspicked this run as best;diagnose_runshows it finished cleanly — no OOM, NaN, or stalls — with the minimum at step 200, while step 300 regressed to ~0.45, so early-stopping at 200 is the right call. The 0.31 evaluation value is the trustworthy one (the semantic layer prefers evaluation results over the 0.35 training-run best).
MCP 工具面(20 个,全部 live)
Section titled “MCP 工具面(20 个,全部 live)”tools/list 经 adapter 返回 20 工具,分三层:
| 层 | 工具 | 说明 |
|---|---|---|
| Observe(11) | list_nodes get_node_status list_jobs get_job get_run_summary query_metrics tail_logs list_events list_checkpoints list_evaluations list_metric_definitions |
只读,read scope token 即可 |
| Control(4) | submit_job evaluate_checkpoint cancel_job retry_job |
写域 token 才暴露;cancel_job(confirm=False) 显式拒绝 |
| Semantic(5) | diagnose_run compare_runs compare_checkpoints get_best_checkpoint explain_failure |
纯 Python 规则+统计,无 LLM;每个结果带 next_actions 引导 agent 下一步 |
启动日志确认写域 token 解锁全部工具:
gpuctl-mcp: token 'admin' has write scope (all tools available).gpuctl-mcp streamable-http on http://127.0.0.1:18603/mcp -> http://127.0.0.1:18601只读 token 下 submit_job/cancel_job/retry_job/evaluate_checkpoint 返回 WriteScopeError(“requires a write-scope token”)。
验收顺带抓到并修复的真实配置问题
Section titled “验收顺带抓到并修复的真实配置问题”| # | 现象 | 根因 | 处置 |
|---|---|---|---|
| 1 | get_best_checkpoint 首跑 404 “no checkpoints with the primary metric found” |
历史种子数据把 eval/acc 设为 primary,但实际 run 记录的指标名是 val_loss;语义层按 primary 名匹配,查不到 |
PUT /metric-definitions/val_loss {is_primary:true} + 清掉 eval/acc/eval/accuracy 的 primary;修复后 get_best_checkpoint 干净返回 ckpt-200/value 0.31 |
这是语义层“该暴露就暴露”的正面案例:指标名与 primary 配置错配时,
get_best_checkpoint不猜、返回明确 404 + 提示(“set a metric_definitions.is_primary and evaluate or train”),Agent 据此诊断并给出修复建议,而非静默兜底一个错误 checkpoint。
语义层(5 函数,纯规则 + 统计,无 LLM)
Section titled “语义层(5 函数,纯规则 + 统计,无 LLM)”packages/server/src/gpuctl_server/semantics.py —— REST 与 MCP 都只是薄暴露层,核心是确定性 Python 函数:
diagnose_run(db, run_id)→ RunDiagnosis:收敛性(best/latest/is_improving/is_stalled)、异常集(ANOMALY_TYPES)、health warnings、next_actionscompare_runs(db, ids, metric)→ 按方向(maximize/minimize)排名 +is_best标记compare_checkpoints(db, ids, metric)→ 同上,checkpoint 粒度get_best_checkpoint(db, run_ids|experiment_id, metric)→ 优先用 SUCCEEDED 评估的 primary,无评估时兜底训练 run 的 best 值(弱信号,结果里标source)explain_failure(db, run_id|job_id)→ FailureExplanation:扫日志/事件模式(CUDA OOM / NCCL / ModuleNotFoundError…)+ exit_code →likely_cause+suggested_fixes+next_actions(如 OOM 后retry_job)
每个返回都带 next_actions: [NextAction(tool, args, reason)],由平台引导 Agent 下一步。
Skills(3 个,ARIS 风格)
Section titled “Skills(3 个,ARIS 风格)”.claude/skills/gpu-training/{run-experiment,monitor-experiment,analyze-results}/SKILL.md,6 字段 frontmatter,allowed-tools 预授权 MCP 工具,references/ 按需加载:
| Skill | 触发 | 工作流 |
|---|---|---|
| run-experiment | 提交/跑训练 | list_nodes→submit_job→poll get_job→get_run_summary→交给 monitor |
| monitor-experiment | 盯训练异常 | 分钟级 watch loop + 异常 runbook 表(LOSS_NAN/OOM/OVERFITTING/GPU_UNDERUTILIZED/AGENT_DISCONNECTED/DISK_LOW)+ 失败 triage(explain_failure→tail_logs,OOM 不盲目重试) |
| analyze-results | 对比/选 ckpt | compare_runs→compare_checkpoints→get_best_checkpoint→evaluate_checkpoint 补评估→keep/revert 循环 |
插件打包 + 部署拓扑
Section titled “插件打包 + 部署拓扑”.claude-plugin/plugin.json:mcpServers.gpuctlstdio 启动gpuctl-mcp stdio,env 注入GPUCTL_SERVER_URL+GPUCTL_MCP_TOKEN;技能与 hook 随插件分发。.mcp.json(仓库根,已 gitignore 含真实 token):{"mcpServers":{"gpuctl":{"type":"http","url":"http://127.0.0.1:18603/mcp","headers":{"Authorization":"Bearer …"}}}},供claude -p直接发现。- AGENTS.md(canonical)+ CLAUDE.md(mirror):仓库级 agent 规则(裸进程+SQLite 红线、实验循环、metric/job/ckpt 约定、OOM runbook、Tailwind v4 令牌 gotcha、部署命令、“never commit ./server ./–help”)。
- gpuctl-mcp 独立进程/独立包(design §15):
packages/mcp,fastmcp 3.4.7 pinned +mcp>=1.29,<2(mcp 2.0 是硬破坏,绝不与 fastmcp3 混装);坏了不影响 server。
验收期间 adapter 以 gpuctl-mcp serve --port 18603 独立进程跑在 Mac,经 tunnel 转发到 WSL server:
claude -p ──MCP/HTTP──► gpuctl-mcp (Mac:18603) ──REST/Bearer──► server (WSL:8600 via tunnel :18601)各模块交付清单(roadmap §3 全 6 项)
Section titled “各模块交付清单(roadmap §3 全 6 项)”| 模块 | 状态 | 交付 |
|---|---|---|
| gpuctl-mcp 包(20 工具) | ☑ | fastmcp 3.4.7 pinned,streamable HTTP stateless 核心;11 observe + 4 control + 5 semantic;独立进程/包 |
| 语义层(5 函数) | ☑ | diagnose_run/compare_runs/compare_checkpoints/get_best_checkpoint/explain_failure;纯规则+统计,无 LLM;返回结构化 JSON + next_actions |
| Skills(3 个) | ☑ | run-experiment / monitor-experiment / analyze-results;6 字段 frontmatter,allowed-tools 预授权 MCP 工具,references 按需加载 |
| 插件打包 | ☑ | .claude-plugin/plugin.json(stdio MCP + skills + hooks);.mcp.json(http,gitignored) |
| AGENTS.md | ☑ | 仓库级规则(canonical),CLAUDE.md 镜像 |
| 读写域分级 | ☑ | GET /auth/whoami 暴露 {name,scope,can_write};写域 gate submit/cancel/retry/evaluate,只读 token 返回 WriteScopeError |
uv run pytest tests/ -q → 107 passed(tests/test_mcp.py 15 例:19 工具注册、bearer 转发、查询参数、语义 JSON、错误透传、whoami、submit body+handle、写域拒绝、cancel confirm gate + result 字段回归、evaluate/retry 转发、query_metrics names CSV 回归、get_job run_id 透传;test_server_api.py 语义端点 diagnose/compare/best + explain_failure 真机场景 + 收敛 stalled/improving、job_id 过滤、单 primary、get_job run_id、retry attempt 回归)。ruff + mypy 全绿。
真机功能复测:发现并修复的 2 个 bug
Section titled “真机功能复测:发现并修复的 2 个 bug”验收后开独立测试 subagent 对 19 工具 + 5 语义端点 + 写域 gate + 边界做逐项复测,整体 PASS,抓到 2 个 MCP 适配层与 REST 契约错位(均不崩溃,已修 + 加回归测试):
| # | 现象 | 根因 | 修复 + 回归 |
|---|---|---|---|
| 1 | cancel_job(confirm=True) 的 TaskHandle 恒报 state:"working", status:null |
server.py 读 job.get("status"),但 REST POST /jobs/{id}/cancel 返回 {"job_id","result":"CANCELLED"|"cancel-requested"|…}(无 status 键) |
server.py 改读 resp["result"]:result=="CANCELLED"→state:"cancelled",否则 working,status=result。回归测试覆盖 CANCELLED + cancel-requested 两态;原测试 mock 用错形状({"status":"CANCELLED"})故漏报,已修正为真实 REST 形状 |
| 2 | query_metrics(names=["train_loss","val_loss"]) 静默只返回 1 条序列 |
client.py 把 list 直接塞 httpx params → ?names=a&names=b;REST 路由声明 names: str + split(","),FastAPI 对重复同名参数只绑单值,丢一个 |
client.py 先 ",".join(names) 再发 → ?names=a,b。REST 层复现确认:CSV 形式返回两序列,重复形式只返一。回归测试断言 URL 恰含一个 names= 且两名都在 |
真机回归(fix 后经 tunnel 实测):cancel_job 对 CANCELLED job 返回 state:"cancelled", status:"CANCELLED";query_metrics(names=[train_loss,val_loss]) 返回两序列各 3 点(train 0.9/0.8/0.7,val 0.6/0.35/0.45)。
端到端真实研究员流复测:发现并修复的 7 个集成问题
Section titled “端到端真实研究员流复测:发现并修复的 7 个集成问题”再开一个 fork subagent 扮演真实研究员,用高保真 torch-free 模拟训练脚本(纯标准库 + gpuctl SDK 真上报指标/checkpoint + lr 驱动的真实 loss 曲线 + 可控失败模式 oom/nan/overfit)跑 lr-sweep:submit×4(好/发散/欠拟合/OOM)→ 监控 → explain_failure → retry → evaluate → compare → recommend,外加 claude -p 自然语言流测 skill 层。原子逐工具测试漏掉、只有连贯真实工作流才暴露的 7 个问题(已修 + 回归测试 + 真机回归):
| # | 等级 | 现象 | 根因 | 修复 + 回归 |
|---|---|---|---|---|
| 1 | HIGH | diagnose_run 把健康收敛 run 误判 stalled(latest==best 时 stalled=true)→ 误导早停 |
_is_stalled 用 abs(latest-best)<=eps,latest 等于 best(仍在创新 best)被判停滞 |
改用 trend:仅 trend=="stable" 才 stalled;unknown/短序列 → False(不哭狼)。回归:8 点递减 run(latest==best)stalled=False,8 点平 run stalled=True |
| 2 | HIGH | diagnose_run 对短序列(<5 点 trend=unknown)误报 improving=false |
_is_improving 对 trend=="unknown" 落到默认分支返回 False |
trend=="unknown" → 返回 None(数据不足),不报 False。回归:4 点递减 run improving=None |
| 3 | HIGH | GET /runs?job_id= 静默忽略,返回全部 run → agent 按文档传 job_id 拿到未过滤列表、映射错 run |
list_runs 路由 + store 都无 job_id 参数 |
路由 + store 加 job_id 过滤。回归:两 job 各一 run,?job_id= 只返对应一个 |
| 4 | MED | metric_definitions 允许多个 is_primary=true 共存,_primary 静默选一个(实测 eval/loss + val_loss 同时 primary) |
upsert_metric_def 不清旧 primary |
设新 primary 时先 UPDATE … SET is_primary=0 WHERE is_primary=1 AND name<>?。回归:连设 a、b primary 后仅 b 为 primary |
| 5 | MED | get_job 返回无 run_id,但 TaskHandle next_actions 指向 get_run_summary(run_id)/diagnose_run(run_id) → submit→monitor→analyze 衔接断裂 |
Job 模型无 run_id 字段 | Job 加 run_id;get_job/list_jobs SQL 加 runs.job_id 子查询 join 填充。回归:链 job→run 后 GET /jobs/{id} 返回 run_id;无 run 的 job 返 None |
| 6 | MED | explain_failure 对 OOM 建议直接 retry_job,但 retry 复用原 command → OOM 必复发,与 CLAUDE.md “改 config 再 retry” 冲突,诱导必败重试 |
next_actions 无差别建议 retry_job | OOM/NaN 时 reason 改为“先应用 suggested_fix(减 batch/降 lr)再 retry,原样重跑必复发;或 submit_job 改参”。回归:断言 retry_job 的 reason 含 unchanged/fix |
| 7 | MED | retry_job 不递增 attempt,重试计数不可见 |
retry 路由未 bump attempt | retry 时 attempt=job.attempt+1。回归:两次 retry 后 attempt=1→2 |
真机回归(fix 后部署 WSL 经 tunnel 实测):GET /jobs/{id} 返回 run_id 正确;GET /runs?job_id= 只返对应 run;diagnose_run 对收敛 run(best=latest=0.195, trend=unknown)返回 improving=None, stalled=False;live DB 仅一个 primary。107 tests pass(+7 回归),ruff + mypy 全绿。
当时未改的设计层面问题:adapter 偶发 “Event loop is closed”;多 run 共用 working_dir 时 checkpoint 路径碰撞。二者已在 2026-08-20 发布验收中修复并由 GPT-5.6 Luna 原生 MCP 真机复验,见 09-v0.3-release-acceptance.md。claude -p 一次性长 prompt 的客户端耗时仍建议通过拆步控制。
原生 MCP 黑盒复测(round 4):真研究员 agent,5 个语义层问题
Section titled “原生 MCP 黑盒复测(round 4):真研究员 agent,5 个语义层问题”前三轮:round 1 端到端、round 2 逐工具原子、round 3 连贯真实工作流(fork,但用 curl/python 调 MCP HTTP = “coding”)。round 4 纠正方法学——真 agent 模式:把 19 个 MCP 工具作为原生工具注册进 Claude Code(.mcp.json + /reload-plugins),开一个全新 general-purpose subagent(非 fork,不继承父上下文 = 防作弊)只凭 mcp__gpuctl__* 工具 + 读 CLAUDE.md/skills 驱动整套 lr-sweep,禁止读 packages/ 源码、grep/glob、ssh/curl。这套工作流暴露的全是前几轮没抓到的语义层问题(run 级语义忽略 checkpoint 评估):
| # | 等级 | 现象 | 根因 | 修复 + 回归 |
|---|---|---|---|---|
| 1 | HIGH | compare_runs 按训练 best 排名,无视 SUCCEEDED checkpoint 评估 → 能把“晚段过拟合”的 run 误评为最佳,与 get_best_checkpoint(eval 优先)结论相左 |
compare_runs 只读 summary.best,不读评估 |
Fix A:eval 优先、训练 best 兜底(与 get_best_checkpoint 同一 _best_eval 助手)。回归:训练 best 0.91 但 eval 0.70 的 run 输给训练 best 0.80 但 eval 0.85 的 run |
| 2 | HIGH | explain_failure 对 OOM 建议 retry_job,但 retry 复用原 command → OOM 必复发,与 CLAUDE.md“改 config 再 retry”冲突,诱导必败重试 |
next_actions 无差别 retry_job | Fix B:OOM/NaN 改建议 submit_job,把原 command 作为 args.command 基底交给 agent 改(减 batch/降 lr),reason 注明“retry_job reuses this command and re-fails identically”。回归:OOM run 的 next_actions 不含 retry_job,含 submit_job 且 args.command == 原 command |
| 3 | HIGH | diagnose_run 只报训练收敛(best/best_step/improving/stalled),对 heldout 评估盲——实测报 best_step=40,而 get_best_checkpoint 指向 step 10(早停点),自相矛盾,且无过拟合信号 |
diagnose_run 不读 checkpoint 评估 | Fix D:convergence 加 eval_best/eval_best_step(同一 _best_eval 助手);当 eval_best_step < train best_step 触发 OVERFITTING health warning + 指向 eval-best ckpt 的 next_action。回归:训练 best 0.91@step3 / eval best 0.95@step1 → overfit warning + eval_best_step=1 |
| 4 | — | 发散/欠拟合 run 不被 EventDetector 标为事件(NaN/OOM 之外的学习病理无覆盖) | EventDetector 只覆盖 OOM/NaN/disconnect | 未改(更大的 EventDetector 增强,提议后续:divergence=loss 单调上升、underfit=末段 loss 仍高于阈值) |
| 5 | LOW | evaluate_checkpoint 的 TaskHandle status:null(与 submit_job 的 status:"QUEUED" 不一致);explain_failure 的 suggested_fix 静态通用 |
adapter 未设 status;fixes 无 run 上下文 | Fix C:evaluate TaskHandle status="QUEUED"(与 submit_job 对齐)。回归在 test_mcp.py。explain_failure run-aware fixes 提议后续 |
真机回归(fix 后部署 WSL 经 tunnel 实测,round 4 的 sim 数据仍在 live DB):
- Fix D:
a3-lr1e3-goodrun 4 个 checkpoint(step 10/20/30/40,heldout eval/loss 0.1596/0.1726/0.1838/0.177)→diagnose_run现convergence.eval_best=0.1596 @ eval_best_step=10(训练 lensbest=0.1963 @ best_step=40),因 10<40 触发OVERFITTINGhealth warning;get_best_checkpoint一致指向 step-10 ckpt。 - Fix A:
compare_runs(a3-good, a2-good)两 run 的best字段均为0.1596(eval-best 值),非训练 latest0.1963——eval 优先生效,且与get_best_checkpoint同值(两语义函数收敛一致)。 - Fix B:
explain_failure对 OOM run 返回submit_job(args.command = 原--fail-mode oom --batch-size 64命令基底),next_actions 无 retry_job,reason 注明“re-fails identically”。
109 tests pass(+3 回归:compare_runs eval-preference / diagnose_run eval+overfit / explain_failure submit_job),ruff + mypy 全绿。
方法学结论:round 2 逐工具、round 3 连贯工作流都漏掉了这 3 个 HIGH,因为它们是跨函数一致性问题(compare_runs vs get_best_checkpoint、diagnose_run vs get_best_checkpoint、explain_failure vs retry_job 语义)——只有真 agent 在完整循环里同时调用多个语义函数、并交叉比对结论才暴露。原生 MCP 注册(非 coding)+ 黑盒防作弊是关键:agent 看不到源码,只能靠工具返回的字段推理,正好复现真实研究员的视角。
round-4 修复的黑盒复验(同一 researcher,native MCP)
Section titled “round-4 修复的黑盒复验(同一 researcher,native MCP)”修复部署 WSL 后,同一 researcher subagent(保留黑盒约束:只调 mcp__gpuctl__* + 读 CLAUDE.md/skills,不读源码/不 grep/不 ssh+curl)对 3 个 HIGH 做了 native-MCP 复验,全 FIXED,循环端到端 clean:
- Fix A:
compare_runs两 good run 各best=0.1596(heldout eval)、latest=0.1963(训练,正确降级到 latest);winnera3-lr1e3-good与get_best_checkpoint(step-10 ckpt, value 0.1596)一致。 - Fix D:
diagnose_runconvergence 同时含训练(best=0.1963 @ best_step=40)与 heldout(eval_best=0.1596 @ eval_best_step=10);health_warnings含OVERFITTING;next_actions指向 eval-best ckpt。与get_best_checkpoint一致——前述“diagnose 说 step 40 / best 说 step 10”的矛盾已消。 - Fix B:
explain_failure对 OOM run 的next_actions[0]=submit_job(args.command = 原 OOM 命令基底),无 retry_job;reason 注明 re-fails identically。round-4 的“OOM 死路”消除。 - 端到端:fresh 提交
a3-verify-loop(cpu_only, 5 步)→submit_jobQUEUED →get_jobSUCCEEDED exit 0 →get_run_summarySUCCEEDED step 5、best eval/loss 0.1641@5、health 干净。submit→get_job→get_run_summary 链通。
残留非阻塞:复验期间 4 个 compare_runs 并发调用时,首个偶发 "Event loop is closed"(传输层,fastmcp stateless),裸重试即返正确 payload。这是 round-3 已记的“首请求偶发”的精化——并行负载下也会触发(非首个请求专属),不破坏正确性但真实 agent 并行调语义函数时会撞到、需重试。建议后续硬化 adapter 的 asyncio 生命周期(per-request client/loop 隔离),但属 fastmcp 薄适配层、可独立于 server。
原生 MCP 黑盒复测 round 5:更宽 lr sweep + 真过拟合 run
Section titled “原生 MCP 黑盒复测 round 5:更宽 lr sweep + 真过拟合 run”换一个全新 general-purpose subagent(sim5,非 fork,黑盒防作弊同前),场景比 round 4 更宽:4 个 lr(5e-4/1e-3/2e-3/3e-3,跨收敛边界找真“最佳”)+ 一个刻意 --fail-mode overfit run(直压 round-4 新增的过拟合检测)+ experiment 级 get_best_checkpoint(experiment_id=)(前几轮只测 run 级)+ 边界探针(cancel 运行中 job、不存在的 run、单 id compare、无 eval 的 experiment 取 best、多指标 query_metrics)。
找到 1 个 HIGH(round-4 Fix D 的自动过拟合警告是反的)+ 4 个待设计层增强:
| # | 等级 | 现象 | 根因 | 处置 |
|---|---|---|---|---|
| H1 | HIGH | 过拟合检测反了:健康 winner run(r5-lr1e-3)被误报 OVERFITTING;真过拟合 run(r5-overfit)反而无警告 |
Fix D 的条件 eval_best_step < best_step,但 best_step 取自 primary(eval/loss)的训练期 in-training 流最佳步——不是训练 loss。过拟合 run 的 in-training eval/loss 也从 step 10 起恶化,故 10<10 false → 漏报;健康 run 的 in-training eval/loss 仍下降到 step 40,故 10<40 true → 误报。任何 step-比较启发式都不对:正确信号需 train 轨迹 vs eval 轨迹对比(短序列安全) |
本轮回退 Fix D 的自动警告:移除 OVERFITTING health warning + 其 next_action;保留 eval_best/eval_best_step 字段(正确数据,agent 据此自行推理——sim5 自己就用这些字段+tail_logs 得出正确结论)。真过拟合检测归入 EventDetector(下方 Problem 4,已据此细化)。回归测试改为断言“字段 surfaced + 不发误报警告”。live 验证:健康 winner 现 health_warnings=[]、字段仍在;过拟合 run 字段仍在、best=1.1366@step10(in-training eval/loss 已峰值)留给 agent 推理 |
| M1 | MED | experiment 级 get_best_checkpoint(experiment_id=) 与 compare_runs 在值并列时选不同 winner:两者 step-10 eval/loss 都 0.1596(sim 持数确定性并列),get_best_checkpoint(experiment_id=) 把“要保留的 ckpt”指向了过拟合 run 的 checkpoint,而 compare_runs 选了健康 run |
tie-breaking 按迭代序取首个,未偏向健康 run;且不提示存在 tie | 待增强:tie 时偏向 compare_runs/diagnose 健康的 run,或在结果里显式标注并列。属推荐层设计 |
| M2 | MED | 过拟合 run 的 list_events 无 OVERFITTING_SUSPECTED 事件(monitor skill 异常表列了该信号却未触发) |
EventDetector 不检测过拟合(同 H1/Problem 4) | 待增强:EventDetector 加过拟合信号(与 H1 同一 detector) |
| L1 | LOW-MED | eval/loss/eval/accuracy 在每个 run 都 trend=unknown(4 点 < summary.py 的 5 点阈值),即便过拟合 run 的 in-training eval/loss 单调恶化 1.14→1.44→2.34→3.44。只有 train/loss 有真 trend。这饿死了 trend-式过拟合检测 |
eval 序列点少(eval-every=10、40 步只 4 点)→ trend unknown | 待增强:eval 指标用更短阈值或 first-vs-last 兜底;或 EventDetector 用直接首末对比绕过 trend |
| L2 | LOW | eval job 名泛化(eval-10..eval-40),跨 run 重名,list_jobs 不读 command 无法区分属于哪个 run/checkpoint |
evaluate_checkpoint 不给 eval job 起 run-aware 名 | 待增强(小):eval job 命名 eval:<run_name>:step<N> |
| L3 | LOW | MCP 面无 metric-definitions 工具,agent 无法经 MCP 列/设 primary 指标(skill 文档指向 REST) | 19 工具未覆盖 metric-definitions | 待增强(小):加 list_metric_definitions(只读)MCP 工具 |
边界探针(clean,无问题):diagnose_run(不存在 run) → 干净 404;get_best_checkpoint(无 ckpt/eval 的 run) → 干净 404 + helpful hint(“set a metric_definitions.is_primary and evaluate or train”);compare_runs(单 id) → 正常 is_best=true;query_metrics([train/loss,eval/loss]) → 两序列各返;tail_logs(过拟合 job) → 逐步日志确认恶化;cancel_job(confirm=True) on RUNNING throwaway → CANCELLED、exit -15、RUN_FINISHED(warning) 事件(危险操作守护生效)。
本轮修复:回退 Fix D 的自动过拟合警告(H1,correctness——发错的信号比不发更糟),保留 eval_best 字段。109 tests pass(回归测试改为断言字段 surfaced + 无误报),ruff + mypy 全绿,WSL live 验证健康 winner 的误报已消。
结论细化:v0.3 的 run 级循环(submit/poll/eval/compare/tail/cancel)端到端 solid;但experiment 级推荐层尚不可全信——三分析函数在并列时会指向不同 winner(M1),过拟合检测未落地(H1/M2/L1,回退后诚实不发误报,待真 detector)。一次 round-5 把“过拟合检测”从“已修”打回“待做”,并把 experiment 级 tie-breaking、eval job 命名、metric-definitions MCP 工具列为后续增强。
round 5 增强项修复 + round 7 黑盒复验
Section titled “round 5 增强项修复 + round 7 黑盒复验”round-5 列的 H1 + M1/M2/L1/L2/L3 全部在本批处理:
| 项 | commit | 处置 |
|---|---|---|
| H1(过拟合自动警告反了) | 8713917 |
回退 Fix D 的 OVERFITTING health warning + 其 next_action;保留 eval_best/eval_best_step 字段(正确数据,agent 据此自行推理)。真过拟合检测归入 EventDetector |
M2(过拟合 run 无 OVERFITTING_SUSPECTED 事件) |
340278e |
EventDetector 的 _check_overfitting 本就有正确信号(train 下降 + val 较其最佳发散 ≥1.5×,非 step-比较),只是 OVERFIT_MIN_POINTS 阈值 10 对 4-eval run 太严。10→4。回归测试:4 点 val [1.14,1.41,2.34,3.44] 触发;4 点健康 val [0.30,0.25,0.22,0.20] 静默 |
| M1(experiment 级并列选过拟合 run) | 340278e |
get_best_checkpoint 在 primary 值并列时偏向无 OVERFITTING_SUSPECTED 事件的更健康 run,并新增 tied_checkpoint_ids 显式列出并列 ckpt |
L2(eval job 跨 run 重名 eval-10) |
340278e |
eval job 命名改 eval:{run_name}:step{N}(run-aware) |
| L3(MCP 面无 metric-definitions 工具) | 340278e |
加 list_metric_definitions(只读)MCP 工具,adapter 20 工具 |
| L1(eval 4 点 trend=unknown) | e6901ff |
见下方 round-7 B2 |
round 7(sim7,黑盒 native-MCP 复验)
Section titled “round 7(sim7,黑盒 native-MCP 复验)”全新 general-purpose subagent sim7(非 fork,黑盒约束同前:只调 mcp__gpuctl__* + 读 CLAUDE.md/skills,不读源码/不 grep/不 ssh+curl)做端到端复验:5 个 lr sweep run(含 1 个 --fail-mode overfit)+ 每 run 4 个 heldout eval + experiment 级 get_best_checkpoint + compare_runs + diagnose_run。
verdict:完整 submit→monitor→eval→compare→diagnose→recommend 循环在 run 级与 experiment 级均流畅、可信、端到端。H1/M1/M2/L2/L3 修复全部 confirmed FIXED。过拟合 run 正确触发 OVERFITTING_SUSPECTED;健康 winner 不再被误报;experiment 级推荐在并列时选健康 run 并列示 tied。
sim7 另报 6 个 polish 项(B1–B6),处置如下:
| # | 现象 | 处置 | commit / 依据 |
|---|---|---|---|
| B1 | 同 ckpt 两次 eval 的 job name 相同(L2 的 run-aware 名仍并列:同 run 同 step 无 per-eval 区分) | 修:名尾追加 ULID 随机尾 eval_id[-8:](时间前缀 [5:13] 同毫秒碰撞) |
e6901ff |
| B2 | 4 点 eval 序列 trend=unknown(同 L1,summary.py _trend 阈值 5) |
修:阈值 5→4(half=2 首末均值在 4 点已够稳) | e6901ff |
| B3 | CheckpointRecommendation 无法区分 value 来自 heldout eval 还是训练兜底 |
修:加 source 字段(evaluation / training_fallback) |
e6901ff |
| B4 | compare_runs 在同 run 内混用 best(heldout)与 latest(训练)源 |
判定设计:compare_runs 的 best/latest 各列本就分列两字段,混用是 run 内既有 eval 又有训练流的诚实展示,非 bug;文档记之,不改 |
设计层 |
| B5 | tail_logs 时间戳批量化、难对齐 |
判定 agent-runner 层:log tail 的批量时间戳是 runner 采集行为,非语义层 bug;文档记之,不改 | agent-runner 层 |
| B6 | compare_runs.next_actions 含 compare_runs 自指 |
误报:compare_runs 的 next_actions 无 compare_runs 自指(sim7 与 diagnose_run 的 next_action 混淆);不改 |
misreport |
round 7 polish 的 live 验证(native MCP,部署 WSL 后)
Section titled “round 7 polish 的 live 验证(native MCP,部署 WSL 后)”- B1:同 ckpt 同毫秒两次
evaluate_checkpoint→ job nameeval:r7-lr1e-3#1:step10:Q9MZAGYK与eval:r7-lr1e-3#1:step10:T57PDZ9F(尾 8 字符不同),均 SUCCEEDED。旧名eval:r7-lr1e-3#1:step10会并列。 - B2:fresh 40 步 / 4-eval run(
r8-b2-trend,lr=1e-3)→get_run_summary返trend.eval/loss="decreasing"、eval/accuracy="increasing"(旧阈值 5 下为"unknown");3 点序列仍"unknown"。 - B3:r6-overfit-verify run(唯一 eval FAILED)→
get_best_checkpoint返source="training_fallback"、value=1.1366、step 10;有 SUCCEEDED eval 的 run 返source="evaluation"。
L3 可见性注记:list_metric_definitions 工具代码与 adapter 均正确(tools/list 返 20 工具,已验),但 Claude Code 会话在会话启动时缓存 MCP 工具表——会话中途新增的工具对已开会话不可见,需用户执行 /reload-plugins(或新开会话)刷新。这是客户端缓存行为,非服务端 bug。
roadmap §3 验收标准达成:Agent 仅凭自然语言与 MCP,无任何人工平台交互,完成“提交训练 → 监控 → 对比 → 推荐”全循环。语义层用确定性规则给出可审计结论,写操作经 token 域分级守护,插件形态可分发。
经 5 轮黑盒 native-MCP 复测(round 2/3/4/5/7,每轮换全新 general-purpose subagent,黑盒防作弊)收敛:run 级与 experiment 级的 submit→monitor→eval→compare→diagnose→recommend 循环端到端流畅可信;过拟合检测已落地于 EventDetector(train 下降 + val 发散 ≥1.5×,4 点即触发,非脆弱 step-比较);experiment 级推荐在并列时偏向健康 run 并示 tied;eval job 名 run-aware + per-eval 唯一;recommend 结果标注 eval vs 训练兜底来源。已知非阻塞项记于此处:compare_runs 同 run 内 best/latest 分列两源是诚实展示(B4,设计);tail_logs 批量时间戳属 runner 采集层(B5,agent-runner);list_metric_definitions 对已开会话需 /reload-plugins 刷新(L3,客户端缓存)。