ARTICLE DETAIL

资讯详情

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

LLM Ops 评测与可观测实战(2):golden dataset 构建:线上数据的采样与标注流程

LLM Ops 评测与可观测实战(2):golden dataset 构建:线上数据的采样与标注流程 问题背景上一篇用两组模拟数据说明了一把太小的尺子如何让 47% 的正确结论被判反结论是评测集必须存在、规模要按可分辨差距倒推。这一篇解决尺子上的刻度从哪来一个跑了三个月的客服 RAG 机器人日志里躺着八万多条真实会话怎么从中提炼出三百来条能代表线上真实分布、标注质量可复核的金标准数据集golden dataset。直接照抄线上问题会踩两个坑一是头部意图吃掉八成流量随机抽出来评测集全是退款多久到账越界拒答、多轮追问这些恰恰最容易出事故的长尾一条没有二是线上原话本身是脏的——错别字、半截话、夹带订单号的隐私串不清洗不能用。本篇把整条流水线拆成四段分层采样、隐私脱敏、近重复去重、双人标注加一致性仲裁每段给出可操作的判据。核心原理**采样的第一原则是按业务意图分层而不是按流量均匀。**均匀随机抽样的方差与层内样本量成反比而流量分布天然头重脚轻占比 3% 的多轮追问意图在 300 条均匀样本里平均只有 9 条单这一层的通过率估计误差就逼近 ±30 个百分点——等于这层没测。工程解法是带下限的分层抽样stratified sampling with floor大层按流量占比分配配额小层强制保底比如每层不少于 25 条代价是评测集分布与线上分布有意的偏离收益是每层都有最低可用分辨率。要注意这个偏离必须在报告里写明分层评测的总分不再是线上期望表现的无偏估计它是各层分数的加权平均权重由你声明而不是流量决定。**标注对象不是正确答案而是判分合同。**golden dataset 里每条用例的真正资产是它的评分契约参考回答要点必须出现哪些事实、负向约束出现哪些内容直接判错比如承诺具体到账日期、以及分类标签该条属于哪个意图、难度、是否含陷阱。三个人独立对同一批 50 条写要点交集部分就是可自动化的硬规则差集部分暴露的是需求理解分歧——标注流程顺带产出的这份分歧清单往往比数据集本身更有价值。**一致性用双标 仲裁闭环控制。**每条用例至少两人独立标注不一致的进仲裁批次级别跟踪标注者间一致性下一篇会用 Cohen’s kappa 给出算法。经验阈值原始一致率低于 80% 的批次整批返工因为这说明标注指南本身有洞修补指南比返工标注便宜。**去重要发生在标注之前。**近重复问题“退款多久到账与退款大概几天能到呢”不仅浪费标注预算还会让评测集对某个高频措辞隐性过采样。字符级 n-gram 的 Jaccard 相似度在中文短query上简单有效阈值 0.8 上下能把同一政策的换皮问法并进一个簇每簇只标一条。实验一分层抽样 vs 均匀随机长尾层的分辨率差多少本机以确定性模拟演示采样机制固定种子生产用真实日志与抽样脚本。importrandom INTENTS{policy_qa:0.42,# 售后政策问答order_query:0.28,# 订单/物流查询complaint:0.12,# 投诉安抚chitchat:0.09,# 闲聊跑题edge_reject:0.06,# 应拒答的越界问题multiturn_followup:0.03,# 多轮追问改写}FLOOR25# 每层最低样本量(小意图保底)TARGET300# 评测集目标规模pool[]rngrandom.Random(7)forintent,shareinINTENTS.items():for_inrange(int(5000*share)):pool.append((intent,q_%s_%04d%(intent,len(pool))))defstratified(pool,target,floor,seed):rngrandom.Random(seed)by_intent{}foriteminpool:by_intent.setdefault(item[0],[]).append(item)picked[]forintent,itemsinsorted(by_intent.items()):quotamax(floor,round(target*len(items)/len(pool)))pickedrng.sample(items,min(quota,len(items)))returnpickeddefnaive(pool,target,seed):rngrandom.Random(seed)returnrng.sample(pool,target)stratstratified(pool,TARGET,FLOOR,seed11)naive_setnaive(pool,TARGET,seed11)defdist(sample):c{}forintent,_insample:c[intent]c.get(intent,0)1returncprint(意图 | 线上占比 | 分层抽样 | 均匀随机抽样)ds,dndist(strat),dist(naive_set)forintent,shareinINTENTS.items():print(%-20s | %6.1f%% | %7d | %7d%(intent,share*100,ds.get(intent,0),dn.get(intent,0)))print(分层抽样总数 %d, 均匀随机总数 %d%(len(strat),len(naive_set)))运行输出意图 | 线上占比 | 分层抽样 | 均匀随机抽样 policy_qa | 42.0% | 126 | 139 order_query | 28.0% | 84 | 75 complaint | 12.0% | 36 | 40 chitchat | 9.0% | 27 | 19 edge_reject | 6.0% | 25 | 17 multiturn_followup | 3.0% | 25 | 10 分层抽样总数 323, 均匀随机总数 300对比最后两行均匀随机下多轮追问层只有 10 条上一篇的置信区间表告诉我们这个规模的通过率半宽超过 ±28 个百分点纯属装饰越界拒答层 17 条同样不够看分层保底后两层各 25 条总数只多花 23 条预算长尾分辨率却从不存在变成可用。另一个细节是总数 323 300下限配额会让总量微超目标工程上接受这个超支或者从最大层里扣回——但绝不为凑整数砍掉小层。实验二标注前去重把同一政策的换皮问法并进一簇importrandom BASE_QUERIES[七天无理由退货怎么操作,退款多久能到账,订单什么时候发货,发票开错了能重开吗,会员积分怎么兑换,跨境商品支持退换货吗,商品价格保价规则是什么,客服人工电话是多少,]SUFFIXES[,呢,啊,急,谢谢,麻烦告诉我,在线等]rngrandom.Random(5)candidates[]forqinBASE_QUERIES:candidates.append(q)forsuffixinrng.sample(SUFFIXES,3):mutatedqsuffixifrng.random()0.4:# 再抖掉一两个字制造脏变体posrng.randrange(len(mutated)-1)mutatedmutated[:pos]mutated[pos1:]candidates.append(mutated)extrarng.randrange(len(BASE_QUERIES))# 另一主题的干净样本candidates.append(BASE_QUERIES[extra][:-2])defshingles(s,k3):return{s[i:ik]foriinrange(max(len(s)-k1,1))}defjaccard(a,b):returnlen(ab)/len(a|b)if(a|b)else0.0threshold0.8clusters[]# 每簇保留第一个成员作为代表shingle_cache[shingles(q)forqincandidates]foridx,qinenumerate(candidates):placedFalseforclusterinclusters:ifjaccard(shingle_cache[cluster[0]],shingle_cache[idx])threshold:cluster.append(idx)placedTruebreakifnotplaced:clusters.append([idx])kept[clusters[c][0]forcinrange(len(clusters))]print(候选 %d 条 - 去重后保留 %d 条(阈值 Jaccard%.1f)%(len(candidates),len(kept),threshold))sizessorted((len(c)forcinclusters),reverseTrue)print(簇大小分布 Top5:,sizes[:5])print(示例簇(代表 | 被合并的近似重复):)biggestmax(clusters,keylen)print( 保留:,candidates[biggest[0]])forminbiggest[1:]:print( 合并:,candidates[m])运行输出候选 40 条 - 去重后保留 27 条(阈值 Jaccard0.8) 簇大小分布 Top5: [4, 3, 3, 2, 2] 示例簇(代表 | 被合并的近似重复): 保留: 跨境商品支持退换货吗 合并: 跨境商品支持退换货吗啊 合并: 跨境商品支持退换货吗呢 合并: 跨境商品支持退换货吗谢40 条候选压成 27 条三成预算省下来了更重要的是簇大小分布本身就是流量结构的信息——跨境退换货聚成 4 条簇说明该政策有歧义、用户变着法问这反而是提示该层要加权采样的信号。两点实操提醒真实日志里 Jaccard 阈值建议从 0.8 起调人工抽检 50 个合并决策再定中文短文本对 3-gram 敏感0.7 以下开始误伤同主题不同意图的问句数据量上万后逐对比较是平方复杂度换 MinHash 或倒排块blocking做候选对生成。流水线落地四步第一步导出与脱敏先于行日志落地评测工作区之前过一遍正则加实体识别手机号、地址、订单号一律替换为保留格式的假值138****0001这类脱敏规则本身要 review——“帮我查订单 20240713xxxx里的日期串经常被漏掉但它能关联到真人。第二步分层配额表评审业务方、标注方、算法方对每张意图层的配额和目标占比签字避免出分后争论为什么闲聊占一成”。第三步标注平台留痕迹每条用例记录标注人、时长、修改轮次纯秒标与十分钟标的可信度不是一个量级事后质检按这个元数据抽查。第四步季度滚动更新每季度从线上新意图里补 20% 新样本、同时把过时的样本降级为归档可见、不进总分防止评测集老化成2024 年的互联网。常见陷阱其一标注指南用形容词“回答有帮助没有可操作性指南必须写成若回答包含 X 且未包含 Y 则判通过级别。其二让最懂业务的人单干标注专家标注最准但单点不可复核、进度是瓶颈且专家脑中的标准没写进指南就永远是黑箱——双标的意义一半在质量一半在逼专家把知识外化。其三评测集里保留隐私原话以便还原现场”金标准数据集会被很多工具和很多人访问脱不干净的样本直接不进集。其四把失败案例只修不存处理完一个线上投诉就删掉现场等于每个 bug 只付一次学费正确动作是清洗后进评测集。其五去重用力过猛把支持退换货吗与退换货要多久并成一簇两个意图被一个合并决策吃掉所以合并必须抽检。落地清单按意图分层设配额并给小层保底≥25 条报告里声明加权方式全量脱敏先于一切规则评审覆盖能关联到真人的组合字段每条用例写判分合同必含要点 负向约束 意图/难度标签双人独立标注批次一致率 80% 整批返工并修指南标注前 n-gram Jaccard 去重0.8 起步人工抽检合并决策季度补样 20%线上坏案例清洗后入集数据集有了接下来的瓶颈立刻转移到打分三百条用例每次变更都跑一遍谁来判每一条的好坏人判太快就糙、太准就贵。下一篇《LLM Ops 评测与可观测实战3自动打分规则评分、LLM-as-judge 与一致性校验》给出三层打分器架构以及怎么证明你的自动打分器不是在瞎打分。参考来源WikipediaStratified samplinghttps://en.wikipedia.org/wiki/Stratified_samplingWikipediaJaccard indexhttps://en.wikipedia.org/wiki/Jaccard_indexWikipediaMinHashhttps://en.wikipedia.org/wiki/MinHashWikipediaInter-rater reliabilityhttps://en.wikipedia.org/wiki/Inter-rater_reliabilityLangSmith 评测与数据集文档https://docs.smith.langchain.com/
返回列表