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 客服系统要把底层模型从旧版升级到新版,怎么确保答案质量不会悄悄退化?
🤔关于 eval 和传统单元测试的关系,正确的是?

本章小结

  • 传统测试在 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


相关链接

💬 评论