速成课 · No. 13

AI 功能建立在一个非确定性组件之上——同样的输入可能给出不同的输出,而「我试的时候看着挺好」并不是测试。评测就是为这个组件准备的测试套件:已知的输入、被打分的输出、每次改动都跑一遍。它是一门不光鲜的纪律,把一个你「但愿能行」的 demo,和一个你「知道它能行」的产品区分开来。

只讲精髓 · 每个想法一个画面 · 用度量,别靠猜

§ 01

普通代码可以做单元测试,因为同样的输入给出同样的输出。LLM 打破了这一点,所以你需要另一种测试——没有它,你就是在盲飞。

你没法给一个「猜测者」做单元测试

给一篇作文评分,不像核对一道加法题——没有唯一正确的字符串可以去对照,而且同一个提示词每次产出的作文都略有不同。

普通测试断言的是 output === expected。LLM 是非确定性且开放式的:同一个提示词给出变化的文本,而「正确」往往是一种质量,不是某个精确的字符串。所以断言这套模型就失效了。评测取而代之:一组输入,每个都配有一种方法去评分输出有多好,反复运行——为一个「不会两次返回同样东西」的组件而生的测试。

感觉经不起改动的冲击

一个从不尝菜、只是反复改配方然后碰运气的厨师——他根本分不清昨晚的改动是帮了忙,还是悄悄毁了这道菜。

没有评测,你就靠感觉调提示词:试点什么,瞄一眼几条输出,觉得更好了就发布。陷阱在于你看不见回退——一个修好了某个用例的改动,常常会弄坏你没回头去查的另外三个。评测把「感觉更好了」变成「打了 84 分对 79 分」,这就是工程与猜测之间的区别。

做到 80% 很快;95% 才是整个项目

任何东西的初稿都来得很快。把一份粗糙的草稿变成可靠的东西,才是真正耗时间的地方。

这就是评测存在所要应对的残酷真相:一个有 80% 的时候是对的 demo,一个下午就能搞出来;从那里硬磨到 95% 才是大部分的活儿。 最后那段路要一个用例一个用例去找——那些边角输入、那些罕见的失败——而只有当你在度量时,你才找得到也修得了它们。评测就是那台让漫长攀登变得可见的仪器。

评测既是你的回退防护网,也是你的规格说明

安全带让攀岩者敢去尝试一个高难度动作,因为打滑也不致命——它让大胆的改动变得安全。

一旦有了评测,你就可以改提示词、换模型、或者重构整条流水线,然后立刻看到它是帮了忙还是帮了倒忙。 它在用户之前抓住回退,而且它会悄悄成为你这个功能里「好是什么样」的真正规格说明。没有它,每一次改动都是一场赌博;有了它,改进就变成一个你真能跑起来的循环。

你没法给一个非确定性组件做单元测试。评测改为给它打分——而没有评测,每一次改动都是一次猜。

§ 02

「它好不好?」是无法度量的。评测的第一项真正工作,是把这个含糊的问题,变成你真能打分的具体事项——并且要选得与你功能所做的活儿相匹配。

把「好」变成具体的检查项

路考不会问「你是不是个好司机?」——它给具体的事项打分:你有没有打转向灯、有没有保持车道、有没有完全停稳、有没有停进线内。

「质量」太含糊,无法优化。把它拆成可度量的属性:答案对不对,是不是有出处依据,格式是否合法,有没有调用对的工具,长度和语气是否合适?每一项都是你可以打分的东西。本事在于挑出对你的任务真正重要的那几个属性,而不是什么都去测。

让度量指标与活儿相匹配

你评判一个译者看的是忠实与流畅,评判一个收银员看的是速度与准确——用错了尺子,评审就毫无用处。

不同的功能需要不同的指标。一个 RAG 系统:它有没有检索到对的片段,答案对它们是否忠实?一个分类器:对照标签的准确率。一个 agent:它有没有挑对工具、有没有达成目标?一份摘要:忠实、完整、简洁。挑那些能映射到「对你的任务而言失败究竟意味着什么」的指标,否则你优化的会是一个无关紧要的数字。

忠实性与正确性是两回事

一个学生可以写出一篇论证优美却是错的答案,也可以写出一篇笨拙却是对的。风格与真相是分开的两项分数。

两个最重要的属性常被混为一谈。正确性是答案究竟对不对;忠实性是它是否有所提供的上下文支撑、没有凭空编造。一个有出处依据的系统可以是忠实的(它用了出处)却是错的(出处本身就错了),也可以是正确的却不忠实(碰巧蒙对,而非出自上下文)。把它们分开度量,能告诉你失败究竟出在哪

一个数字藏住的东西太多

一个「舒适」的平均温度,可能藏着一间冰冷的房间和一间闷热的房间。这个汇总值抹掉了你最需要看见的那些情况。

单一的总分是一个起点,不是全貌。把结果按类别和难度拆开——简单对困难、每一种问题类型、每一种失败模式——因为一个平平的 85% 可能是在简单用例上 100%、在那些真正要紧的用例上只有 40%。评测的价值,就在那份能精确告诉你下一步该往哪儿瞄的拆解里。

「它好不好?」不是一个指标。把质量拆成为你的任务挑选的、具体可打分的属性——并且永远不要信任单一的汇总数字。

§ 03

一个评测有多诚实,取决于里面的那些例子。数据集——那些输入和它们已知为好的结果——才是真正的资产,大部分的用心都花在这里。

黄金集:配有已知为好结果的输入

老师的标准答案——题目搭配上一个正确回应应有的样子,这样打分才会是一致的,而不是看心情。

评测的核心是一个黄金集:一批有代表性的输入,每个都配有一个已知为好的答案,或一个清晰的成功定义。这就是你拿来对照打分的东西。构建它是实打实的活儿——正是在这里你才把「对你的功能而言『正确』究竟是什么意思」给钉死——而它也是那项让今后每一次改动都可度量的资产。

覆盖边角情况和已知的失败

一座桥要用最重的卡车和最糟的风暴去做压力测试,而不是只在晴天里跑一辆普通的车。

一组只有简单、典型输入的集合,只会给你一个好看却没用的分数。要刻意纳入那些困难用例:含糊的问题、边角输入、对抗性的提示词,尤其是你已经见过的那些失败。每当一个 bug 跑到生产环境,就把它加进评测,这样它再也无法悄悄回来。评测集应当集中在系统最可能出问题的地方。

从真实的生产例子里收割

最好的测试题来自学生真正考过的那张卷子,而不是老师想象他们会问的东西。

评测用例最丰富的来源是真实使用:用户实际问了什么,尤其是他们问的、结果出了岔子的那些。从你的日志和反馈里挖出真实的输入,把它们叠进集合。编造的例子反映的是你以为会发生的事;生产例子反映的是实际发生的事,包括那些乱七八糟的措辞和你做梦也想不到的古怪请求。

小而锋利胜过大而陈旧

五十道精挑细选、专戳真正薄弱处的题目,比一千道你从不细看的随机题教给你的更多。

你不需要成千上万个用例才能起步——几十个精挑细选的、覆盖主要行为和已知失败模式的就够,足以抓住大多数回退并指引调参。从小处起步,保持它诚实,随着你逐渐摸清系统在哪里出问题,再让它生长。一个你真会去跑的评测,胜过一个你不跑的庞然大物。

黄金集才是资产。把真实的输入和已知为好的结果配对,集中在困难的和坏过的地方,随着你逐渐摸清而让它生长。

§ 04

有了输入和预期之后,你需要一种方法给每条输出打分。这些方法从便宜而精确,到灵活而易错——而你要伸手去拿的,是最便宜又够用的那个。

能用精确和基于规则的检查就用

批一份选择题不需要任何判断——答案要么对要么不对,机器瞬间就能打分。

当输出受限时,就确定性地给它打分:分类用精确匹配,格式用正则或 schema 检查,agent 用「有没有调对工具」,抽取用「数字对不对」。这些检查便宜、快、无可争辩。永远把你评测中尽可能多的部分推进这一类——它是现有最可靠的打分方式。

模糊的部分用 LLM-as-judge

对于一篇作文,你请来第二位带着清晰评分细则的考官——是判断,但有明确标准引导,所以保持一致。

对于开放式的输出——这份摘要忠实吗?这个答案有帮助吗?——没有精确匹配可言,所以你用一个模型来打分,即 LLM-as-judge:把输出、上下文,和一份清晰的评分细则交给第二个模型,让它打分。它把类似人类的判断扩展到成千上万个用例。它是那些「规则查不了的质量」的标准工具——但它带着一些需要注意的地方。

裁判本身也需要被审

第二位裁判也可能有偏见——偏向更长的答案,或者听上去更自信的那个——所以你要先拿他和几次人类判定对一对,再去信任他。

一个 LLM 裁判有它的失败模式:它可能偏爱啰嗦的答案、被自信的语气带偏,或者偏离你的本意。所以要在一个样本上拿裁判去和人类打的分校准,给它一份带例子的锋利评分细则,并保持简单。一个你没核查过的裁判,不过是又一个未经度量的模型——在你确认它的分数确实贴合你真正在意的东西之前,别信它。

让人留在环里,小剂量地留

即便有了自动打分,厨师还是会尝一口菜——一次快速的抽查,看看仪器有没有悄悄出错。

自动打分能扩展,但要定期亲自去读那些真实的输出。 抽查一个样本,死死盯住那些失败,核一核你的指标是否仍然反映真实质量。人工评审无法扩展到每一个用例,但小而规律的一剂能抓住你的自动分数漏掉的东西——并阻止你去优化一个已经和现实脱节的数字。

能确定性打分就确定性打分,模糊的其余部分用 LLM 裁判——并且要校验这个裁判,因为一个没核查过的打分者不过是又一次猜。

§ 05

Agent 需要的不止是一个最终答案的分数,因为它可能通过一个坏掉的过程到达一个干净的答案。要信任一个 agent,你度量的是旅程,而不只是终点。

一个干净的最终答案可能藏着一个坏掉的中段

一场只按最终数字打分的数学考试,会放过那个靠两个相互抵消的错误到达答案的学生——你对究竟发生了什么一无所获。

在一个多步骤的 agent 里,一个中间的错误可能通过最终输出的检查,却腐蚀了整个过程——它检索到对的出处,半途把一个事实张冠李戴,然后写出一份干净却是错的摘要。只给最终答案打分,你就把它放行了。你最需要抓住的那些失败,恰恰是只看终点的检查最看不见的。

给轨迹打分

路考考官看的是整段驾驶——每一次打灯、看镜、变道——而不只是你有没有到达那个地址。

对 agent,你评估的是轨迹:在每一步,它有没有挑对工具、传对参数、检索对上下文、推理是否站得住?这能抓住只看终点的检查漏掉的坏中段,而且它会告诉你是哪一步失败的——这远比只知道结尾错了更有可操作性。步骤,才是一个 agent 的可靠性真正成形或丢失的地方。

直接检查工具调用和检索

审计一位研究助理,意味着核查他实际拉取了哪些出处、发起了哪些调用——而不只是读他最后那份备忘录。

一个 agent 的许多行为都是具体而可打分的、不需要判断:它在该调用 send_email 时有没有调用,收件人对不对?它有没有检索到相关的文档?它有没有待在自己的工具权限之内?这些是对步骤的确定性检查,便宜又精确,它们能精准地钉住相当一大批 agent 失败。

那些枯燥的运营真相也要度量

评判一家快递服务,不只看包裹有没有送到,还看花了多久、试了几次、成本是多少。

除了正确性,还要追踪一个 agent 的运营指标:它走了多少步、绕圈或重试了多频繁、延迟,以及每个任务的成本。一个本可三步搞定却用二十个昂贵步骤才到达正确答案的循环,是你最终答案分数显示不出来的可靠性与成本问题。这些数字会揭示那些能跑、但在生产环境活不下来的 agent。

一个 agent 可能通过一个坏掉的过程到达一个干净的答案。给轨迹打分——那些工具、那些检索、那些步骤——而不只是终点。

§ 06

评测分两种,各干各的活:你在发布前跑的测试套件,以及你在生产环境里持续跑着的度量。两者你都需要,因为这世界不是你的测试集。

离线评测:发布前的闸门

一条测试跑道,你在车真正载客之前让它使出浑身解数——受控、可重复、在发布前运行。

离线评测就是你的黄金集,在一个改动发布前拿来对照运行:这个新提示词或新模型,在你精心整理的用例上分数更高了吗?它受控且可重复,是你安全地抓住回退、对比各个选项的地方。这是你要接进开发循环的评测——最好做成 CI 里的一道闸门,这样任何会拉低分数的东西都发布不出去。

在线评测:度量真实世界

无论测试跑道多好,车一上真正的路你还是要盯着它们——因为真正的路上有跑道从来没有过的坑。

生产环境的表现,没有哪个离线集能完全预料:真实用户用你没想到的方式措辞,输入的分布也在漂移。所以你也要在在线度量——采样并给实时输出打分,盯着质量指标,收集用户信号。离线集证明一个改动可以安全发布;在线度量则告诉你,它出去之后实际正在发生什么。

用户反馈是一个免费的评测信号

一个点赞按钮和一个投诉收件箱,就是一份持续的、免费的调查,看看这东西对正在使用它的人到底管不管用。

不管你收不收集,你的用户都在给你打分。把这些信号捕捉下来——点赞/点踩、纠正、重试、放弃、升级求助——并把它们反馈回去。重试或点踩的激增是一个早期预警;那些具体的失败会成为明天的评测用例。这就闭合了循环:生产环境教会评测集现实长什么样,而评测集守护下一次发布。

提防漂移

一台秤慢慢偏离校准——没什么戏剧性,只是每一次读数都更偏一点点,直到某天数字就错了。

一个曾经分数很好的系统,可能会悄悄退化:某个模型提供方更新了,你的数据变了,使用模式变了。没有持续的在线度量,你会从愤怒的用户那里、而不是从一块仪表盘上得知。要随时间追踪质量,让漂移以一条你能据以行动的趋势呈现,而不是一个意外。评测不是一道一次性的闸门;它是一项你要不断读取的生命体征。

离线评测把守发布;在线评测盯住现实。前者证明一个改动是安全的,后者告诉你实际正在发生什么。

§ 07

评测是一门持续的实践,不是一次性的事。本事在于:在你觉得还没准备好之前就开始,把度量接进循环,并且不让那个数字变成一个会骗你的目标。

先评测,再调参

你先校准温度计,再开始调烤箱——否则每一次调整都只是乔装成进展的瞎猜。

常见的错误是没完没了地调提示词,然后才——也许——去构建一个评测。把它反过来:在优化之前先写一个小评测,这样每一次改动从一开始就被度量。哪怕十个用例也胜过零个。没有评测的调参感觉很有产出,其实大多没有——你在挪一些你看不见的数字。

把评测做成一道 CI 闸门

测试不过,代码就不许合并——同样的纪律,能挡住一个悄悄变差的提示词到达用户。

一旦有了评测,就在每一次改动上自动跑它,并在分数跌破某条线时拦住发布。这把评测从一件你想起来才做的事,变成一道始终开着的护栏——AI 版的「测试通过的测试套件」。「有评测,否则不算发布」是值得坚守的标准:一个对 AI 功能未经度量的改动,就是一次未经测试的部署。

别让度量指标变成目标

纯粹为了应试而教,教出的是能在考试上拿高分却干不了活的学生——分数上去了,它本该度量的东西却没有。

当一个度量变成目标,它就不再是一个好度量了——Goodhart 定律。 你可以把提示词过拟合到你那些特定的评测用例上,眼看着分数往上涨,而真实质量原地踏步。要防着它:留一部分用例不放进训练、用新的生产失败来刷新集合,并记住评测是用户价值的一个代理,而不是价值本身。一个用户感觉不到的上涨数字,是一个警告,不是一场胜利。

从小处起步,随失败而生长

一个好的测试套件不是一次写完的——它是攒出来的,一个 bug 一个用例,直到它悄悄覆盖了曾经出过的一切问题。

别等一个完美、全面的评测——你永远发布不了。先用一小把用例覆盖核心行为,然后每当有东西坏掉就加一个。 久而久之,这个集合恰好沿着你系统真正出问题的轮廓生长,而那正是你想要覆盖的地方。评测随产品一同成熟,而它永远不会真正完工。

在你发布一个 AI 功能之前
  • 到底有没有一个评测——一组配有打分输出方法的输入? - 指标是否与活儿相匹配,是否按难度和失败类型拆开、而不是一个平平的总分? - 集合是否包含那些困难的、古怪的、以前坏过的用例,且取自真实使用? - 打分是否用了最便宜可靠的方法——能确定性就确定性,不能就用一个校验过的裁判? - 对 agent,步骤有没有被打分,而不只是最终答案? - 它有没有在 CI 里运行,以及它上线之后你打算怎么度量质量?
说明你在盲飞的气味测试
  • 调提示词靠读几条输出,觉得更好了就发布。 - 一个单一的汇总分数,没有按用例类型或难度拆开。
  • 一个只有简单、典型输入的评测集——没有边角情况,没有过往失败。 - 一个你从没拿去和人类打分校验过的 LLM 裁判。 - 没有生产监控——你会从一条投诉里才得知质量下滑。
说明你做得好的迹象
  • 一个由真实输入配以已知为好结果、集中在困难用例上的黄金集。 - 映射到任务的指标,用最便宜可靠的方法打分并拆开。 - 对 agent 的步骤级打分,以及对模糊部分的校验过的裁判。 - 评测是一道 CI 闸门,一个回退会被加成一个用例、而不是溜过去。 - 在线度量与用户反馈把新的失败反馈回集合里。

评测不是一次性的测试。它是那个循环——度量、找出失败、把它们叠回去——把一个非确定性组件变成你能信任并能改进的东西。

速成课结束 · 7 章 · 用度量,别靠猜

接下来是实践:今天就挑一个 AI 功能,给它写十个评测用例——真实的输入、已知为好的结果、给每个打分的方法——并在你下一次改提示词之前把它们跑一遍。然后每当有东西坏掉就加一个用例。但有一个念头要高于其余:那个跑一次就行的 demo,是容易的那 80%。一切让它成为一个你能信任的产品的东西——爬到 95%、抓住的那些回退、看见的那些漂移——只有在你度量时才有可能。没有评测,你就是凭感觉调参;有了它,改进就变成一个你能跑起来的循环。