跳到主要内容

AI 的测试金字塔倒置:为什么单元测试是 LLM 功能的错误投资

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的团队上线了一个新的 LLM 功能。单元测试全部通过,CI 是绿色的,你部署了。然后用户开始反馈 AI "就是不好用"——回答格式奇怪,智能体选错了工具,在多步骤任务进行到一半时上下文丢失。你查看测试套件,它仍然是绿色的。每个测试都通过了。但这个功能是坏的。

这不是运气不好,而是当你把确定性测试哲学应用于概率性系统时必然发生的结果。经典测试金字塔——宽泛的单元测试底座、较小的集成测试中间层、狭窄的端到端测试顶端——建立在一个如此根本的假设之上,以至于没有人会把它写下来:代码每次都做同样的事情。LLM 在每个层面都违反了这个假设。建立在其上的测试策略需要从头重建。

为什么单元测试对 LLM 功能会带来虚假信心

测试金字塔之所以有效,是因为单元测试廉价、快速且精准。你调用一个函数,断言输出,完成。其隐含的契约是:如果所有单元测试通过,则逻辑是正确的。

当"逻辑"存在于语言模型内部时,这个契约立即失效。考虑一个分类功能的提示词级单元测试:你给模型一个输入,断言它返回 "positive"。测试通过了。你再次运行它,它返回 "positive"。你部署了。在生产环境中,在不同的批处理负载、不同的并发请求,以及困扰 GPU 推理的轻微浮点数不确定性下,同样的输入有 8% 的概率返回 "neutral"。你的测试从未捕获到这一点,因为它在 CI 中是确定性运行的。

LLM 中的非确定性比大多数团队意识到的更为普遍。即使在 temperature=0 时,现代推理服务器也会根据并发负载动态调整批处理大小,对于相同的输入,根据同时运行的其他内容产生不同的输出。测试环境总是"安静的"——没有其他任何东西与你的 CI 任务同时运行。生产环境从不安静。

除了非确定性之外,提示词级单元测试还存在一个更根本的问题:它们断言了错误的东西。一个检查 output == "I'd be happy to help with that" 的测试,是在测试精确的 token 序列,而不是语义内容。一个检查 "sorry" not in output 的测试,是在测试表面模式,而不是意图。当你完全模拟 LLM 响应时,你测试的是模型周围的管道,而对模型的实际行为一无所知。开发者记录了正是这种失败:AI 生成的测试在 CI 中干净地通过,同时却在断言错误的结构不变量——对序列化输出的相等性断言而非语义属性、硬编码的时间戳、掩盖真实时序和重试行为的 Mock。

提示词级单元测试最糟糕的结果不是它们失败——而是它们在隐藏真实失败的同时通过了。它们制造出一种正确性的虚假信号,推迟了问题的发现,直到用户发现它们为止。

故障实际发生在哪里:工具边界

如果单元测试无法捕获重要的故障,那些故障实际上在哪里发生?对于大多数 LLM 应用,答案是模型与外部世界之间的边界:工具调用、API 调用、检索结果、数据库写入。

这是集成层,也是 AI 系统中投入测试精力回报最高的地方。原因如下:LLM 是一个概率性决策者,它的大多数决策都体现为关于调用哪个工具以及传递什么参数的选择。当模型选择了错误的 API 端点、生成了格式错误的参数、幻觉出了 Schema 中不存在的字段名,或者调用了它从训练数据中记住的已弃用端点时——这些失败是可观察的、可测试的,并且是有实际影响的。

工具边界的集成测试能够捕获单元测试在结构上看不到的一类故障:

  • 当用户询问退货时,模型持续调用 search_orders 工具,但正确的工具是 search_return_requests——一种只有在两个工具同时出现在上下文中时才会显现的工具选择失败。
  • 当工具 Schema 要求 {"date": "2026-04-17"} 时,模型生成了 {"date": "April 17"}——一种格式不匹配,在测试中悄然无声,但在生产中会抛出 400 错误。
  • 模型链接了三个各自成功的工具调用,但以一种使第四次调用上下文损坏的方式积累了状态——一种对每次调用的单元测试不可见的顺序状态失败。

这里的实用方法是录制重放(record-and-replay):对真实 API 进行一次真实工具交互的捕获,然后在 CI 中确定性地重放它们。这消除了传统 AI 集成测试中的 Mock 与现实之间的差距。录制的响应是真实的,被测试的智能体行为是真实的,并且在初始录制之后,测试仍然是快速且免费的。当新的模型版本或提示词变更导致智能体对相同的录制输入进行不同的工具调用时,测试会捕获到它。

系统级行为评估:真正预测用户体验的信号

集成测试告诉你智能体是否正确执行。行为评估(Behavioral evals)告诉你智能体是否做了正确的事。这是两个不同的问题,只有第二个问题才能预测用户满意度。

行为评估在系统级别运行:给定一个真实的用户输入,完整的系统——模型、工具、检索、编排——是否产生了合适的输出?"合适"有意不是二元意义上的"正确"。对于"总结这份合同并标记不寻常的条款",没有唯一正确的答案。有许多可接受的答案和许多不可接受的答案。行为评估衡量的是你的系统属于哪个类别,针对一个具有代表性的输入样本。

这就是 LLM-as-judge 评估发挥作用的地方。你使用第二个模型来评估第一个模型的输出,根据一个评分标准打分:响应是否解决了用户的实际目标?它是否包含幻觉事实?工具选择序列是否合理?运行多轮评分可以平滑评审员自身的非确定性。结果是你的评估集上质量分数的分布——不是二元的通过/失败,而是一个比率。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates