2026-07-27

从测试集到评估集

LLM 把不确定性引入了工程系统,几个测试用例跑通已经不够,我们需要用评估集持续衡量和驱动工程能力。

#技术#LLM#Evals#工程

过去做服务端,或者做大数据效果工程时,我脑子里默认有一种很强的确定性思维。

一个接口,给定输入,就应该有稳定输出。一个任务,依赖、参数、数据源都确定以后,结果大体也应该是确定的。工程里的很多工作,是把这种确定性一层层守住:单元测试、集成测试、回归测试、线上监控、日志排查。只要测试用例覆盖得足够多,边界条件想得足够清楚,很多问题就能被提前挡住。

这种思路当然很重要。到现在我也觉得,确定性仍然是工程里最让人安心的东西。

只是大模型出来以后,事情变得不太一样了。

LLM 不是一个传统意义上的函数。它当然也有输入和输出,但中间多了一层模型能力,也多了一层不确定性。同样的需求,换一种表达,结果可能不一样;同样的 prompt,模型版本变了,结果可能不一样;同样的系统,在几个样例里表现很好,到了真实用户的问题里,可能又会暴露出完全不同的短板。

这让原来那种“跑通测试用例就差不多了”的工程直觉,变得不够用了。

不是说测试用例没有价值。相反,基础的确定性测试仍然非常必要。代码有没有异常,参数有没有传对,工具调用链路有没有跑通,结构化输出能不能解析,这些问题都应该用传统测试守住。只是这些测试更多是在证明系统“能运行”,而不是证明系统“有能力”。

这两件事在 LLM 工程里差别很大。

一个 agent 能正常调用工具,不代表它真的能解决问题。一个 RAG 流程能返回答案,不代表答案真的可靠。一个 prompt 在手工挑的几个 case 上效果不错,也不代表它在整体问题分布上更好。以前我们看一个服务是否正确,常常看它有没有按照预期执行;现在我们还要看它面对一类任务时,到底能不能稳定地做出足够好的判断。

所以我越来越觉得,做 LLM 应用,外围的 harness 工作会变得非常重要。

所谓 harness,不只是把模型调用包一层。它更像是一套围绕 LLM 系统的工程支架:能记录输入输出,能保存中间过程,能回放历史样例,能比较不同 prompt、不同模型、不同策略下的结果,能把一次主观感觉变成可复查、可讨论、可迭代的证据。

没有这套东西,很多优化都会变得很虚。

今天改了 prompt,感觉好了一点;明天换了模型,感觉又聪明了一点;后天加了一个工具,某几个例子确实过了。可是到底是整体变好了,还是只是刚好命中了眼前几个例子?到底是能力提升了,还是只是把问题从一个地方挪到了另一个地方?如果没有评估集,没有稳定的对比方式,这些判断很容易停留在体感上。

而 evals 的价值,正是在这里。

它不是为了给模型打一个漂亮的分数,也不是为了让工程流程看起来更正规。它真正有用的地方,是让我们拥有一个相对稳定的参照物。每一次 prompt 调整、工具改造、检索策略变化、模型升级,都可以拿同一批问题重新跑一遍,看哪些能力上去了,哪些能力掉下来了,哪些 case 仍然过不去。

评估集本身也不是一次性写完的。

它应该随着工程一起长出来。线上遇到的坏例子,可以沉淀进去;人工发现的边界问题,可以沉淀进去;某次模型升级后退化的场景,也可以沉淀进去。时间久了,这个评估集就不只是测试材料,而是这套系统能力边界的记录。

对我来说,这也是从传统工程走向 LLM 工程时,一个很重要的心智变化。

以前我更关心“这个逻辑有没有按预期执行”。现在我还要关心“这套系统面对一类问题时,能力到底有没有变好”。前者可以靠测试用例守住,后者则需要评估集持续衡量。

当然,evals 也不会让 LLM 变成完全确定的系统。它不可能覆盖所有真实问题,也不可能消除模型本身的不确定性。但它至少能把这种不确定性放进一个可观察、可比较、可迭代的框架里。工程并不是要假装不确定性不存在,而是要给不确定性建一个可以工作的边界。

也许这就是 LLM 时代工程能力的一部分。

不是只会调 prompt,也不是只会接模型 API,而是能够围绕模型建立一套持续评估和持续改进的机制。测试用例告诉我们系统有没有坏,评估集告诉我们系统有没有变强。

这两件事都重要。

只是到了 LLM 这里,后者变得前所未有地重要。