解决agent系统无法单元测试的核心问题,引入golden set、LLM-as-judge、CI回归门禁等标准工程实践,确保质量可靠。
你无法通过单元测试来验证 LLM 的正确性,因为同一个输入在下一次运行时可能走出不同的路径。Evals 是概率系统的测试套件:它通过可评分、可重复的检查,判断系统在多次运行中是否达到了可接受的结果,而不是判断它是否曾经返回过某个完全一致的字符串。
核心工具并不复杂:一套精心整理的输入到预期结果示例,也就是 golden set;在 CI 中离线运行这套数据,同时针对线上真实流量进行检查;再为每项任务选择合适的评分方式——断言、golden set、LLM-as-judge 或人工审核。把它接入 CI,作为回归门禁,一旦质量下降,就让构建失败。
单元测试会断言一个确定性函数返回某个完全一致的值。LLM 并不是确定性的:temperature、模型更新以及模型自身的采样机制,都意味着同一个 prompt 在不同运行中可能产生不同的输出,措辞也可能完全不同。把测试绑定到单一的预期字符串,只会得到一个时好时坏的测试,或者一个什么有用信息都断言不了的测试。
Evals 用另一个问题取代了这种断言:系统在多次运行中,是否以可接受的比例达到了可接受的结果?这会把测试重新定义为一个度量问题。你需要根据目标为输出评分,在一组样本上汇总结果,并持续追踪分数随时间的变化。它更像是带有错误预算的 SLO,而不是非通过即失败的单元测试。正是这种工程纪律,让你能够在不盲目冒险的情况下修改 prompt 或替换模型。
Golden set(也叫 reference set 或 eval set)是一组精心整理的“输入到预期结果”示例。每一行都会把系统实际可能遇到的输入,与一个你认为正确或可接受的结果配对——有时是一个精确答案,但更多时候是一套评分标准,或者答案必须满足的一组属性。
最有价值的 golden set 并不是预先凭空设计出来的,而是从真实故障中逐渐积累起来的。每一个生产环境 bug、每一个漏网的边界情况、每一次“模型在这里做了件怪事”,都应该变成一个新的数据行。随着时间推移,这套数据会逐渐编码真实流量的分布形态以及最棘手的案例,因此一次通过的 eval 运行才具有具体意义。把这套数据纳入版本控制,确保它具有代表性,而不只是规模庞大;并且把事故发生后补充测试用例视为修复工作的一部分。
离线评估会在发布之前,通常是在 CI 中,让系统针对固定的 golden set 运行。它可以重复执行,便于比较不同改动,而且成本低到足以在每次提交时运行。它的局限在于,只能衡量那些你事先想到并纳入数据集的案例。
在线评估则会在发布后,根据线上真实流量和真实结果衡量系统行为——对生产运行进行采样和评分,并观察任务完成率、用户纠正行为或下游成功率等真实信号。它可以发现分布偏移,以及 golden set 从未预料到的输入。离线评估告诉你某项改动是否可以安全发布;在线评估告诉你它在现实中是否真的有效。两者缺一不可,而线上失败案例正是新增离线测试用例的最佳来源。
对于主观性质的质量——例如摘要是否忠实、语气是否合适、回答是否切中了问题——通常不存在一个可以精确匹配的字符串。LLM-as-judge 使用一个模型,根据评分标准为输出打分。它可以把原本需要人工逐条完成的评分工作扩展到更大规模;对于许多任务来说,它与人工判断的相关性已经足够高,因此具有实用价值。
但它也是一个确实存在失效模式的组件,你应该把 judge 本身也当作必须评估的对象:
位置偏差:在成对比较中,无论质量如何,judge 都可能偏爱它先看到的答案。
冗长偏差:更长、听起来更自信的答案往往会得到更高的分数,即使它们实际上并没有更好。
不一致性:judge 本身也是概率性的,因此同一对答案在不同运行中可能得到不同的分数。
可操纵性:输出可能被优化成讨好 judge,而不是服务用户;让模型评判自己的输出时,它还可能偏爱自身的表达风格。
缓解办法是:在信任 judge 之前,先用一批由人工标注的数据验证它,衡量它的评分与人工标签之间的一致程度;当你更换 judge 模型或修改其评分标准时,还要重新检查这种一致性。未经验证的 judge 只是一个没有经过评分的假设,并不是一种度量手段。
只有实现自动运行,eval 套件才能真正发挥价值。把离线 golden set 的运行接入 CI,这样,一旦某项改动导致质量回退到阈值以下,就让构建失败,就像单元测试失败或覆盖率下降会阻止合并一样。这会把“我们觉得这次 prompt 改动更好”变成一个经过度量的结论,也能防止有人编辑 prompt、升级模型或重构执行框架时,悄无声息地把质量回退发布到生产环境。
有一项原则比其他所有原则都重要:选择能够反映任务是否成功或结果是否正确的指标,而不只是衡量字符串相似度。两个答案的措辞可能完全不同,但都正确;精确匹配会误判正确答案,而相似度评分也可能放过一个流畅却错误的答案。评估你真正关心的事情——它是否完成了任务——然后让门禁强制执行这项标准。
在扩大规模之前建立 eval。不要等到使用规模扩大以后才搭建 eval 执行框架。没有它,你就无法判断一次 prompt 修改、模型升级或新增工具究竟让事情变好了还是变坏了——你只是在凭感觉发布。能够回答“质量是上升还是下降,变化了多少”的团队,才能安全迭代;回答不了这个问题的团队,最终一定会在生产环境中引入回退,而且不知道原因。在招聘中,eval 设计也是判断一个人是否真的交付过 LLM 系统的最有力信号。
本文刚刚建议你把 evals 接入 CI,并使用人工标签验证 judge。我们两件事都没有做。下面是 aiarch.dev 上实际用于约束 coach 改动的门禁,其中也包括它没有达到上述建议的两个地方。
整个体系分为两层。npm run eval 使用 mocked model 对 golden set 进行评分——成本低、可在本地运行,并且每次修改 coach 时都会执行。它上面还有一道线上门禁:发起真实调用,并根据这里真正重要的行为进行评分,包括逐步淡化提示而不是直接揭晓答案、指出误解,以及在连续两个回合停滞后升级引导。在发布 coach prompt 或进行部署之前,它的通过率必须达到 80%。它最多运行三次,以多数结果为准;一旦两次运行通过,就会提前退出。即使评分本身并非概率性的,coach 仍然是概率性的,因此根据单个样本得出的结论只是噪声。
这里没有 CI。仓库中不存在 .github/workflows/,执行 git push 也不会部署任何东西——发布需要有意识地执行 wrangler deploy,而这些门禁则是人工预先运行的命令。这是一个缺口,而不是设计选择。这也解释了为什么离线层必须保持快速:真正执行的门禁,胜过只停留在描述里的门禁。
第二个缺口更加明显。线上门禁会根据一套评分标准评估每一条回复,但没有任何模型会读取这套标准。
评分采用确定性断言——包括针对 coach 文本的 11 个正则表达式、4 项检查当前回合实际调用了哪些工具的断言(它是否获取了课程上下文?是否对学习者从未尝试过的内容进行了评分?)、两个最低长度限制,以及一项 stop_reason 检查。这个执行框架在自己的注释中坦率地说明了局限:其中两项检查被标注为“必要但不充分的证据”,并指出需要由人工或 LLM judge 来确认真正的辅导质量。因此,80% 衡量的是可观察到的规则遵循情况,而且所依据的是我们自行选择的阈值,并不是根据人工标签校准出来的阈值。它可以捕捉回归,但不能认证质量。把它当作质量认证,恰恰就是上一节警告过的错误。关于它如何接入发布流水线,请参阅 eval-gate 模式。
课程材料:aiArch Track B(eval 设计)——golden set、离线与在线评估、judge 以及 CI 门禁。
Eval 设计指南:Anthropic 关于构建和评估工具调用型 Agent 的文档。
Anthropic——关于评估的平台文档(设计和运行 evals)。
LLM-as-judge 的局限,包括位置偏差、冗长偏差、不一致性、可操纵性,以及必须根据人工标签进行验证,在评估领域的文献中已有广泛记录。
本文只是一份概念性概览;不主张任何具体的 benchmark 或数据,而且 API 形态会发生变化——实施前请根据当前服务提供商的文档进行核实。更正联系:hello@aiarch.dev。
本文最初发布于 aiarch.dev/llm-evaluation-guide,并会在那里持续更新。
不想读长文,只想要项目骨架?aiarch-templates 提供了 src/lib/ 的衔接结构、一个带阈值门禁的 eval stub,以及一个成本模型骨架。它被刻意设计为空实现——它只固定结构,具体实现由你编写。Apache-2.0。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。