跳到主要内容

68 篇博文 含有标签「testing」

查看所有标签

评分了每一轮对话却错过了整个交流:聊聊多轮评估的陷阱

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的评估仪表盘是一片绿色。单轮准确率(turn-level accuracy)保持在 95%,LLM 裁判(LLM judge)与你的标注人员意见一致,每一项回归测试在上线前都顺利通过。然后,一名用户提交了一个 bug:在第九轮对话中,智能体推荐了一个 Postgres 索引,但这直接违反了用户在第一轮中设定的“我们使用的是 DynamoDB”这一约束。你调出对话记录。每一轮对话如果拆开来看,都是合理的回复。但如果把整个对话作为一个整体来看,那就是一场灾难。

这是单轮评估(turn-level evaluation)的核心谎言。它对“请求-响应”对进行评分,因为这是标注成本最低的单位,并且它默认假设一段对话仅仅是你可以取平均值的独立轮次的组合。事实并非如此。第九轮的响应是以之前发生的一切为条件的,而那些真正触达用户的失败几乎从未存在于单轮对话内部——它们存在于轮次之间的缝隙中,在那里状态丢失、假设变得僵化,微小的错误复合成一个完全错误的最终答案。

没有预发布模型的预发布环境

· 阅读需 11 分钟
Tian Pan
Software Engineer

你可以搭建一个预发布数据库。你可以搭建预发布队列、预发布支付沙箱,以及你所依赖的每一个第三方 API 的预发布副本。三十年来,整个预发布(pre-production)学科都建立在一个假设之上:你可以创建一个足够忠实于生产环境的副本,对其进行测试,并从中了解到发布后会发生什么的真实情况。

然后,你将托管模型添加到了关键路径中,这个假设便悄然瓦解了。那个现在主导你产品行为的组件——决定你的应用实际上在说什么、做什么的那个东西——正是你无法搭建预发布副本的唯一组件。它由别人进行版本管理,由别人进行速率限制,并由别人按照你无法察觉的时间表悄悄更新。你的预发布环境拥有一切的预发布副本,唯独没有预发布模型。

不稳定的测试毒化 Agent 循环的速度比伤害人类时快得多

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个人类工程师在看到一个明明不可能由改动引起的测试失败时,会做一件智能体做不到的事:耸耸肩。他们会点击重新运行,对着 CI 大神嘀咕几句,然后继续工作。这种耸肩行为包含了多年积累的上下文 —— 这个测试从 3 月份开始就一直不稳定(Flaky),那个服务的预发布环境每逢周一就会崩溃,没人信任 WebSocket 的测试套件。而编码智能体完全没有这些。它看到一个红色的 X,就将其视为绝对真理,因为它的所有训练和提示词都在告诉它:测试失败意味着代码是错误的。

接下来的环节才是最昂贵的部分。智能体不会耸耸肩 —— 它会采取行动。它会“修复”那些从未损坏的代码。它会因为应用改动后测试套件变红而回滚一个正确的改动。它会消耗大量的 Token 预算去追赶一个幻影,在原本正常的代码路径中添加重试、等待和防御性检查,直到那个不稳定测试碰巧通过,智能体便得出结论:它最后的突变就是解药。测试基座中的非确定性一直是人类注意力的一种负担。而对于智能体循环来说,它更糟糕:它是直接注入到决策系统中的受损训练信号,而系统正以机器速度基于此做出反应。

世界没有预发布环境

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的技术栈通常有三个环境。开发环境(Dev)是可抛弃的,预发环境(Staging)是类生产环境,而生产环境(Prod)是神圣不可侵犯的。二十年的工程文化——CI 门禁、金丝雀发布、蓝绿部署——全都建立在这种分层之上。但当你给智能体(Agent)一个调用 Salesforce、QuickBooks 或 Gmail 的工具时,这种分层就悄然消失了。对于客户的组织来说,并没有“预发环境下的 Salesforce”。也没有发票账本的影子副本。一旦工具调用跨越网络边界进入第三方 SaaS,环境就只剩下一个,那就是生产环境。

这是智能体工程(Agent Engineering)中最少被讨论的鸿沟。我们已经擅长对智能体的“计算”进行沙箱化处理——容器、出口白名单、资源配额。我们在评估智能体的“推理”方面也做得还行——离线评估、LLM 作为评委、轨迹评分。但智能体对“外部世界”的操作仍然是在包含真实客户数据的生产系统上运行的,因为对于大多数 SaaS 界面来说,除此之外别无他法。团队只能通过仅有的两种方式悄悄解决这个问题:要么在生产环境中测试,要么根本不测试。

确定性种子:为什么供应商将其视为“提示”而非“契约”

· 阅读需 12 分钟
Tian Pan
Software Engineer

CI 测试只有一个断言:相同的模型、相同的温度、相同的提示词、相同的种子(seed)、相同的输出字符串。它在每个开发者的笔记本电脑上都通过了,在前一百次 CI 运行中也通过了,但在三周内每五十次运行就会出现一次随机失败(flake),直到最后有人承认这种模式是真实存在的。第一个假设是显而易见的——测试工具中某处存在非确定性依赖——三天的调查却一无所获。实际原因隐藏在供应商 API 参考文档的一个脚注中:“seed 提供尽力而为的确定性。”团队读到了参数名称,并将其视为一种契约。而供应商记录的只是一个提示。

这是托管推理的一种特定失败模式,它困扰着那些围绕单一心智模型设计测试基础设施的团队:模型是其输入的纯函数,而 seed 是使函数具有可重现性的关键。在生产环境中,这个模型的这两个部分都是错误的。API 表面与底层物理原理之间的差距如此之大,以至于团队在供应商明确否认的假设之上,构建了整个评估和回归测试栈。

为什么你的智能体在开发中表现完美,在生产中却状况百出

· 阅读需 12 分钟
Tian Pan
Software Engineer

Agent 演示总是能成功。数据库里有三个客户,一个匹配记录,向量索引中有 12 篇文档,一个带有无限空档的空日历。Agent 选对行,检索到正确的文档,预订好正确的会议。上线吧。

接着,生产环境交给了同一个 Agent 一千万个客户,其中在同一个城市有三个 “John Smith”;一个返回了四千行的过滤器,因为 Agent 本想表达 status = 'active' 时却自信地写成了 status != 'closed';一个向量查询返回了七篇看似合理的文档,而 Agent 从未被要求在这几篇文档之间做选择;以及一个每个空档都需要协商的日历。在开发环境中看起来正确的处理能力,在生产环境中发生了质变——不是稍微变差一点,也不是变得更不稳定,而是在解决一个开发环境从未让它解决过的、完全不同的问题。

这就是“在本地运行正常”所掩盖的鸿沟。对于确定性代码,这句话在处理边缘案例时已经算是个谎言。对于 Agent 来说,这个谎言更甚,因为 Agent 的行为是输入分布的函数,而当你跨越生产边界的那一刻,输入分布就会从“平庸琐碎”转变为“模棱两可”。

你从未注入过的故障:给你的 Agent 提供一个说谎的工具

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开你的智能体(agent)韧性测试套件,看看它实际上在测试什么。你会发现超时。你会发现连接中断、500 错误、频率限制响应、格式错误的 JSON,也许还有一个在失败前卡死三十秒的工具。所有这些都是经典模式下的故障注入:工具坏了,问题在于你的智能体是否能优雅地降级。

现在找找看那个工具完全没坏的测试。那个工具在 80 毫秒内响应,返回了完全符合 schema 的有效 JSON,但里面的值纯粹是错的。一个过期了三天的余额。一个交换了两个字段的客户记录。一个两位数移位的订单数量。一个本应返回四十行却返回空的查询结果列表。

你找不到它。几乎没有人注入过这种故障。而这正是你的智能体最无法抵御的故障,因为所有其他故障都会自我宣告,而这种故障不会。

那些在你没留意时变简单的评估集

· 阅读需 10 分钟
Tian Pan
Software Engineer

你在 18 个月前编写了这套评估集(eval set)。那时它是一个非常有用的工具:低价模型的得分是 71%,更好的模型得分是 84%,而当出现回归(regression)时,分数会下降并被察觉。这套测试套件在 CI 中赢得了一席之地。于是你不再关注它了。

今天再运行它,每个候选模型的得分都是 96、97、98。新版本的得分与旧版本相同。你怀疑表现较差的模型与你认为更好的模型得分也一样。仪表盘上的数字依然显示为绿色,检查依然通过,但它实际上什么也没告诉你。你的评估集并没有坏。它只是变简单了——因为底层的模型变强了——而没人在意它失去区分度的那个瞬间。

这就是评估饱和(eval saturation),这不仅是你可能遇到的失效模式,更是任何静态测试套件在足够长的时间跨度下必然走向的终局。一个所有模型都能通过的测试,已经不再是测试了。

Eval 测试集是滞后指标:你的绿色仪表盘只反映上季度的失败

· 阅读需 9 分钟
Tian Pan
Software Engineer

每一个成熟的 AI 团队构建其评估套件的方式都如出一辙,而且几乎没有人会公开说出那个潜台词。生产环境中出现了一个故障。有人写了一份复盘报告。一名工程师将该事故提炼为一个测试用例,将其添加到评估套件中,于是仪表盘再次变绿。重复这个循环一年,你就会拥有几百个案例、一个令人满意的通过率,以及一个足以让你在演示幻灯片上感到无比安心的数字。

潜台词是:那个评估套件其实是一个博物馆。每一件展品都是团队已经挺过来的故障类别。98% 的通过率证明了你的系统可以抵御过去 —— 抵御那些已经发生过的特定破坏方式 —— 而对于模型迁移、提示词编辑或用户行为转变即将引入的新型故障模式,它几乎给不出任何参考。评估集是一个披着先行指标外衣的滞后指标。

Happy Path 是你的 Agent 评估测试过的唯一路径

· 阅读需 11 分钟
Tian Pan
Software Engineer

看看大多数智能体(Agent)评测集是从哪里来的。有人构建了智能体,向团队演示,演示成功了,于是演示脚本就变成了评测套件。那些通过评审的案例,正是有人已经亲眼看到它们运行成功的案例。评测集在构建之初,几乎就是“快乐路径”(Happy path)的录音——即在截屏当天成功运行的那一段工具调用序列。

所以,当仪表盘显示智能体得分为 94% 时,它实际上是在说:它通过了我们能想象到的案例。它完全没有提及搜索 API 在多步计划中途返回 429 错误的情况,或者用户推翻了两轮前设定的约束的情况,亦或是检索结果为空,智能体必须在胡乱猜测和承认不知道之间做出选择的情况。这些情况并非没有通过你的评测。它们压根就没在评测里。

这就是黄金路径偏见(Golden-path bias),除非你刻意对抗,否则它就是智能体评测套件的默认形态。解决方法不是增加案例数量,而是增加不同种类的案例——这些案例应根据失败模式(Failure mode)来选择,从生产环境中收集,并针对刻意引入的故障进行压力测试。

那个由智能体编写的、实际上什么也没测的测试

· 阅读需 11 分钟
Tian Pan
Software Engineer

让一个编程智能体 (AI agent) “为这个模块添加测试”,你会得到测试。它们格式整齐,遵循你的项目规范,而且能够通过。覆盖率会上升。这个 PR 看起来非常尽职。然而,这些测试中很大一部分根本无法捕捉到你可能引入的任何 Bug。

这并不是一个关于模型太蠢的故事。智能体完全按照要求完成了任务。问题在于,“添加测试”和“添加能约束行为的测试”是不同的请求,而其中只有一个是能被一眼验证的。无论是真正的断言还是同义反复(tautology),绿色的对勾看起来都一模一样。

结果就是,测试套件的代码行数在增加,但效能却在萎缩。你最终得到了更多的文件、更多的 CI 耗时、更多的维护成本——而交付回归缺陷的概率却与开始前几乎无异。

悄然失效的评估:当你的测试套件在衡量一个已不存在的世界

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的评测套件通过了。240 个案例全是绿色,和上周一样。你发布了代码。两天后,支持工单激增。当你阅读对话记录时,你发现了一种你的套件完全没有立场的失败模式——不是某个案例从通过变成了失败,而是用户开始问一些你的套件从未想到过要去问的问题。

这就是评测(evals)的无声失败。我们将“全绿”视作对现状的肯定:“系统运行正常”。实际上,它只是对过去的一种陈述——即编写这些评测案例的那一刻。六个月前编写的评测编码了当时的三样东西:产品的范围、模型的失败模式,以及真实用户表达请求的方式。而这三者都在变化。功能增加了新的界面,模型升级了两次。随着用户了解产品的功能,输入分布也发生了漂移。套件没有随之移动,因此全绿的运行结果越来越只是在证明一个不再存在的世界。

没有人注意到,因为没有东西崩溃。过时的评测不会报错。它会继续自信地通过,但衡量的关键内容却越来越少。