Skip to content

⚠️ 自动镜像 · 此页由 docs-site/scripts/mirror-adr.mjsdocs/adr/0026-tool-unit-class-and-attribute-binding.md 生成,请勿直接编辑此处;改源文件后 pnpm docs:build 会自动同步。

0026 — 类别与属性按工具单位 (tool_unit) 强隔离绑定

  • Status: Accepted
  • Date: 2026-05-19
  • Deciders: core team
  • Supersedes:

Context

v0.10.16 之前,项目的类别 (classes + classes_config) 与属性 schema (attribute_schema) 是项目级扁平字段,所有工具共享同一份:bbox 工具下拉里看到的类、polygon 工具下拉里看到的类、AI 交互工具的类必须完全一致。客户反馈的现实场景需要更细颗粒度:

"我希望 bbox 工具标行人/车辆,polygon 工具标可行驶区/天空,AI 交互工具又有自己的类。一张大表混着塞不下,且现在的'类型' (image-det / image-seg) 枚举太死,新场景每来一次就要加 type_key。"

同期还有两条需求叠加:

  • 新建项目向导的 type 枚举从 7 种(image-det / image-seg / image-kp / video-mm / video-track / mm / lidar)收敛 — 实际只有 image / video / lidar 三种数据载体,具体能做什么由"工具集"决定。
  • TemplateEditModal 后端 schema 已支持 classes_config / attribute_schema / rendering_config,前端 Modal 没暴露;若工具维度生效,模板也得按工具单位携带。
选项主要卖点主要劣势
A: 工具独占类别 (强隔离)不同工具的同名类是两条独立记录;可同名不同色;调色板 / 属性面板 / 导出 categories 都按工具单位天然分组bbox 与 polygon 同时想用「人」类要重复输入;客户跨工具复用类的场景需手工同步
B: 工具选择类别子集项目仍有一份扁平类别池,每工具勾选可用子集;复用方便UI 复杂度从「写一份」变成「写一份+按工具勾选」;后端导出仍需按工具分组,服务层逻辑跨字段
C: 类别共享 + 属性绑定工具类别保持项目级,只把 attribute_schema 按工具拆分"同一目标用不同工具时填不同属性"是少数场景;类别下拉无变化 → 客户原始诉求未解决

Decision

采用方案 A: 工具独占类别 (强隔离)。每个被启用的工具单位 (tool_unit_id) 独立持有 classes + attribute_schema,bbox 工具的「人」与 region 工具的「人」是两条独立记录。

工具单位枚举 (与后端 app/schemas/_jsonb_types.ToolUnitId Literal 严格对齐):

tool_unit_id包含工具 (Workbench ToolId)type 限制
bboxBboxToolimage, video
polyline(v0.10.17 占位, 未实现)image, video
regionPolygonTool + MaskTool 打包image
ai_interactiveSmartPointTool + SmartBoxTool + TextPromptTool + ExemplarTool + MagicBoxTool 打包image
lidar_box_3d(v0.10.17 占位, 未实现)lidar

实施要点

  • 新增 Project.tool_bindings: JSONB,形状 { tool_unit_id: { enabled, classes: [...], attribute_schema: {...} } }
  • Annotation.tool_unit_id / Prediction.tool_unit_id 列必填,默认 bbox;alembic 0072 按 annotation_type (polygon/mask → region) 反向 backfill。
  • Project.classes_configattribute_schema 保留为派生只读字段,运行期由 app/services/project.pyapply_tool_bindings_legacy_sync 同步双写:
    • 写 tool_bindings → 派生覆盖 legacy
    • 旧客户端只写 legacy → coalesce_legacy_into_tool_bindings 按 type_key 反推到对应 unit
    • 单源真值 = tool_bindings;legacy 字段在 v0.10.18 删除。
  • COCO 导出 categories 按 tool_unit 分组,带 supercategory = tool_unit_id;cat_map 改为 (tool_unit_id, class_name) → category_id
  • AAP JSON schema_version1.1,envelope 加 project.tool_bindings,annotations / predictions 数组每条加 tool_unit_id (1.0 reader 走 extra="ignore" 仍兼容)。
  • 工作台 useToolBindings(project, activeToolId) 派生当前激活工具的 classes / classesConfig / attributeSchema;切工具时若 activeClass 不在新 unit 类别集自动切首个类。
  • ProjectTemplate 同步加 tool_bindings 字段 (alembic 0073) + CLONEABLE_PROJECT_FIELDS 收入。

Consequences

正向

  • 客户原始诉求 (同项目内不同工具用不同类) 直接解决,无需手工 hack。
  • 工具维度的类别天然解耦:同名不同色合法 (强隔离),不存在"项目级类名重复"歧义。
  • 导出 COCO/AAP JSON 的语义更清晰:supercategory / tool_unit_id 标注了类别来源工具,下游训练 pipeline 能区分。
  • 新建向导只需问"启用哪些工具单位",不再依赖 type_key 列出 7 种排列组合;新增工具时不破坏 type 枚举。
  • ProjectTemplate 与 Project 共享同一份 tool_bindings 结构,模板的工具集 / 类别 / 属性可独立编辑 (TemplateEditModal v0.10.17 已实现 3-tab UI)。

负向

  • 跨工具复用同名类需重复输入 (bbox 加「人」 / region 加「人」是两次操作);若客户后续反馈"想共享类别名字 / 颜色",再加可选 alias_to: (tool_unit, class_name) 链(本版不做)。
  • 老项目数据迁移:alembic 0072 默认把所有 image-det / video-track 等项目类塞到 bbox unit;若客户实际混用 polygon 工具,需事后到 ProjectSettings 把类复制region unit(强隔离,不能共享)。这条已在 CHANGELOG / docs-site/user-guide 标注。
  • API 增加一层概念:annotation 创建必须带 tool_unit_id,旧 SDK / 第三方调用者需升级 (本版默认 bbox 保兼容,但服务层 422 校验严格)。
  • v0.10.17 期间 legacy 字段双写,有短暂"数据冗余",约 v0.10.18 删除派生字段后回归单源。

Alternatives Considered(详)

方案 B (项目共享类别池 + 工具勾选子集):更折中的形态,但需要新增"类别池"实体 + 每工具的"可用子集"映射两层数据结构,UI 上还得引导用户先建池再勾。客户的诉求是"独立",不是"共享后再勾",方案 B 中间多了一层抽象不必要。否决。

方案 C (类别共享 + 属性绑定工具):"同一目标用不同工具时填不同属性"是少数场景,不能覆盖原始诉求"bbox 标行人, polygon 标道路" — 道路根本不应该出现在 bbox 工具的类别下拉里。否决。

方案 D (彻底打散为 N 个独立项目):把 image-det 与 image-seg 拆成两个项目,分别配类。问题:同一份图像数据(dataset)要跑两次标注,任务调度 / 进度 / 成员管理双轨,客户更头疼。否决。

Notes

  • 实现代码位置:
    • 后端: apps/api/app/db/models/project.pyannotation.pyprediction.pyproject_template.py;apps/api/app/schemas/_jsonb_types.py;apps/api/app/schemas/project.py / annotation.py / prediction.py / project_template.py / aap_json.py;apps/api/app/services/project.py (新建,含 derive_legacy_classes_config / coalesce_legacy_into_tool_bindings / apply_tool_bindings_legacy_sync);apps/api/app/services/annotation.py (class_name 软校验);apps/api/app/services/prediction.py (derive_tool_unit_from_ls_type 派生);apps/api/app/services/export.py (COCO categories);apps/api/app/api/v1/projects.py (create/update/rename_class);apps/api/app/api/v1/tasks.py (create_annotation 透传)。
    • 迁移: alembic/versions/0072_project_tool_bindings.py0073_template_tool_bindings.py
    • 前端: apps/web/src/constants/toolUnits.tsapps/web/src/components/projects/CreateProjectWizard.tsxapps/web/src/pages/Projects/sections/{ClassesSection,AttributesSection,ToolUnitTabs,useProjectToolBindings}.{tsx,ts}apps/web/src/pages/Workbench/state/useToolBindings.tsapps/web/src/pages/Workbench/stage/tools/{MagicBoxTool,toolUnits}.tsapps/web/src/pages/Workbench/stage/shared/geometry/bbox.tsapps/web/src/pages/ProjectTemplates/TemplateEditModal.tsx
  • 相关 ROADMAP / ADR: ROADMAP §A「新建项目向导」「项目模板」、§C.3「Magic Box」、ADR-0023 (ProjectTemplate)、ADR-0024 (AAP JSON)。
  • 触发后续工作: ROADMAP §A 加入 v0.10.18+ 「删除派生 classes_config / attribute_schema」「polyline / lidar_box_3d 工具实现」「跨 tool_unit 类别软关联 (alias_to)」「Snap-to-edge Canny/Sobel」「rendering_config 共享编辑器 (供 TemplateEditModal 复用)」等延伸项。

Released under the MIT License.