大模型预标注+人工审核:RAG与Agent评测数据集搭建实战
2026/9/13 6:50:23 网站建设 项目流程

先说结论:这周我把 RAG 和 Agent 项目的评测数据从 0 到 1 跑通了,用的就是标题里那套组合拳:大模型预标注 + 人工审核。整个过程没有买标注平台的商业版,也没招专职标注团队,就靠一个预标注脚本、一个多维表格、一个评审小组,攒出了第一批能用的评测数据集。这篇博文我把方案选型、跑批细节、审核工作流、踩过的坑全部写出来,给正在为评测数据发愁的朋友做个参考。

先交代一下背景,我手头有两条业务线:一条是检索增强生成的知识库问答,另一条是带工具调用的 Agent 流程。问题在于模型性能评测这件事,卡住我的不是模型本身,而是压根没有一份靠谱的评测集。开源评测集大多是通用知识,和业务场景里的术语、格式、边界条件根本不搭;让研发凭印象出题,又很容易写出"自己人偏好"的样本,后面模型迭代时会严重误导方向。所以我才决定自己搭一套数据标注工程流水线。

很多人一听"数据标注"就以为是要花钱、招人、搞一堆标注平台,这是最大的误解。RAG/Agent 项目的评测数据标注,核心不在于"人在电脑前一点点挑错",而在于把大模型当第一道粗筛,人工只做验证和修正。合理设计的情况下,人工审核的速度可以比全手动标注快 3 到 5 倍,同时通过抽检和一致性校验把质量兜住。

这篇文章我会完整拆解整个工程,包括标注类型设计、预标注脚本怎么写、prompt 怎么卡格式、人工审核工作台怎么搭、审核标准怎么定、以及我在过程中踩到的典型问题。如果你想给 RAG 知识库或者 Agent 项目建自己的评测集,这篇可以直接照着复现。

1. 为什么评测数据集会成为 RAG 和 Agent 项目的硬瓶颈

1.1 我踩到的最现实的坑:没数据就调不了召回

先说个典型的调试场景。知识库问答做多路召回的时候,某类问法召回率明显偏低,比如用户问的是"报销流程需要什么凭证",但库里说的是"票据清单及附加材料"。这类语义换用的 case,你改 embedding 阈值、换重排模型、调 chunk 大小,都需要一批带标注的 question 和 chunk 相关性对来验证效果。没有标注数据,你连"改完到底变好了还是变差了"都说不出来,所有优化都是在盲调。

Agent 场景更麻烦。工具调用链路的验证,比如"用户说帮我订明天下午三点的会议室",模型是不是正确识别了意图、填对了参数、没有乱编空闲时段,需要标注的是一整条调用轨迹,而不仅仅是一个问答对。这类数据如果只靠人工从对话记录里挑,基本要累死。

所以我的第一个结论很明确:评测数据不是"锦上添花",它是 RAG 和 Agent 迭代的基础设施。没有它,召回参数调不动、提示词改不动、模型升级不敢上。

1.2 单点评测 vs 端到端评测:先想清楚你要建哪种集

建评测集之前,我先把目标拆成了两类,因为它们对应的标注方式完全不同。

  • 单点评测集:针对链路的某个环节,比如"query 和 chunk 的相关性标注"、"给定上下文的答案忠实性标注"。这类样本结构简单,大模型预标注的准确率很高,人工审核更多是走查。
  • 端到端评测集:针对完整流程,输入一个用户问题,输出整个回答/工具调用序列。这类样本需要标注者理解完整上下文,预标注只能提供草稿,人工审核的负担明显更重。

我做的时候是分开建集的。单点评测集用来快速验证模块改动,端到端评测集用来做版本验收。如果是刚开始做,建议从单点评测集切入,因为它的标注一致性更容易保证,产出速度也更快。

1.3 "预标注 + 人工审核"为什么适合这种场景

这套模式本质上就是大模型行业里常说的 human-in-the-loop,但很多人把它想复杂了。它的逻辑其实是:

让大模型先做一遍"笨而全"的标注,把所有候选样本和初步结论都列出来,然后让人来做判断和修正,而不是让人从零开始写标注。

举个例子,人工从 5000 条真实用户日志里筛选出 "query 和知识片段相关" 的样本,可能要两天。大模型预标注先把候选和相关度分数都跑出来,人工只需要审核模型标记为"相关"的那些,再抽查标为"不相关"的,工作量直接降一个数量级。

当然这套模式的前提是:你有一个质量还过得去的大模型,以及标注任务适合用自然语言指令描述。如果任务极其依赖特殊领域知识、需要看图或者听音频,那预标注的可用性会大打折扣。

2. 动手前先把标注类型和数据结构定义清楚

2.1 按链路拆标注任务:检索、生成、Agent 三类

我在项目里把标注任务拆成了三类,每类对应一条独立的数据集文件。拆开的原因是混合在一份数据里会让审核标准特别混乱——比如一个检索相关性问题和一个 Agent 工具调用问题,你很难用同一套打分标准去审核。

第一类是检索相关性标注。输入是一条用户问题和一个知识片段,输出是相关与否,以及相关程度的等级分。这一类的核心作用是用来调 embedding 参数、chunk 大小、召回条数。

第二类是生成忠实性标注。输入是用户问题、检索到的上下文片段、模型生成的答案,输出是答案是否有幻觉、是否完整、是否可读。这类数据直接喂给评测脚本,用来在发版前卡模型回答质量。

第三类是 Agent 轨迹标注。输入是多轮对话记录和模型产生的工具调用序列,输出是意图识别是否正确、工具选择是否正确、参数是否合理、最终回复是否有效。这块最难标注,我留在项目后期才重点补。

2.2 每条样本长什么样:数据结构设计

数据结构的坑,我一开始就踩了。第一版预标注脚本输出的样本字段是"意思对了但没法聚合统计",比如把"是否相关"写成了自然语言"相关",结果后续按分数统计时不得不做额外的字符串映射。后来我统一成了严格的 JSON 结构,每类样本单独一个字段 schema。

以检索相关性样本为例:

{ "sample_id": "ret-00001", "type": "retrieval_relevance", "query": "差旅费报销需要保留哪些票据?", "chunk": "报销差旅费时,须提供发票、支付记录及行程单;住宿费还需提供水单。", "pre_label": { "is_relevant": 1, "relevance_score": 1, "evidence": "chunk 内容直接回答了票据类型问题", "needs_review": false }, "human_label": null, "status": "pending", "remark": "" }

字段统一以后,下游脚本处理起来特别省事,人工审核的时候也知道该看哪些字段。这里我强烈建议在项目第一天就把 schema 定好,不要嫌麻烦,后面改结构会牵动脚本、审核界面、统计逻辑三处一起改。

2.3 定义打分口径,别让"相关"变成玄学

我见过很多项目死在"标注标准不统一"上。两个审核员对同一对 query-chunk,一个说相关一个说不相关,然后开始互相说服,效率极低。为了规避这个问题,我把打分口径尽量做了行为化描述

相关性分三档:

分值含义判断标准
0不相关chunk 与 query 的主题无交集,或仅有泛泛的背景表述
1部分相关chunk 只涉及 query 的某一方面,或需要其他片段补充才能完整回答
2完全相关chunk 可独立支撑完整回答,不需要额外信息

回答忠实性也分三档,但口径完全不同:

分值含义判断标准
0幻觉/错误答案中存在无法由 context 支撑的关键信息
1部分忠实答案主体正确,但存在细节编造或遗漏
2忠实完整答案中关键信息都能在 context 中找到依据,且覆盖完整

定义完标准后,我还让标注人员在审核时强制填写 evidence 字段,也就是"你为什么给这个分"。这个动作能逼着审核员回到标准上,而不是凭感觉打分。

3. 大模型预标注的具体实现

3.1 预标注适合干什么,不适合干什么

这一步是整个流水线的引擎。我在实践中找到的边界是:预标注特别适合做分类、判断、打分这类"判别式"任务,因为这类任务答案空间有限,大模型表现稳定;但如果是"根据对话生成一条符合要求的测试问题"这类开放生成任务,预标注结果会五花八门,后续审核成本反而高。

我实际跑的预标注任务主要有:

  • 给真实用户日志中的 query 打上意图标签和难度标签;
  • 判断 query 与检索 chunk 的相关性并打分;
  • 根据给定 context 和行为规范,生成低质量/高质量参考答案各一版;
  • 对 Agent 工具调用轨迹标记"是否出现参数缺失"等异常点。

这些任务的共同点是只需要模型输出有限个离散标签或短文本,非常适合用 prompt 约束。

3.2 prompt 模板:把任务收敛到单点判断

预标注的 prompt 设计是我花时间最多的地方。我总结出一条核心经验:prompt 一次只做一件事,并且强制输出 JSON。不要试图让模型在一个 prompt 里既做相关性判断又做答案生成,那样指标会互相污染,后续解析也容易翻车。

下面这个是我在检索相关性预标注时用的模板,我减掉了业务字段,只保留骨架:

你是一名评测数据标注助理。请根据给定的用户问题 query 和知识库片段 chunk,完成相关性判断。 判断标准: - 0分:不相关。chunk 与 query 主题无关,或只有泛泛的背景表述。 - 1分:部分相关。chunk 仅覆盖 query 的部分信息,单靠它不足以完整回答 query。 - 2分:完全相关。chunk 本身可以支撑完整回答 query。 要求: 1. 只输出 JSON,不要输出任何解释。 2. JSON 格式如下: {"is_relevant": 0或1或2, "evidence": "一句话说明你的判断依据", "missing_info": "如果答案不完整,指出缺少什么"} query: {query} chunk: {chunk}

这里有几个关键细节:

  • 输出格式约束放在 prompt 的最后,模型对末尾指令的记忆更强;
  • 评分标准描述必须包含反例边界,"仅覆盖部分信息"这句话能明显减少模型打 2 分的冲动;
  • 强制让模型输出 evidence,虽然这会消耗一点 token,但它能帮人工审核快速定位模型判错的逻辑,审核效率高很多。

3.3 批量调用脚本:并发、重试与成本控制

prompt 设计好以后,剩下就是批量跑了。这里最怕的是脚本写得太粗暴,一跑就崩,中间还夹着限流和 JSON 解析错误。我自己的脚本结构大概是这样的:

"""pre_annotate.py 通过 OpenAI 兼容接口批量调用大模型做预标注。 """ import json import time from concurrent.futures import ThreadPoolExecutor, as_completed from openai import OpenAI client = OpenAI( base_url="http://your-model-service.example.com/v1", api_key="your-api-key", ) SYSTEM_PROMPT = "你是一名严谨的评测数据标注助理。" USER_PROMPT_TEMPLATE = """ 请判断下面这个知识片段和用户问题是否相关。 (评分标准省略,按上面 3.2 节的完整模板替换) query: {query} chunk: {chunk} """ def annotate_one(item: dict) -> dict: query = item["query"] chunk = item["chunk"] resp = client.chat.completions.create( model="your-model-name", messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": USER_PROMPT_TEMPLATE.format(query=query, chunk=chunk)}, ], temperature=0.0, max_tokens=200, response_format={"type": "json_object"}, ) content = resp.choices[0].message.content parsed = json.loads(content) item["pre_label"] = parsed return item def run(items: list[dict]) -> list[dict]: results = [] with ThreadPoolExecutor(max_workers=8) as pool: futures = {pool.submit(annotate_one, it): it for it in items} for fut in as_completed(futures): try: results.append(fut.result()) except Exception as exc: # 失败样本不能直接丢掉,要留到重试队列 failed_id = futures[fut]["sample_id"] print(f"sample {failed_id} failed: {exc}") return results if __name__ == "__main__": with open("raw_samples.json", "r", encoding="utf-8") as f: items = json.load(f) annotated = run(items) with open("pre_annotated.json", "w", encoding="utf-8") as f: json.dump(annotated, f, ensure_ascii=False, indent=2)

这段脚本我实际改动过很多次,踩过的坑包括:

  • 并发数不要一次性拉太高。某些模型服务的限流策略很隐蔽,20 并发跑一半就开始超时。我最后稳定在 8 到 12 并发。
  • temperature 必须设 0。预标注阶段要的是确定性和稳定性,温度高了同一个样本每次跑出来分数都不一样,这会在人工审核时造成大量困惑。
  • JSON 解析要做异常兜底。即使加了 response_format,模型偶尔也会输出残缺 JSON。脚本里捕获异常后,把失败样本单独落盘,而不是中断整个批。
  • 成本估算。我习惯在跑批前用 token 估算脚本算一次总量。1 万条 query-chunk 相关性标注,如果每条消耗约 600 token(含输入输出),大概需要 600 万 token。核算下来在可接受范围内,但如果你用的是外面 API,必须在跑之前心里有数。

3.4 预标注完成后先做一轮"模型自检"

很多人跑完预标注就直接扔给人工审核,这是不对的。我会先对预标注结果做一轮规则层面的统计,过滤掉明显异常的样本。比如相关性分数分布明显偏斜(90% 以上都打 2 分),说明评分标准里的反例描述失效了;比如 evidence 字段出现重复内容,说明模型在"抄模板"而不是真的在判断。

这一步看起来简单,但能省下大量人工审核时间。我的经验是:在进入人工环节前,先用规则脚本把"一眼假"的预标注样本过滤掉,或者标记出来单独处理。具体规则包括:得分全部相同、内容为空、JSON 结构异常、同一 query 的相邻 chunk 分数冲突等。

4. 人工审核环节怎么组织

4.1 审核工作台选型:不需要高大上

预标注跑完,接下来就是人工审核。很多团队一上来就采购商业标注平台,说实话有点杀鸡用牛刀。我这次选型的原则很简单:审核员能快速看到上下文,能直接改 label,能留下备注

我自己用的是电子表格类工具,把所有待审核样本平铺开,每行一条,预标注字段分列展示,审核员只需要改两个单元格:human_label 和 remark。这样做的好处是上手零成本,问题是没有审计追踪,如果审核员改错了不好追溯。

如果你的团队已经有内部工具平台,或者愿意搭一个简单的 Web 界面,我建议至少做到:展示原始 query、chunk、模型答案、预标注结果,以及一个下拉框选择最终标签。不需要复杂的流程引擎,一个可以多人协作的表格系统足够跑通前几千条数据。

这里补充一个选型建议表格:

方案优点缺点适合场景
电子表格(Excel/在线表格)上手快、零成本、容易改字段审计能力弱、多人同时编辑易冲突百到千级规模,团队 2-3 人
开源标注平台(Label Studio 等)字段类型丰富、支持多人协作部署和配置有学习成本千到万级规模,需要长期迭代
自研审核界面完全贴合字段和流程初期成本高万级以上且需要复杂审核流

4.2 审核策略:全量审核还是抽检?

对于评测数据集,我的建议是首版全量审核。很多人觉得全量审核成本太高,但我认为评测集是用于后续所有模型迭代的,样本量一般不会特别大,如果真的特别大,应该思考是不是混入了大量噪音。第一批数据全量审核能把标准差住,后续新增数据就可以用抽检策略来控制成本。

抽检策略我用的方案是分层抽样。先把预标注结果按分数分层,比如相关性 0 分、1 分、2 分各抽一定比例;再按 query 类型分层,确保每类意图都有覆盖。这里最重要的原则是:不能只抽模型判得"好"的样本。如果你只审核模型打了 2 分的样本,那 0 分样本的质量就完全失控了。

还有一个数据量层面的建议:我做第一批评测集时给自己定的目标是 800 到 1200 条有效样本。这个量级既能cover住主要业务场景,又不至于让审核周期拖垮项目节奏。如果你业务范围特别大,也建议按业务线拆成多个子集,而不是追求一次性搞定一个"万能评测集"。

4.3 一致性检查:避免审核员各说各话

审核环节最大的风险不是慢,而是标准漂移。两个人审同一批数据,前 100 条还能互相统一,后 300 条就越跑越偏。解决这个问题的办法是引入一致性检查。

我的做法是:从预标注结果里随机抽 30 条,让两位审核员独立标注一遍,然后计算他们之间的一致率。如果两人在"相关/不相关"二分类上的一致率低于 85%,我会把分歧样本找出来开会对齐,再更新标注规范。

这里有个特别现实的经验:分歧往往不是"某个人错了",而是标注标准本身有歧义。比如"部分相关"和"完全相关"的边界,可能在一个真实案例里就是模糊的。这时候需要在标准里补一个具体例子,用"当 chunk 包含答案但不包含推导过程时,最多给 1 分"这类规则来消除歧义。

4.4 规范沉淀:把审核过程变成文档

这次项目我最大的收获之一是:审核环节不只是"改 label",它还在沉淀标注规范。我会要求审核员在遇到拿不准的 case 时,不要闷头做决定,而是在备注里写一句"这个 case 我认为怎么判,理由是啥"。隔几天统一整理一次,把高频分歧和典型例子写进标注规范文档里。

这份文档最后会成为评测集的一部分,也是新审核员培训的教材。有了它,后面哪怕换个人来审核,质量也不会出现断崖式下跌。

5. 常见问题与排障实录

5.1 预标注三连坑:幻觉、漏检、格式抖动

先讲幻觉。大模型在回答忠实性标注时,有时候会"替用户脑补"一个更好的答案,然后用自己的脑补去评价原始答案,导致把原本有幻觉的答案判成"忠实完整"。我在 prompt 里反复强调"只能依据给定的 context 判断,不能引入外部知识",同时要求输出 evidence 时引用原文片段,比单纯说"你是一个严格的标注员"有用得多。

再讲漏检。在 Agent 工具调用轨迹标注里,预标注模型经常漏掉"参数类型错误"这类问题,比如把时间参数传成了字符串而不是时间戳。这类依赖代码语义的任务,纯靠 prompt 很难完全改善,我最终的解决方案是:在预标注 prompt 里提供工具的 JSON Schema,并要求模型先解析参数结构再判断,漏检率下降明显。

格式抖动是另一个烦人的点。我见过模型输出is_relevant: 2而不是 JSON 对象,也见过把relevance_score写成relevanceScore的。解决方式是三重保险:response_format 强制 JSON、prompt 里明确字段名、代码里做容错解析(比如允许大小写不一致)。

5.2 测试集污染:别让评测集"作弊"

做评测数据集必须警惕数据泄漏。我这次用的原始样本一部分来自线上日志,一部分来自人工拟写的业务问题。如果直接拿网上的开源问答集来充数,很可能这些内容已经在模型训练数据里了,评测结果会虚高。

我的排查经验是:如果某个版本的模型在评测集上分数突然暴涨,先别高兴,去检查评测集和训练语料的 overlap。更稳妥的做法是用最近线上且未公开的日志作为评测集的主要来源,即使数量少一些,可信度也远高于拿公开数据拼凑。

5.3 标准漂移与"审核员疲劳"

标准漂移在前面提过,这里再补一个场景:审核员看到大量相似样本后会产生惯性,后面的样本可能凭直觉打分。我的应对手段是:

  • 把待审核样本按类型乱序排列,避免同一类 query 连续出现 50 条;
  • 每条样本都要求填 evidence 字段,这一步虽然烦,但能强制审核员保持注意力;
  • 每人每次连续审核不超过 200 条,中间必须休息。

5.4 兜底建议:小步快跑,先跑通 100 条

最后给一个非常实际的建议:不管你的目标评测集是 1000 条还是 10000 条,第一步一定是跑通"100 条闭环"。选 100 条样本,完成预标注、人工审核、统计指标、修正 prompt、迭代标准这整个循环。等这 100 条的质量已经能让你信任时,再放量去跑剩余数据。

为什么是 100 条而不是 1000 条?因为在 100 条的量级上,你可以人工逐条检查预标注质量,快速发现 prompt 里的系统性偏差,比如"模型总是把包含关键词但语义无关的 chunk 判为相关"。等你修正完 prompt,再拿另一批 100 条验证,确认改进有效后再放量,返工成本会低很多。这个思路和写代码先写单元测试是一模一样的逻辑,在数据工程里同样成立。


我自己在实际操作中的体会是:大模型预标注 + 人工审核这套流程,真正难的不是代码,也不是 prompt,而是在项目初期就确定一套清晰且可执行的标注规范。规范定得越细,预标注准确率越高,人工审核需要改的地方就越少。后面如果你有精力,还可以在这个基础上继续扩展:比如把审核结果沉淀成 few-shot 样本,反哺给预标注模型做微调;或者把标注脚本变成定期执行的离线任务,每次发版前自动产出一批新的评测样本。这些都是后话,先把第一批数据集跑出来,比什么花活都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询