ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

在 Label Studio 中通过插件连接 LLM 后端:为标注工作流注入 AI 分析与自动回填能力

在 Label Studio 中通过插件连接 LLM 后端:为标注工作流注入 AI 分析与自动回填能力 在 Label Studio 中通过插件连接 LLM 后端为标注工作流注入 AI 分析与自动回填能力【免费下载链接】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 的 Connect to LLM Backend连接 LLM 后端插件允许标注界面直接向一个开放的 LLM 端点发送提示词并将模型返回的文本回复、分类结果与推理理由自动写入当前标注。本文基于 llm_backend.md 原文结合仓库中插件运行机制LSI、标注结果序列化实现与标签配置示例完整讲解插件的安装配置、代码逐段解析、标注模板设计与调试排障方法让你能够在自己的标注项目中复现输入提示词 → 调用 LLM → 自动生成标注结果的完整闭环。插件概述在标注流程中内联调用 LLM该插件归属于插件库中的Automation自动化与LLM类别见 index.ejs其核心能力是在标注页面中提供一个 Analyze 按钮标注人员输入提示词后插件把提示词以 HTTP 请求的形式发送到配置好的 LLM 端点随后把响应中的三部分信息回填到标注结果中LLM_response模型生成的文本回复写入textarea类型结果Category.category模型判定的类别写入choices类型结果Type.reason模型给出的推理理由写入另一个textarea类型结果。因此这个插件非常适合需要人工出题、模型作答、人工复核的评测场景例如对模型回答进行内容分类、理由归因、安全合规初筛等标注任务。需要说明的是Label Studio 的插件机制是项目级、纯前端 JavaScript的详见 custom.md它只能在标注界面与标注工作流内执行不能用来扩展核心后端能力同时plugin FAQ 与 plugins 指南 中都提示了启用插件前需要了解的安全注意事项请务必在受控环境中使用。使用前须知插件的执行模型与 LSI 接口在粘贴插件代码之前先理解它的运行环境。插件脚本在每次标注被展示时都会重新执行例如打开任务、切换任务、新建标注、切换标注版本等场景见 custom.md。这意味着脚本内的顶层逻辑包括setup()调用会随标注视图重复运行因此在插件中推荐使用LSI.on()注册事件处理器——该方法注册的处理器会在切换到其他标注时自动取消订阅避免重复触发带来的内存泄漏与无限循环问题。LSILabel Studio Interface是专为插件设计的辅助对象本插件用到了其中几个核心成员详见 custom.mdLSI 成员类型/方法在本插件中的用途LSI.annotationgetter返回当前选中的标注对象用于读取/写入标注结果LSI.annotation.namesMap按name查找标注配置中的控制组件如promptTextAreaLSI.annotation.deserializeResults(results)方法将插件构造的结果数组反序列化合并进当前标注Htx.showModal(message, type)全局对象方法弹出提示框用于空提示词、请求失败等错误反馈提示deserializeResults()是前端标注 store 的公开方法实现在 Annotation.js且从源码注释看它的合并行为是增量式的additive会向现有标注中追加新的 areas/results而不会清空原有内容见 annotationLazyHydration.ts这正是插件能安全回填结果的前提。配置 LLM 端点替换MY_URL_ROOT插件通过fetch直接调用远端接口因此需要先准备一个不需要认证open的 LLM 端点例如内网服务或经过代理暴露的 LLM 服务。打开插件的脚本编辑区将以下代码中的占位符替换为你的真实 URLconst baseUrl MY_URL_ROOT;请求以POST方式发送Content-Type: application/json并附带查询参数prompt、llm_endpoint_name示例中为chatgpt与redteam_categories。也就是说你配置的端点需要能够接收如下形式的请求并返回 JSONPOST {baseUrl}?prompt...llm_endpoint_namechatgptredteam_categoriescat1响应 JSON 需包含插件约定的字段结构响应字段含义对应标注结果LLM_response模型生成的文本回复responseTextArea 结果Category.category类别数组如[cat1]categoryChoices 结果Type.reason推理理由文本reasonTextArea 结果如果响应中某字段缺失或为空插件会跳过对应结果的写入详见下文代码解析中的空值判断。插件代码逐段解析完整插件代码如下核心逻辑来自 llm_backend.md下面按函数逐一说明。1. 发起 LLM 请求fetchLLM(prompt)window.LSI LSI; const baseUrl MY_URL_ROOT; /** * Makes a request to the configured LLM sending the given prompt */ async function fetchLLM(prompt) { const params { prompt, llm_endpoint_name: chatgpt, redteam_categories: [cat1], }; const searchParams new URLSearchParams(params).toString(); const url ${baseUrl}?${searchParams}; const response await fetch(url, { method: POST, headers: { Content-Type: application/json, // No auth needed because the API is open }, }); const data await response.json(); }window.LSI LSI;将 LSI 暴露到全局供脚本各处引用URLSearchParams负责把prompt、llm_endpoint_name、redteam_categories序列化为查询字符串由于端点开放、无需认证请求头中不携带任何凭证返回的data即上节约定的响应 JSON 对象。2. 组装标注结果sendPrompt()/** * Sends the introduced prompt to the LLM endpoint and attaches the given results to the annotation */ async function sendPrompt() { const promptTag LSI.annotation.names.get(prompt); promptTag.submitChanges(); const prompt promptTag.result?.value.text.join(\n); if (!prompt) { Htx.showModal(The prompt is empty, error); return false; } let response; // console.log(Input prompt: prompt); try { response await fetchLLM(prompt); } catch (error) { Htx.showModal( Error fetching the LLM endpoint ${baseUrl}: ${error.message}, error, ); return false; } const results []; const llmResponse response.LLM_response; if (llmResponse) { const llmResult { from_name: response, to_name: placeholder, type: textarea, value: { text: [] }, }; results.push(llmResult); } // console.log(Response: llmResponse[LLM_response]); const category response.Category?.category; if (category?.length) { const attackResult { from_name: category, to_name: placeholder, type: choices, value: { choices: category }, }; results.push(attackResult); // console.log(Category: category); } const reasonText response.Type?.reason; if (reasonText) { const reasonResult { from_name: reason, to_name: placeholder, type: textarea, value: { text: [reasonText] }, }; results.push(reasonResult); // console.log(Reason: reason); } LSI.annotation.deserializeResults(results); }这段代码的流程是读取提示词通过LSI.annotation.names.get(prompt)拿到标注配置中名为prompt的 TextArea 控件先调用submitChanges()提交编辑中的内容再通过promptTag.result?.value.text.join(\n)读取已提交的文本。若为空弹出错误提示并终止。调用 LLM 并容错用try/catch包裹fetchLLM(prompt)失败时以Htx.showModal(..., error)展示包含端点地址的错误信息。构造结果对象依次判断响应中的LLM_response、Category.category、Type.reason按需构造标准的 Label Studio 结果对象。每个结果对象都遵循{ from_name, to_name, type, value }四元组结构其中from_name对应标注配置中的控件nameresponse/category/reasonto_name指向占位控件placeholdertype为结果类型textarea/choices。回填标注将结果数组一次性交给LSI.annotation.deserializeResults(results)合并进当前标注。合并后这些内容即可随标注一起保存与导出。3. 注入按钮setup()/** * Sets up the onClick event of the template to trigger the LLM request */ function setup() { const aBtn document.querySelector(.analyzeButton); const button document.createElement(button); button.textContent Analyze; // Set the button text // Attach an onclick event to the button button.onclick sendPrompt; // Insert the button into the div aBtn.replaceChildren(button); } setup();setup()在标注配置中查找类名为analyzeButton的容器View动态创建一个文本为 Analyze 的button并绑定onclick sendPrompt。由于插件脚本会在每次标注展示时重新执行setup()会被重复调用——这里使用replaceChildren清空旧按钮再插入新按钮恰好避免了按钮被反复叠加的重复订阅问题这也是编写插件时值得借鉴的每次运行先清理上次运行的做法。Labeling 标注配置详解插件需要与下面的标注配置配合使用完整配置见 llm_backend.md配置中包含 4 个控件与若干样式。占位控件PlaceholderView classNameplaceholder Text nameplaceholder valueplaceholder / /Viewplaceholder是一个隐藏占位控件通过 CSSdisplay: none隐藏它的作用是为其他控件提供一个共同的toName锚点。注意提示词输入框、响应展示框、类别选择、理由文本框的toName全部指向它这是 Label Studio 结果对象to_name字段的合法取值来源。提示词输入框PromptHeader valueEnter Prompt to Analyze:/ TextArea nameprompt toNameplaceholder transcriptiontrue showSubmitButtonfalse editabletrue rows4 maxSubmissions1 placeholderType the prompt here... /nameprompt与插件代码中LSI.annotation.names.get(prompt)一一对应transcriptiontrue使输入作为文本结果被记录editabletrue允许标注人员自由编辑提示词rows4控制输入框高度。技巧可以给 TextArea 加上value$text属性将任务数据中的text字段预填为提示词即把示例数据里的问题直接作为初始提示词省去手动输入详见 llm_backend.md。响应、类别与理由展示区Header valueLLM Response:/ View classNamellm_response TextArea nameresponse toNameplaceholder transcriptiontrue rows6 showSubmitButtonfalse editablefalse smartfalse maxSubmissions1 placeholderThe LLM response will appear here... / /View Header valueCategory:/ Choices namecategory toNameplaceholder choicesingle showInLinetrue Choice valuenone / Choice valuecat1 / Choice valuecat2 / Choice valuecat3 / /Choices Header valueReason/ TextArea namereason toNameplaceholder rows2 editablefalse smartfalse maxSubmissions1 placeholderWrite something here... /response与reason均为只读editablefalse、非智能smartfalse的 TextArea用于展示模型输出防止标注人员误改maxSubmissions1限制每个任务最多提交一次category使用单选Choices预置none、cat1、cat2、cat3四个候选值与插件代码中redteam_categories: [cat1]呼应——你可以把类别扩展为实际的攻击类别、内容风险类别或任务自定义分类相关标签的完整参数说明可分别查阅 View、Style、Text、Header、TextArea、Choices。样式与交互反馈Style .lsf-main-content.lsf-requesting .prompt::before { content: loading...; color: #808080; } .placeholder { display: none; } .llm_response textarea { background-color: #f0f0f0; /* Optional: gives a disabled feel */ } .analyzeButton button { background: #1890ff; border-color: #1890ff; box-shadow: 0 2px 0 rgba(0, 0, 0, .045); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, .12); padding: 4px 15px; } /Style.lsf-main-content.lsf-requesting .prompt::before在标注处于请求中状态时于提示词区域前追加灰色loading...文字提供加载反馈.placeholder { display: none; }隐藏占位 Text 控件.llm_response textarea给响应框加浅灰背景营造禁用观感.analyzeButton button把动态创建的 Analyze 按钮样式化为蓝色主按钮Ant Design 风格的#1890ff。示例数据准备评测题目插件与配置就绪后导入如下格式的任务数据每条任务的data.text既可作为预填提示词的来源也可作为任务题目[ { data: { text: What is the closest relative to an opossum? } }, { data: { text: What is a fun opossum fact? } }, { data: { text: Why are opossums cool? } } ]导入数据后打开任意任务在提示词输入框中输入或预填问题点击Analyze按钮插件便会调用 LLM 并将响应回填到下方的 Response、Category 与 Reason 区域中。调试与排障替换占位符最常见的问题是忘记把MY_URL_ROOT替换为真实可访问的 LLM 端点此时点击 Analyze 会弹出Error fetching the LLM endpoint MY_URL_ROOT...错误框先核对端点地址与网络可达性。Testing 面板在插件编辑器中添加插件后脚本下方会出现Testing面板可以用示例数据测试插件、手动触发事件并观察触发过程详见 custom.md。如果标注配置存在校验错误Testing 面板不会出现请先检查 Code 面板。浏览器开发者工具插件本质是前端脚本可使用浏览器 Console 面板查看console.log输出插件代码中预留了若干被注释的日志如console.log(Input prompt: prompt)可取消注释用于排障Network 面板可以检查请求是否发出、状态码与响应体是否符合约定结构。响应字段约定插件依赖响应 JSON 中的LLM_response、Category.category、Type.reason字段若模型服务返回的字段命名不一致需要自行调整sendPrompt中的取值逻辑或在服务端做字段映射。源码级原理小结结果对象结构插件构造的每个结果都是{ from_name, to_name, type, value }这与 Label Studio 的标注结果存储格式一致保存与导出格式可参考 task_format.md。deserializeResults的增量语义该方法实现在 Annotation.js用于将 JSON 结果合并进当前标注从 annotationLazyHydration.ts 的注释可以看到MST 的deserializeResults是增量式的additive只会追加新的 areas/results因此同一标注内多次调用不会破坏既有标注内容。LSI 接口LSI.annotation、LSI.annotation.names等均由插件运行环境注入其行为在 custom.md 有说明如需订阅前端事件如标注切换、结果变更以实现更复杂的自动化可参考 frontend_reference.md 中的事件列表并使用LSI.on()注册处理器。扩展方向鉴权改造当前插件面向无认证的内网端点若 LLM 服务需要鉴权可在fetch的 headers 中注入 API Key 或 Bearer Token注意前端暴露凭证的安全风险。多轮对话可将历史对话记录拼入prompt实现多轮评测。结果校验结合LSI.on()监听标注结果变更对 LLM 回填内容做二次校验或格式化。更多结果类型参照category的choices写法可扩展labels、rating等结果类型把模型输出映射为更多样的标注形式。更详细的插件开发规范、LSI 方法签名与调试手段请继续阅读 Customize and Build Your Own Plugins 与 Plugins for projects。【免费下载链接】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),仅供参考
返回列表