1. 点下“开始训练”以后,接口应该什么时候返回
在LeRobot 平台里,创建训练任务看起来也是一个表单:选择数据,填写参数,点击提交。
但它和新增一条普通记录有个明显区别:后面的计算可能持续很久。
一个容易想到的写法,是在 Controller 里启动 Python,等待进程退出,再返回“完成”。如果只为了理解命令怎样被启动,它很直观;放到页面请求里,就会出现几个难题。
浏览器连接断开,进程是否继续?反向代理超时后,用户再次点击会不会启动第二次训练?Java 服务更新重启,页面还能找到之前的进程吗?
这些问题让我更愿意把一次点击拆成两件事:先受理一个持久任务,再由后台执行这个任务。
2. 返回任务编号,比一直等着更清楚
下面是一个简化的接口约定:
POST /training-jobs
Content-Type: application/json
Idempotency-Key: orange-training-request-1
{
"datasetVersionId": 12,
"recipeVersionId": 3,
"baseModelVersionId": 5
}服务确认权限和输入后,把任务持久保存为排队中,再返回:
HTTP/1.1 202 Accepted
Location: /training-jobs/231
{
"id": 231,
"status": "QUEUED"
}202 表示受理,不是训练完成,也不保证此时已经分配到 GPU。页面根据编号查询进度;用户关闭页面,任务身份也不会随连接消失。
为了避免响应丢失后重试创建出两个任务,同一次提交应复用请求键。后端在项目和用户范围内,通过唯一约束保存请求键与规范化后的参数摘要,并与任务创建一同提交。同键同参数返回原任务,同键不同参数拒绝;用户明确要再训练一次时,才使用新的键。
请求里的编号属于平台示例,不是 LingBot-VLA 自带的 HTTP API。后端需要检查这些版本属于允许访问的项目、数据已准备好,并且训练配方与所选模型和数据相容。
表单不直接接受一整段任意 Shell 命令。配方可以由服务端维护固定入口、允许的参数和环境,后台再将业务请求转换成执行配置。
3. Java 管任务,执行器管进程
可以先用这样的职责划分:
浏览器
↓ 创建 / 查询 / 申请取消
Spring Boot
├─ MySQL:任务、执行次数、输入和结果
└─ 后台调度:领取已持久保存的排队任务
↓
Python Worker
├─ 加载数据与模型
├─ 运行训练进程
└─ 上报进度、日志和产物小型平台可以先轮询数据库里的排队任务,不一定马上引入消息队列。关键是领取需要原子操作,多个后台实例不能同时把同一任务当成自己的新任务。
如果以后改成消息队列,也仍然要处理数据库已经保存、消息却没有成功发送的窗口,以及消息重复投递。多了一个组件,不代表任务生命周期自然可靠了。
我更关心这几个边界能否说清楚:谁确认受理,谁取得执行权,谁真正运行,谁有资格报告最终结果。
Java 当然可以启动 Python 进程。需要分离的是 HTTP 请求与长时间计算的生命周期,而不是禁止两个语言在一台机器上合作。
4. LingBot-VLA 让“训练参数”变得具体
对照 LingBot-VLA 提交 4eb34b7 的训练配置说明,一次后训练会涉及基础模型、数据路径、机器人配置、归一化统计、训练步数和检查点保存等信息。
这些内容不能只藏在 Worker 上一个随时被修改的 YAML 文件里。任务受理时,应把将要使用的配置固定下来:
| 输入 | 需要回答的问题 |
|---|---|
| 数据集版本 | 具体读哪些数据,内容是否固定? |
| 模型版本 | 从哪份预训练权重开始? |
| 训练配方 | 使用哪个代码版本、入口和环境? |
| 机器人配置 | 如何解释状态、动作与相机字段? |
| 统计文件 | 归一化对应哪份数据和处理配置? |
这里的配方可以包含 Python、PyTorch、CUDA 和相关依赖的固定环境描述。安装了 LeRobot,不等于任意 LingBot-VLA 版本和权重都能直接运行,仍需按对应仓库的安装与配置要求准备。
权重文件大小也不等于训练显存需求。运行时还涉及激活、梯度、优化器以及所选精度、批量大小等因素。因此,页面不能根据下载大小就承诺某张显卡一定能完成训练。
5. 排队、运行、成功,每一步由什么事实决定
AWS Batch 的任务状态区分了提交、准备、可调度、启动、运行与最终结果。它提醒我,点击按钮和进程开始之间还有一段过程。
对于这里的小平台,可以先对外提供更少的状态:
QUEUED → RUNNING → SUCCEEDED
└→ FAILED排队表示请求已经保存;运行表示执行器确认进程已经启动。成功则至少要求进程正常结束、预期产物写完并通过必要的完整性检查。一个日志里出现过 success 单词,不是可靠的完成信号。
还应分别记录提交时间、开始时间和结束时间。提交到结束包含排队,开始到结束才是执行区间。只有这些时间和资源信息都有来源,训练耗时才有可比较的含义。
没有可信上报时,页面可以显示“执行器连接中断”。不要因为几次心跳没有到,就立刻把任务改成已失败并重启;原进程可能还在占用资源运行。
6. 重试一次,不要覆盖上一次
任务 #231 第一次因为数据路径不可读而停止。修正环境以后再次运行,第二次和第一次仍然是两次执行。
因此可以在任务下面保存执行记录:
TrainingJob #231:固定输入与请求
├─ Attempt #1:启动时间、资源、失败原因、日志
└─ Attempt #2:启动时间、资源、运行状态、产物每次执行使用独立的输出目录。Worker 上报时需要携带任务编号和执行编号,通过受信身份认证;平台只允许当前有效执行推进状态,拒绝旧执行迟到的完成消息。
同一执行的重复消息也应去重,并限制状态前进方向。已经成功的执行,不能因为晚到的“运行中”消息又退回运行中。
如果领取者在启动进程后失联,需要先通过执行记录、运行时标识和资源侧查询确认原任务是否仍存在。租约到期只说明联系中断,不足以证明旧进程已经停止。
这也是重试不能无限自动进行的原因。路径写错、配置不相容等问题,重新运行相同输入往往还会失败,应该把原因交给用户处理。
7. 取消和恢复,都需要执行器确认
用户点击取消,只代表平台收到了停止意图。排队任务可以在尚未被领取时原子取消;运行中的任务则需要通知执行器,并等待进程或运行时确认结束。
可以在界面上显示“正在取消”。如果完成和取消同时发生,平台要按实际执行结果以及明确的状态规则落库,不能仅因按钮先返回就声称 GPU 已经释放。
恢复训练也不是重新执行同一条命令。训练检查点可能需要包含模型之外的优化器、调度器等状态,并与代码及配置兼容。LeRobot 的训练说明也把从检查点恢复作为单独操作。
Worker 重启只恢复一个进程环境。它能否接着上一次的步数继续,需要训练程序自身支持,并且保留可用的检查点。
8. 生成了模型文件,还要知道它能做什么
LingBot-VLA 的仓库说明要求推理模型目录包含权重及配置,并提供单独的开环评估入口。因此,平台登记模型时,不应只保存一个模糊的 model_path。
可以保存产物清单、内容摘要、来源任务和执行编号,再由评估记录关联具体检查结果。
此外,它的开环评估脚本在没有指定数据路径时,会使用配置中的训练数据。想判断未见数据上的表现,就要明确传入独立验证数据,并核对实际使用范围。
训练成功、开环结果合理和真机完成任务,是不同的结论。记录里应该分别表达;不能因为 Worker 退出码为零,就让模型页显示“机械臂部署验证通过”。
9. 下一步,我更想补齐中断后的过程
把长任务移出请求线程以后,真正值得继续追的是:进程还在不在,当前由谁负责,日志和检查点保存到哪里,以及失联之后怎样重新确认结果。@Async 可以改变调用在哪个线程执行,但不会单独回答这些问题。
因此,我会先把任务记录管理和实际执行分别列清楚,再逐项补齐启动、取消与恢复的链路。只有它们能对应起来,页面上的状态才足以帮助用户判断下一步该等待、重试,还是先处理输入和环境问题。
相关阅读
- 从 AI 数据平台到 LeRobot:我对 Java 后端开发理解的变化
- 机械臂训练之前,数据到底长什么样?
- 机器人训练数据怎样送到 GPU:Kubernetes、JuiceFS 与 Lance 的分工
- 模型推理服务怎样运行在 Kubernetes:版本、KServe、灰度与回滚