1. Controller 后面,应该先想到什么
刚做 Java 后端时,分层是一个很有吸引力的起点。一个接口经过 Controller、Service、Mapper,最后落到数据库,结构清楚,也容易照着写。
但当我围绕AI 数据标注平台和LeRobot 数据平台梳理业务时,越来越在意一个问题:Service 里的逻辑究竟从哪里来?
如果只是把请求字段复制进 Entity,很多功能确实能很快写出来。可是一旦问起“谁能修改”“什么时候不能修改”“修改以后历史怎么办”,就必须回到业务本身。
这两个项目给我的启发,主要就在这些看起来很小的问题里。
2. 第一条主线:一份标签怎样走到交付
标注平台最容易让人想到图片和标签。但跟着一张图片往后走,很快就会遇到任务分配、提交、审核、返工和导出。
图片 → 标注任务 → 一次提交 → 审核结果 → 导出批次这里最关键的区别,是图片、任务和提交结果不应该混成同一个东西。
图片可以一直不变,任务可以经历多次返工,提交结果则需要保留每次具体内容。否则,今天修改标签以后,昨天的审核意见就不知道在说什么。
AWS 的标注工作流文章将初始标注与后续质检组织成流程。把这个问题缩小到一张图片,我才更容易理解:后端表结构是在为人与人之间的协作保存上下文。
以前看到一个审核按钮,我容易先想到增加接口。现在会先想:审核的是哪份提交?这个人是否有项目权限?自己的提交能不能自己通过?别人已经处理以后,还能不能继续写?
这些问题说清楚,接口参数和 SQL 条件才有依据。
3. 状态、权限和并发,开始连在一起
状态机、角色权限、乐观锁,分开来看是三个概念,也很容易各自当成独立知识点。放在审核这件事里,它们对应的是三个连续问题。
| 问题 | 对应约束 |
|---|---|
| 现在是否允许审核? | 任务状态与当前提交 |
| 这个人是否可以审核? | 项目权限与提交人限制 |
| 写入时前提还成立吗? | 条件更新和事务 |
如果只在页面上隐藏按钮,就没有保护后端;如果只查角色,没有看任务状态,就可能跳过流程;如果先查后改却没有写入条件,两个审核请求又可能互相覆盖。
它们不需要一开始就变成复杂的框架。一个明确的业务动作、一条带条件的更新、一组一起提交的记录,就已经包含值得认真解释的内容。
我也因此更能接受接口返回失败。并发时拒绝一个过时的审核请求,可能恰恰说明系统守住了规则。关键是说明发生了什么,让用户看到当前结果,而不是一律显示操作成功。
4. 第二条主线:一次动作怎样进入模型
到了 LeRobot 场景,数据从一张图片变成了一段连续交互。
设备配置 → 采集任务 → Episode → 数据集版本
↓
训练记录 → 模型 → 评估变化不只是多了几张表。Episode 里的图像、状态和动作要按时间解释;数据集版本要说明选中了哪些内容;训练还要知道如何理解各个字段。
对照 LingBot-VLA 的数据指南,机器人特征映射和归一化统计也是后训练输入的一部分。这让我对“数据准备完成”多了一层认识:文件可以打开,不代表它已经满足某个模型的输入约定。
例如,两组动作数组长度相同,但关节顺序或单位不同。数据库字段都是 JSON,也不能说明它们具有相同含义。
因此,元信息需要让程序找到数据,还需要让人解释数据。很多问题会表现为某个字段不匹配,往前追却可能来自一次相机配置或采集方式变化。
5. 版本的价值,是让历史还能被解释
标注平台里,审核需要关联不可变的提交。LeRobot 平台里,训练需要关联固定的数据集版本。两者其实在回答一个相似问题:这次判断或计算,到底使用了哪份内容?
标注场景:审核 #802 → 提交 #502
训练场景:模型 #7 → 训练 #231 → 数据集版本 #12如果训练只关联一个不断变化的目录,下次打开同一路径,文件可能已经被替换。加一个名为 version 的字段,也不能阻止这种变化。
我更愿意把版本理解成一个可以再次定位的内容集合:明确成员、固定文件或提交、保留必要配置,并且能读回它们。
平台查询可以从模型编号开始,逐步展示这些关联。下面的类型只是用来说明页面需要什么入口:
public record ModelOrigin(
long modelVersionId,
long trainingJobId,
long attemptId,
long datasetVersionId,
long recipeVersionId
) {}这里的编号必须指向真实保留的记录。只有一串关联编号,底下文件早已删除,仍然无法回答数据具体是什么。
可追溯也不等于自动找到原因。如果模型表现下降,这条关系能帮助确定检查范围;究竟是数据、配置还是模型本身的问题,还需要继续分析。
6. Java 平台和模型计算之间,需要有清楚的接口
另一个变化,是我不再把“页面上有训练按钮”和“系统能够可靠运行训练”当成同一件事。
新增训练记录,是数据库操作。启动训练,则涉及环境、进程、算力、长时间运行和结果产物。
LingBot-VLA 的训练配置包含模型、数据和运行参数。Java 平台可以负责固定这些输入,执行器再按约定运行并报告结果。
Java 平台:谁提交、使用哪份输入、当前进度、历史结果
↕
执行器:启动进程、实际计算、日志、检查点和产物这种分工不要求一开始就部署很多服务。它首先要求接口能讲清楚:任务是否只是排队,执行器是否真的启动,失败是哪一步,重试属于哪次执行。
服务重启以后,还能找到任务记录,只代表记录没有丢失。能否恢复计算,还要看执行器和训练检查点。平台状态与实际进程,需要持续核对,不能互相代替。
7. 有些“成功”,我会先问成功了哪一步
这两个项目里,同一个成功提示可能指很多不同的事情:
| 已经发生的事 | 还不能直接推出的结论 |
|---|---|
| 标签保存成功 | 标签正确,可以交付 |
| 文件上传成功 | 数据完整,满足训练要求 |
| 训练请求受理成功 | 已分配资源并开始计算 |
| 训练进程正常结束 | 模型适合部署到实际设备 |
| 开环评估输出合理 | 真机可以稳定完成任务 |
这张表让我意识到,返回码和业务结果之间还需要解释。
例如,看板缓存可以晚一点刷新,但审核判断不能跟着使用旧数据;训练日志可以显示进度,但最终结果需要对应执行记录和产物;评估数字需要说明数据范围与评价方式,否则不同结果不一定能比较。
对这些项目来说,把这些边界说清楚,比给每一步都贴上“高性能”“高可用”的标签更有用。能准确说明当前做到哪一步,下一步要解决什么,才便于继续往下做。
8. 我想继续补上的能力
这些问题没有让我离开 Java 后端,反而让我更清楚后面想学什么。
一方面,需要把普通后端基础学扎实:事务为什么这样划分,SQL 条件怎样保护规则,权限怎样限定范围,缓存失败以后会发生什么。
另一方面,需要看懂数据和计算的输入输出。至少能回答一段 Episode 表达什么,一个模型需要哪些配置,为什么训练任务要保留执行历史,以及评估记录怎样关联回来源。
如果继续完善项目,我会优先补齐一条可连续追踪的流程:从一份固定数据,到一次有明确执行归属的任务,再到一份能够读取和解释的结果。把这条路走清楚,再决定是否需要更复杂的调度和多机能力。
我希望继续做 Java Backend,也希望逐渐能承担 AI 数据平台和训练管理中的后端工作。这要求我既会写接口,也能解释接口背后的数据与规则。
相关阅读
- 从 CRUD 到 AI 数据平台:一条标注数据如何走到交付
- 机械臂训练之前,数据到底长什么样?
- 我终于理解了 LeRobot 里的 Episode 是什么
- 为什么训练任务不应该直接塞进 Spring Boot 请求里