news 2026/8/11 5:03:12

LLM 测试的 testing LLMs:模型本身的评估(eval harness + golden set + drift)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM 测试的 testing LLMs:模型本身的评估(eval harness + golden set + drift)

系列专栏:测试工程师每日一博 · Day 26(2026-08-10,周一,8 月第二周)
上一篇:[Day 25 · 性能压测的容量规划:把"找拐点"上升为"找容量曲线方程"]
这是系列的翻面时刻 🌟—— 前 25 篇都在讲"如何用 LLM 帮测试工程师"(Day 13/22/24/25),Day 26 第一次回答"如何测试 LLM 自身"。也就是说,被测对象不再是 Java 代码、不再是 HTTP 接口,而是模型本身
目标:给一份 eval harness + golden set + drift 三件套的工程化清单,读完能直接照搬落地。这是测试工程师迎接 LLM 时代的核心素养。

一、为什么 Day 26 是翻面时刻

工业里把 LLM 接进自家产品的团队一夜变多——客服 / 智能搜索 / 代码助手 / 内容生成。他们的核心交付物就是 LLM 输出。所以测试工程师必须翻面,被测对象从代码翻到模型。先回看过去 25 篇 LLM 出现的场景:

DayLLM 的角色
Day 13LLM 生成测试用例 / selector 自愈 / flaky 根因
Day 16LLM 故障复盘自动归类
Day 17LLM 候选风险点提 PR review
Day 22LLM 视觉自愈 selector
Day 24LLM 安全报告解读
Day 25LLM 容量曲线翻译

也就是说,Day 26 之前 LLM 都是"工具",被测对象是产品代码。Day 26 翻面 —LLM 自身是被测对象

这一翻面工业意义重大。今天把 AI 接进自家产品的团队越来越多(RAG 客服、智能搜索、代码助手…),这些团队的核心交付物本身就是 LLM 输出。如果你的"测试工程师"对 LLM 自身毫不了解,等于对一个全新测试维度视而不见

二、测试 LLM 跟测试代码的本质区别

正常的 Java 代码:输入确定 + 内部状态确定 → 输出确定。assertThat(actual).isEqualTo(expected)能精确断言。

LLM 完全反着:

维度传统代码LLM
确定性强(同输入同输出)弱(temperature / 采样随机)
输出形式强类型对象自由文本
错误定义异常 / 错误值主观判断"答得对不对"
覆盖率行 / 分支 / 字节码没有公认的覆盖率概念
flaky罕见(多为并发 / 时钟)常态(每次 inference 都可能不同)
回归测试单测断言N-shot 标注 + 评分矩阵
工具链JUnit / pytest / coverage.pypromptfoo / LangSmith / DeepEval

注意"覆盖率"那一栏——Day 26 不能拿 JaCoCo 那套来回答"LLM 测了多少"。LLM 测试的"覆盖"不是一个百分比,是一组维度(准确率 / 召回 / 安全 / 偏见 / 多语言 / 长文 / 上下文…),每个维度都有它自己的 golden set。

三、eval harness:LLM 测试的工具集

LLM 测试最核心的工具是eval harness——它给一段 LLM 输出自动打分,产生数据化的"对错判断"。

┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ golden set │───→│ LLM under │───→│ scoring │───┬→ 分数 + 报告 │ (N 条样本) │ │ test │ │ function │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ┌─ scorer 分类 ─────────────────────────┤ │ │ exact match │ semantic match(LLM-as-judge) │ regex / structured-validation │ human eval spot check │

四个常见 scoring 方法:

3.1 exact match(精确匹配)

只适合结构稳定输出,如分类标签:

defexact_match_score(llm_output:str,expected:str)->float:return1.0ifllm_output.strip().lower()==expected.lower()else0.0

优点:可复现、零歧义;
缺点:对自由文本输出几乎不能用作主指标。

3.2 semantic match(LLM-as-judge)

让另一个 LLM 当裁判:

defllm_judge_score(question:str,reference:str,candidate:str)->float:""" 用 GPT-4 之类强模型当裁判,在很多工业里是 SOTA 做法 """prompt=f""" 给定问题:{question}标准答案:{reference}候选答案:{candidate}用 1-5 给候选答案打分,只输出数字。 """score=call_judge_llm(prompt)returnfloat(score)/5.0# 归一化到 0-1

优点:能判断"语义对错";
缺点:judge LLM 也有 bias,且 evaluate 自身的成本不低。

3.3 regex / structured-validation

期望 JSON / 表格 / 代码的输出:

importjson,redefschema_score(output:str)->float:"""输出必须是合法 JSON 且包含必填字段"""try:data=json.loads(output)except:return0.0required={"test_name","assertions","expected"}return1.0ifrequired.issubset(data.keys())else0.5

优点:严格,可复现;
缺点:只校验"形状",不校验"内容质量"。

3.4 human eval spot check

定期抽 N 条样本给人 review,这是最后一道质量门。这是工业里**「绝对必要」**的一步(再强的自动评估也代替不了人评)。

3.5 promptfoo / DeepEval 的角色

它们都是开源 eval harness 框架,把上面 §3.1-3.4 全部封装为 YAML 配置:

# promptfooconfig.yaml(已有用例就一份 yaml)prompts:-"给客户问题「{{question}}{describe:测试:「订单查询」场景下,模型能正确给出订单状态}/{测试:{question西班牙语 / 问无意义问题 /...}}providers:-openai:gpt-4o-openai:gpt-4o-mini-local:my-inhouse-model:v2tests:-vars:{question:'我的订单 ABC123 状态?'}assert:-type:containsvalue:'已发货'-type:llm-rubricvalue:'答案应该说明预计送达时间'-assert:-type:javascriptvalue:'output.length < 200'

跑一条promptfoo eval,对每个 provider × 每个 test 跑一套评分,输出对比表。这就是 LLM 测试的"最佳实践骨架"。

3.6 一个综合 scorer:加权融合

实际工业里没有单一 scorer 能解决一切
成熟的 eval harness 会把上面 4 类 scorer 加权融合,
权重跟业务场景对齐:

defcomposite_score(llm_output,expected,**ctx):weights=ctx.get('weights',{'exact':0.2,# 结构化输出底线'semantic':0.5,# 语义对错(主指标)'schema':0.2,# 形状合规'safety':0.1,# 不输出危险内容})scores={'exact':exact_match_score(llm_output,expected),'semantic':llm_judge_score(ctx['question'],expected,llm_output),'schema':schema_score(llm_output),'safety':safety_score(llm_output),}# 任一维度 0 分 → 复合分直接 0(强约束,避免平均掩盖)ifany(s==0forsinscores.values()):return0.0returnsum(scores[k]*weights[k]forkinweights)

关键设计是任一维度 0 分 → 复合分直接 0
这条强约束避免了「语义优秀但安全维度挂了,
总分 90% 假象通过」的常见坑——
这种坑放出去就是给客服 LLM 留 PII 输出的后门。

四、golden set:LLM 的回归测试基线

跟 Day 16 §7"生产故障转红灯"类似,LLM 测试也需要golden set—— 一组精心挑选的样本,代表系统"应该答对"的所有维度。

4.1 golden set 不是随便 N 条样本

工业里很容易踩一个坑:随机采 1000 条 prompt 作为 golden set。结果是:复杂样本占比太少,跑出来分数看着高,实际上系统在边角案例上挂成狗

golden set必须按维度分桶:

# golden-set.yamldimensions:-name:basic_qa# 基础问答samples:50# 占比 30%sample_proportion:0.3-name:multi_turn# 多轮对话samples:30sample_proportion:0.18-name:long_context# 长上下文(> 4K tokens)samples:15sample_proportion:0.09-name:multilingual# 多语言samples:20sample_proportion:0.12-name:edge_cases# 边角:错别字 / 错引上下文 / 矛盾输入samples:25sample_proportion:0.15-name:safety# 安全:不能输出 PII / 歧视 / 危险信息samples:25sample_proportion:0.15total:165

关键设计:

  • 每个维度独立计分;
  • 报告里看,单维度分数 < 阈值就是红灯(不要看总分);
  • 维度配比跟业务场景相关(客服系统 safety 占比要更高,搜索系统应召回优先)。

4.2 golden set 何时升级

工业真实经验:

  • 每月新增 10 条:从生产日志里挑"模型答错了人补回的",或"用户投诉"案例;
  • 季度大 review:重审维度配置,配比改了就重跑全集(旧数据失效);
  • 大模型版本切换前:必跑全套,且保留历史作为对比 baseline。

4.3 golden set 进 git

跟 Day 14 §6.2 fixture 户口同源 — golden set必须进 git,带 .meta 户口:

# golden-set/safety-001.yaml.metaid:safety-001description:模型应拒绝提供信用卡盗刷建议created_by:"@qa_engineer"created_at:2026-07-15tags:[safety,p0,content_mod]last_passed_at:2026-08-09fail_rate_30d:0owner:"@security_team"

任一条 case 30 天失败率 > 0,触发 review

4.4 跟 Day 16 红灯回扣的具体衔接

Day 16 §7 把生产故障转成「@Tag(prod_incident) 红灯 case」,
LLM 时代也有完全对位的动作 ——
线上 LLM 答错的 case转成 golden set 新条目:

# tools/incident_to_golden.pydefincident_to_golden_sample(incident_log:dict)->dict:""" 输入:线上记录的「用户问题 + 模型答错 + 真实正确答案」 输出:符合 §4.1 维度结构的 golden set 新条目 """return{'id':f'prod_incident_{incident_log["id"]}','dimension':classify_dimension(incident_log),# 自动归类到 6 维'question':incident_log['question'],'expected':incident_log['corrected_answer'],'tags':['prod_incident',incident_log['severity']],'source':'production','created_at':now(),}

这条流程就是 Day 16 §7 在 LLM 测试体系的镜像:
生产红灯反向灌入 golden set,循环改善。

工业实践里,一条 P1 投诉的 LLM 答错对应一条 golden 新样本,
golden set 每周 10-30 条新样本,
其中 60% 来源于生产反思(Day 16 镜像),
另 40% 来源于探索性测试准备(回扣 Day 11 左移)。

五、模型 drift:LLM 的 flaky 等价物

LLM 测试最大的"流动性"问题:同样的 prompt、同样的 golden set,模型版本一变,行为就漂移。这是 Day 10 flaky 在 LLM 时代的等价物。

5.1 drift 类型分类

defdetect_drift(history:list[dict],window_days:int=30)->dict:""" history: [{"ts", "score", "dimension"}] 返回各维度近 N 天的漂移 """today=now()recent=[hforhinhistoryifh["ts"]>today-window_days]baseline=[hforhinhistoryiftoday-2*window_days<h["ts"]<=today-window_days]drift={}fordimindimensions:recent_score=avg([r["score"]forrinrecentifr["dimension"]==dim])baseline_score=avg([b["score"]forbinbaselineifb["dimension"]==dim])drift[dim]={"recent_score":recent_score,"baseline_score":baseline_score,"drift_pct":(baseline_score-recent_score)/baseline_score*100ifbaseline_scoreelse0,}returndrift

drift 阈值:

  • 各维度 drift > 5% → 黄灯警告;
  • drift > 15% → 红灯,立刻停止新版本发版,排查原因

5.2 drift 的常见 3 种根因

1. 模型版本切换(gpt-4o-2026-05 → gpt-4o-2026-08) ─ 即使是新版本"更好",它输出更啰嗦 / 更严格 / 更短也可能让你的 golden set 短期失败 2. 数据 drift(RAG 系统的索引更新) ─ 知识库内容变了,旧答案不再"最新" ─ 解法:把"时效敏感"和"时效不敏感"的 case 分桶评估 3. prompt drift ─ 业务方调整了 prompt(把"简洁"改成"详细"),所有 case 输出量级变 ─ 解法:prompt 进 git(回扣 Day 13 §8.3 prompts/ YAML)

每类 drift 的解法不同,不能"看到红灯就重测"——必须先分类根因再决定怎么改。

六、LLM 测试的金字塔

回到 Day 1 测试金字塔的精神,LLM 测试也能套用三角形:

┌─────────────┐ │ human eval │ ← 1%(每周抽 10 条人评) ├─────────────┤ │ 大规模样本 │ ← 9%(几百条 random sample 评估 recall/precision) ├─────────────┤ │ golden set │ ← 30%(每版本跑,N=165 标注样本) ├─────────────┤ │ invariants │ ← 60%(schema / 安全 / 不输出 PII 等结构化断言) └─────────────┘

4 层由下到上:

  • invariants(60%):自动化的 schema / 安全断言,跑得最快(CI 每次);
  • golden set(30%):每次模型版本切换跑;
  • 大规模样本(9%):月度评估,用 LLM-as-judge;
  • human eval(1%):每周抽 10 条人评,最重要但不频繁

每层的"代价跟价值成反比":invariants 跑得快但价值低;human eval 跑得慢但价值最高。比例60/30/9/1就是这种反比的最佳折中。

6.1 跟 Day 1 测试金字塔的根本差异

Day 1 经典金字塔 70/20/10,「单测越多越好」;
Day 26 LLM 金字塔 60/30/9/1,自动 invariants 占 60%,人评只占 1%
这两个比例不可类比 ——
传统单测是「严格断言」,LLM invariants 也是严格断言;
传统集成测是「多组件协作」,LLM golden set 是「多维样本评估」;
传统 E2E 是「真实用户路径」,LLM random sample 是「线上随机采样」。
金字塔是精神同源,具体执行完全异类

这次回到 §2 那张表的核心意义:
不能用 JaCoCo 覆盖率思路测 LLM,但能借测试金字塔的精神

七、把 LLM 测试接进 CI

# .github/workflows/llm-eval.ymlname:LLM Evalon:pull_request:paths:['prompts/**','golden-set/**','model-config/**']schedule:[{cron:"0 4 * * 1"}]# 每周一.depth evaljobs:eval:runs-on:llm-runnersteps:-uses:actions/checkout@v4-name:Run invariants(CI 每次)run:promptfoo eval tests/invariants/--threshold 0.99-name:Run golden set(PR + 每周)if:github.event_name == 'schedule'run:promptfoo eval golden-set/--threshold 0.92-name:Drift detection(每周)if:github.event_name == 'schedule'run:python tools/drift_detect.py--window 30d-name:Notify on regressionif:failure()run:|gh issue create --title "[LLM Eval regression] ${{ github.run_id }}" \ --body "@qa-leads,本周 LLM eval 退步,请参 §5.2 三种根因分类排查"

这条 pipeline三次红线:- invariants < 99% → PR 红(CI 拦下),prompt / golden 改动 PR 红灯;

  • golden set < 92% → 拦新版上线(Day 23 卡发布决策树 §4 Q5 中的应用);
  • drift 红灯 → 自动 issue,人分类根因

八、回扣系列

Day在 Day 26 它接到的线
Day 1 测试金字塔§6 LLM 测试金字塔(60/30/9/1)
Day 3 覆盖率§2 LLM 无 JaCoCo 概念,用多维度替代
Day 8 TDD§7 invariants 在 PR 同 TDD 红绿一致
Day 10 flaky 治理§5 drift 是 LLM 时代的 flaky
Day 13 prompt 工程就是新单元测试§3 eval harness 是它的扩展,全篇是 Day 13 §8 的延伸
Day 14 fixture§4.3 golden set 进 git + .meta 户口
Day 16 红灯回扣§4 golden set 来源是生产发现错答
Day 17 代码评审§7 prompt + golden set 改动 PR review
Day 19 三方协作§7 LLM 测试需要 QA + MLE + DevOps 三方
Day 20 OKR 度量§5 drift_pct 进度量体系
Day 23 微服务测试矩阵LLM 作为后端服务时,它该进 Q5(外部表现型)
Day 24 安全测试golden safety 维度

12 条回扣Day 26 是系列第二次"翻面时刻"—— 第一次是 Day 11 左移、Day 12 右移把时间轴展开;Day 26 把"被测对象"从代码翻到模型。

九、给测试工程师的实操入门路径

如果你团队刚把 LLM 接上,不知道从哪儿下手:

  1. 第一周:写 20 条最关键的 case(覆盖核心业务路径美),手跑,不接 CI;
  2. 第二周:把 20 条用 §3.1 exact match / §3.3 schema validation 接到 promptfoo,写跑 invariants;
  3. 第三周:把第 1 周的 20 条扩到 100 条,按 §4.1 维度分桶,接 CI;
  4. 第 4-8 周:加 drift 监控 + LLM-as-judge + 月度大 eval;
  5. 第 9+ 周:开始研究 RAG / agentic 评估(下一个高度)。

关键:不要追求一上来就 200 条 case + LLM-as-judge——前 4 周先把 invariants 和 golden set 落地,让团队建立"LLM 也能测"的工程感;然后再加深度。

十、反模式清单

反模式表现干掉的办法
golden set 随机采样复杂场景占比太少,分数虚高§4.1 按维度分桶
只看总分单维度挂但被总分掩盖报告看维度,不看总分
不接 CI提交 prompt / model 升级就跑不了§7 三次红线接进 CI
没有 human evalLLM-judge 也错,人评缺失每周抽 10 条,1% 的工作量
drift 红灯"重测吧"没分类根因直接重跑,根本问题没解§5.2 三种根因分类
golden set 半年不更新边角案例永远没补§4.2 月增 10 条案例
用 JaCoCo 思路算覆盖率LLM 无对应概念§2 维度替代
模型版本升级不跑 eval上线后 unknown regression§7 schedule 跑全套

十一、原则三句话

  1. LLM 测试不是断言,是评分矩阵——exact / semantic / schema / human 四档各有适用场景;
  2. golden set 按维度分桶,不看总分看单维度——5% drift 就告警,15% drift 就拦发版;
  3. invariants + golden set + random sample + human eval 60/30/9/1—— LLM 测试金字塔。

十二、思考题

留给今晚:

  1. 你团队目前在用 LLM 吗?如果有,"测试 LLM 自身"做过吗?用什么方法?
  2. 团队上次升级 prompt / 模型版本有没有跑 eval?如果没跑,要不要在下次升级前补上?
  3. 你的 LLM 系统每条样本都生成 PII 风险分析吗?如果没,§7 invariants 加一条;
  4. drift 红灯亮了第一个动作是什么?不要立刻重测,先用 §5.2 三种根因分类;
  5. eval harness 进 CI 后,谁在维护 golden set?

12.1 一句口诀(贴墙上)

读过本篇后,团队做 LLM 上线决策时可套用 ——
「invariant 拦断、golden 分维、random 抽样、human 把关」:
CI 自动 invariants 拦 P0 红线(60%);
golden set 按维度分桶看分数不看总分(30%);
每周大规模 random sample 跑 recall/precision(9%);
每周 10 条人工人评补盲点(1%)。
三层自动 + 1% 人工,LLM 测试金字塔就此落地

十三、TL;DR

  • Day 26 是系列翻面时刻:前 25 篇 LLM 是"工具",Day 26 LLM 是"被测对象";
  • eval harness= golden set + scoring function;
  • 4 种 scoring:exact match / semantic(LLM-as-judge)/ schema-validate / human eval;
  • golden set 按维度分桶,6 维(基础 / 多轮 / 长文 / 多语 / 边角 / 安全)推荐配比;
  • drift 是 LLM 的 flaky:5% 黄灯,15% 红灯拦发版;
  • LLM 测试金字塔 60/30/9/1(invariants / golden / random sample / human);
  • 12 条对位前文,Day 13 §8(“prompt 当单元测试”)→ Day 26(“prompt + golden set 升级版”)。

下一篇:Day 27 ·测试工程师的开源参与:从提 issue 到核心贡献者的实操路径
Day 6 LLM 测试是个深度话题,Day 27 转向工程师外延 — 把测试工程师的影响力延伸到公司之外的 OSS圈。
拆 issue → PR → reviewer → maintainer 四阶段,以及如何让公司支持这种社区投入。
下篇见 👋

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/11 5:02:40

UniT统一Transformer:多模态多任务AI模型架构解析与实践

1. 项目概述&#xff1a;一个模型&#xff0c;多种感官&#xff0c;多项任务如果你在过去几年里深度参与过AI项目&#xff0c;无论是图像识别、文本理解还是语音处理&#xff0c;大概率会有一个切身的体会&#xff1a;我们好像总是在“造轮子”。为了处理一张图片&#xff0c;需…

作者头像 李华
网站建设 2026/8/11 5:00:29

Keras与vLLM集成前瞻:简化LLM部署,提升推理性能

今天我们来关注一个对深度学习开发者来说非常重要的技术动向&#xff1a;Keras社区会议正式召开&#xff0c;并将核心议题聚焦于vLLM的集成。这不仅仅是两个流行开源项目的简单结合&#xff0c;它预示着未来在本地高效部署和推理大型语言模型&#xff08;LLM&#xff09;时&…

作者头像 李华
网站建设 2026/8/11 4:56:56

Unity 2D岛屿地图碰撞系统:Tilemap Collider与Composite优化实战

1. 项目概述&#xff1a;岛屿地图与碰撞的共生关系在Unity里做2D游戏&#xff0c;尤其是像RPG、生存冒险这类带有探索元素的&#xff0c;地图设计绝对是核心中的核心。一个精心设计的岛屿地图&#xff0c;不仅仅是视觉上的风景&#xff0c;更是玩家所有交互行为的物理舞台。而要…

作者头像 李华
网站建设 2026/8/11 4:56:55

解决IDEA中Maven插件解析失败:从缓存清理到网络配置的完整指南

1. 问题场景重现&#xff1a;当IDEA突然“不认识”你的Maven插件 相信很多用IntelliJ IDEA做Java Web开发的朋友&#xff0c;都遇到过这个让人瞬间血压升高的报错&#xff1a; Cannot resolve plugin org.apache.maven.plugins:maven-war-plugin 。上一秒项目还好好的&#x…

作者头像 李华
网站建设 2026/8/11 4:56:51

Java集合框架:List接口与ArrayList、LinkedList深度解析

1. Java集合框架中的List接口核心定位List作为Java集合框架中最基础也最常用的接口之一&#xff0c;它定义了有序集合&#xff08;也称为序列&#xff09;的核心契约。与Set不同&#xff0c;List允许重复元素&#xff0c;并且通过索引精确控制每个元素的插入位置。在实际开发中…

作者头像 李华
网站建设 2026/8/11 4:56:16

MySQL B+ 树查询全过程详解

MySQL B 树查询全过程详解基于 InnoDB 存储引擎&#xff0c;以单次等值查询为主线&#xff0c;贯穿从根节点到数据行的完整路径。一、前置知识&#xff1a;B 树结构 1.1 一棵 InnoDB B 树长什么样┌─────────────────────┐│ [根节点] Page 3 ││ …

作者头像 李华