9.2 构造评测集:查询与金标对


9.2 构造评测集:查询与金标对

本节摘要:评测集 = 一组 (查询, 金标) 对,金标是 {chunk ID: 相关度} 的字典。查询三来源:①Agent 工具调用日志(6.3 埋的钩子)——查询分布天然真实,且带隐式反馈(搜索后模型 read 了哪个文件,哪个就是弱金标);②开发者访谈——问同事"你上次想找什么没找到",半小时能攒十条高价值难题;③git 历史弱标注——commit 首行当查询、变更文件当弱金标,量大但噪声高,必须人工复核。金标纪律:三级相关度(9.1)双人独立标注、分歧仲裁;金标指向 4.2 稳定 chunk ID 而非行号;上线前抽样人审金标对(金标错比检索错更隐蔽)。配比与规模:起步 50~100 条(经验值),标识符型/意图型/混排型三类的配比贴近查询日志的实际分布——7.4 表 3 已经证明过权重甜点随查询类型漂移,评测集的配比就是把这个信息带进调参。评测集构造的通用方法论(抽样、标注协议、一致性检验)详见《Evals 实战》第 3 章,本节聚焦代码检索特有:chunk 级对齐、Agent 日志挖掘与 git 弱标注。

学习目标

阅读完本节,你应当能够:

  1. 写出评测集的 JSONL 格式与各字段的用途;
  2. 从三个来源采集查询并说清各自的信噪比;
  3. 执行三级金标的标注纪律(双人、仲裁、抽样人审);
  4. 解释配比为什么要贴近查询日志、规模为什么 50~100 条起步。

一、评测集的形态

{"qid": "q001", "query": "处理用户注销时删了哪些东西", "type": "intent", "gold": {"demo_repo/auth/session.py:3-8:kill_session": 3, "demo_repo/storage/token_store.py:1-4:revoke_refresh_token": 2}} {"qid": "q002", "query": "upload_with_retry", "type": "identifier", "gold": {"demo_repo/tasks/upload_task.py:3-10:upload_with_retry": 3}} {"qid": "q003", "query": "revokeToken 在哪被调用", "type": "mixed", "gold": {"demo_repo/storage/token_store.py:1-4:revoke_refresh_token": 3, "demo_repo/auth/session.py:3-8:kill_session": 2}}

字段说明:type 三分类(identifier/intent/mixed)——7.4 表 3 证明过不同类型的参数甜点不同,打标后指标可以分组看;gold 的键是 7.2 的稳定 chunk ID(path:start-end:symbol),值是 9.1 的三级相关度。金标不写行号:行号随编辑漂移,ID 至少跟着符号与区间走;仓库演进后按 ID 复核金标,比按行号追着改便宜一个量级。

二、查询从哪来:三来源与信噪比

来源 做法 信噪比 备注
Agent 工具日志(6.3) search_code 每次调用的 query + 返回命中 + 后续 read 的文件 高:真实分布 + 隐式反馈 隐式反馈要谨慎:read 了 ≠ 相关,需人审确认
开发者访谈 "你上次想找什么、最后在哪找到的" 高:难题富集 半小时约 10 条;适合补充日志里没覆盖的类型
git 历史弱标注 commit 首行当查询、变更文件当弱金标 低:量大噪声大 只当候选池,人工复核后才入集
# weak_label.py — 从 git 历史造弱金标候选(写法示意:subprocess 调 git,需人工复核) import subprocess def commits_touching(repo: str, since: str = "6 months ago") -> list[tuple[str, list[str]]]: """返回 (commit 首行, 变更文件列表)。合并提交与空首行会被朴素解析漏掉—— 弱标注只求量,不求全。写法示意,以 git 文档为准。""" out = subprocess.run( ["git", "-C", repo, "log", f"--since={since}", "--name-only", "--pretty=%s", "--no-merges"], capture_output=True, text=True, check=True).stdout pairs, subject, files = [], None, [] for line in out.splitlines(): if not line.strip(): # 空行 = 一条 commit 结束 if subject and files: pairs.append((subject, files)) subject, files = None, [] elif subject is None: subject = line.strip() else: files.append(line.strip()) if subject and files: pairs.append((subject, files)) return pairs

弱标注的使用姿势:跑出几百条候选 → 按类型分层抽样 ~50 条 → 人工判断"这个 commit 首行像不像一次真实的代码检索查询"(fix: typo 类直接丢)→ 像的再定金标。全程人工的那步不可省——弱标注是候选池,不是评测集

三、金标怎么标:纪律比热情重要

标注协议(可直接抄给两位标注者):

  1. 每条查询先独立检索一遍(用当前系统),再看 gold 候选(含系统没召回的——从 git 变更、文件结构里补);
  2. 按 9.1 三级表打分,规则疑问记备注不打中庸分;
  3. 两人独立完成后对表:等级完全一致的对入集;差一级的仲裁(第三人或讨论);差两级以上的说明两人对"查询意图"理解不同——这条查询本身有歧义,改写或丢弃;
  4. 上线前抽样 10% 人审金标对:金标错比检索错更隐蔽——它会让你把对的排序判成错的,且没有报错。

💡 金标是评测集里唯一"手工且持续"的部分。仓库演进会让金标漂移(函数改名 → ID 失效):把"金标复核"排进每次嵌入模型或切分参数升级的流程里(9.3 的循环里有这个检查点),漂移的 ID 在 metas.jsonl 里对不上号时就该处理——评测脚本应当对"金标 ID 找不到 chunk"报警,而不是默默跳过。

四、规模、配比与版本化

  • 规模:起步 50~100 条(经验值)。低于 30 条时指标的置信区间宽到没法分辨 2 个点的改进;超过 200 条后边际价值递减,增量留给回归期自动扩充(线上坏例回填,见下);
  • 配比:按 6.3 日志统计的 type 分布配。没有日志的冷启动,从 identifier:intent:mixed = 3:5:2 起步(经验值——真实使用里意图型占大头,标识符型有 BM25 兜底不需要过度采样);
  • 版本化:评测集进版本库(JSONL diff 友好),每次增删改都留 commit message 说明动机;调参用全量集,另留 20% 做_holdout(从不参与调参)防过拟合——参数在调参集上涨分、holdout 不涨,就是过拟合的信号;
  • 回填:线上发现的新坏例(用户反馈、Agent 走了弯路的 trace)修完后回填进评测集——评测集是活的资产,不是一次性试卷。评测集的持续运营方法论详见《Evals 实战》第 3 章。

本节要点回顾

  1. 评测集 = (query, type, {chunk ID: 三级相关度});金标指向稳定 ID,不写行号;
  2. 查询三来源:Agent 日志(真实+隐式反馈)、访谈(难题富集)、git 弱标注(候选池,必须人审);
  3. 标注纪律:双人独立、分级仲裁、歧义查询改写或丢弃、上线前抽审 10%;
  4. 50~100 条起步、配比贴日志、留 holdout、坏例回填——评测集是活资产。

尺子(9.1)与被测物(9.2)都齐了。下一节把两者装进循环:跑评测、记历史、一次一变量地迭代,再用回归护栏守住阵地。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U