节点 Agent
HeteroFlow 是本站的异构算力与大模型调度产品。节点 Agent。节点 Agent 硬件验证边界(截至 2026-10-20) :本文中的“10 种品牌/检测器”指代码检测与适配范围,不代表 10 种硬件都完成了真机端到端验证。现有项目记录显示 NVIDIA 多机型已有真机端到端证据;国产加速卡检测逻辑已做软件及厂商命令输出对照,真机端到端验证…
节点 Agent
> 硬件验证边界(截至 2026-10-20):本文中的“10 种品牌/检测器”指代码检测与适配范围,不代表 10 种硬件都完成了真机端到端验证。现有项目记录显示 NVIDIA 多机型已有真机端到端证据;国产加速卡检测逻辑已做软件及厂商命令输出对照,真机端到端验证仍待对应硬件。
Agent 是部署在每台 GPU 节点上的常驻执行进程,是平台控制面伸向算力机的"手和眼":向上,它每 5 秒心跳一次,把 CPU / 内存 / 逐卡 GPU 指标 / Docker 可用性 / 推理引擎 / 缓存模型尽数上报;向下,它领取调度器派来的任务,拉镜像、起容器、挂载卷、注入 GPU 可见性,一直监护到终态上报与容器回收。整个体系有一条贯穿始终的安全约束——Agent 零监听:客户 GPU 机器不开任何入站端口,Agent 只做 HTTP client 主动拉取指令(任务、文件、分片、镜像四条通道全部是"agent 主动拉"形态),控制面永远无法被动连接到算力机,从根上消除了入站攻击面。
典型形态
控制面 (Server :3333) ←──心跳/上报── Agent(每节点一个,systemd 常驻)
│ │
└──任务/指令(agent 主动拉)──→ Executor → docker run → 监护 → 终态 CAS功能总览
| 能力 | 说明 | 状态 |
|---|---|---|
| 一键部署全家桶 | 4 个安装/升级脚本由 Server 直接下发;单向网络可部署;部署后首跳心跳自注册 | 生产生效 |
| X-API-Key 认证 + 节点作用域 | 单一系统级凭据,但只能操作本节点相关数据,禁止全量枚举与建任务 | 生产生效 |
| 心跳上报 | 默认 5 秒一跳,20+ 字段全量上报;指数退避重连;独立协程采集 CPU | 生产生效 |
| 任务执行器 Executor | CAS 认领 → 拉镜像 → 起容器 → 监护 → 终态上报 → 回收,全链路 | 生产生效(Docker 执行路径,真机长期运行) |
| GPU 检测 | 10 种检测器优先级注册链 + 虚拟 GPU 兜底;运行中掉卡逐卡探测 | 国产卡[适配] / NVIDIA[真机],详见《GPU 检测》篇 |
| 推理引擎探测 | vLLM / SGLang / llama.cpp / MindIE 四引擎;每 20 次心跳重探并回灌 | 生产生效 |
| 模型扫描器 | 启动全量 + 每 12 次心跳增量扫描模型目录 | 生产生效 |
| 分片指令执行 | create_slice / destroy_slice 破坏性指令串行执行、二次校验、如实回报 | MIG 真机验证(A800) |
| 镜像预热执行 | 运维态拉取(2 小时预算 + 60 秒进度心跳),与任务态拉取完全隔离 | 生产生效 |
| checkpoint 续跑注入 | 训练任务启动前自动探测最新完整 checkpoint 并注入续跑变量 | 生产生效 |
| 僵尸容器清理 | 终态黑名单 + 三条判据,宁漏勿错 | 生产生效 |
| 优雅关闭 | SIGTERM/SIGINT 优雅退出;任务容器刻意不停(by design) | 生产生效 |
| 版本机制 | ldflags 注入三元组、心跳上报落库、服务端下发强制升级脚本 | 生产生效 |
| WSL2 兜底支持 | findCommand 路径兜底表 + systemd PATH 注入 | 真机验证(RTX 4060 WSL) |
一键部署全家桶
Server 在自己的端口上直接托管 Agent 的全部安装与升级脚本(core/pkg/api/server.go 注册的四个路由),节点侧一条 curl 即可完成部署:
| 脚本 | 路由 | 用途 |
|---|---|---|
| install-agent.sh | /install-agent.sh | 全新安装:下载二进制、写配置、装 systemd 服务 |
| install-agent-user.sh | /install-agent-user.sh | 无 root 权限场景的用户态安装 |
| update-agent-quick.sh | /update-agent-quick.sh | 快速升级(保留现有配置) |
| update-agent-force.sh | /update-agent-force.sh | 强制升级(服务端强制升级通道,用于统一推平集群版本) |
配合聚合脚本 deploy-agent-all-in-one.sh(从 :5173 下发),在真正安装之前脚本会先带 X-API-Key 向服务端做一次密钥预检:返回 401 说明密钥与服务端不匹配,当场终止安装——否则装完后 Agent 会一直 401,节点永远无法上线。整套流程不要求控制面能主动连到节点(单向网络可部署,NAT 后的节点同样适用),安装完成、Agent 发出第一跳心跳时服务端自动创建节点记录(隐式注册),无需在控制面手工登记。生成的 systemd 服务文件显式注入 PATH(含 /usr/lib/wsl/lib),保证 WSL2 节点上也能找到 nvidia-smi。Agent 二进制本身由 GET /static/agent 分发,?arch=arm64 取 AArch64 交叉编译产物(纯 Go 静态链接;缺 arm64 产物时回 404 附构建指引,不静默发错架构)。
X-API-Key 认证与节点作用域
Agent 与 Server 的全部交互只使用 X-API-Key 请求头携带系统密钥,不使用 JWT。这把密钥是基础设施级凭据,不绑定任何租户——因此服务端对它施加了节点作用域约束,防止一把 Agent 密钥失窃后被用来横向打探全局:
GET /api/v1/tasks必须同时携带node_id与status查询参数,只能按节点取数,禁止全量任务枚举;- 不允许用该密钥创建任务(只能执行调度器已经派给自己的);
- 密钥配置错误时 401 不重试——静态密钥的 401 重试无意义,直接报错留痕;
- 服务端生产模式下对密钥做 fail-fast 校验:缺失、等于历史默认值或长度不足 16 字符时拒绝启动。
心跳上报
| 参数 | 值 |
|---|---|
| 间隔 | 默认 5 秒,可配置,合法区间 [5, 300] 秒 |
| HTTP 超时 | 10 秒 |
| 重连 | 指数退避,每次失败间隔翻倍,最大退避 5 分钟;成功后立即恢复 |
| CPU 采集 | 独立 goroutine 每 2 秒采样一次(读 /proc/stat 两次、间隔 100ms 算使用率),心跳时直接读缓存 |
每跳上报的 HeartbeatData 覆盖节点全貌:CPU 使用率 / 内存 / 核数;逐卡 gpu_stats(温度、利用率、显存、功耗——只在本轮真正采到时才携带,采不到不写 0,让服务端区分"真 0"与"未知");逐卡采集失败明细 gpu_collect_failures 与采集器整体错误 gpu_collector_error(掉卡探测的信号源,详见《GPU 检测》篇);Docker 可用性、执行模式、错误信息、MAC / 内网 IP;缓存模型与扫描发现的模型;任务健康报告与实测存活任务 ID live_task_ids(服务端据此发现"agent 健在但容器僵死"的停滞);GPU 拓扑标签;分片能力位 gpu_capabilities;Agent 版本三元组;可用推理引擎及版本。第一跳心跳即完成节点隐式注册。
任务执行器 Executor 全链路
Executor 是 Agent 的核心模块,Worker Pool 2~16 个 worker(并发上限 = CPU 核数,夹在 2–16),每 5 秒轮询本节点任务并走完整生命周期:
1. 领取:GET /api/v1/tasks?node_id={本节点}&status=scheduled&limit=20;
2. CAS 认领:PUT 把 scheduled → running,UPDATE 带 from_status 谓词;409 表示已被并发认领,任务留给下一轮;
3. 拉镜像:ensureImagePulled 任务态预算 3 次 × 5 分钟,本地已存在不占预算;私有仓库任务可携带拉取凭证,agent 用后即焚的临时 docker config 做 docker login(密码走 stdin,不落命令行参数);镜像引用支持 :tag 与 @sha256: 摘要的精确形式,带版本的引用逐字核对本地镜像;
4. 起容器:docker run -d --name task-{id}(刻意不带 --rm,休眠/唤醒复用同一容器);宿主机目录挂载先过卷前缀白名单/黑名单;任务 env 里不带 = 的裸名项一律丢弃(防止借 docker -e NAME 继承 agent 进程的环境变量——纵深防御);GPU 可见性变量(NVIDIA_VISIBLE_DEVICES 等)由 agent 独占,分片任务总是注入 agent 按分配结果算出的值;多卡推理与训练任务按需注入 --ipc=host / --shm-size,vLLM 类引擎在宿主 /dev/shm 不够时直接拒绝启动而不是起一个必死的容器;
5. 监护:HTTP/TCP 健康探针(推理服务)、超时看门狗(超时按 exit code 124)、每 5 秒轮询服务端状态感知停止/删除意图(连续 3 次 404 视为任务已删,主动停容器,不等僵尸清理兜底);
6. 终态上报:completeTask CAS running → completed/failed,带 exit code;网络错误 / 5xx / 408 / 429 做有限次退避重试(默认 6 次、1s 起退避封顶 10s、总墙钟 60s,参数可在配置文件调);重试安全的前提是服务端终态 CAS 本身带状态谓词——"写成功但响应丢了"之后的重试只会拿到 409 而不会覆盖结果;
7. 未送达保留容器:重试预算耗尽仍未送达时不删容器,把结论记在内存,等 60 秒一轮的对账看到"已退出容器 + 服务端 running"时 CAS-only 补报(容器日志从保留的容器现抓),补报成功才回收——堵死"任务真完成了却被对账误判 failed、输出随容器一起蒸发"的漏洞;
8. 容器回收:docker rm -f。
另有一条 60 秒对账循环兜底一切:对照本机 task-* 容器与服务端任务状态,收养 agent 重启窗口里的失联容器、补报未送达终态、把"服务端 running 但本机无容器"的任务 CAS 置 failed;下终态结论前先向服务端做权威确认(任务仍 running 且 node_id 仍指向本节点才动手),防节点失联期间任务被改派后的 ABA 误判。
GPU 检测(摘要)
Agent 内置 10 种 GPU 检测器,按优先级注册链遍历:MUSA(摩尔线程) → 海光 DCU → 寒武纪 → 壁仞 → 昇腾 → 昆仑 → 天数智芯 → NVIDIA → 沐曦 → 燧原(国产卡优先),全部落空时回落虚拟 GPU(共 11 种类型枚举)。8 种已完成检测适配,寒武纪 / 昆仑芯开发中;国产卡的解析逻辑对齐官方文档与输出夹具但无真机端到端验证[适配],NVIDIA 全链路真机验证[真机]。心跳每跳逐卡采集实时指标,单卡失败且能归因到具体卡时写入 gpu_collect_failures(服务端连续 3 拍确认后判 GPU_LOST 并把该卡移出分配)——逐家掉卡探测能力矩阵(NVIDIA 完整支持、昇腾/昆仑/寒武纪部分、位置匹配解析的厂商不支持)与功耗采集口径详见《GPU 检测》篇。分片能力位随心跳每跳上报:mig_capable 只在 nvidia-smi 明确报告 MIG mode Enabled 时为 true,mps_capable 只在 MPS daemon 当前确认存活时为 true——宁可少报不可假报,服务端据此推导 gold/silver/bronze 档位。
推理引擎探测
Agent 启动时探测节点上可用的推理引擎,之后每 20 次心跳重探一次(覆盖"先装 Agent 后拉镜像"的标准运维顺序),探测结果无条件回灌 Executor 的引擎路由器——路由器永不冻结在启动快照上,引擎中途上线/消失下一轮就生效:
| 引擎 | 标识 | 探测方式 |
|---|---|---|
| vLLM | vllm | NVIDIA CC ≥ 7.5 + 镜像 vllm/vllm-openai:latest 或 pip 安装 |
| vLLM(旧卡) | vllm-legacy | pip 安装 vLLM 且 CC < 7.5(P100 等老卡) |
| llama.cpp | llama-cpp | 镜像名含 llama.cpp/llama-cpp,或 pip 装 llama-cpp-python |
| MindIE | mindie | 昇腾节点上的 mindieservice_daemon 命令 |
| SGLang | sglang | 镜像名含 sglang(任意 tag),或 pip 安装 |
探测用裸 tag 精确匹配防私仓假阳性:模板镜像名必须在本地 docker images 里逐字存在才算可用——节点上只有 172.29.x.x:5000/lmsysorg/sglang:latest 这种私仓前缀副本时,不会误报"sglang 可用"导致控制面派发模板、运行时却去公网拉镜像起废容器。此外 agent 还会读运行中推理容器的 /v1/models API,把已加载模型并入 discovered_models 上报。引擎优先级可按模型格式在配置文件 engine_priorities 自定义。
模型扫描器
ModelScanner 启动时全量扫描、之后每 12 次心跳增量扫描一次模型目录,发现的模型随心跳上报(discovered_models)。默认扫描 /data/heteroflow/model-cache,配置 model_scan_dirs 是替换而非追加(/data/models 例外,始终额外扫描)。扫描细节与控制面的模型路径自动发现机制见《模型管理》篇。
分片指令执行
GPU 硬件分片是破坏性指令(nvidia-smi mig -cgi 重跑一次会多切一组分区,-dgi 重跑可能删掉别人的分片),因此这条通道的纪律与任务通道完全不同:
- 通道形态:同样是 agent 主动拉——
GET /api/v1/agent/slice-requests?node_id=X&status=pending拉取认领,PUT /api/v1/agent/slice-requests/{id}回报;5 秒轮询,单轮最多 5 条,单条指令 2 分钟执行时限; - 串行执行:所有分片指令在一个 goroutine 里排队执行(驱动侧不可重入,跨卡并发的几秒收益不值这个险);
- 执行前二次校验:控制面下发的参数 agent 再校验一遍(不信任控制面版本——旧 server 配新 agent、DB 被手工改过都可能送来畸形指令);
- 失败如实回报:批量创建"前几个成功、后一个失败"是真实形态,已建出的实例连同错误一起 PUT 回去,控制面才知道卡上留了几个孤儿实例要清理——静默丢弃等于制造查不出来的显存黑洞。
指令厂商当前两家:nvidia-mig(MIG 创建/销毁)与 hygon-hami(海光 DCU cgroup 限额分片,第一阶段执行层已通)。
镜像预热执行
运维态镜像操作通道(/api/v1/agent/image-requests),供平台管理员在任务到来之前把 10GB 级推理/训练镜像提前拉到节点。与任务态拉取是两套不共享常量、不共享代码的路径,边界清晰:
| 维度 | 任务态(ensureImagePulled) | 运维态(本通道) |
|---|---|---|
| 触发 | 任务启动 | 管理员主动下单 |
| 预算 | 3 次 × 5 分钟,超时判任务失败 | 2 小时(覆盖公网慢速拉大镜像),失败不关联任何任务 |
| 存活判据 | 任务超时体系 | 60 秒一次进度心跳,服务端 10 分钟没心跳才判僵死 |
| 并发 | 随任务并行 | 容量 1 的拉取闸彼此串行(防多个大镜像吃满带宽拖慢任务启动);任务态不经闸,任务启动永远不等运维拉取 |
镜像拉取幂等非破坏,失败就如实 PUT failed + docker 输出尾部;支持 check(本地是否已存在)与 pull 两类指令,引用支持 tag / digest 精确形式与 repository 前缀匹配两种模式。
checkpoint 续跑注入
声明了 checkpoint_path 但未显式填 resume_from_checkpoint 的训练任务,每次 docker run 前由 agent 在该任务的 /checkpoint 卷对应宿主机目录里探测最新完整 checkpoint:显式填写优先(不探测);探测路径过与卷挂载同一份白名单围栏、目录内不跟随符号链接;完整性按框架规则判定(Megatron 记录文件 / DeepSpeed latest + 分片计数 / TF 状态文件 / DCP .metadata / 平台完成标记),无完成证据一律不选;找到则注入 HETEROFLOW_RESUME_FROM_CHECKPOINT,找不到照常从头训练,结论上报服务端写容错事件。节点断电、agent 升级、任务迁移重排后的自动续跑都靠这条路径。
僵尸容器清理
每次心跳成功后自动执行。设计从早期的"活跃状态白名单"整体改为终态黑名单:只有明确查到任务已终态(completed/failed/cancelled/stopped)、或任务在服务端已彻底查不到,才判僵尸;其余任何状态一律不碰——状态机未来新增任何中间态都不会再被误杀(白名单时代真实复现过两次误删事故)。单任务确认有三条判据,全部是服务端明确告知的事实而非猜测:查任务 404(已删除);恰已翻转为终态;归属已转移(响应里 node_id 非空且不是本机——节点失联期间任务被改派到别的节点真实运行,本机残留旧容器必须清)。安全措施:跳过最近 60 秒内创建的容器(可能正在启动)、容器名正则验证防注入、docker 命令带超时、查询/解析失败一律按"不是僵尸"处理——宁漏勿错。
优雅关闭
收到 SIGINT/SIGTERM 后:停心跳通道并等心跳循环真正退出 → 停止 Executor → 发最后一跳心跳标记节点离线 → 退出。任务容器刻意不停(by design):Agent 发版/升级的窗口里,正在运行的客户任务必须原样活着,新 Agent 启动后由启动对账接管——运行中的继续监护、已退出的补报终态并清理。这也是"升级不掉业务"的关键一环:agent 自身可以随时重启,客户容器跨越 agent 生命周期存活。
版本机制
| 字段 | 说明 |
|---|---|
agent_version | 语义版本,单一事实来源是 agent/VERSION |
agent_git_commit | git 短哈希;工作树有未提交改动时带 -dirty 后缀 |
agent_build_time | agent/ 目录最后一次改动的 commit 时间(不是编译时刻)——可复现构建:同一份源码任何机器任何时刻编出的二进制 md5 一致 |
三元组由 agent/build.sh 在编译期 -ldflags -X 注入,裸 go build 的产物自曝 dev/unknown,来路不明的构建一眼可辨。每次心跳上报三元组,服务端接住落库(nodes 表),版本变化记日志留痕;前端节点卡片展示版本指纹,-dirty 构建会被显式标警(dirty 二进制无法从 commit 还原源码,是交付合规的硬伤),并做集群版本一致性比对。升级通道就是部署一节的 update-agent-quick.sh / update-agent-force.sh(服务端直接下发,force 用于集群推平);二进制三处分发路径(server GET /static/agent、前端源码副本、前端静态服务)强制保持同一 md5,并有守卫测试钉住 arm64 三处一致性。
WSL2 兜底支持
检测器统一通过 findCommand 定位厂商管理工具——它是 exec.LookPath 的严格超集:先试固定路径(/usr/bin、/opt/<tool>/bin 等),再查按命令名的兜底路径表(nvidia-smi 追加 /usr/lib/wsl/lib/nvidia-smi 与 /opt/cuda/bin/nvidia-smi),最后才走 PATH。背景是真实踩坑:agent 以 root 跑在 systemd 下时 PATH 往往不含 /usr/lib/wsl/lib(WSL2 GPU 半虚拟化驱动的固定安装位置),裸 LookPath 会让 HasGPU 误判"无 GPU",RTX 4060 被报成虚拟 GPU。兜底表 + 部署脚本的 systemd PATH 注入双重修复,WSL 真机节点已长期在线运行[真机]。
Agent 与控制面交互全景
一次任务从领取到终态的完整交互序列:
调度器 Agent(Executor / 心跳) Docker / 硬件
│ 把任务置 scheduled 并写 node_id │ │
│◄─────────── 5s 心跳(gpu_stats / live_task_ids / 引擎…)────┤
│ │ │
│ ① GET /tasks?node_id=X&status=scheduled&limit=20(5s 轮询) │
│ ② PUT /tasks/{id} CAS scheduled→running(409=被抢,放弃) │
│ │ ③ 训练任务:checkpoint 完整性探测│
│ ④ (可选)POST checkpoint-resume 上报续跑结论 │
│ │ ⑤ ensureImagePulled(3×5min, │←docker login
│ │ 私仓凭证走 stdin) │←docker pull
│ │ ⑥ 构建参数:卷白名单/env 独占/ │
│ │ GPU 可见性/shm/MPS 管道 │
│ │ ⑦ docker run -d task-{id} ────→│
│ │ ⑧ 监护:健康探针+超时看门狗+ │
│ │ 每 5s GET /tasks/{id} 感知 │
│ │ 停止意图(404×3=已删除) │
│ ⑨ 心跳并行上报 task_health / live_task_ids(每 5s) │
│ │ ⑩ 容器退出 │←exit code
│ ⑪ PUT /tasks/{id} CAS running→completed/failed(有限重试) │
│ └ 未送达:保留容器,60s 对账 CAS-only 补报后再回收 ─────│→docker rm -f
│ ⑫ 对账循环(60s):收养失联容器 / 补报 / 反向核对 failed │四条"agent 主动拉"指令通道(任务 / 文件 / 分片 / 镜像)共享同一形态:GET …?node_id=X&status=pending 认领、PUT /{id} 回报,5 秒轮询——控制面对 agent 永远零主动连接。
页面功能介绍(Agent 在前端的可见面)
Agent 没有独立页面,它的信息分布在四个页面的真实组件里:
| 页面 / 组件 | Agent 可见面 | 前端源码 |
|---|---|---|
| 节点管理(NodeList 卡片) | Agent 版本指纹行:版本 (commit) 日期 三元组;dirty 构建带 ⚠ 显式警示(无法从 commit 还原源码的合规问题不藏在正常文本里);旧版 Agent 未上报时灰色斜体标注;悬浮提示注明 build_time 是"agent/ 最后改动的提交时间"而非编译时刻 | web/src/components/nodes/NodeList.tsx(Row 2.5)、agentFingerprint.ts |
| 节点管理(同上) | 集群 Agent 版本一致性比对(在线且上报了 commit 的节点参与,dirty / 版本不一致在卡片区顶部汇总提示) | NodeList.tsx + agentFingerprint.ts(computeClusterConsistency) |
| 节点管理(同上) | 在线 / 离线分组(节点状态由心跳驱动);推理引擎标签(Agent 探测结果 inference_engines + 版本,按引擎着色);GPU 状态条上"N 张判定丢失"计数(源自 Agent 逐卡采集失败信号,服务端连续 3 拍确认 GPU_LOST 后展示);CPU / 内存 / 显存 / 利用率 / 任务统计均为心跳数据 | NodeList.tsx(引擎标签、GPU 状态条、在线分组) |
| 节点管理(空状态) | "节点由 Agent 自动注册,无需手动添加"——首次使用的指引即隐式注册的入口说明 | NodeList.tsx(nl__empty) |
| 环境检测页(EnvironmentPage,仅平台管理员) | 运维态镜像通道的前端:对在线节点下发镜像检测(Agent 5 秒一轮认领、秒级回结果)、关注镜像清单按来源分组(推理引擎自动纳入)、检测结果展示本地已有版本;缺失镜像可一键"部署"——后台拉取 + 进度轮询,就是 image-requests 通道的 pull;离线节点明确标注"无法检测"且不下发指令 | web/src/pages/EnvironmentPage.tsx |
| 训练详情页(TrainingPage) | Agent 文件通道的前端入口:任务组完成后对 checkpoint 目录发起一次 list 文件请求,展示 Agent 真实探测到的产出文件清单——与用户手填的 checkpoint_path 配置意图分开展示,"写了什么"和"实际产出什么"不互相冒充 | web/src/pages/TrainingPage.tsx(fileRequestApi) |
| 任务详情(TaskPage 容错事件时间线) | 节点失联迁移 / 容器丢失重排 / 抢占 / GPU 卡丢失等容错事件——事件源头多为 Agent 上报链路(对账、逐卡采集失败、checkpoint 续跑结论) | web/src/pages/TaskPage.tsx |
| GPU 健康页(GPUHealthPage) | ECC 等指标的"未知"语义注明("没采到可能是厂商工具无此项、ECC 关闭或 Agent 版本过旧,不代表为 0")——Agent 诚实采集口径在前端的直接体现 | web/src/pages/GPUHealthPage.tsx |
前端没有 Agent 部署对话框或脚本下载按钮——部署脚本由 Server / 前端静态服务按 URL 直接下发(见"一键部署全家桶"一节),页面上只有空状态指引文字;任务容器日志的实时查看入口当前也不在前端。Agent 的其余运行细节(对账、终态补报、分片指令执行、僵尸清理)仅通过 API 与服务端日志可见。
后端逻辑概念表
| 概念 | 含义 |
|---|---|
| Agent 零监听 | 节点不开任何入站端口,所有指令由 agent 以 HTTP client 主动拉取;客户 GPU 机器零入站攻击面 |
| 节点作用域 | Agent 系统密钥的取数边界:必须带 node_id 按节点取,禁止全量枚举、禁止建任务——防凭据失窃后横向移动 |
| CAS 认领 | scheduled→running 的 UPDATE 带 from_status 谓词,多 agent 并发只有一个成功(409 其余);终态上报同构,"写成功响应丢了"后重试只会 409 不会覆盖 |
| 隐式注册 | 首跳心跳自动建节点记录,无需控制面预登记 |
| 能力位 | mig_capable / mps_capable 随心跳每跳上报,只在能力当前真实成立时为 true(MIG Enabled / daemon 存活),服务端推导 gold/silver/bronze |
| 裸 tag 匹配 | 推理引擎可用性判定:模板镜像名必须在本地逐字存在,私仓前缀副本不算——防"上报可用、运行时去公网拉镜像"的假阳性 |
| 未送达保留容器 | 终态上报重试耗尽后不删容器、结论记内存,由对账补报成功后才回收——防真完成被误判 failed、输出蒸发 |
| 终态黑名单 | 僵尸清理只认"确认终态/已删除/归属已转移"三类明确事实,其余一律不碰——宁漏勿错 |
| 权威确认 | agent 下终态结论前先向服务端确认任务仍归本节点 running,防失联期间任务被改派后的 ABA 误判 |
| 启动对账 | 新 agent 启动时接管升级窗口里的存量容器:运行中的继续监护、已退出的补报终态 |
| 可复现构建 | build_time 取 agent/ 最后改动的 commit 时间而非编译时刻,同一源码编译产物 md5 一致;dirty 构建显式标警 |
API 参考(agent 侧端点)
Agent 全部使用 X-API-Key 头;所有端点在 Server :3333 上:
| 端点 | 方法 | 用途 |
|---|---|---|
/api/v1/nodes/{nodeID}/heartbeat | POST | 心跳上报(首跳隐式注册) |
/api/v1/tasks?node_id=&status=scheduled&limit=20 | GET | 领取本节点任务(强制带 node_id+status) |
/api/v1/tasks/{id} | PUT / GET | CAS 认领与终态上报 / 停止意图轮询 |
/api/v1/agent/tasks/{id}/checkpoint-resume?node_id= | POST | 续跑探测结论上报(best-effort) |
/api/v1/agent/slice-requests[/{id}]?node_id= | GET / PUT | 分片指令拉取认领 / 回报 |
/api/v1/agent/image-requests[/{id}]?node_id= | GET / PUT | 镜像预热拉取认领 / 进度与结果回报 |
/api/v1/agent/file-requests[/{id}]?node_id= | GET / PUT / POST | 文件通道:checkpoint 列表 / 分块上传 |
/static/agent[?arch=arm64] | GET | Agent 二进制分发(arm64 缺产物回 404+构建指引) |
/install-agent.sh 等 4 脚本 | GET | 安装 / 升级脚本下发 |
已知边界
1. 任务只有 Docker 一条执行路径:心跳里的 exec_mode: docker|baremetal 只是能力上报(Docker 不可用时 agent 标记 baremetal 并如实上报 docker_available=false),Executor 没有裸金属执行分支,Docker 不可用的节点不会被派发容器任务。
2. 国产卡检测与功耗解析无真机端到端验证[适配]:解析逻辑对齐官方文档与输出夹具;昇腾功耗字段因无可信样例明确不覆盖;掉卡探测矩阵仅 NVIDIA 完整支持,其余按位置匹配解析的厂商不支持,且全部未经真机掉卡验证。
3. arm64 产物无真机运行验证:交叉编译产物经 ELF 头校验与守卫测试钉住三处一致性,但尚无 ARM 真机在线运行。
4. 终态补报的内存边界:未送达结论只存内存(上限 256 条),agent 重启后退化为按容器退出码推导;任务超时被看门狗停掉且退出码会误导推导的情形,宁可丢输出也不补报成 completed。
5. 增量上报协议位已铺未启:models_unchanged 等增量标记字段已在心跳协议里,当前仍每跳携带完整数据,待服务端配合后才启用裁剪。
6. x86 三处分发路径 md5 一致仍靠人工核对(arm64 已有守卫测试自动钉住)。
7. 僵尸清理宁漏勿错:拿不准的容器一律保留,极端情况下可能残留需人工介入;这是刻意取舍而非缺陷。
<!-- 状态标注说明:本文能力状态口径——"真机验证"指在真实 GPU 节点端到端运行并确认结果(GPU 检测:NVIDIA 全链路含 WSL2 RTX 4060;MIG 分片指令:2026-09-02 A800-SXM4-80GB 全套矩阵);"生产生效"指代码合入、有测试钉住并在开发环境长期运行;"[适配]"指国产卡检测/解析逻辑按厂商官方文档与输出夹具对齐但无真机端到端验证,不构成已交付承诺。GPU 检测器细节、掉卡探测能力矩阵与功耗采集口径以《GPU 检测》篇为准;模型扫描细节以《模型管理》篇为准;分片调度侧(账本/准入/QoS 推导)以《GPU 显存分片》篇为准。 -->