周六那个晚上,是我来江苏以来最快乐的一天晚上,那一天我们寝室三个人有一搭没一搭的聊着,从游戏、公司制度、出差聊到薪资、以后、发展,来到这边第一次这么放松的跟人聊天。很开心,周五练完胸之后,周六去做了有氧,周末就休息了一天。
不要去预想没有发生过的事,这个事会不会让我被裁?会不会让我感到尴尬?会不会挂科?会不会让我丢掉实习?会不会回不了家?会不会被坏人。。。。。。担心这些没有用,不如把精力放在那些我们能改变的事情上,比如现在Ai发展迅猛,Ai会不会把我替代掉?你不能把控Ai的趋势,能把控的只有自己,多学习AI技术。至于会不会挂科,每节课找好代课,至于挂不挂科,那是老师的事,我们能做的只有把到签了,把作业做了就行。
静姐没让我做标注工作之后我就可以专心在webai-lite上了,今天上午一直在排查lite与推理客户端的联调,主要是配置发送给客户端的json文件的格式,有些事错的,之前mentor在抄原webai的时候没抄对。现在把烂摊子丢给我让我来调。
调试过程总结
阶段一:加密狗无权限
用户打开程序时遇到 “加密狗无权限!” 弹窗。
排查过程:
阅读 main.cpp:112-137 的启动校验逻辑
程序启动时走两条路径:先尝试 VirBox(深思软许可),失败后降级到 HASP 物理加密狗,两条都失败才弹这个框
结论:需要插入 USB 加密狗或安装 VirBox 许可文件
阶段二:sample.zip 标注 JSON 字段为空导致训练崩溃
用户提供 webai-lite.json,训练报错:
1 | config_utils.py:19 load_config |
排查过程:
分析 annotation JSON 结构,发现 tags: []、sampleImage_id: “”、uuid: “”、attributes 缺失等多处字段为空
用户修复后仍然报同样的 IndexError
阶段三:误判 config.zip 中的 null 值
用户把 config.zip 解压出来的 fsnet_detect_train.json 放到当前目录。
排查过程:
发现 parameters.inputs[2].value 是 null,推测修复它即可
用户将 null 改为 0 后重新训练,依然报同样的 IndexError
阶段四:追溯 pyd 源码找到真因
用户提供了新任务的完整目录 4491f9c7-…,其中 code/build_temp/ 下有 Cython 生成的 .c 中间文件。
关键突破:在 config_utils.c:3436 找到第 19 行的原始逻辑:
1 | config_dict[p['param_name']] = p['realVaule'].split('|')[int(s['value'])] |
代码把 s[‘value’] 当作数组索引,去取 realVaule.split(‘|’) 的元素。但服务端下发的 config 中传的是实际值而非索引:
参数 realVaule 传的 value 用作索引
image_channel [1, 3] 3 索引3 → ❌ 越界
image_shape [320,640,1024,1280,1536,2048] 640 索引640 → ❌ 越界,第 1 轮循环就崩溃了。
根因结论
服务端生成 config.zip 时的 bug:combox 类型参数应该传的是下拉选项的索引值(0/1/2…),但实际传了选项本身的真实值。
知识点
config_utils.pyd 是 Cython 编译产物,无法直接读源码
但 build_temp/ 目录下的 .c 中间文件保留了行号映射,可以用来反推 Python 逻辑,这是排查 pyd 内部问题的有效手段
中午吃饭的时候和mentor聊天说到我要同时打开三个vscode窗口,一个原webai,一个webai-lite,一个webai-device导致内存不够,电脑很卡。mentor和我说你可以在vscode中添加工作区。我现在才知道有这个东西,还是要和前辈交流啊,能学到很多东西的~
总之,很大一部分就是json格式的问题,解决完之后,还有一个问题就是样本太少,但是训练代码中规定样本数量必须大于3000张,这个都是训练时候的报错了,这个不归我管了,那么就是说,训练的整个最小流程是跑通了。
接下来就是跑推理的流程,杰哥先叫我去webai的测试环境那边去下了一个实例分割模型,放在lite本应该训练出来模型放置的地方,假装已经训练出一个模型了。然后开始跑推理流程。最开心的是,好像居然一次就跑通了,推理结果也回传了。但是客户端与服务器的训练和推理进度的端口没有打通,所以看不到进度,但是推理没有报错,结果全部回传。现在的任务就是把进度的端口再调一下。
端口差不多调好之后,mentor让我去会议室,说是leader要一起看一下mentor在做的agent和我在做的webai-lite进度。会上mentor的agent基本上都跑通了。可以多轮训练,评估,生成报告,很专业。我展示的时候就说现在整个训练推理流程基本跑通,但是因为样本数量问题所以客户端训练失败,但这都是训练内部问题了。接下来我本来还想展示推理流程,leader说他要看的是整个业务的流程,图像,mask标注,训练推理结果都保存在哪里?他不关心这些细节,还说推理失败这个问题可以往后放,我们先把多工作区做好,然后问我多久能完成,我说这个星期我抓紧。也就是说这周五必须完成多工作区。之后我就动用了禁忌力量:“Codex”帮我写PRD在最后。
leader还说样本不用3000张,随便多少都可以跑的,这个只能等多工作区搞好了以后再看了~
哟呵~今天来了一位新同事,看起来不像应届生,也不像实习,像是社招进来的大哥。
今日工作内容
- 跑通webai-lite整个训练,推理流程
- 整理webai-lite实现多工作区PRD
下阶段计划
- 梳理webai-lite文件结构,新增多工作区
- 规范webai-lite项目文件
多工作区数据隔离改造PRD
Goal
将 webai-lite 从“单工作区 _workspace/current”改造为支持多个工作区。每个工作区需要独立保存样本集、图片、标注、训练结果和推理结果,避免不同项目或场景之间的数据、job 和产物互相覆盖。同时必须兼容已经部署好的 D:\webai2.0-device 训练推理客户端,不能要求修改、重新编译或重新部署客户端。
What I Already Know
- 当前后端的工作区服务是单例函数式访问:
services/workspace.py:current_dir()固定返回settings.workspace_root / "current"。 - 当前数据目录集中在
_workspace/current/:images/保存图片。annotations/保存标注 JSON。masks/保存 sidecar mask PNG。jobs/保存训练/推理 job JSON。models_parameter/、flows_parameter/保存当前现场覆盖配置。dataset.yaml、deployment.yaml位于 current 根目录。
- 当前产物目录是全局的
settings.artifacts_root / "latest",训练模型写到latest/model,推理结果写到latest/result。 train.py/infer.py提交 job 时没有写入workspace_id,幂等检查也只按当前全局 job 列表扫描。jobs.py只读写current/jobs。device_submit_bridge.py和device_task_bundle.py会用全局 current 工作区导出 COCO 样本包,并把完成后的模型/推理结果搬到全局 artifacts。catalog.py的现场覆盖、deployment.yaml过滤规则也绑定全局 current。- 前端 Zustand 目前只保存
images/classes/currentImageId,没有workspace_id。 - 前端训练/推理的 last job localStorage key 只按
training/inference区分,未按工作区隔离。 - 外部训练推理客户端位于
D:\webai2.0-device,是已部署的 Qt/C++ 服务,不能修改。 - 该客户端不直接拉取 webai-lite 的
/api/jobs。webai-lite 内嵌device_manager通过现有 TCP 协议向客户端发送start_training/start_inference。 - 设备客户端只识别 36 位设备任务 UUID,并按
<win_root_path>/training/<uuid>、<win_root_path>/inference/<uuid>隔离本地任务目录。 - 设备客户端推理完成后把
res/压缩为result.zip,再通过training_res/inference_resTCP 命令回传结果文件。 - 设备客户端 HTTP 端口固定为 8001,服务端可用
GET /api/tasks/status?uuid=<device_task_uuid>查询状态,用GET /api/tasks/cancel?uuid=<device_task_uuid>取消任务。
Requirements
- 后端必须引入明确的
workspace_id概念,并提供工作区列表、创建、读取详情的 API。 - 每个工作区必须独立保存:
- 图片样本:
images - 标注:
annotations - mask sidecar:
masks - 类别与数据集配置:
dataset.yaml - 现场模型/流程覆盖:
models_parameter、flows_parameter - 部署过滤配置:
deployment.yaml - 训练/推理 job:
jobs - 训练产物:模型、指标、日志等
- 推理产物:可视化结果、
result.zip、报告、日志等
- 图片样本:
- 前端必须提供当前工作区选择入口。切换工作区后,数据页、标注页、训练页、推理页均应重新加载当前工作区数据。
- 前端提交训练/推理时必须携带当前
workspace_id,返回的 job 也必须带workspace_id。 - job 查询、详情、状态回写、取消必须能按
workspace_id定位,避免不同工作区中的 job 混淆。 - 训练/推理后台桥接必须使用 job 里的
workspace_id导出对应工作区样本,并把结果写回同一工作区的产物目录。 - 必须保持
D:\webai2.0-device当前 TCP/HTTP 协议完全兼容:- 不向客户端新增
workspace_id字段。 - 不改变 36 位设备任务 UUID。
- 不改变文件发送顺序。
- 不改变状态查询、取消和结果回传格式。
- 不向客户端新增
- webai-lite 必须持久化
workspace_id + job_id + device_job_id映射,并在客户端回传结果时通过该映射定位目标工作区。 - artifacts 列表和下载必须限定在当前工作区产物目录内,继续使用路径穿越防护。
- 保留对现有单工作区数据的兼容策略:未传
workspace_id时默认使用default工作区,旧目录_workspace/current可被视为 default 数据源或在启动/首次访问时迁移。 - 测试需要覆盖至少两个工作区之间的图片、标注、job、artifacts 隔离。
Acceptance Criteria
- 可以创建至少两个工作区,并在前端切换。
- 工作区 A 导入图片、保存类别和标注后,工作区 B 的图片/类别/标注列表不受影响。
- 工作区 A 提交训练 job 后,工作区 B 的 job 列表默认不显示该 job。
- 工作区 A 的训练结果写入 A 的产物目录,工作区 B 的 artifacts 列表不可见。
- 工作区 A 的推理结果写入 A 的产物目录,工作区 B 的 artifacts 列表不可见。
- 无需修改或升级
D:\webai2.0-device,现有设备客户端仍能接收训练/推理任务、查询/取消任务并回传结果。 - 两个工作区提交的任务即使发往同一设备客户端,结果仍分别归档到各自工作区。
- webai-lite 重启后仍可从磁盘 job 记录恢复
device_job_id对应的业务 job 和工作区。 - 旧版不传
workspace_id的调用仍可落到default工作区。 -
safe_join仍阻止 workspace file/artifact 下载路径穿越。 - 后端
pytest -q通过,前端npm run build至少通过类型与构建检查。
Technical Approach
Recommended Direction
采用“服务端路径上下文 + 前端全局 workspace_id + 设备协议兼容层”的增量改造。
- 在后端
services/workspace.py中引入 workspace-aware 路径解析,将当前全局current_dir()扩展为可接收workspace_id的版本。 - 默认工作区 ID 使用
default,兼容旧 API。 - 新目录建议采用:
1 | _workspace/ |
- 为了兼容旧数据,
default可以先读取/迁移_workspace/current与_workspace/artifacts;最终服务逻辑统一通过 workspace path helper 访问。 - API 兼容策略:
- 现有路由继续保留,例如
/api/workspace/images、/api/annotations/{image_id}。 - 新增或统一支持
workspace_idquery/header/body,例如?workspace_id=xxx。 - 前端统一由
lib/api.ts自动附加当前workspace_id。
- 现有路由继续保留,例如
- job JSON 中固化
workspace_id,所有后续后台任务、回写、取消、产物归档均以 job 中的workspace_id为准。 - 设备兼容层保持现有协议:
- 对客户端仍只发送
device_job_id、任务文件与现有 TaskData 字段。 - 不扩展客户端 TCP header,也不修改客户端 HTTP endpoint。
device_job_id写回业务 job JSON,作为服务端关联键。- 客户端返回
training_res/inference_res后,服务端通过业务 job 找到workspace_id,再归档结果。
- 对客户端仍只发送
Alternative Considered
新增全套嵌套路由,例如 /api/workspaces/{workspace_id}/images、/api/workspaces/{workspace_id}/jobs。语义更清晰,但需要一次性改动前后端所有调用,对现有外部训练推理客户端和旧调用冲击更大。当前任务优先选择兼容式 query/header/body 携带方式,降低迁移风险。
Decision (ADR-lite)
Context: 已部署设备客户端只理解现有 TCP/HTTP 协议和设备任务 UUID,无法识别业务工作区,也不能修改代码。
Decision: 工作区信息只存在 webai-lite 服务端和 Web 前端。服务端使用业务 job 持久化 workspace_id 与 device_job_id 的关联;设备客户端继续按原协议处理设备任务。
Consequences:
- 客户端零改动,部署兼容风险最低。
- 服务端 job 文件成为跨工作区、跨进程重启关联的事实来源。
- 所有异步回写和产物搬运函数必须显式接收或从 job 解析
workspace_id,不能再读取全局 current/artifacts。
Expansion Notes
- Future evolution:
- 后续可增加工作区重命名、删除、归档、复制/克隆、容量统计。
- 后续可支持把某个工作区导出为离线包。
- Related scenarios:
- Catalog 现场覆盖和
deployment.yaml必须跟随工作区,否则不同项目的可用模型/流程会串。 - last job、样本勾选、当前图片都要随工作区切换清空或分桶保存。
- Catalog 现场覆盖和
- Failure and edge cases:
workspace_id必须做白名单校验,不能允许路径片段、空白、..、斜杠。- 删除/重命名工作区若进入本任务,需要处理 active job;本任务先不做删除。
- 旧客户端或旧 API 调用若不传工作区,应继续使用
default。
Out of Scope
- 工作区权限、用户登录、多租户安全模型。
- 工作区删除、重命名、复制、导入导出整包。
- 数据库化改造;本任务继续使用磁盘 JSON 和目录结构。
- 同一个 job 跨工作区移动。
- 修改真实训练/推理算法逻辑;本任务只改数据定位、打包、回写和产物归档边界。
- 修改、编译、发布或部署
D:\webai2.0-device。 - 修改设备客户端 TCP header、HTTP endpoint、结果包格式和本地缓存数据库结构。
Research References
research/device-client-compatibility.md- 已部署设备客户端的任务 UUID、TCP 回传和服务端兼容边界。
Definition of Done
- 后端路径访问全部通过 workspace-aware helper,不再在业务逻辑中直接拼
settings.workspace_root / "current"。 - 前端 API 层统一携带当前工作区 ID,页面切换工作区后状态不会串。
- 已部署设备客户端不需要任何代码修改。
- 多工作区隔离测试覆盖关键链路。
- 后端 lint/test 和前端 build 检查通过,无法运行的检查需记录原因。
Technical Notes
- 关键后端文件:
api/webai_lite_api/config.pyapi/webai_lite_api/services/workspace.pyapi/webai_lite_api/services/workspace_ext.pyapi/webai_lite_api/services/attach.pyapi/webai_lite_api/services/jobs.pyapi/webai_lite_api/services/catalog.pyapi/webai_lite_api/services/cvat_coco_export.pyapi/webai_lite_api/services/device_task_bundle.pyapi/webai_lite_api/services/device_submit_bridge.pyapi/webai_lite_api/routes/workspace.pyapi/webai_lite_api/routes/annotations.pyapi/webai_lite_api/routes/artifacts.pyapi/webai_lite_api/routes/jobs.pyapi/webai_lite_api/routes/train.pyapi/webai_lite_api/routes/infer.py
- 关键前端文件:
web/src/lib/api.tsweb/src/store/index.tsweb/src/App.tsxweb/src/pages/Data.tsxweb/src/pages/Annotate.tsxweb/src/pages/Train.tsxweb/src/pages/Infer.tsxweb/src/components/WorkspaceSamplePicker.tsxweb/src/components/JobResultPanel.tsxweb/src/lib/jobHelpers.ts
- 外部只读参考:
D:\webai2.0-device
- 相关规范:
.trellis/spec/backend/index.md.trellis/spec/backend/directory-structure.md.trellis/spec/backend/quality-guidelines.md.trellis/spec/frontend/index.md.trellis/spec/frontend/state-management.md.trellis/spec/frontend/quality-guidelines.md