ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Label Studio Redact Annotator PII 插件:基于 whoami 角色鉴权的标注者身份脱敏方案

2026/9/13 17:00:13 拓冰建站 浏览量
Label Studio Redact Annotator PII 插件:基于 whoami 角色鉴权的标注者身份脱敏方案 Label Studio Redact Annotator PII 插件:基于 whoami 角色鉴权的标注者身份脱敏方案【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studioLabel Studio 提供了一款名为Redact Annotator PII的标注接口插件,用于在标注流与审核流中隐藏标注者的姓名、头像和日期等个人可识别信息(PII),从而降低多人协作标注中的身份偏见,并仅在用户为组织管理员时自动恢复可见性。本文以官方插件文档 Redact Annotator PII 为主线,完整给出其 Labeling 配置、插件脚本与示例数据,并结合开源仓库中whoami接口的真实实现(用户 URL 注册、接口实现),解析这套CSS 默认隐藏 脚本按角色解禁的脱敏机制底层原理。读完后,你将能够:为项目配置一套可复制运行的 PII 脱敏方案,并理解角色判定、样式移除等关键细节在源码中的落点。插件定位与双层工作原理该插件面向的核心场景是:当多个标注者在同一批任务上产出标注时,标注者之间可能因为知道这条标注是谁做的而产生从众、权威效应等偏见;在审核(Review)阶段,审核者知道被审标注的作者身份同样会引入偏差。因此该插件的目标是对普通标注者/审核者匿名化标注者信息,同时对管理员保留完整信息以便追溯。从文档给出的实现看,整套方案由两层机制配合完成:Labeling 配置层(默认隐藏):通过Style标签注入 CSS,把标注按钮上的用户名、头像、日期,以及评论、标注历史中的人名/日期/头像元素的display全部置为none。这一层不依赖任何脚本,只要配置生效,所有用户在标注流与审核流中看到的 PII 都是被遮蔽的。插件脚本层(按角色恢复):JavaScript 插件在每次标注展示时执行,通过平台的whoami接口获取当前登录用户的组织角色;如果角色是管理员(枚举值AD),就移除 Labeling 配置中注入的Style节点,从而反隐藏PII。需要说明的是,该文档在 front-matter 中标记为tier: enterprise,即这一完整能力在 HumanSignal/Label Studio Enterprise 的产品语境下提供;文档中脚本硬编码了 SaaS 域名的 whoami 地址,自建部署时对应的是站点自身的/api/current-user/whoami路径(下文结合源码说明)。插件本身的编写规范——包括事件订阅、LSI接口对象与调试面板——参见 Customize and Build Your Own Plugins;插件的通用背景与常见问题可查 Plugins for projects 与 Plugin FAQ。Labeling 配置:用 CSS 在标注/审核流中隐藏 PII文档给出的完整 Labeling 配置如下,这是一个文本情感分类示例:最外层View通过idAttrnoPII给整个视图容器打上noPII属性(供脚本定位),内部Style负责遮蔽 PII 元素,业务部分则是Text展示加单选Choices:View idAttrnoPII Style .lsf-annotation-button__user { display: none; } .lsf-userpic { display: none; } .lsf-annotation-button__userpic { display: none } .lsf-annotation-button__date { display: none; } .lsf-comment-item__name { display: none; } .lsf-comment-item__date { display: none; } .lsf-comment-item__userpic { display: none; } .lsf-history-item__name { display: none; } .lsf-history-item__date { display: none; } /Style Text nametext value$text / View stylebox-shadow: 2px 2px 5px #999; padding: 20px; margin-top: 2em; border-radius: 5px; Header valueChoose text sentiment / Choices namesentiment toNametext choicesingle showInLinetrue Choice valuePositive / Choice valueNegative / Choice valueNeutral / /Choices /View /View这组 CSS 选择器覆盖了三类界面元素,可整理为如下对应关系:选择器隐藏的对象.lsf-annotation-button__user标注/审核按钮上显示的用户名.lsf-userpic、.lsf-annotation-button__userpic各处头像图片.lsf-annotation-button__date标注按钮上的操作日期.lsf-comment-item__name、.lsf-comment-item__date、.lsf-comment-item__userpic评论区中的评论者姓名、时间与头像.lsf-history-item__name、.lsf-history-item__date标注历史条目中的作者名与时间其中idAttrnoPII的作用是把noPII这个属性写到根View渲染出的 DOM 元素上,使插件脚本能够用document.getElementById(noPII)精确找到这段配置所渲染出的容器,再删除它下面的Style子节点——这是CSS 隐藏与脚本解禁两层之间唯一的衔接点。相关标签的用法详见 View、Style、Text、Header、Choices。插件脚本:基于 whoami 的角色化恢复文档提供的完整插件脚本如下:/* Hide annotator personal information (PII) if the logged user is not an Admin */ /** * Fetch currently logged user via the HumanSignal API */ async function fetchUserInfo() { const whoamiUrl https://app.humansignal.com/api/current-user/whoami; const response await fetch(whoamiUrl, { method: GET, headers: { Content-Type: application/json, // No Auth credentials needed for same-origin given Session-Based Authentication is used in the API } }); if (!response.ok) { throw new Error(Error: ${response.status} ${response.statusText}); } const data await response.json(); return data; } /** * Give visibility to the given selector */ function displayEl(sel) { const els document.querySelectorAll(sel); if (els) { els.forEach(function (el, idx) { el.style.display block; }); } } /** * If the logged in user is an Admin, remove the styling added to the view that hides * the annotator identity */ async function hidePII() { let user, role try { const userInfo await fetchUserInfo(); user userInfo.username || Unknown; role userInfo.organization_membership.role || Unknown; } catch (error) { Htx.showModal(Error fetching user information: ${error.message}); } if (!user) { console.warn(Did not find a username and it was not Unknown); return; } if (role AD) { // console.log(Role is admin; displaying PII); // If admin, remove the nulled Style tag const firstChild document.getElementById(noPII).firstChild; if (firstChild.tagName STYLE) { firstChild.remove(); } } } (async () { await hidePII(); })();脚本的执行链条可以拆解为四步:fetchUserInfo():向 whoami 接口发起 GET 请求。按注释说明,由于该接口使用基于 Session 的认证且请求为同源的,浏览器会自动携带会话凭证,无需额外credentials配置;非 2xx 响应会抛错进入catch。hidePII():从响应中取出username与organization_membership.role两个字段,分别兜底为Unknown。如果拉取失败,通过Htx.showModal弹出错误提示(这是插件运行环境中可用的界面提示能力)。角色判定:仅当role AD时才执行恢复逻辑。样式移除:定位noPII容器的第一个子节点,确认其是STYLE标签后调用remove()将其从 DOM 中删除——注意是移除整个 Style 节点而非逐条改回display,因此恢复效果干净彻底。非管理员用户则不做任何操作,PII 保持 CSS 遮蔽状态。脚本末尾的 IIFE(async () { await hidePII(); })();使整个流程在插件被调用时异步自执行,契合插件运行在异步上下文、可使用await的执行模型。另外,脚本中保留了一个未参与主流程的displayEl(sel)工具函数,用于把指定选择器的元素重新设为display: block;它体现了一种备选恢复思路(逐个恢复元素),而当前主实现选择的是整块移除Style。whoami 接口在仓库源码中的落点文档脚本里写死了 SaaS 域名https://app.humansignal.com/api/current-user/whoami;对于自建部署,对应的就是本仓库中注册的同名路径。在 users/urls.py 中可以看到该路由:path(api/current-user/whoami, api.UserWhoAmIAPI.as_view(), namecurrent-user-whoami),其视图实现见 users/api.py:UserWhoAmIAPI继承自generics.RetrieveAPIView,要求IsAuthenticated,get_object直接返回request.user,即以当前会话用户作为查询对象,响应由WhoAmIUserSerializer序列化。该序列化器在 users/serializers.py 中定义为BaseWhoAmIUserSerializer,在基础用户字段之上额外附带了permissions字段:class BaseWhoAmIUserSerializer(BaseUserSerializer): permissions serializers.SerializerMethodField() class Meta(BaseUserSerializer.Meta): fields BaseUserSerializer.Meta.fields (permissions,) def get_permissions(self, user) - list[str]: return [perm for _, perm in all_permissions]脚本读取的username与organization_membership.role正是这一用户序列化输出的一部分。而role AD这个判定依据的角色枚举,在 core/settings/base.py 的 OpenAPI 枚举定义中可以得到印证:OrganizationRoleEnum包含OW(Owner)、AD(Administrator)、MA(Manager)、RE(Reviewer)、AN(Annotator)、DI(Deactivated)、NO(Not Activated)、VO(View Only),其中AD即文档脚本中用于判定管理员的取值。此外,前端模板(如 users/user_account.html)也通过{% url current-user-whoami %}调用同一接口,说明 whoami 是平台内通用的我是谁查询入口,插件只是复用了它。示例数据文档配套了三条文本分类样例,与 Labeling 配置中的$text字段对应,可直接用于导入测试:[ { data: { text: I recently purchased a portable Bluetooth speaker and have been impressed with its clear sound and long battery life. The speaker is compact and easy to use, making it perfect for outdoor adventures. } }, { data: { text: I bought a smartwatch from this vendor and it has exceeded my expectations. The device offers an intuitive user interface and tracks my daily activities accurately while looking very stylish on my wrist. } }, { data: { text: I ordered a pair of noise-cancelling headphones and they dont do anything to cancel out noise. Waste of money. } } ]配置上述 Labeling 配置与示例数据后:普通标注者/审核者进入标注流与审核流时,标注按钮、评论区与标注历史中的用户名、头像、日期均不可见;管理员进入同一界面时,Style节点被插件移除,身份信息完整可见。适用边界与限制文档特别强调了几条边界,配置该方案前应明确:遮蔽范围有限:Labeling 配置中的 CSS 只作用于标注流与审核流界面,不会遮蔽 Data Manager(数据管理界面)中的标注者信息。若需要连 Data Manager 也限制访问,文档建议通过 项目设置 禁止 Annotator 与 Reviewer 角色访问 Data Manager,从权限层面补齐。whoami 地址随部署形态变化:文档脚本中的 whoami URL 是 HumanSignal SaaS 域名,依赖同源会话认证;自建部署时应把该 URL 指向自身站点的/api/current-user/whoami路径(路由定义见 users/urls.py),否则会因跨域或域名不存在导致fetch失败并弹出错误提示。执行模型:按 自定义插件文档 的说明,插件在每次标注展示时都会执行一次(打开任务、切换任务/标注、查看历史版本等场景)。本插件的恢复逻辑是幂等的——非管理员路径不操作 DOM,管理员路径删除的是当前这一次渲染中的Style节点,下一次标注展示时 DOM 重新渲染、脚本重新执行,天然不会累积副作用;但如果你要在此基础上扩展(例如用displayEl逐元素恢复),仍需遵循避免重复订阅事件、做好运行间清理的插件开发规范。角色判定仅识别AD:当前脚本只把AD(Administrator) 视为可见 PII 的角色,OW(Owner)、MA(Manager) 等其他角色同样会被遮蔽;如需扩大解禁范围,需要自行扩展role的判定条件。参考与延伸阅读插件文档主体:Redact Annotator PII插件开发与调试(Testing 面板、LSI接口、debugger):Customize and Build Your Own Plugins插件总览与安全注意事项:Plugins for projects插件 FAQ:Plugin FAQ前端界面事件与结构参考:Frontend reference源码证据:whoami 路由、UserWhoAmIAPI、BaseWhoAmIUserSerializer、组织角色枚举【免费下载链接】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),仅供参考