Skip to content

25 · 评测驱动:把「够好」写进架构

一句话点题:17 章 抽走了「同样输入 → 同样输出」这块地基,assert result == expected 失效了。怎么防止「换个模型、改句提示,质量就悄悄变差」?把「够好」量化成一套评测(eval),当 CI 门禁——让质量从「靠投诉发现」变成「可量化、可守门」。


🤝 AI 协同设计篇第 3 章 · 本章只练一件事

24 章 靠人逐条审,但「答案质量稳不稳」人查不过来。本章把关从「人工逐条」升级成「机器持续」。这是 20 章 ADR-00522 章 反复点到的 eval 门禁的完整展开,也是非确定性(17 章)唯一靠谱的解法。


一、为什么传统测试在 AI 系统上失效

传统系统的测试,建立在确定性上:

   传统:  assert summarize(x) == "预期的那一句"     ← 二元:对 / 错,能精确断言
   LLM:   summarize(x) 这次输出 A,下次可能输出 A'   ← 温度、模型版本、上下文一变就不同
          两次都「对」,但字面不同 → assert == 直接挂

17 章 说过:LLM 把确定性地基抽走了。于是传统测试遇到两个死局:

  1. 断言不了:输出不固定,== expected 必然误报。
  2. 退化无声:你把模型从旧版升到新版、或改了句系统提示,某类问题的答案悄悄变差了——没有任何测试会红,你只能等用户投诉(20 章 那个「两周后才从投诉发现」的坑)。

核心转变(来自 17 章):从「断言单条正确」转向「衡量一个质量分布」。 不再问「这一条对不对」,而问「这一代表性输入,整体质量分够不够好、有没有比上一版退」。这就是 eval。


二、eval 的三件套

   ┌─────────────┐   ┌──────────────┐   ┌─────────────────────┐
   │ ① eval 集    │──▶│ ② 评分        │──▶│ ③ 门禁(CI)          │
   │ 代表性输入   │   │ 规则/模型/人工 │   │ 分数低于基线 → 拦截   │
   │ + 期望要点   │   │ 给每条打分     │   │ 不许上线             │
   └─────────────┘   └──────────────┘   └─────────────────────┘

① eval 集:测试集的 AI 版

一批代表性输入 + 每条的期望要点(注意:是「要点 / 判据」,不是「逐字的标准答案」)。

例(AI 客服):输入「我上周买的鞋开胶了能退吗?」→ 期望要点:①引用了质保政策 ②给出明确的能/否 ③没有编造不存在的政策 ④口吻得体。评的是这几个要点中了几条,不是逐字比对。

② 评分:谁来判「够不够好」

评分方式适合代价 / 风险
规则 / 程序判定有客观判据(含不含某关键词、格式对不对、有没有引用)便宜、稳定,但只能判「硬」标准
LLM-as-judge(模型当裁判)主观质量(答得好不好、相不相关)灵活,但裁判本身也非确定、也会错、也烧 token
人工抽样校准前两者、兜底高价值场景最准,但慢、不可规模化

实务:规则能判的先用规则(便宜可靠),主观的用 LLM-as-judge,再定期人工抽样校准裁判——别盲信模型裁判,它也是个会出错的模型。

③ 门禁:让 eval 真正「守门」

光跑 eval 不够,要接进 CI 当门禁(这正是 20 章 ADR-005):换模型、改提示、改检索策略前,CI 自动跑 eval;整体分数低于基线,一律拦截、不许上线。

   改提示 / 换模型 ──▶ CI 跑 eval ──┬─ 分数 ≥ 基线 → 放行
                                   └─ 分数 < 基线 → 🔴 拦截(防悄悄退化)

三、怎么建第一个 eval(别想一步到位)

新手一听「评测集」就想攒几千条——然后永远没开始。正确姿势是从小、从真实失败长出来:

  1. 种子来自线上真实 bad case:每一条用户投诉、每一个「答错了」的截图,都是最值钱的 eval 样本——它们是真实失败,不是你想象的失败。先攒十几二十条,就能跑起来。
  2. 离线 + 在线两条腿:
    • 离线 eval:固定数据集,进 CI,把关「上线前」(防退化)。
    • 在线 eval:对真实流量抽样打分 / 影子运行打分(正是 21 章 的影子流量!),把关「上线后」(真实分布永远比你的数据集刁钻)。
  3. 持续加 case:每次发现新的失败模式,就把它固化成一条 eval——和写回归测试一个道理。eval 集是「活」的,随系统一起长大。

这套「从真实失败攒起、小步快跑、持续扩充」的节奏,和 21 章 GitHub Scientist 的「用真实流量当裁判」、07 章 的「架构是迭代出来的」完全同源——别等完美的评测集,先用粗糙的把循环跑起来。


四、eval 不替代传统测试,是新增一层

别误会成「有了 eval 就不写单测了」。一个 AI 系统里,确定性的部分照样用传统测试,eval 只管「非确定性输出的质量」:

            ╱╲   eval 层(新增)
           ╱  ╲  ── 非确定性输出的「质量分布」:答得好不好、退没退化
          ╱────╲
         ╱  E2E  ╲    传统金字塔(照旧)
        ╱──────────╲  ── 确定性逻辑:退款幂等、状态机、鉴权、API 契约
       ╱  集成 / 单测 ╲    这些有唯一正确答案 → 照样 assert ==
      ╱────────────────╲

回到 19 章 那个 AI 客服:退款服务是确定性的 → 用单测断言幂等、断言金额校验(assert == 照样有效);模型生成的答案是非确定的 → 用 eval 评质量分布。两套并存,各管一段。「把不确定性挡在副作用之外」(19 章)的好处在这又兑现了一次:确定的部分还能用确定的方法测。


五、eval 的成本与陷阱(它不是免费的)

把 eval 当架构组件,就要像对待任何组件一样看它的代价:

  • 跑 eval 烧钱、耗时:每条样本都要真实调用模型,LLM-as-judge 更是「评一次 = 又一次模型调用」。eval 集越大、跑得越勤,成本越高——要在覆盖度和成本之间权衡(又一次 06 章 的取舍)。
  • 裁判会错:LLM-as-judge 本身非确定、有偏见(比如偏好长答案)。要用人工抽样校准它,别把它的分当圣旨。
  • 过拟合与老化:盯着固定 eval 集调久了,会「为了考高分而优化」,对集外样本未必好;且业务变了,旧 eval 集会过时。要持续更新(同 23 章 AGENTS.md「过时比没有更糟」)。

架构智慧:eval 是 AI 系统的「质量适应度函数」——它之于答案质量,正如 14 章 的适应度函数之于架构边界:都是「把你在乎的东西,变成一道会失败、能卡 CI 的自动检查」。区别只是:架构边界能精确断言,答案质量只能评分布。把「够好」写进 eval、接进门禁,你才敢放心地升级模型、迭代提示——否则每一次「升级」,都是闭着眼睛赌它没变差。


🎯 随堂检验

🤔一个 AI 客服系统要把底层模型从旧版升级到新版,怎么确保「答案质量不会悄悄退化」?
  • A跑一遍单元测试,断言输出字符串等于预期答案,通过就升级
  • B维护一个代表性「输入→期望要点」的 eval 集,升级前用规则 + LLM-as-judge 打分,整体分数低于基线就在 CI 拦截
  • C先上线,等用户投诉多了再回滚
🤔关于 eval 和传统单元测试的关系,正确的是?
  • A有了 eval 就不用写单元测试了,AI 系统全靠 eval
  • Beval 不替代传统测试,而是新增一层:确定性逻辑(如退款幂等、鉴权)照样用 assert == 单测,eval 只管非确定性输出的质量分布
  • C两者完全一样,只是名字不同

本章小结

  • 传统测试在 AI 上失效:输出非确定,assert == 误报;更糟的是质量退化无声,只能等投诉。
  • 从「断言单条」转向「衡量分布」:这是 17 章 的核心转变,落地就是 eval。
  • eval 三件套:① eval 集(代表性输入 + 期望要点)② 评分(规则 / LLM-as-judge / 人工抽样)③ 门禁(进 CI,低于基线就拦,即 20 章 ADR-005)。
  • 从真实失败小步攒起:种子来自线上 bad case;离线(CI)+ 在线(影子,21)两条腿;持续加 case。
  • eval 不替代传统测试:确定性逻辑照样单测,eval 只管非确定输出的质量分布。
  • eval 不免费:烧钱、裁判会错、会过拟合老化——要权衡覆盖与成本、持续校准维护。它是 AI 系统的「质量适应度函数」(14)。

承上启下:到这儿,AI 协同的三件武器齐了——规格(23)给约束、清单(24)审产出、eval(25)守质量。但什么时候用哪件、什么时候干脆放手 vibe、什么时候必须 spec-first?AI 协同设计篇最后一章 26 · 协作决策树:何时 vibe、何时 spec-first 把这三件武器收成一套可照着走的 workflow


相关链接

💬 评论