Label Studio 插件实战:使用 Plotly 在标注界面中嵌入交互式数据图表
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
在数据标注工作中,很多任务需要标注者先观察数据趋势、再做出判断——例如判断时间序列是否递增、识别曲线的异常波动、比较多条曲线的走势。Label Studio 提供了基于 JavaScript 的插件机制,允许你在标注界面中注入任意前端能力。本文基于仓库中的官方插件示例 plotly.md,完整讲解如何通过 Plotly 将图表渲染进每一次标注会话,让标注者在看到数据全貌的同时完成分类、打标等操作。读完本文,你将能够:编写并部署一个可复用的 Plotly 图表插件、正确编写配套的标注配置与任务数据,并理解LSI.import、idAttr等关键机制的底层实现。
一、插件用途与适用场景
Plotly 是一个成熟的 JavaScript 可视化库,支持散点图、折线图、柱状图、热力图等丰富的图表类型。Label Studio 的 Plotly 插件做的事情很直接:将任务数据中以 Plotly trace 格式存放的字段,渲染为标注界面中的真实图表。
该插件的核心价值在于,图表会出现在用户打开的每一个标注(annotation)中,而不是停留在任务列表之外。这意味着:
- 标注者无需切换到外部工具即可看到数据走势,标注效率显著提升;
- 图表与标注控件(如单选、多选、标签)同屏展示,标注判断有据可依;
- 数据即图表、图表即数据,任务文件本身无需额外的图片预处理。
需要说明的是,插件功能仅在 Label Studio Enterprise 中提供,Community 版本不可用(参见 Plugins for projects 中的 Enterprise 限制说明)。同时,由于插件本质是在每台标注机上执行任意 JavaScript,启用前必须了解 guide/plugins.md 中列出的安全注意事项。
二、运行机制:插件何时执行、图表如何出现
要正确编写插件,首先需要理解它的执行时机。根据 Customize and Build Your Own Plugins 中的说明,插件在每次标注被展示时都会执行一次,包括:打开任务、在任务间切换、新建标注、切换不同标注版本、查看旧版本标注等场景。
这一点对 Plotly 插件有两个直接影响:
- 每次打开标注都会重新渲染图表——只要任务数据中包含
plotly字段,图表就会自动出现在<View idAttr="plot"/>指定的位置; - 插件脚本需要具备幂等性——由于同一标注可能被执行多次,脚本逻辑应避免产生重复绑定、重复渲染等副作用。
插件运行在一个异步函数上下文中,因此可以直接使用await等待外部资源加载完成,这也是下面示例代码能够await LSI.import(...)的原因。
三、编写插件脚本:从 CDN 加载 Plotly 并渲染图表
官方示例插件的完整代码如下:
await LSI.import('https://cdn.plot.ly/plotly-2.26.0.min.js', 'sha384-xuh4dD2xC9BZ4qOrUrLt8psbgevXF2v+K+FrXxV4MlJHnWKgnaKoh74vd/6Ik8uF'); let data = LSI.task.data; if (window.Plotly && data) { Plotly.newPlot("plot", [data.plotly]); }这段代码只有三行,却完成了"加载依赖 → 读取任务数据 → 渲染图表"的完整链路,下面逐一拆解。
3.1LSI.import(url, integrity):安全地加载外部脚本
第一行通过LSI.import()从官方 CDN 加载 Plotly 2.26.0。这是 Label Studio Interface(LSI)提供的实例方法,其签名与参数说明如下(详细定义见 custom.md):
| 参数 | 类型 | 说明 |
|---|---|---|
url | string | 外部脚本文件的 URL |
integrity | string | 子资源完整性(SRI)哈希,浏览器会校验加载的脚本内容,防止源文件被篡改时脚本仍然执行 |
两个值得注意的细节:
- 必须使用
await:LSI.import()是异步方法,返回的 Promise 在脚本加载完成后 resolve。等待其完成后再执行主逻辑,可以避免出现window.Plotly尚未定义的竞态问题; - 尽量携带 SRI 哈希:示例中的
sha384-...即为 Plotly 官方提供的完整性校验值。在真实环境中,建议从官方渠道获取与你所用版本匹配的最新哈希,以抵御供应链攻击风险。
3.2LSI.task.data:读取当前任务数据
let data = LSI.task.data;LSI.task是 LSI 提供的 getter,返回当前任务的信息,其中data字段就是任务的核心数据结构(即原始待标注数据)。LSI.dataObj是其别名,指向同一个对象。也就是说,任务 JSON 里写了什么字段,这里就能读到什么字段——后续的data.plotly正是取自任务数据中的plotly字段。
3.3 渲染图表
if (window.Plotly && data) { Plotly.newPlot("plot", [data.plotly]); }Plotly.newPlot(container, traces)是 Plotly.js 的核心 API:第一个参数是目标 DOM 容器的 id,第二个参数是 trace(数据系列)数组。这里把"plot"作为容器 id,与标注配置中<View idAttr="plot"/>的idAttr值一一对应;[data.plotly]则将任务数据中的 plotly 字段包装为包含单个 trace 的数组。
if (window.Plotly && data)的双重判断保证了健壮性:window.Plotly未加载成功或任务数据缺失时,脚本不会抛错。
3.4 在项目设置中放置并测试插件
插件在项目设置的Labeling Interface区域进行管理(详见 Plugins for projects):
- 进入Project > Settings > Labeling Interface;
- 在插件区域粘贴上述脚本并保存;
- 添加插件后,脚本字段下方会出现Testing面板,可用示例数据即时测试、手动触发事件并观察事件流;
- 如果测试面板未出现,先检查Code面板是否存在标注配置校验错误,也可借助浏览器开发者工具的Console与Network面板排查问题(插件信息会随
/project/:id接口返回)。
建议先在测试项目上验证插件,再应用到正式项目,避免影响线上标注流程。
四、标注配置:用<View idAttr="plot"/>声明图表容器
插件只负责"画",而图表画在哪里由标注配置(Labeling Config)决定。官方文档给出的最小要求是:在配置中加入<View idAttr="plot"/>。
一个完整的配置示例如下:
<View> <Text name="function" value="Is it increasing?" /> <Choices name="slope" toName="function"> <Choice value="Increasing" /> <Choice value="Decreasing" /> <Choice value="Non-monotonic" /> </Choices> <View idAttr="plot"/> </View>该配置的实际效果:顶部展示问题文本"Is it increasing?",中间渲染 Plotly 折线图(由插件绘制),下方提供三个选项供标注者选择函数趋势。
4.1 从源码理解idAttr
idAttr不是 View 标签的独有属性,而是编辑器通用对象模型中"唯一 ID 属性"的一部分。在编辑器的模式定义中可以看到该属性的声明,例如 tags.json 中对idAttr的描述;而在 View.jsx 的组件注释中,idAttr被定义为"Unique ID attribute to use in CSS",即用于 CSS 选择器的唯一 ID 属性。
这意味着<View idAttr="plot"/>在渲染后会产生一个id="plot"的 DOM 节点,而插件中的Plotly.newPlot("plot", ...)正是通过这个 id 找到容器完成渲染。从源码结构看,任何支持idAttr的块级标签(如View)理论上都可以作为图表容器,但官方示例固定使用View,这也与文档中列出的相关标签一致:
- View 标签——布局容器,类似 HTML 的
div; - Text 标签——展示文本内容;
- Choices 标签——单选/多选控件。
4.2 与数据字段的对应关系
配置中的value="Is it increasing?"是静态文本,不依赖任务数据;而图表的数据来源data.plotly则来自任务数据。因此任务中每个对象都必须携带plotly字段,否则该对象对应的图表区域将为空。
五、任务数据格式:plotly字段即 Plotly trace
官方示例数据如下:
[ { "plotly": { "x": [1, 2, 3, 4], "y": [10, 15, 13, 17], "type": "scatter" } }, { "plotly": { "x": [1, 2, 3, 4], "y": [16, 5, 11, 9], "type": "scatter" } } ]字段含义:
| 字段 | 类型 | 说明 |
|---|---|---|
x | number[] | 横轴数据点 |
y | number[] | 纵轴数据点 |
type | string | 图表类型,示例使用scatter(散点/折线) |
从数据结构看,plotly字段直接采用了 Plotly trace(数据系列)的标准格式,因此你可以按需扩展其内容:例如增加mode: 'lines+markers'控制折线样式、添加name区分多条曲线、或将type换成 Plotly 支持的其他图表类型(如柱状图bar)。由于插件在渲染时执行的是Plotly.newPlot("plot", [data.plotly]),只要 trace 数据符合 Plotly.js 规范即可被完整渲染,具体扩展能力以 Plotly 官方 API 为准。
示例数据中的两条记录对应两个不同的标注任务,分别代表"先升后降"与"波动下降"两类曲线,正好与标注配置中的Increasing/Decreasing/Non-monotonic选项形成语义闭环,可用于直观演示和测试。
六、安全与限制:在企业版环境中谨慎使用插件
由于插件机制允许在标注机上执行任意 JavaScript,必须注意以下约束(详见 guide/plugins.md 的安全章节):
- 仅 Enterprise 可用,且需要显式开启;组织开启插件时成员不能同时属于多个组织,这是数据安全层面的强制约束;
- 默认只有Admin、Owner、Manager角色的用户能查看、添加和编辑插件,也可以进一步限制为仅 Admin/Owner 可编辑;
- 脚本来源可信:本示例从 CDN 加载第三方库,务必使用带 SRI 哈希的版本,并定期核对哈希值与版本的一致性;
- 幂等与清理:插件可能对同一标注执行多次,事件处理器应使用
LSI.on()绑定(切换标注时会自动退订),避免无限循环、内存泄漏等问题。
仓库的 feature flag 定义中也保留了插件相关的灰度开关(例如 stale_feature_flags.py 中的fflag_feat_root_47_plugins_without_eval),说明插件能力在服务端由特性开关控制,属于受控开放的功能,生产环境中应遵循管理员指引启用。
七、进阶:围绕 LSI 扩展你的图表插件
Plotly 插件展示了一个典型的 LSI 使用范式:LSI.import加载依赖 → 读取LSI.task.data→ 操作 DOM 渲染。沿着这个范式,还可以进一步扩展:
- 多 trace 图表:将任务数据组织为
plotly: [trace1, trace2]数组,配合<View idAttr="plot"/>渲染多条曲线对比; - 联动标注状态:通过
LSI.on(eventName, handler)订阅前端事件,根据标注结果动态更新图表(例如选中某标签后高亮对应曲线)。注意顶层事件(如labelStudioLoad)在插件初始化前已触发,不能用于插件; - 动态查询数据:插件可以
await异步请求外部接口,为标注者拉取上下文数据后再渲染图表; - 自定义校验:结合 How Label Studio saves results in annotations 中的结果结构,在图表旁展示标注结果的实时反馈。
更多 LSI 方法与前端 API 实现细节,可参考 Customize and Build Your Own Plugins 与 Plugin FAQ。
总结
本文完整还原了 Label Studio Plotly 插件的三要素:插件脚本(LSI.import加载 Plotly + 读取LSI.task.data+Plotly.newPlot渲染)、标注配置(<View idAttr="plot"/>声明容器,idAttr在 View.jsx 中被定义为 CSS 选择器用的唯一 ID)、任务数据(plotly字段采用标准 Plotly trace 格式)。三者通过"plot"这一 id 串联成完整链路:任务数据 → trace → 图表 → 标注决策。掌握这一模式后,你可以在企业版项目中快速落地"看图标注"类任务,并以此为模板扩展出更多数据可视化辅助标注的玩法。
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考