Label Studio 构建 RAG 人工反馈评估界面:检索相关性、回答相关性与忠实度标注实战
【免费下载链接】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 官方模板 llm_rag_human_feedback 为核心,讲解如何用标签配置(Labeling Config)搭建一套面向 RAG(Retrieval-Augmented Generation,检索增强生成)流水线的人工评估界面。你将掌握三类核心评估维度(检索文档的上下文相关性、回答相关性、回答忠实度)的标注界面设计,学会使用List、Ranker、Collapse、Choices等标签组合完成可视化评估,并通过 Label Studio SDK 与 LlamaIndex 对接,把真实检索结果自动构造成可导入的标注任务。
RAG 评估为什么需要人工反馈
在 RAG 流水线中,最终输出的质量不只取决于大语言模型(LLM)的生成能力,还取决于检索阶段返回的文档质量。官方模板文档明确指出,评估目标不应局限于单条 LLM 回答,还应纳入对检索文档的多维度评价,主要包括:
- Contextual relevancy(上下文相关性):被检索回来的文档与当前问题是否相关;
- Answer relevancy(回答相关性):LLM 基于这些文档生成的回答是否与问题相关;
- Answer faithfulness(回答忠实度):回答是否在事实上忠于检索到的上下文、是否存在幻觉(hallucination)或编造内容。
借助 Label Studio,你可以把上述三类评估以可视化的形式呈现给人工标注员,让他们把检索文档拖入“相关/不相关”桶中,并对回答给出二元打分,从而沉淀可用于 RAG 效果分析、检索器调优乃至 RLHF 对齐的高质量人工反馈数据。
配置标注界面:完整标签配置解析
按文档指引,创建项目 后将下面的 XML 配置粘贴到 Labeling Config 中即可生成评估界面:
<View> <Style> .htx-text {white - space: pre-wrap;} .question { font - size: 120%; width: 800px; margin-bottom: 0.5em; border: 1px solid #eee; padding: 0 1em 1em 1em; background: #fefefe; } .answer { font - size: 120%; width: 800px; margin-top: 0.5em; border: 1px solid #eee; padding: 0 1em 1em 1em; background: #fefefe; } .doc-body { white - space: pre-wrap; overflow-wrap: break-word; word-break: keep-all; } .doc-footer { font - size: 85%; overflow-wrap: break-word; word-break: keep-all; } h3 + p + p {font - size: 85%;} /* doc id */ </Style> <View className="question"> <Header value="Question"/> <Text name="question" value="$question"/> </View> <View style="margin-top: 2em"> <Header value="Context"/> <List name="results" value="$similar_docs" title="Retrieved Documents"/> <Ranker name="rank" toName="results"> <Bucket name="relevant" title="Relevant"/> <Bucket name="non_relevant" title="Non Relevant"/> </Ranker> </View> <View className="answer"> <Header value="Answer"/> <Text name="answer" value="$answer"/> </View> <Collapse> <Panel value="How relevant is the answer to the provided context?"> <Choices name="answer_relevancy" toName="question" showInline="true"> <Choice value="Relevant" html="<div class="thumb-container" style="display: flex; gap: 20px;"> <div class="thumb-box" id="thumb-up" style="width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; border-radius: 5px; cursor: pointer; transition: background-color 0.3s;"> <span class="thumb-icon" style="font-size: 48px;">&#128077;</span> <!-- Thumbs Up Emoji --> </div></div>"/> <Choice value="Non Relevant" html="<div class="thumb-container" style="display: flex; gap: 20px;"> <div class="thumb-box" id="thumb-down" style="width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; border-radius: 5px; cursor: pointer; transition: background-color 0.3s;"> <span class="thumb-icon" style="font-size: 48px;">&#128078;</span> <!-- Thumbs Down Emoji --> </div> </div>"/> </Choices> </Panel> </Collapse> <Collapse> <Panel value="If the answer factually aligns with the retrieved context?"> <Choices name="faithfulness" toName="question" showInline="true"> <Choice value="Relevant" html="<div class="thumb-container" style="display: flex; gap: 20px;"> <div class="thumb-box" id="thumb-up" style="width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; border-radius: 5px; cursor: pointer; transition: background-color 0.3s;"> <span class="thumb-icon" style="font-size: 48px;">&#128077;</span> <!-- Thumbs Up Emoji --> </div></div>"/> <Choice value="Non Relevant" html="<div class="thumb-container" style="display: flex; gap: 20px;"> <div class="thumb-box" id="thumb-down" style="width: 100px; height: 100px; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; border-radius: 5px; cursor: pointer; transition: background-color 0.3s;"> <span class="thumb-icon" style="font-size: 48px;">&#128078;</span> <!-- Thumbs Down Emoji --> </div> </div>"/> </Choices> </Panel> </Collapse> </View>各标签在评估界面中的作用
<View>:所有标签配置都必须包含的基础容器标签。本配置中它类似 HTML 的div,用于组织界面区块布局(例如className="question"、className="answer"以及内联style="margin-top: 2em"的分组)。<Style>:定义作用于View内元素的 CSS 样式,为问题区、回答区、文档正文等区块统一排版(如pre-wrap保留换行、固定宽度与背景边框)。<Header>:通过value参数显示界面标题,本配置用于标注“Question”“Context”“Answer”等区块名。<Text>:展示输入数据提供的文本。以示例数据为准,该标签分别渲染源 JSON 中的question与answer键,你需要根据自己 JSON 数据的键名调整value="$question"、value="$answer"。<List>:展示检索回来的文档列表。以示例数据为准,列表数据来自源 JSON 的similar_docs字段。其value参数指向一个对象数组(含id、title、body等字段),参见 List 标签文档。<Ranker>:为列表项创建可拖拽排序的 UI 元素,将列表项拖入不同Bucket完成分类。其name与toName参数中,toName必须指向所连接的List标签名,参见 Ranker 标签文档。<Bucket>:在Ranker内定义一个分类容器。本配置定义了relevant(相关)与non_relevant(不相关)两个桶,标注员把每条检索文档拖入其一,即可完成上下文相关性评估。<Collapse>/<Panel>:创建可折叠/展开的区域。Collapse提供折叠容器(支持accordion、bordered、open等参数),Panel的value定义折叠面板标题,用于收纳两个评分问题,保持界面简洁,参见 Collapse 标签文档。<Choices>/<Choice>:Choices通过name与toName参数呈现一组选项,Choice定义单个选项。本配置中两个Choices(answer_relevancy与faithfulness)均设置了showInline="true"让选项同行显示,并借助html参数将选项渲染为可点击的 👍/👎 图标按钮。
关于 html 参数与导出值
两个评分选项的html参数中包含了内联样式的大拇指图标 HTML(Thumbs Up/Down Emoji),这正是标注界面呈现“点赞/点踩”视觉效果的关键。根据 Choice 标签参数说明,html用于展示富内容、优先级高于value,但导出结果中使用的仍是value(且需正确转义 HTML 实体)。因此标注后落库的是Relevant/Non Relevant这样的稳定枚举值,便于后续统计与训练,图标仅承担视觉表达。你可以替换其中的 emoji(👍与👎)、尺寸或颜色来定制视觉风格,而无需改动分析逻辑。
输入数据格式
本模板的任务数据需包含提示词(prompt/question)、回答(answer)以及用于上下文的检索文档(similar_docs),如下所示:
[ { "data": { "question": "Can I use Label Studio for LLM evaluation?", "answer": "Yes, you can use Label Studio for LLM evaluation.", "similar_docs": [ {"id": 0, "body": "Label Studio is a data labeling tool."}, {"id": 1, "body": "Label Studio is a data labeling tool for AI projects."} ] } } ]要点说明:
- 每条任务是一个包含
data键的对象,data下的字段名必须与标签配置中的$question、$answer、$similar_docs一一对应。 similar_docs是对象数组,每个对象至少应包含id与body(List标签还支持title、html字段用于富展示,参见 List 标签文档)。- 实际项目中,这三个字段均可由 RAG 管线直接产出:
question为用户查询,answer为 LLM 生成回答,similar_docs为检索器返回的 Top-K 文档。
用 LlamaIndex 采集评估数据
模板文档提供了基于 LlamaIndex 的数据采集示例。首先安装:
pip install llama-index然后可用如下脚本构建 RAG 流水线,针对 GitHub issues 回答问题,并从中提取检索到的文档:
import os from llama_index.readers.github import GitHubRepositoryIssuesReader, GitHubIssuesClient from llama_index.core import VectorStoreIndex, StorageContext, load_index_from_storage from llama_index.core.callbacks import CallbackManager, LlamaDebugHandler, CBEventType reader = GitHubRepositoryIssuesReader( github_client=GitHubIssuesClient(), owner="HumanSignal", repo="label-studio", ) llama_debug = LlamaDebugHandler() callback_manager = CallbackManager([llama_debug]) # check if storage already exists PERSIST_DIR = "./llama-index-storage" if not os.path.exists(PERSIST_DIR): # load the documents and create the index documents = reader.load_data(state=GitHubRepositoryIssuesReader.IssueState.CLOSED) index = VectorStoreIndex.from_documents(documents, callback_manager=callback_manager) # store it for later index.storage_context.persist(persist_dir=PERSIST_DIR) else: # load the existing index storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR) index = load_index_from_storage(storage_context, callback_manager=callback_manager) query_engine = index.as_query_engine() question = "Can I use Label Studio for LLM evaluation?" answer = query_engine.query(query) # accessing the list of top retrieved documents from callback event_pairs = llama_debug.get_event_pairs(CBEventType.RETRIEVE) retrieved_nodes = list(event_pairs[0][1].payload.values())[0] retrieved_documents = [node.text for node in retrieved_nodes]脚本的关键链路:
- 用
GitHubRepositoryIssuesReader读取 GitHub 仓库的 issue 数据(示例读取 HumanSignal/label-studio 仓库已关闭的 issue); - 通过
VectorStoreIndex建立向量索引,并借助CallbackManager+LlamaDebugHandler监听检索(CBEventType.RETRIEVE)事件; - 首次运行时把索引持久化到
./llama-index-storage,后续直接load_index_from_storage加载,避免重复建库; - 从
LlamaDebugHandler的回调事件中提取 Top 检索节点文本,得到retrieved_documents列表。
随后即可用 Label Studio SDK 把结果构造成可直接导入上文标签配置的任务:
task = { "question": question, "answer": answer, "similar_docs": [{"id": i, "body": text} for i, text in enumerate(retrieved_documents)] }这里similar_docs用enumerate生成自增id,保证List/Ranker中每个列表项有稳定标识(最终拖拽排序的结果也会以这些id序列化,详见下文结果格式说明)。SDK 导入后即可在 Label Studio 中逐条人工评估。
仓库内的同类实现参考
本模板在仓库中不是孤例,label_studio/annotation_templates/generative-ai/目录下汇集了多个同族模板:
- llm-ranker/config.yml:官方内置的 “LLM Ranker” 模板,将多个 LLM 回答以
List+Ranker展示并拖入relevant_results/biased_results桶,用于模型对比与偏好收集; - 同一目录下的
chatbot-assessment、human-feedback-collection、response-grading、supervised-llm、visual-ranker等模板,覆盖了对话评估、反馈收集、回答打分等 LLM 评估场景。
上述内置模板与本文介绍的 RAG 评估模板在 UI 架构上同源:都用List承载动态条目、用Ranker+Bucket做分类/排序、用Choices做二元或多选评分,体现了 Label Studio 在 LLM 评估模板家族中的一致设计模式。从 gallery_llm_evals.ejs 的模板画廊可以看到,本模板与 “LLM Response Grading”“Side-by-Side LLM Output Comparison”“Evaluate RAG with Ragas” 等共同组成 LLM 评估模板体系,开发者可按需选用或组合。
标注结果格式与后续利用
Ranker标签保存的结果格式可参考 Ranker 标签文档:当存在多个Bucket时,结果是一个字典,每个键为桶名、值为落入该桶的列表项id数组。对本模板而言,标注一次会产出类似下面的 annotation result:
{ "value": { "ranker": { "_": [], "relevant": ["0"], "non_relevant": ["1"] } }, "from_name": "rank", "to_name": "results", "type": "ranker", "origin": "manual" }其中_键存放未被拖入任何桶的列表项(如果列表使用List且存在未分类项);若在<Bucket>上添加default="true",则未分类项会直接落入默认桶且原“未分类”列会被隐藏。answer_relevancy与faithfulness两个Choices则各自产出一条二元分类结果(值为Relevant/Non Relevant)。
由此你可以得到三类结构化标注结果:
- 检索文档 × 相关性:
relevant/non_relevant桶中的文档id列表,可直接换算成每个查询的上下文相关性命中情况; - 回答相关性:
answer_relevancy的二元标签; - 回答忠实度:
faithfulness的二元标签。
这些结果可以通过 Label Studio 的导出功能或 API 批量取回,用于计算 RAG 评估指标(如检索命中率、回答相关率、忠实率),也可作为偏好数据参与模型微调或 RLHF 流程,形成“人工评估 → 指标分析 → 系统优化”的闭环。
小结
本文围绕 Label Studio 的 RAG 人工反馈评估模板,从界面配置、输入数据、LlamaIndex 数据采集到结果序列化给出了完整方案。核心要点可归纳为:用List+Ranker+Bucket完成检索文档的上下文相关性评估;用Collapse+Panel+Choices完成回答相关性与忠实度的二元评估;用 LlamaIndex 的检索回调自动组装任务并通过 SDK 导入。结合仓库内置的 LLM 评估模板家族与 Ranker、List、Collapse、Choices、Choice 等标签文档,你可以快速扩展出符合自身 RAG 系统的评估界面。
【免费下载链接】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),仅供参考