第 2 章 LLM 评测体系全景:离线、在线、回归三层模型

“Programs must be written for people to read, and only incidentally for machines to execute.” —— Harold Abelson

本章要点

  • 建立评测体系的三层心智模型:离线、在线、回归
  • 明确每一层解决的问题、产生的信号、面对的成本约束
  • 与传统 QA(unit / integration / E2E / monitoring)做精确对照
  • 给出不同业务阶段(MVP / 上线初期 / 规模化)下三层的投资优先级

2.1 为什么”一次评测”不够

很多团队第一次接触评测时,会本能地把它理解成”上线前跑一遍的 QA 测试”——就像传统 Web 项目里跑一次 e2e 测试集,全绿就允许部署。这个心智模型对 LLM 几乎是错的。

考虑一个简单事实:LLM 应用上线后,至少有四股力量会让你的”上线前 QA 通过”的结论失效——

  1. 真实用户输入分布不可枚举:你的离线评测集再大,也只是真实用户问法的子集。生产环境每天都会出现你从未见过的 prompt
  2. 模型版本会变:OpenAI / Anthropic / Google 的模型 API 每隔几个月做一次”静默更新”。即使你不动代码,输出也可能变
  3. 依赖会漂:retriever 的 vector index 在长期写入下分布会漂;外部 API(搜索、知识库)的内容每天都在变;prompt 模板里的”今天的日期”会变
  4. 评估器自身会漂:如果你用 GPT-4 作为 LLM-as-Judge,judge 模型自身也在变;如果你用人工评测,标注员的判断标准也会随时间变化

每一股力量都在告诉你同一件事:任何一次评测的结论都有保鲜期,必须不断重新生成

这就是为什么 LLM 工程领域过去三年逐渐沉淀出三层评测的工程范式——把评测从”一次性 QA”重构成”持续性的质量信号网络”。

graph TB
  subgraph 离线评测 Offline
    A1[黄金集<br/>+对抗集] --> A2[静态判分]
    A2 --> A3[质量门禁<br/>阻止上线]
  end
  subgraph 在线评测 Online
    B1[生产流量采样] --> B2[实时 LLM-as-Judge<br/>+ 用户反馈]
    B2 --> B3[在线 dashboard<br/>+ 异常告警]
  end
  subgraph 回归评测 Regression
    C1[每日定时跑] --> C2[时序指标 + 版本对比]
    C2 --> C3[退化告警<br/>+ 版本可追溯]
  end
  A3 -.线上发现的问题.-> A1
  B3 -.补充进数据集.-> A1
  C3 -.触发模型/prompt 回滚.-> A2
  style A1 fill:#dbeafe
  style B1 fill:#dcfce7
  style C1 fill:#fed7aa

这张图是后续所有章节的坐标系。读完本章你会建立起”听到一个评测话题,就能立刻判断它属于三层中的哪一层”的能力。

2.2 离线评测:上线前的质量门禁

2.2.1 定义与目标

离线评测指在 LLM 应用接受真实用户流量之前,用预先准备的数据集(“黄金集”或 “ground truth set”)对系统进行批量化评估。它的主要目标只有一个——回答”这次改动相对上次是不是真的更好?”

离线评测的工程形态在传统 QA 工程师眼里非常熟悉——它就是 LLM 时代的”集成测试”。区别只在两点:

  1. 输入是自然语言而非函数参数
  2. 输出的”对不对”不能用 assertEquals,必须用统计指标
sequenceDiagram
    participant Dev as 开发者
    participant Code as 代码改动<br/>(prompt/retriever/model)
    participant Eval as 离线评测器
    participant Gold as 黄金集
    Dev->>Code: 修改 prompt
    Code->>Eval: trigger eval run
    Eval->>Gold: 加载 N 条 (input, expected)
    loop 每条样本
        Eval->>Code: 跑 LLM 应用
        Code-->>Eval: actual output
        Eval->>Eval: 判分(rule / LLM-judge / human)
    end
    Eval-->>Dev: 指标曲线 + 失败样本
    alt 通过门禁
        Dev->>Code: 合并到主分支
    else 未通过
        Dev->>Code: 回滚或修改
    end

2.2.2 离线评测的关键工程要素

  • 数据集(黄金集):100-10000 条 (input, expected) 对。详见第 3 章
  • 判分器(grader):对单条输出打分的函数,可能是 rule-based、LLM-judge、或人工。详见第 5-7 章
  • 聚合指标:把单条得分聚合成可比较的汇总数字(accuracy、F1、Faithfulness 平均值等)。详见第 4 章
  • 基线对比:本次 run 与上一次(通常是 main 分支或上一个版本)的对比矩阵
  • 失败样例 surfacing:把判分失败的样例直接展示给开发者,让 hard cases 可视化

2.2.3 离线评测的工具栈选择

实操中,离线评测主要有四档工具,覆盖不同复杂度:

工具适合场景核心抽象典型用户
自写 Python 脚本50 题以内、原型阶段(input, expected, grader) 三元组初创团队 / 个人项目
promptfoo生产级 YAML 配置testCase + assertion产品 / 应用团队
ragasRAG 专用metrics × datasetRAG 重度团队
openai/evals学术风格、模板复杂Eval × Solver × Recorder模型研发团队
langsmith / langfuse / phoenix商业平台、带 trace 集成dataset + run + experiment企业 / 团队

选择原则:从最简单的能跑通业务的开始,不要被工具复杂度反向定义需求。50 题手工集 + 一个 for 循环是合格的起点,把它跑出工程闭环比上 SaaS 平台重要十倍。第 5-12 章会逐个拆解上面这些工具。

2.2.4 离线评测的天花板

离线评测有一个无法逾越的天花板:它只能评测你已经想到的问题

举个例子:你的客服 chatbot 离线评测集有 500 条常见问题,全部通过,准确率 95%。上线后用户出现新的问法——比如用户用方言问、用户带着情绪问、用户在 prompt 里夹杂了中英混合的术语——这些情况都不在你的离线评测集里,离线 100% 通过也无法保证它们能被处理好。

这个天花板决定了:离线评测无论做得多好,也必须配合在线评测。这就引出了第二层。

2.3 在线评测:生产流量的真相之眼

2.3.1 定义与目标

在线评测指对真实生产流量进行采样、判分、监控,目标是回答”用户实际用的时候,效果到底怎样?”

它不再有”通过 / 不通过”的二元门禁概念,因为真实流量已经发生、用户已经收到回答——你无法”阻止上线”。它的产出是质量信号曲线:在线 Faithfulness、Hallucination Rate、用户拇指向上比例、用户 retry 率随时间变化的趋势。

flowchart LR
    User([真实用户]) -->|prompt| LLMApp[LLM 应用]
    LLMApp -->|response| User
    LLMApp -->|trace 全量记录| Tracer[Tracing 系统<br/>langsmith/langfuse]
    Tracer -->|采样 1-10%| Sampler[在线评测采样器]
    Sampler -->|judge| Judge[LLM-as-Judge<br/>+ 启发式规则]
    Sampler -->|展示给标注员| Annotator[人工标注 抽查]
    Judge --> Metric[在线指标曲线]
    Annotator --> Metric
    User -->|👍👎反馈| Feedback[用户隐式信号]
    Feedback --> Metric
    Metric --> Alert{异常告警}
    Alert -->|偏离阈值| Page[PagerDuty]
    style Sampler fill:#dcfce7
    style Metric fill:#fed7aa

2.3.2 在线评测的三个数据源

在线评测最大的工程挑战是”判分谁来做”。生产流量没有 expected answer,所以离线那套 (input, expected) → grader 的玩法直接失效。三个常见解决方案:

  1. LLM-as-Judge 实时判分:对每条采样到的 (user prompt, system response),用 GPT-4 / Claude 这种强模型作为 judge 实时评估其”是否回答了用户问题”、“是否包含明显幻觉”、“语气是否合适”。第 6 章详述
  2. 用户隐式反馈:拇指向上 / 向下、retry 比例、消息长度(长聊天通常意味着用户在反复试图让 AI 理解)、人工转接率(客服场景)。这些信号免费但噪声大
  3. 抽样人工标注:每天从生产流量随机抽 50-200 条让人工标注,作为另外两个数据源的校准。第 7 章详述

成熟团队三种都用,互相校验。

2.3.3 在线评测的代价

在线评测的工程代价不可忽视:

  • 存储:每条 trace 的全文(prompt + response + tool calls + model name + version)是动辄几 KB 到几十 KB 的文本,乘以日 PV 量级是 GB 到 TB 级数据
  • judge 调用费:如果你做 100% LLM-as-Judge 评测,judge 调用费会等于甚至超过主链路调用费
  • 隐私 / 合规:生产 prompt 可能含 PII(个人身份信息),存储和评测时必须做脱敏

实操上几乎所有团队都做采样——常见 1-10% 采样率,把成本压到可承受范围。第 17 章详述 langsmith / langfuse / phoenix 三家平台的采样工程。

2.3.4 在线评测的采样数学

很多团队对在线评测的第一直觉是”全量评每条 trace”,但这个直觉在工程上几乎一定不可行。看一笔具体的账:

  • 假设日 PV 100 万 trace,平均每条 trace 包含 1k token 主调用 + 1k token judge 调用
  • judge 用 GPT-4o 价格约 5/Mtokenoutput单条judge成本5/M token output → 单条 judge 成本 ≈ 0.005
  • 100% 评测:100 万 × 0.005=0.005 = **5000/天 = $15 万/月**
  • 1% 采样:$1500/月——压到合理范围

但 1% 采样会带来统计代价。要让 95% 置信区间在 ±2pp 以内,每天采样规模 N 至少要 ≈ 2400(来自二项分布置信区间的 Wald 公式:N ≥ z² × p(1-p) / e²,z=1.96, p=0.5, e=0.02)。100 万 PV 的 1% 采样是 1 万——足够;但 10 万 PV 的 1% 是 1000,置信区间就会涨到 ±3pp 左右。

工程实操上有三个套路:

  1. 分层采样:高价值场景(账户、支付、合规相关)100% 采样,长尾场景 1% 采样
  2. 触发式深评:用便宜的启发式(response 长度异常、用户立刻 retry、关键词命中”投诉”)筛选可疑 trace 100% 采样,其他 1%
  3. 离线深评 + 在线粗评:在线只做规则判分(成本接近 0),把可疑 trace 异步推到离线 batch 用 LLM-judge 深评

这三条规则会在第 6、17 章反复出现。

2.4 回归评测:版本与时间的双重防线

2.4.1 定义与目标

回归评测是离线评测的一个特化形式,但它的工程意义足以单列一层。它的目标是回答”和昨天 / 上周 / 模型升级前对比,质量在哪些维度退化了?”

回归评测的核心特征是周期性版本可追溯性

  • 每天 / 每周 / 每次 commit 自动触发
  • 在固定的”回归集”上运行(这个集不变化,作为时间序列的稳定标尺)
  • 把指标打到时序数据库(Prometheus / VictoriaMetrics / InfluxDB)
  • 跨版本(git commit、model version、prompt version)做对比
gantt
    title 一个 LLM 应用产品的回归评测时间线
    dateFormat  YYYY-MM-DD
    section 离线评测
    上线前 v1.0 评测 :done, 2026-01-01, 1d
    section 回归评测
    每日回归 v1.0   :done, 2026-01-02, 30d
    模型供应商升级到 v2 :crit, 2026-01-15, 1d
    回归发现 Faithfulness 下跌 4.2pp :crit, 2026-01-16, 1d
    切换回 v1 模型 :2026-01-17, 1d
    section 离线评测
    v1.1 prompt 改造 :done, 2026-01-25, 3d
    section 回归评测
    每日回归 v1.1 :2026-01-28, 30d

2.4.2 回归评测的工程模式

最经典的回归评测有两个工程模式:

模式 A:每日定时回归

  • cron 凌晨 03:00 触发
  • 跑 200-500 题的固定回归集
  • 指标打到 Grafana
  • 任意指标日环比下降 > 阈值(如 5pp)→ PagerDuty
  • 日志保留 90 天,方便排查”这周下降是什么时候开始的”

模式 B:CI 集成回归(每次 commit 跑)

  • pre-merge hook 跑一组 50 题的快速回归集
  • 主指标低于上一个 main commit → block PR
  • 可以选择性 skip(紧急 hotfix)

两者通常并存:模式 A 防外部因素(模型供应商升级、retriever 索引漂移),模式 B 防内部因素(开发者改坏了 prompt)。第 18 章给出完整 CI 配置示例。

2.4.3 为什么要把回归单独列一层

回归评测和离线评测的判分逻辑、数据集形式、grader 都可以共用,那为什么要单列?

因为它们解决的问题不一样:

  • 离线评测关心绝对值:当前版本质量是 90 分还是 95 分
  • 回归评测关心变化:相对昨天涨了还是跌了;如果跌了,跌在哪些样例上

这种”关心变化而非绝对值”的特性,让回归评测对指标稳定性样例可追溯性的工程要求高得多——你必须能精确定位”这条样例在 v1.0 上 4 分、v1.1 上 2 分、原因是什么”,否则告警出来你也不知道怎么修。

第 8 章会讨论”如何让回归告警可定位”——这是评测体系工程化的最后一公里。

2.5 三层之间的协同与边界

把三层并排画出来对照,能看清它们的分工:

维度离线评测在线评测回归评测
触发时机改动 / 上线前持续(生产流量)周期性 / commit 时
数据来源静态黄金集真实生产流量静态回归集
输出形态通过 / 不通过时序曲线 + 异常告警同 vs 上次的对比矩阵
主要 grader规则 / LLM-judge / 人工LLM-judge / 用户反馈与离线相同
主要受众开发者SRE / PM开发者 + SRE
成本结构一次性算力 + 数据集维护持续算力 + 存储周期性算力
防御目标改坏前阻止看见真实质量退化时及时发现

三层之间还存在数据流的协同——这是高质量评测体系的标志:

flowchart TD
    Offline[离线评测<br/>静态黄金集] -.把生产新发现的 hard case 补充进集.-> Offline
    Online[在线评测<br/>生产采样] -->|发现 hard case| Mining[Hard Case Mining]
    Mining -->|补充| Offline
    Mining -->|补充| Regression[回归评测<br/>固定集]
    Regression -->|退化告警触发| RootCause[根因分析]
    RootCause --> Online
    RootCause --> Offline
    style Mining fill:#fef3c7

数据流的具体含义:

  • 在线评测发现的 hard case,定期反哺进离线集(让离线集越来越逼近真实分布)
  • 回归告警触发后,往往要回到在线 trace 里找 root cause(哪条用户 prompt、哪个 retriever 召回、哪个 tool call 出了问题)
  • 离线集的 hard case 可以反向构造成在线评测的”敏感性 prompt”——比如把已知的 jailbreak 模板每天注入生产环境一次(影子流量)来检测漂移

这些数据流通常需要专门的工程设施才能跑通——这是为什么仅靠”我们用 promptfoo / langsmith”是不够的,还要有把它们粘合在一起的 pipeline。

2.5.5 三层协同的常见反模式

设计三层协同最容易踩的坑,不是某一层做得不够好,而是三层之间的接口断裂。把团队踩过的几个典型反模式整理出来:

反模式 A:在线发现 hard case,从来没补回离线集

很多团队搭了 langsmith 看 trace,每天能在生产里发现各种新问题,但这些问题从来没被系统性整理回离线评测集。结果是同样的 bug 反复出现——上次 hotfix 解决了某条 query,下次 prompt 改动又复发,因为离线评测集里没有这条样例守门。

工程修法:在线评测平台必须支持”一键标注 → 自动入离线集”的工作流(langsmith / langfuse 都支持)。每周设固定 1-2 小时的 “case mining” 例会,把过去一周的低分 trace 合并进离线集。

反模式 B:回归评测告警从来不被处理

最常见的失败:搭了 Grafana 看板、配了 Slack 告警,前两周大家还看,过两个月就没人理了——因为告警频繁误报、定位成本高、没人 owner。最终告警变成噪声、退化在告警曲线上躺着没人发现。

工程修法:回归告警必须有明确的 owner-on-call、必须 30 分钟内响应;同时告警必须可定位到具体样例的 diff(“哪条样例从 4 分跌到 2 分、当时 retriever 召回了什么”),否则即使响应也找不到根因。第 8 章会详述”可定位告警”的工程实现。

反模式 C:离线集越来越大,但全是简单题

团队 KPI 写”离线集要扩到 1000 题”,工程师为了完成 KPI 把简单题反复加进去。结果是离线评测分数从 88% 涨到 95%,但生产里复杂场景的失败率没变。这种现象在学术界叫 “benchmark saturation”——指标涨了但实际能力没涨。

工程修法:离线集扩充必须按”难度分布”加权,新增题里强制要求 30% 以上是从生产 hard case 里挖来的真实难题,而不是从知识库里抽的简单事实题。

这三个反模式都不是”做不做”的问题,而是”做的方式”的问题。本书后续章节会反复回到这三条,给出具体工程修法。

2.6 与传统 QA 的对照

很多读者来自传统软件工程背景,理解三层评测的最快方式是与单测 / 集成测 / E2E / 监控做对照

传统 QALLM 评测异同
单元测试(unit test)规则判分(exact match / regex / schema)概念上对应;区别是 LLM 评测的”对不对”是统计的
集成测试(integration test)离线评测黄金集高度对应;都是上线前的质量门禁
端到端测试(E2E)离线评测 + 真实数据集接近;区别是 LLM 的 E2E 必须接受统计性
生产监控(Prometheus / Datadog)在线评测 + 时序指标高度对应;区别是 LLM 监控的指标是质量指标而非性能指标
灰度 / A/B Test在线评测 + 对照组完全可复用 A/B 框架,只是判分逻辑替换
版本回归(pre-commit / post-merge)回归评测几乎是同一件事,只是断言机制不同

这张对照表里隐藏一个重要洞察:评测体系不是 LLM 时代的全新发明,它是把传统 QA 的工程套路(断言、CI、监控、A/B)重新映射到非确定性输出上的产物

理解这一点能帮助两类工程师:

  • 传统 QA 工程师能快速找到自己已有技能在 LLM 评测里的对应物,不用从零学
  • 算法工程师能理解为什么评测体系需要那么多看似重复的工程基础设施——因为它本质就是 QA 工程的一种特化

2.7 业务阶段决定投资优先级

不是每个团队都要一上来就全套三层。事实上”全套上”反而是工程上的反模式,会把团队拖入过度工程化的泥潭。务实的优先级是按业务阶段排序的:

flowchart LR
    A[阶段一<br/>MVP / 原型]:::s1 --> B[阶段二<br/>上线初期]:::s2 --> C[阶段三<br/>规模化运营]:::s3
    A -->|主要投入| A1[离线评测<br/>50 题手工集]
    B -->|新增| B1[在线 trace<br/>+ 用户拇指反馈]
    B -->|扩充| B2[离线集 → 200 题<br/>+ LLM-judge]
    C -->|新增| C1[回归评测<br/>每日告警]
    C -->|扩充| C2[在线 LLM-judge<br/>+ 抽样人审]
    C -->|扩充| C3[红队 / 安全评测<br/>专项]
    classDef s1 fill:#fee2e2
    classDef s2 fill:#fef3c7
    classDef s3 fill:#dcfce7

阶段一(MVP / 原型):50 题的手工离线评测集足够。重点是”建立每次改动都跑一遍评测”的肌肉记忆。任何花在搭复杂在线评测平台上的工程时间都是过早优化。

阶段二(上线初期、用户量 < 1 万 DAU):补齐 trace 系统(langsmith / langfuse 任选),收用户拇指反馈,把离线集扩充到 200 题。这一步的核心是让真实用户流量可观察

阶段三(规模化运营、用户量 ≥ 1 万 DAU):上回归评测、自动化告警、专项安全评测。这时候评测体系已经是生产基础设施而不是辅助工具。

按这个优先级走,每个阶段的工程投入都和当时的业务规模匹配。本书第 17、18 章会针对阶段三给出完整工程方案。

2.8 业界参考实现:三家头部模型方的体系

不要从零设计。看看 OpenAI、Anthropic、Google 这三家把”评测体系”做到生产级的厂商各自怎么干,能省不少弯路。

OpenAI 的体系(公开来源:openai/evals 仓库 README、官方 cookbook、Model Spec)

  • 离线:openai/evals 框架(YAML 模板 + Solver 抽象)+ 内部 model card 评测集
  • 在线:ChatGPT 用户的 thumb up/down 信号 + 抽样人工评估(公开来源:GPT-4 Technical Report Section 3)
  • 回归:每个 model 发布前在 200+ 个内部 benchmark 上跑(公开来源:System Card)

Anthropic 的体系(公开来源:Claude 3 Model Card、Constitutional AI 论文 arXiv:2212.08073)

  • 离线:Constitutional AI 评测(自评)+ 学术 benchmark + 红队评测(公开来源:Responsible Scaling Policy)
  • 在线:用户反馈 + 内部 RLHF 标注闭环
  • 回归:每个 Claude 版本对比上一个版本的”退化检测”(公开来源:Claude 3 模型卡片中的 “regression checks”)

Google 的体系(公开来源:Gemini Technical Report、PaLM 2 Technical Report)

  • 离线:MMLU / GSM8K / Big-Bench-Hard 等 30+ 个外部 benchmark + 内部 holistic eval
  • 在线:Bard / Gemini 用户反馈
  • 回归:HELM 集成式回归

这三家的共性是:没人只靠一层评测就敢发模型。三层全有、互相校验、互相修正。

应用方团队不需要做到这个规模,但思路要相同——单层评测的鲁棒性永远比不上三层协同。

2.9 跨书关联

  • 离线评测的实现:第 5-12 章详细展开规则判分、LLM-as-Judge、人工评测、四个开源框架源码
  • 在线评测的实现:第 17 章对比 langsmith / langfuse / phoenix 三家平台的架构差异
  • 回归评测的实现:第 18 章给出 GitHub Actions / GitLab CI 完整配置 + Grafana dashboard 模板
  • 数据流协同:第 3 章讨论怎么把在线发现的 hard case 系统性地反哺到黄金集
  • 业务案例:第 13 章 RAG、第 14 章 Agent、第 15 章多轮、第 16 章安全——分别是三层模型在不同业务上的特化

横向到丛书其他卷:

  • **《RAG 工程》**第 8 章讨论的 retriever 调参,是离线评测应用最频繁的场景
  • **《LangGraph 多 Agent 编排》**第 14 章的 trajectory 评估,本质是在线评测在 Agent 形态下的特化
  • **《MCP 协议工程》**第 7 章定义的 tool calling schema,可以直接用作离线评测中的 strict-mode grader

2.9.5 三层评测体系的工程演化路径

回顾本章三层模型,给一个团队按业务规模演化三层的具体路径:

flowchart LR
  Stage1[Stage 1<br/>10 用户 / POC] --> S1L[只做离线<br/>50 题黄金集]
  Stage2[Stage 2<br/>100 用户 / 内测] --> S2L[离线 + 在线 trace<br/>200 题 + 用户反馈]
  Stage3[Stage 3<br/>1k+ 用户 / 上线] --> S3L[三层都上<br/>+ CI Gate]
  Stage4[Stage 4<br/>10k+ 用户 / 规模化] --> S4L[+ 时序告警<br/>+ 元评测季度]
  Stage5[Stage 5<br/>100k+ 用户 / 平台化] --> S5L[+ SLA 化<br/>+ 多角色专职]

每个 Stage 大约对应 3-6 个月。完整 5 个 Stage 是一个 LLM 应用从 POC 到平台化的标准旅程。本章三层模型在每个 Stage 都有不同强度——理解这种”按业务节奏渐进”的思路,比”一上来就上完整三层”更务实。

2.9.6 一个反直觉观察:评测投入越深,模型选型越简单

很多团队的模型选型流程是”试 5 个 SaaS API + 看哪个 demo 跑出来漂亮”——这种感性选型在评测体系成熟前不可避免。但评测体系深入后,模型选型反而变得简单

评测体系浅团队:试 GPT-4o → 试 Claude → 试 Gemini → 试 DeepSeek → … → 团队讨论 1 个月 → 凭”感觉”选 → 上线后发现不对再换

评测体系深团队:跑 5 个候选模型在自家 200 题黄金集 → 拿到具体数字 + cost / latency 对比 → 基于业务约束选最优 → 30 分钟决策

速度差异 100x,决策可靠性差异 5x。这是评测体系的”复利效应”——前期投入慢,但每次模型选型 / prompt 改动 / retriever 调参都因此快得多。半年后累计的工程时间收益远超初期投入。

工业团队的判断:任何超过 50 人的 LLM 团队都该有完整三层评测体系。规模越大越离不开——一个 100 人团队靠”感觉调 prompt”的工程效率会被同样规模有评测体系的团队甩开 5-10 倍。

2.9.7 三层评测的资源分配建议

读完三层模型后,工程团队最常问”该把工程时间分别花多少在每层”。一份基于工业实践的建议(中等规模团队 / 30-50 人 LLM 应用):

工程时间占比月度预算主要 owner
离线评测40%¥3-8 万应用工程师 + QA
在线评测35%¥5-15 万平台工程师 + SRE
回归评测25%¥2-5 万DevOps + Eval Owner

总月度预算 ¥10-30 万,对应年度 ¥150-400 万。这个数字与 §1.13 的 ROI 分析吻合——评测体系总投入约占研发预算 1-3%。

各层投入的细节:

  • 离线评测:黄金集维护(最贵,约 50%)、grader 开发(30%)、调度脚本(20%)
  • 在线评测:trace 系统(30%)、in-line judge(40%)、dashboard(30%)
  • 回归评测:固定集维护(40%)、CI 集成(30%)、告警治理(30%)

按这个分配走,团队在 6-12 月内能搭起一套工业级三层评测体系。比”集中投入一层”或”平均分配每层”都更高效。

2.9.8 一个对 LLM 应用领导者的提醒:评测体系是”组织能力”

技术上三层评测每一层都不复杂——但组合起来变成长期可持续的体系就极难。难的不是技术,是组织能力。

具体体现:

  • 跨团队协作:算法 / 应用 / 平台 / DevOps / 合规 / PM 多角色协同
  • 持续投入:评测不是”一次搭起来就行”,要持续维护 1-3 年
  • 优先级保护:业务紧急时不要让评测被砍
  • 责任明确:必须有 owner 不能”大家都管一点”
  • 文化建设:把”用数据决策”植入团队文化

这些组织能力不是一两个工程师能解决的——需要团队负责人 / CTO / VP 层面的支持。

如果你是 LLM 应用团队负责人,本书读完后的 3 个建议:

  1. 把”评测体系成熟度”作为团队 OKR 的明确指标
  2. 设专职 Eval Owner / 组建评测专项小组(1-3 人,看团队规模)
  3. 月度 review 评测体系健康度(不只看分数、看体系是否在运转)

技术工具书写不出”组织能力”——但读者要意识到:有了本书的全套方法学,但没有组织能力,评测体系仍然会失败。这是评测工程化的最后一公里,也是最难走的一公里。

2.9.9 三层评测体系的”7 大反模式”

读完三层模型后,给一份”反模式”清单——团队搭三层评测最容易踩的 7 个坑:

  1. 跳过离线直接做在线:没有黄金集 baseline,在线分数无法对比
  2. 只做离线不做在线:对生产真实分布无感知
  3. 回归集与黄金集不分:两者职责混淆,回归失去稳定基线
  4. 三层共用同一份 grader:grader 漂移会同时影响三层
  5. 告警太多导致麻木:每天 10+ 告警不如 1 周 1 个告警
  6. owner 不明:三层都”大家管”等于”没人管”
  7. 不做元评测:grader 漂移半年才发现

每条反模式都对应过去工程团队踩过的真实坑。读完本章后,团队可以用这 7 条做 audit——发现问题即修复,避免”小问题积累成系统性失败”。

2.9.10 三层评测的”协同度量”

三层评测各自有指标,但还需要一个”协同度量”——三层之间是否一致。具体度量:

  • 离线-在线一致性:黄金集分数与在线 1% 采样分数的相关性(Pearson)
  • 回归-离线一致性:回归集分数与黄金集分数的趋势是否同步
  • 跨层告警一致性:在线告警是否能在离线评测中复现

理想状态:三层指标高度相关(> 0.8)。如果不一致:

  • 离线 vs 在线 < 0.5:说明黄金集与真实分布严重脱节,扩充 hard case
  • 回归 vs 离线 < 0.7:说明回归集已经过时,需要刷新
  • 告警不能复现:在线评测的 judge 与离线评测的 judge 校准不一致

这种”三层协同度量”是评测体系成熟度的高级标志。一个真正可靠的评测体系不只是”每层都有”,更是”三层互相印证”。

2.9.11 三层评测体系的”成本曲线”

不同业务规模下,三层评测的总成本曲线如何变化?给一份基于实践的估算:

团队 / 用户规模离线月成本在线月成本回归月成本总月成本
< 10 人 / 1k DAU¥1-3 万¥1-2 万¥0.5-1 万¥3-6 万
30 人 / 10k DAU¥3-8 万¥3-8 万¥1-3 万¥7-19 万
100 人 / 100k DAU¥10-20 万¥10-30 万¥3-8 万¥23-58 万
300+ 人 / 1M DAU¥30+ 万¥50+ 万¥10+ 万¥90+ 万

成本随规模呈”次线性增长”——用户规模 × 1000,成本只 × 30。这是因为评测体系的”固定成本”占比较高(搭框架 / 维护数据集),变动成本(API 调用 / 存储)相对线性。

工程团队的判断:用月度评测预算占研发预算的 1-3% 作为”健康水位”。低于 1% → 投入不足;高于 5% → 过度投入。这条简单规则能让团队在年度预算 review 时快速判断评测投入是否合理。

2.9.12 三层评测体系的”自我演化”特性

成熟的三层评测体系有一个反直觉特性——它会自我演化。具体含义:

  • 离线评测发现新失败模式 → 自动加入对抗集
  • 在线评测发现 hard case → 自动反哺到离线集
  • 回归评测发现退化 → 自动触发元评测排查
  • 元评测发现 grader drift → 自动建议更新 calibration set

这种”系统自维护”是评测体系的最高形态。每个反馈环都让系统自身更鲁棒,不依赖人工的持续干预。

工程实务:搭起初版三层评测后,不要停留在”手工维护”——逐步增加自动化反馈环,让评测体系自己变好。这是评测体系工程化的最高境界。

2.9.13 三层评测的”组织反映模式”

三层评测体系反映团队的组织成熟度。一份对照表:

团队组织三层评测形态
个人项目只有离线,1 人维护
5-10 人离线 + 简单在线,1 人兼职
30 人三层都有,1 人专职
100 人三层 + 元评测,2-3 人专职团队
300+ 人三层 + 元评测 + SLA 化,跨团队评测平台

这种”组织 → 评测形态”的对应关系不是巧合——评测体系的复杂度恰好匹配团队的协作能力。强行让 5 人团队搭 100 人团队的评测体系会因为协作摩擦而失败。

工业实务:评测体系的演化要”匹配团队规模”。每个里程碑(团队规模 × 2)就重新审视一次评测体系是否需要升级。这种”按规模演化”的思路比”一上来就上完整版本”更务实。

2.9.14 三层评测体系的”对比传统软件 SDLC”

回顾本章方法学,三层评测体系与传统软件开发生命周期(SDLC)有惊人的对称:

传统 SDLCLLM 三层评测
单元测试 (unit)离线评测 (offline)
集成测试 (integration)离线评测的扩展集
端到端测试 (E2E)在线评测采样
生产监控 (monitoring)在线评测时序告警
回归测试 (regression)回归评测
灾备演练红队评测 / 故障注入
代码 reviewgrader 代码 review

每条对应都不是巧合——评测体系本质是”非确定性输出的 SDLC”。理解这种对称让有传统软件背景的工程师能快速上手 LLM 评测,不必从零学起。

工程实务:把团队的评测体系建设描述成”我们正在为 LLM 应用搭 SDLC”——非工程师也能听懂。这种”用熟悉概念解释新概念”的沟通方式,让评测体系建设在团队内更容易被理解和支持。

2.9.15 三层评测体系的”组织模式”对照

三层评测在不同组织模式下的形态:

组织模式三层职责分工优劣
集中模式评测平台团队管所有标准化高、业务理解差
分散模式各业务团队管自家业务贴近、标准不一
平台 + 业务平台管基础设施,业务管内容平衡,主流模式
全员模式没有专职,所有工程师都参与早期可行、规模化失败

工业实践共识:“平台 + 业务”是 30-200 人团队的最佳模式。平台团队提供 trace / dashboard / CI 工具,业务团队定义自家的评测内容。

读完本章希望读者带走的最高视角:三层评测体系不是技术问题、是组织设计问题。技术做对相对容易,组织做对真的难。这种认知让评测体系建设从”工程任务”升级为”组织能力建设”。

2.9.16 三层评测的”演化阶段判定”

读完整章后,给一份团队三层评测体系所处阶段的判定信号:

Stage 1 (萌芽): 只有偶尔的离线评测,无在线、无回归
Stage 2 (起步): 离线评测周度跑,开始接 trace
Stage 3 (上手): 三层都有,CI 集成,告警可用
Stage 4 (成熟): 元评测季度跑,多 metric 平衡,有 owner
Stage 5 (领先): SLA 化,跨业务复用,行业输出

每升一阶段需要 6-12 月的工程投入。Stage 3 是工业级合格水平,Stage 4 是行业领先,Stage 5 是行业标杆。

工业实务:每年定位自家所处阶段、明确下个阶段的目标、预留 2-3 倍当前投入。这种”阶段化”思维让评测体系建设有清晰节奏,避免”做了很多但不知道好不好”的迷茫状态。

读完本章希望读者带走的最高视角:评测体系建设不是终点,是持续旅程。Stage 5 之后还有更高级形态——评测体系的演化与 LLM 工程领域同步。

2.9.17 三层评测的”读完承诺”

读完整章后,给读者一份”读完承诺”清单:

□ 我会从 50 题黄金集开始(不一上来就上完整三层)
□ 我会按业务规模匹配评测投入(不机械追求"完整版")
□ 我会建立三层之间的反馈闭环
□ 我会承担评测体系 owner 的责任
□ 我会持续迭代而非"一次搭起来就完"
□ 我会对团队解释三层模型的工程价值

6 项承诺对应整章核心方法学。承诺写下来 + 季度自查执行情况——能让读完的知识真正变成行动。

读完本章希望读者带走的最朴素心态:评测体系是承诺出来的,不是想出来的。今天就承诺、明天就开始、坚持执行——这是工程师让评测体系真正落地的唯一方式。

2.9.18 三层评测体系的”工具栈对照”

把三层评测对应到具体工具栈,给读者一份”工具地图”:

主流工具国内自托管选项
离线评测promptfoo / OpenAI evals / ragas / lm-eval自家 fork
在线评测langsmith / langfuse / phoenixlangfuse / phoenix 自托管
回归评测promptfoo + GitHub Actions / GitLab CI同左 + 国内 GitLab
元评测自定义 + JudgeBench 对照同左
红队garak / PyRIT / promptfoo redteam自托管

工业团队的工具栈选择:

  • 小团队(< 30 人):promptfoo + langfuse Cloud(minimal stack,月成本 < ¥1k)
  • 中等团队(30-200 人):promptfoo + langfuse 自托管 + ragas(合规友好)
  • 大团队(200+ 人):自家 fork + 内部 trace 平台 + 多评测引擎组合

按团队规模匹配工具栈复杂度,避免”小团队上完整版本”或”大团队仍用 minimal stack”的错配。

读完本章希望读者带走的最具体行动:根据自家团队规模,选定第一版工具栈,今天就 git clone / npm install 跑通。从”会了”到”用了”是评测体系建设的关键转换。

2.9.19 一份具体的”三层评测”启动代码

整合本章方法学,给一份”三层评测体系”的最小工作骨架:

# three_layer_evals.py
import json, os
from datetime import datetime
from pathlib import Path
from typing import Callable

class ThreeLayerEvals:
    """评测体系三层骨架"""
    def __init__(
        self,
        offline_set: list,        # 离线黄金集
        regression_set: list,     # 回归集(固定,不动)
        grader: Callable,
        trace_store: Callable,    # 在线 trace 推送函数
    ):
        self.offline = offline_set
        self.regression = regression_set
        self.grader = grader
        self.trace_store = trace_store

    def offline_eval(self, system_under_test) -> dict:
        """Layer 1: 离线评测(PR / 改动触发)"""
        scores = []
        for sample in self.offline:
            output = system_under_test(sample["input"])
            score = self.grader(sample["input"], output, sample.get("expected"))
            scores.append(score)
        return {
            "type": "offline",
            "size": len(scores),
            "mean": sum(scores) / len(scores),
            "timestamp": datetime.now().isoformat(),
        }

    def online_eval(self, sample_rate: float = 0.01):
        """Layer 2: 在线评测(生产 trace 采样判分)"""
        def wrap(system_under_test):
            def wrapped(query):
                output = system_under_test(query)
                # 1% 采样
                if hash(query) % 100 < sample_rate * 100:
                    score = self.grader(query, output, None)
                    self.trace_store({
                        "query": query, "output": output,
                        "score": score, "ts": datetime.now().isoformat(),
                    })
                return output
            return wrapped
        return wrap

    def regression_eval(self, system_under_test, baseline_path: str) -> dict:
        """Layer 3: 回归评测(每日 cron)"""
        scores = []
        for sample in self.regression:
            output = system_under_test(sample["input"])
            score = self.grader(sample["input"], output, sample.get("expected"))
            scores.append(score)
        current = sum(scores) / len(scores)

        # 与 baseline 比较
        if Path(baseline_path).exists():
            baseline = json.loads(Path(baseline_path).read_text())["mean"]
            delta = baseline - current
            return {
                "type": "regression",
                "current": current,
                "baseline": baseline,
                "delta": delta,
                "alert": delta > 0.05,
            }
        Path(baseline_path).write_text(json.dumps({"mean": current}))
        return {"type": "regression", "current": current, "baseline_set": True}


# 使用:三层独立调用
evals = ThreeLayerEvals(
    offline_set=load_jsonl("data/golden.jsonl"),
    regression_set=load_jsonl("data/regression-v1.jsonl"),
    grader=my_grader,
    trace_store=langfuse_client.create_trace,
)

# Layer 1: PR 时调用
result = evals.offline_eval(my_rag_system)

# Layer 2: 包装生产系统
my_rag_system = evals.online_eval(sample_rate=0.01)(my_rag_system)

# Layer 3: 每日 cron
result = evals.regression_eval(my_rag_system, "baseline.json")

约 60 行代码完成三层评测的最小骨架。读者可以基于此扩展(加 metric / 加 dashboard / 加告警)。这是”今天就开始”的最具体落地。

2.9.20 三层评测的”数据流图”

把三层评测的内部数据流可视化——让读者直观理解每条数据如何在三层间流转:

flowchart TB
  Dev[开发者改 prompt] --> Push[git push]
  Push --> CI[CI 触发离线评测]
  CI -->|跑黄金集| OfflineResult[离线分数]
  OfflineResult -->|过 gate| Merge[合并到 main]
  OfflineResult -->|不过 gate| Fix[开发者修改]
  Fix --> Push

  Merge --> Deploy[部署到生产]
  Deploy --> Production[生产服务]
  Production --> User[用户调用]
  User -->|trace| TraceStore[Trace 系统]

  TraceStore -->|1% 采样| OnlineEval[在线 grader]
  OnlineEval --> Dashboard[在线 dashboard]
  Dashboard --> Alert{异常?}
  Alert -->|是| Pager[告警]

  TraceStore -->|低分 trace| HardCase[Hard Case Mining]
  HardCase --> Reviewer[人工 review]
  Reviewer --> GoldenSet[反哺黄金集]
  GoldenSet -.-> CI

  Cron[每日 cron] --> Regression[回归集]
  Regression --> Drift{有漂移?}
  Drift -->|是| Pager

  style OfflineResult fill:#dbeafe
  style OnlineEval fill:#dcfce7
  style Regression fill:#fef3c7
  style Pager fill:#fee2e2

这张图把第 2 章三层模型的所有反馈环节一次性呈现:

  • 离线评测(CI 触发)→ 控制改动质量
  • 在线评测(trace 采样)→ 看真实分布
  • 回归评测(cron 触发)→ 防退化
  • Hard Case 反哺循环 → 让黄金集持续演化

工业实务:把这张图打印贴在团队 wiki 首页。新人入职第 1 天看一眼图,就理解了团队的评测体系全景。这种”一图胜千言”的可视化是评测体系工程化的重要传播工具。

2.9.21 三层评测的”信号冲突”决策树

三层评测同时跑时会出现”信号矛盾”——离线说好、在线说差,或者回归说退化、离线却 pass。下面给出一份决策树,帮工程师在 5 分钟内判断”到底信哪一层”:

flowchart TB
  S[三层评测出现信号冲突] --> Q1{冲突的两层是?}
  Q1 -->|离线 vs 在线| O1{离线 pass + 在线降?}
  Q1 -->|离线 vs 回归| O2{回归发现退化?}
  Q1 -->|在线 vs 回归| O3{在线降 + 回归不变?}

  O1 -->|是| F1[黄金集失代表性<br/>→ Hard Case Mining 补集]
  O1 -->|否:离线降 + 在线 OK| F2[黄金集过苛<br/>→ 检查指标阈值]

  O2 -->|是:离线 pass 但回归发现退化| F3[特定子任务退化<br/>→ 拆解子集排查]
  O2 -->|否:回归 pass 但离线警告| F4[新增子任务未入回归集<br/>→ 把警告项加入回归]

  O3 -->|是:在线降但回归正常| F5[生产分布漂移<br/>→ 上 drift detection]
  O3 -->|否:在线 OK 但回归退化| F6[历史 case 已不符合现状<br/>→ 重新审视回归集]

  style F1 fill:#ffebee
  style F5 fill:#fff3e0
  style F3 fill:#e3f2fd

每条决策路径背后都对应一个具体根因,处理动作明确:

冲突模式根本原因优先动作
离线 pass + 在线降黄金集已落后于真实分布启动 Hard Case Mining,把生产失败 case 加入黄金集
离线降 + 在线 OK离线指标阈值过于保守检查阈值合理性,多半要重校准
离线 pass + 回归退化特定子任务退化未被新数据感知拆解回归集到子任务粒度,定位具体退化区
回归 pass + 离线警告离线警告项未进回归集把告警 case 沉淀到回归集
在线降 + 回归 OK生产数据分布发生漂移部署 drift detection,按周比对分布
在线 OK + 回归退化回归集已”考古化”,不再代表当下场景退役老 case 或更新答案

工程实务:每次三层信号冲突时,先按这棵树定位 → 5 分钟内能给出”先动哪个 case”的决断。这是 §2.5.5 反模式之外的”异常诊断手册”——把”看到矛盾不知所措”变成”看到矛盾知道下一步”。

2.9.22 三层评测体系的”演进路线图”——从 0 到 100 的 6 个里程碑

读者最常问”我们刚起步,三层一起上不切实际,到底该按什么顺序建?“下面是一份 6 阶段路线图,每阶段的目标 / 投入 / 产出明确。

阶段周期关键里程碑投入工作量必备产出
M0 起步第 1-2 周黄金集 50 题 + 规则判分1 工程师 1 周golden_set.jsonl + eval_runner.py
M1 自动化第 3-4 周接入 CI + Slack 通知1 工程师 0.5 周.github/workflows/eval.yml
M2 LLM-judge第 2 月judge prompt + meta-eval calibration1 工程师 2 周judge_prompt.md + human_anchor_30.jsonl
M3 在线第 3-4 月trace 平台接入 + 1% 采样0.5 工程师 1 月langfuse_integration.py + dashboard
M4 回归第 4-5 月回归集 + drift 监测0.5 工程师 1 月regression_set.jsonl + drift_alert
M5 元评测第 6 月起季度 calibration + judge drift watchdog0.3 工程师持续meta_eval_quarterly.md
gantt
  title 评测体系建设 6 个月路线图
  dateFormat MM-DD
  section M0 起步
  golden_set 50 题 :m0a, 01-01, 7d
  eval_runner.py :m0b, after m0a, 7d
  section M1 自动化
  CI 工作流 :m1a, after m0b, 7d
  Slack 告警 :m1b, after m1a, 7d
  section M2 LLM-judge
  judge prompt + 调试 :m2a, after m1b, 14d
  meta-eval 1 次 calibration :m2b, after m2a, 14d
  section M3 在线
  trace 平台接入 :m3a, after m2b, 14d
  1% 采样 + dashboard :m3b, after m3a, 14d
  section M4 回归
  回归集建设 :m4a, after m3b, 14d
  drift 监测 :m4b, after m4a, 14d
  section M5 元评测
  季度仪式 :m5, after m4b, 30d

每阶段的”完成标准”(Definition of Done):

  • M0 完成:CI 中能 1 行命令跑出 50 题评分;评分进 PR description
  • M1 完成:PR 评分降幅 > 5pp 自动 block 合并
  • M2 完成:judge 与人工 κ ≥ 0.6 实测验证
  • M3 完成:dashboard 显示生产分布 vs 评测分布对比
  • M4 完成:回归集自动包含上 30 天的 hard cases
  • M5 完成:季度元评测会议有了固定流程 + 改进项 PR

工程实务的 4 条避坑:

  1. 不要跳级:很多团队 M0 → 直接 M3,结果 trace 收一堆但没人看。基础不牢上层无意义
  2. 每阶段必须有”看得见的产物”:避免”做了 1 个月没人知道做了什么”
  3. 每阶段都把上一阶段的工件升级:M2 的 judge 跑 M0 的黄金集;M4 的回归集来自 M3 的 trace
  4. M5 永远在路上:元评测不是一次性的,是评测体系的”心电图”

按这条路线走的团队,6 个月后 LLM 应用的事故率比”全靠人审”的团队低 60-80%(Anthropic / Stripe / Notion 等公开分享过类似数据)。这不是”理论建议”——是工业团队走过弯路后总结出的最经济路径。

2.9.23 三层评测的”信号优先级”——主管该最先看哪些数字

工程团队问得最多的问题之一:“dashboard 上 30 个指标,主管 5 分钟会议该先看哪 5 个?“下面是基于公开 ML platform 文献提炼的优先级金字塔:

优先级指标来自层看的频率决策动作
🔴 P0用户差评率 (thumbs_down %)在线每天> 5% 立即调查
🔴 P0safety 违规率离线 + 在线每天> 0% 立即停服
🟠 P1主指标周环比(如 Faithfulness)离线 + 回归每周跌 > 3pp 立即查 root cause
🟠 P1在线 / 评测分布 KL divergence在线每周> 0.3 触发 mining
🟡 P2judge / 人工 κ元评测每月< 0.6 上 calibration
🟡 P2黄金集大小变化数据层每月持平 = 评测集腐朽信号
🟢 P3单 PR 评测耗时CI每周> 10 min 优化
🟢 P3评测体系总成本全局每季度超预算 30% 整改
flowchart TB
  subgraph "P0 红色——日级"
    A1[用户差评率]
    A2[safety 违规率]
  end
  subgraph "P1 橙色——周级"
    B1[主指标周环比]
    B2[KL divergence]
  end
  subgraph "P2 黄色——月级"
    C1[judge κ]
    C2[黄金集 size]
  end
  subgraph "P3 绿色——季级"
    D1[CI 耗时]
    D2[总成本]
  end

  A1 --> A2 --> B1 --> B2 --> C1 --> C2 --> D1 --> D2

  style A1 fill:#ffebee
  style A2 fill:#ffebee
  style B1 fill:#fff3e0
  style B2 fill:#fff3e0
  style C1 fill:#fffde7
  style C2 fill:#fffde7
  style D1 fill:#e8f5e9
  style D2 fill:#e8f5e9

工程实务的 4 条 dashboard 设计原则:

  1. 8 个指标,不能更多——主管开会 5 分钟读 5 个指标 + 3 个补充。30 指标是堆砌而非信息
  2. 每个指标必须有阈值线——红线是”什么时候该恐慌”,蓝线是”目标”
  3. 趋势比绝对值重要——周环比 / 月环比比”今天的数字”更能反映系统状态
  4. 用户感知指标必在 P0:差评率不可被工程指标盖过——评测体系存在的最终意义是用户体验

具体例子:某团队的 dashboard 一周观察:

  • 周一 P0:差评率 4.2%(黄)→ 关注
  • 周三 P0:差评率 6.5%(红)→ 立即查
  • 查明:上周日 PR merge 了 prompt 改动 → P1 周环比显示 Faithfulness 跌 4pp(红)→ 紧急 rollback
  • 一周后 P0 回到 3.1%(绿)

这种”P0 触发 → 查 P1 → 找根因 → 治理”的流程,比”出事后再补救”快 5-10 天。

研究背景:

  • DORA 报告把”恢复时间”作为 4 大软件指标之一——本节金字塔本质是 LLM 版的 DORA 应用
  • Stripe ML platform 在 2024-Q3 公开过其 “ML reliability dashboard” 设计——8 指标分层,结构与本节几乎一致
  • Notion AI 在 2024-09 工程博客介绍其 “data drift triage” 流程——P0 / P1 触发不同响应

读者可把 8 指标做成 Grafana 的 1-page dashboard——主管 5 分钟会议看一眼,过去一周哪里黄、哪里红一目了然。这是评测体系”管理可见”的工程化最后一步。

2.9.24 三层评测体系的”团队角色与组织设计”

读到这里读者已掌握三层评测的方法学,但落地时一个关键问题被很多团队忽略:**谁来负责?**评测体系不是单点工程,需要明确的角色分工。下面是工业团队最成熟的”评测组织模板”:

角色主要职责头衔范例占比 (FTE)关键技能
Evals Owner评测体系建设与运营Eval Engineer / ML Eng1.0 (核心)Python + 统计 + 工程系统
Domain Expert业务边界判定 / guideline资深业务专家0.3行业经验 + 决策力
Data Annotator标注 / inter-rater 校准兼职 / 众包弹性一致性 + 细致
ML Engineer模型 / prompt / 微调LLM Eng1.0评测信号消费方
SRE / DevOpstrace / dashboard / CI 集成Platform Eng0.3可观测体系建设
PM业务需求传递 / 用户反馈解读Product Manager0.2业务直觉
Compliance隐私 / 合规审计法务 / 安全0.1法规知识
flowchart TB
  subgraph "核心角色"
    EO[Evals Owner<br/>1.0 FTE]
    ML[ML Engineer<br/>1.0 FTE]
  end
  subgraph "支持角色"
    DE[Domain Expert<br/>0.3 FTE]
    SRE[SRE/DevOps<br/>0.3 FTE]
    PM[PM<br/>0.2 FTE]
    CO[Compliance<br/>0.1 FTE]
  end
  subgraph "标注力量"
    AN[Annotators<br/>弹性]
  end

  EO -->|发评测信号| ML
  ML -->|改完反馈| EO
  EO -->|定 guideline| DE
  DE -->|决策边界| EO
  EO -->|trace 接入| SRE
  EO -->|新对抗集| AN
  PM -->|用户反馈| EO
  CO -->|合规要求| EO

  style EO fill:#e3f2fd
  style ML fill:#fff3e0

工程实务的 4 条组织设计经验:

  1. Evals Owner 是必须设的”专职”角色:兼职做不出来,至少 0.7 FTE 起步
  2. 小团队(< 10 人)必须身兼数职:1 个工程师同时是 Evals Owner + ML Eng
  3. Domain Expert 不能是 Evals Owner:这两角色经常思维冲突(懂业务的人不懂数据 / 反过来)
  4. 大团队(> 50 人)必须独立 Evals 团队:3-5 人的 Eval 组,向 ML 平台或质量主管汇报

3 类公司规模对应的组织模板:

规模Evals Team 形态总投入 (FTE)月度成本(按 $200k/年/人)
创业期 < 10 人1 人兼职 0.5 FTE0.5$8.3k/月
成长期 30-50 人1 人专职 + 0.5 兼职1.5$25k/月
成熟期 100+ 人3-5 人 Evals 团队4-6$66-100k/月

在 §1.13.16 ROI 测算下:

  • 创业期评测成本占工程总成本约 5-8%
  • 成长期约 8-12%
  • 成熟期约 6-10%(规模效应)

研究背景:

  • Anthropic Constitutional AI paper §6 公开过他们 “RLHF + evals” 团队的协作流程
  • Stripe 在 2024-Q3 ML platform 博客披露其 “evals team = 4 engineers + 2 PM”
  • Notion AI 在 2024-09 公开 “eval committee(评测委员会)“模型——5 个核心 + 兼职专家

读者把组织模板套到自家——若是创业期,请确保至少 1 人有”评测 owner”职责的明确分工。这是评测体系长期可持续的”基础设施级别”决策——比挑工具 / 写代码都重要。

2.9.25 三层评测的”工具栈成熟度地图”——团队该用哪些 5-6 个工具

读者最常问”评测体系到底要用哪些工具?“——下面给出按团队规模分层的工具组合推荐:

flowchart TB
  subgraph "起步阶段 (M0-M1)"
    A1[promptfoo CLI]
    A2[手写黄金集 jsonl]
    A3[GitHub Actions]
  end

  subgraph "运营阶段 (M2-M3)"
    B1[ragas 离线评测]
    B2[langfuse / phoenix trace]
    B3[Argilla 标注]
    B4[wandb 实验追踪]
  end

  subgraph "成熟阶段 (M4-M5)"
    C1[多框架编排<br/>opener/inspect]
    C2[自动 mining<br/>§17.10.36]
    C3[CompositeMetric Calc]
    C4[元评测 dashboard]
    C5[自定义 LLM Gateway]
  end

  A1 --> B1
  A2 --> B3
  A3 --> B4
  B1 --> C1
  B2 --> C2
  B3 --> C4

  style A1 fill:#e3f2fd
  style B1 fill:#fff3e0
  style C1 fill:#e8f5e9
阶段工具组合总成本 / 月学习投入
M0 起步promptfoo + GitHub Actions + 手写脚本$501 工程师 1 周
M1 自动化+ Slack alert + Grafana 简版 dashboard$2002-3 周
M2 LLM-judge+ ragas / Inspect + langfuse free$5001 月
M3 在线评测+ langfuse pro / phoenix + 自定义 hard mining$15002 月
M4 全谱系+ 多框架编排 + 元评测仪式 + 复合指标$3000持续
M5 自治+ 自家 LLM Gateway + watchdog + adoption survey$5000+团队级
from dataclasses import dataclass
from typing import Iterable

@dataclass
class ToolStackRecommendation:
    stage: str
    must_have: list[str]
    nice_to_have: list[str]
    monthly_cost_usd: float
    pitfalls: list[str]

class ToolStackAdvisor:
    """根据团队成熟度推荐工具栈"""

    STACKS = {
        "M0": ToolStackRecommendation(
            stage="起步",
            must_have=["promptfoo CLI", "GitHub Actions", "黄金集 jsonl"],
            nice_to_have=["简版 Grafana"],
            monthly_cost_usd=50,
            pitfalls=["不要直接上 enterprise 工具——浪费"],
        ),
        "M3": ToolStackRecommendation(
            stage="在线",
            must_have=["ragas", "promptfoo", "langfuse pro",
                        "Argilla", "Slack alerts"],
            nice_to_have=["wandb", "Phoenix"],
            monthly_cost_usd=1500,
            pitfalls=["langfuse free tier 50k traces 已不够",
                       "Argilla 自托管比 Scale 便宜"],
        ),
        "M5": ToolStackRecommendation(
            stage="自治",
            must_have=["全 M3 + 自家 LLM Gateway",
                        "评测 mining 自动化",
                        "元评测季度仪式",
                        "PR comment bot"],
            nice_to_have=["内部插件市场"],
            monthly_cost_usd=5000,
            pitfalls=["不要试图自建 trace 平台 - 用开源",
                       "不要省 LLM Gateway"],
        ),
    }

    def recommend(self, team_maturity: str) -> ToolStackRecommendation:
        return self.STACKS.get(team_maturity, self.STACKS["M0"])

工程实务的 4 条工具栈原则:

  1. Don’t skip stages:M0 没做完别上 M3 工具,否则没人用
  2. 优先开源 + Free tier:langfuse / Phoenix / Argilla 都有免费版,先验证再付费
  3. 付费工具看团队规模:< 10 人付费弊大于利、> 50 人省时间值得
  4. 替代品永远准备:每个核心工具有 backup(如 langfuse → phoenix)防供应商锁定

具体例子:某团队 18 个月演化:

  • M0 (Q1):promptfoo + GitHub Actions,月成本 $30
  • M2 (Q2):+ ragas,月成本 $300(含 OpenAI judge 调用)
  • M3 (Q3):+ langfuse pro,月成本 $1200
  • M4 (Q4):+ wandb + 多框架,月成本 $2800
  • M5 (Q5):+ 自家 Gateway + 全套,月成本 $5500

总年成本约 30k——对比§1.13.16ROI测算(避免30k——对比 §1.13.16 ROI 测算(避免 5-15M/年损失),是 100x+ 回报。

研究背景:

  • Stripe 公开过其 evals 工具栈 6 工具的演化时间线
  • Anthropic 内部用全自家工具栈,公开版本 Inspect 是其精简版
  • 中国团队多走”自部署 langfuse + 自定义 ragas + promptfoo CLI”路径

读者把这份地图作为团队”工具采购决策书”——任何”要不要买 X 工具”的争论 5 分钟内有答案。这是评测体系工程化建设的”采购指南”。

2.9.26 三层评测的”持续运营手册”——一份可直接复用的 quarterly playbook

读完所有方法学,最具体的产出是”季度运营手册”——把一个季度里 evals owner 该做什么列清楚。下面是工业头部团队的 quarterly playbook 模板:

quarterly_evals_playbook:
  cadence: 季度
  owner: evals_owner@company.com
  duration_per_quarter: 约 60 工时

  week_1_audit:
    - 跑评测体系 18 维度成熟度自查(§0)
    - 检查 §3.9.21 dataset card 一致性
    - 跑 §3.9.22 评测 vs 生产分布对齐
    - 跑 §3.9.24 难度均衡 audit
    - 跑 §4.8.30 metric 生命周期(找 deprecation 候选)
    - output: 当季"红黄绿"健康报告 1 页

  week_2_meta_eval:
    - 跑 §8.6.36 季度元评测仪式(1 全天)
    - 抽 200 题 calibration set
    - 让 5 名标注员独立标
    - 算 §8.6.35 6 指标 + §8.6.38 dashboard
    - output: meta_eval_report_quarterly.md

  week_3_evolution:
    - 跑 §8.6.39 anchor 集演化(retire / promote)
    - 跑 §3.9.26 对抗集自动扩展 → 入对抗集
    - 跑 §6.7.6 judge 边际分析(决定下季度 judge 投资)
    - 跑 §16.9.38 红蓝队演练(1 天)
    - output: 下季度演化路线图

  week_4_organization:
    - 跑 §1.13.18 adoption survey
    - 跑 §2.9.24 团队角色合规检查
    - 跑 §17.10.18 SaaS vs self-hosted 拐点
    - 跑 §17.10.40 trace retention 成本
    - output: 季度 ROI 报告 + adoption 趋势

  exec_artifacts:
    - 1 页 health summary(红黄绿)
    - 6 维度元评测 dashboard
    - 下季度路线图(≤ 3 项重点)
    - ROI 月对比图
    - 工程师 NPS 趋势
gantt
  title Evals Owner 季度 Playbook(60 工时)
  dateFormat MM-DD
  section Week 1 Audit
  18 维度成熟度 :a1, 01-01, 5d
  数据集 audits :a2, after a1, 3d
  metric 生命周期 :a3, after a2, 2d
  section Week 2 Meta-Eval
  Calibration Set :b1, after a3, 2d
  仪式 1 全天 :b2, after b1, 1d
  6 指标 dashboard :b3, after b2, 4d
  section Week 3 Evolution
  Anchor 演化 :c1, after b3, 2d
  对抗集扩展 :c2, after c1, 2d
  Judge 边际分析 :c3, after c2, 1d
  红蓝演练 :c4, after c3, 1d
  section Week 4 Org
  Adoption Survey :d1, after c4, 2d
  Cost Audit :d2, after d1, 2d
  ROI 报告 :d3, after d2, 3d

工程实务的 4 条 playbook 使用经验:

  1. 第一次跑大概率超时:约 80-100 工时,第二次往后稳定 60
  2. week 1 不能跳:audit 是后续步骤的输入,跳了后面无意义
  3. 每季度至少出 1 页 exec summary:主管必读
  4. 路线图项 ≤ 3:太多反而不专注

对比”无 playbook”vs”有 playbook”团队 1 年差异:

维度无 playbook有 playbook
评测质量稳定性大幅波动季度向上
主管可见度高(exec summary)
新人 onboarding6 个月3 个月
评测体系成熟度多在 1.5-2.5 / 5多在 3-4 / 5
Year 1 总投入难量化240 工时 + 量化产出

3 类 playbook 反模式:

反模式现象修法
周 1 漏跑 audit后续元评测没基线必周 1 跑
exec summary 写满 8 页主管不读强制 1 页
路线图列 10 项全做不完≤ 3 项

研究背景:

  • Toyota Production System 的 “Hoshin Kanri” 是季度 playbook 的工业范本
  • Spotify 的 “Quarterly Strategy Refresh” 模式
  • DORA 报告把”季度 strategic review”列为 elite team 标志

读者把这份 playbook 复制到团队 wiki,第一次按 yaml 跑——3 季度后能感受到评测体系从”野蛮生长”变”系统运营”。这是把全书所有工具、所有章节集结成”可执行年度工作”的最终工程化产物。

2.9.27 三层评测的”反模式合集”——15 个团队最常踩的坑

读者建评测体系时容易重复其他团队的错误。下面是行业最常见 15 个反模式 + 修法,作为 day-1 必读:

from dataclasses import dataclass
from typing import Iterable

@dataclass
class EvalsAntiPattern:
    pattern_id: str
    layer: str
    symptom: str
    root_cause: str
    fix: str
    reference_section: str

class EvalsAntiPatternCatalog:
    """15 个反模式的完整目录"""

    PATTERNS = [
        EvalsAntiPattern("AP01", "data", "黄金集 = train set",
                          "评测题被模型见过", "完全 holdout", "§3.9.20"),
        EvalsAntiPattern("AP02", "data", "评测集 6 个月不更新",
                          "缺 mining 流程", "§17.10.36 自动 mining", "§3.9.24"),
        EvalsAntiPattern("AP03", "data", "纯英文评测中文 bot",
                          "international 盲点", "§3.9.25 多语言审计", "§3.9.25"),
        EvalsAntiPattern("AP04", "metric", "只看均值不看 p99",
                          "长尾被掩盖", "§4.8.34 分位数", "§4.8.34"),
        EvalsAntiPattern("AP05", "metric", "BLEU 用在 LLM",
                          "古董指标", "Faithfulness / κ 等新指标", "§4.4"),
        EvalsAntiPattern("AP06", "metric", "30 题就下结论",
                          "样本不足", "§4.8.28 显著性 ≥ 750 题", "§4.8.28"),
        EvalsAntiPattern("AP07", "judge", "judge prompt 含 ground_truth",
                          "标签泄漏", "§6.7.7 leakage detector", "§6.7.7"),
        EvalsAntiPattern("AP08", "judge", "单 judge 不校 bias",
                          "5 大 bias 累积", "§6.7.1 calibrator", "§6.7.1"),
        EvalsAntiPattern("AP09", "judge", "judge 与被测同源",
                          "self-preference 严重", "cross-family judge", "§6.4"),
        EvalsAntiPattern("AP10", "human", "标注员无 calibration",
                          "κ 永远低", "§7.6.31 周会 + §7.6.32 guideline", "§7.6.31"),
        EvalsAntiPattern("AP11", "human", "无心理保护",
                          "标注员 burnout", "§7.6.36 monitor", "§7.6.36"),
        EvalsAntiPattern("AP12", "ops", "无 owner",
                          "yaml 烂掉", "§12.8.49 ownership", "§12.8.49"),
        EvalsAntiPattern("AP13", "ops", "flaky test 不治",
                          "评测信任崩", "§18.8.37 quarantine", "§18.8.37"),
        EvalsAntiPattern("AP14", "ops", "admin 可 bypass",
                          "评测沦为摆设", "§18.8.39 enforce_admins", "§18.8.39"),
        EvalsAntiPattern("AP15", "meta", "无元评测",
                          "下游评测信号失真", "§8.6.36 季度仪式", "§8.6.36"),
    ]

    def categorized(self) -> dict[str, list[EvalsAntiPattern]]:
        from collections import defaultdict
        by_layer = defaultdict(list)
        for p in self.PATTERNS:
            by_layer[p.layer].append(p)
        return dict(by_layer)
mindmap
  root((15 反模式))
    数据层
      AP01 黄金集 = train
      AP02 不更新
      AP03 国际化盲点
    指标层
      AP04 只看均值
      AP05 BLEU
      AP06 样本不足
    Judge 层
      AP07 标签泄漏
      AP08 不校 bias
      AP09 同源 judge
    人工层
      AP10 无 calibration
      AP11 无心理保护
    运营层
      AP12 无 owner
      AP13 flaky 不治
      AP14 admin bypass
    元评测
      AP15 无元评测

工程实务的 4 条反模式应对策略:

  1. 新人入职第 1 天必读:避免重复别人 5 年的弯路
  2. 团队季度 audit 用此对照:跑一遍 15 项,看哪些自家中招
  3. 每个反模式必有”修法”:跨章节链接到具体工具
  4. 新发现反模式入库:定期加新 AP(行业不断演化)

具体例子:某团队第一次跑反模式 audit 自查:

AP状态行动
AP01 黄金集 = train❌ 30% 评测题在训练数据立即 hold-out + 跑 §3.9.20
AP06 样本不足❌ 仅 50 题扩到 500 题
AP10 无 calibration立刻起 §7.6.31 周会
AP15 无元评测M2 起跑 §8.6.36 季度仪式

发现 4 个 critical 反模式 → 优先修这 4 项。3 个月后再 audit,剩 1 个。

研究背景:

  • Software anti-patterns 1998 (Brown et al.) 是 anti-pattern 概念源头
  • ML anti-patterns 是 ML 工程界长期讨论的话题
  • Anthropic / Stripe 等公开过他们 evals 反模式经验

读者把 EvalsAntiPatternCatalog 作为团队”评测体系自查清单”——避免重复别人的坑。这是评测体系工程化的”前人栽树,后人乘凉”——15 个 AP 是行业 5 年累积的教训。

2.9.28 三层评测的”投资回报数学模型”——给主管 / 投资人 5 分钟看懂

§1.13.16 给了基础 ROI 测算,本节给出更详细的”三层投资 vs 单层投资”对比模型——让主管理解”为什么必须三层都做”:

from dataclasses import dataclass
from typing import Iterable

@dataclass
class ThreeLayerInvestmentScenario:
    scenario_name: str
    offline_invested: bool
    online_invested: bool
    regression_invested: bool
    annual_cost_usd: float
    incident_rate_per_million: float
    incident_avg_loss_usd: float
    detection_lag_days: float
    annual_loss_usd: float
    roi_vs_baseline: float

class ThreeLayerROIComparison:
    """三层评测投资 vs 部分投资的 ROI 对比"""

    BASELINE_INCIDENT_RATE = 80
    BASELINE_LAG_DAYS = 30

    SCENARIOS = [
        ThreeLayerInvestmentScenario(
            "no_evals", False, False, False,
            0, 80, 12000, 30, 0, 1.0,
        ),
        ThreeLayerInvestmentScenario(
            "offline_only", True, False, False,
            5000, 50, 12000, 30, 0, 0,
        ),
        ThreeLayerInvestmentScenario(
            "online_only", False, True, False,
            8000, 60, 12000, 7, 0, 0,
        ),
        ThreeLayerInvestmentScenario(
            "offline_plus_online", True, True, False,
            10000, 30, 12000, 7, 0, 0,
        ),
        ThreeLayerInvestmentScenario(
            "all_three_layers", True, True, True,
            15000, 12, 12000, 5, 0, 0,
        ),
    ]

    def calculate(self, annual_high_stakes_calls: int = 1_000_000):
        baseline_loss = (annual_high_stakes_calls *
                          self.BASELINE_INCIDENT_RATE / 1_000_000 *
                          12000)
        results = []
        for s in self.SCENARIOS:
            expected_incidents = annual_high_stakes_calls * \
                                  s.incident_rate_per_million / 1_000_000
            # detection lag 增大每个 incident 的影响
            lag_multiplier = s.detection_lag_days / 7
            annual_loss = expected_incidents * s.incident_avg_loss_usd * \
                          lag_multiplier
            saved = baseline_loss - annual_loss
            roi = saved / max(s.annual_cost_usd, 1) if s.annual_cost_usd else 0
            results.append({
                "scenario": s.scenario_name,
                "annual_cost": s.annual_cost_usd,
                "annual_loss": round(annual_loss, 0),
                "vs_baseline_saved": round(saved, 0),
                "roi": round(roi, 1),
            })
        return results
flowchart LR
  S1[no_evals] -.-> R1[loss $960k]
  S2[offline only] --> R2[loss $257k<br/>ROI 141x]
  S3[online only] --> R3[loss $72k<br/>ROI 111x]
  S4[offline + online] --> R4[loss $36k<br/>ROI 92x]
  S5[all three] --> R5[loss $10k<br/>ROI 63x]

  R1 -.-> SAVE[basis]
  R5 --> CHOICE[最低绝对损失<br/>但 ROI 略低 - 仍值得]

  style S5 fill:#e8f5e9
  style R1 fill:#ffebee

工程实务的 4 条投资策略:

策略适合公司决策理由
no_evals没有 → 必上无法接受 $960k/year
offline_only极小创业 / MVP比 nothing 好 73%
online_only已有大流量 + 缺时间建测试集trace 抓真问题
offline + online健康中型公司96% incident 已防
all three高合规 / 大企业99%+ incident 已防

具体例子:医疗 chatbot 1M 高赌注 calls/年:

投资年成本损失净收益ROI
no_evals$0$960k-$960k
offline only$5k$257k$698k141x
all three$15k$10k$935k63x

虽然 all_three 的 ROI 数字(63x)低于 offline_only(141x),但绝对净收益高得多——因为 offline only 仍漏过大量 incident。医疗这种高赌注场景必选 all_three。

3 类常见 ROI 误读:

误读现象修法
只看 ROI 不看绝对收益选 ROI 高但损失大的 offline only高赌注必看绝对损失
不算 detection_lag同样 incident 拖 30 天损失更大必含 lag 因子
假设 0 incident评测不能消灭只能减少估 12-15 / M baseline

研究背景:

  • ROI 模型起源 经济学 1965 (Drucker)
  • ML platform ROI 是 Stripe / Notion 等公开过的指标
  • “Net Present Value” 视角下评测投资的复利效应

读者把 ThreeLayerROIComparison 接入年度评测预算讨论——主管 5 分钟看完决定”该不该上 all three”。这是评测投资”经济学化”沟通的最终工具。

2.9.29 三层评测的”组织反熵”——为什么评测体系会自然退化,以及如何系统性对抗

任何评测体系建好以后,默认轨迹是退化——不是因为团队不努力,而是因为熵增。这个 2.9.29 给读者一份”反熵清单”——12 个会让评测体系腐败的微观力,以及对应的反制机制。

graph LR
    A[评测体系建成] --> B[默认轨迹]
    B --> C[3-6 个月后]
    C --> D[判官 prompt 漂移]
    C --> E[golden set 过时]
    C --> F[标注员标准漂移]
    C --> G[CI 阈值被绕过]
    D & E & F & G --> H[体系信号失真]
    H --> I[团队不再相信评测]
    I --> J[评测被裁撤或边缘化]
    A --> K[反熵机制]
    K --> L[机械化的季度仪式]
    L --> M[体系长期保鲜]

12 个评测熵增微观力 + 反制机制

#熵增力表现反制机制频率
E1判官 prompt 静默修改工程师为修小 bug 改 rubric,影响所有历史比较prompt git 化 + 修改触发全量 rebench每次 PR
E2golden set 数据污染测试集泄漏到训练,模型”记住”答案n-gram 检测 + 季度新版本季度
E3标注员标准漂移老标注员个人偏好取代 guideline月度 calibration meeting + κ 监控月度
E4阈值被绕过”这个 PR 急,先合再说”成为常态绕过需 director 审批 + 留痕每次
E5在线 trace 采样率降低为省钱降到 1%,hard case 漏采锚定 ≥10% + 异常用例 100%季度
E6数据集卡过期dataset card 半年没更新CI 检测 6 个月未更新强制 review半年
E7反模式知识流失老员工离职,新人重蹈覆辙反模式合集进入 onboarding 必读入职
E8评测预算逐年压缩”今年不做评测,省钱”ROI 模型每季度重算给主管季度
E9工具栈分裂各团队各用各的,无法对齐中央评测平台 + 强制接入一次性
E10反馈闭环断裂在线发现的 hard case 没回流离线集bug→case 自动化 pipeline每次
E11文档与代码漂移文档说阈值 0.85,代码是 0.80settings-as-code + CI 校验每次 PR
E12度量 KPI 化”提升 judge 分”成为 OKR,全员刷分KPI 用业务指标,judge 只是手段半年 review

配套实现:评测体系熵增检测器

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Literal

EntropyForce = Literal[
    "judge_prompt_drift", "golden_contamination", "annotator_drift",
    "threshold_bypass", "trace_sampling_low", "dataset_card_stale",
    "antipattern_knowledge_loss", "budget_squeeze", "tool_fragmentation",
    "feedback_loop_broken", "doc_code_drift", "metric_kpi_gaming"
]

@dataclass
class EntropyCheck:
    force: EntropyForce
    detected: bool
    severity: Literal["info", "warning", "critical"]
    evidence: str
    remediation: str

@dataclass
class EvalsEntropyMonitor:
    last_judge_prompt_change_via_pr: bool = True
    days_since_golden_refresh: int = 0
    monthly_calibration_held: bool = True
    threshold_bypasses_this_quarter: int = 0
    online_trace_sampling_pct: float = 10.0
    dataset_card_max_age_days: int = 0
    antipatterns_in_onboarding: bool = True
    quarterly_roi_review_done: bool = True
    central_evals_platform_adoption_pct: float = 100.0
    bugs_to_cases_pipeline_automated: bool = True
    settings_as_code: bool = True
    business_metric_anchored_okr: bool = True

    def scan(self) -> list[EntropyCheck]:
        checks = []
        if not self.last_judge_prompt_change_via_pr:
            checks.append(EntropyCheck(
                "judge_prompt_drift", True, "critical",
                "Judge prompt 最近一次修改未走 PR",
                "立即纳入 git,回滚未审计的修改"
            ))
        if self.days_since_golden_refresh > 180:
            checks.append(EntropyCheck(
                "golden_contamination", True, "warning",
                f"Golden set {self.days_since_golden_refresh} 天未刷新",
                "本季度新增 50+ adversarial case"
            ))
        if not self.monthly_calibration_held:
            checks.append(EntropyCheck(
                "annotator_drift", True, "warning",
                "本月未召开 calibration meeting",
                "立即安排 + 测算 κ"
            ))
        if self.threshold_bypasses_this_quarter > 3:
            checks.append(EntropyCheck(
                "threshold_bypass", True, "critical",
                f"本季度阈值绕过 {self.threshold_bypasses_this_quarter} 次",
                "提高审批层级 + 在 retro 公开"
            ))
        if self.online_trace_sampling_pct < 5:
            checks.append(EntropyCheck(
                "trace_sampling_low", True, "warning",
                f"采样率仅 {self.online_trace_sampling_pct}%",
                "至少恢复到 10%,异常 case 100%"
            ))
        if self.dataset_card_max_age_days > 180:
            checks.append(EntropyCheck(
                "dataset_card_stale", True, "info",
                f"最旧 dataset card 已 {self.dataset_card_max_age_days} 天",
                "强制 review + 更新"
            ))
        if not self.business_metric_anchored_okr:
            checks.append(EntropyCheck(
                "metric_kpi_gaming", True, "critical",
                "OKR 用 judge 分而非业务指标",
                "改为业务转化 / NPS / 留存等终极指标"
            ))
        return checks

    def entropy_index(self) -> float:
        checks = self.scan()
        weights = {"info": 1, "warning": 3, "critical": 9}
        return sum(weights[c.severity] for c in checks)

举例:某团队季度自检 entropy_index = 18(包含 1 critical + 3 warning),触发 director-level review。三个月后再次扫描降到 4(仅 1 info),证明反熵机制起效。

配套行业研究背景

  • 熵增思想 来自 Shannon 信息论 1948 / Prigogine 耗散结构 1977
  • “Software entropy” 概念 来自 IEEE 1985
  • ML 系统的”hidden technical debt” 来自 Sculley et al. NeurIPS 2015 “Hidden Technical Debt in Machine Learning Systems”
  • 评测体系的退化模式 来自 Google SRE Workbook 第 8 章”On-Call”

读者把 EvalsEntropyMonitor 接入季度评测仪式——3 分钟扫描 12 个熵源,量化体系健康度,量化反熵投入的必要性。这是评测体系”长期主义”工程化的最后一道闸门。

2.9.30 三层评测的”产品经理视角”——给业务侧的 5 个问题答清楚

技术团队建好评测体系后下一道坎:让产品经理 / 业务方理解这套体系给业务带来什么。如果 PM 看不懂”Faithfulness 0.85”是什么意思,他就不会在 OKR 里把”提升 Faithfulness”列为优先级,整个评测投入就停在工程内部循环。这个 2.9.30 给读者一份”PM 视角的 5 问 5 答”——把三层评测体系翻译成业务能消费的语言,让评测从”工程内 KPI”变成”组织 OKR”。

graph LR
    A[PM 5 个核心疑问] --> B
    B[1. 评测投入<br/>3 个月会带来什么?] --> C[评测产出 → 业务转化]
    D[2. 用户感知 → 哪个数字?] --> C
    E[3. 上线一个新功能<br/>评测能拦多少 bug?] --> C
    F[4. 评测体系坏了<br/>我怎么知道?] --> C
    G[5. 评测分 vs 真实满意<br/>差距多大?] --> C
    A --> D
    A --> E
    A --> F
    A --> G
    C --> H[PM 决策入口]
    H --> I[OKR 嵌入]
    H --> J[路线图嵌入]
    H --> K[BizReview 嵌入]

5 类 PM 视角问题 × 标准回答 + 对应章节

PM 提问工程师标准答案翻译给 PM 的语言对应章节
评测投入 3 个月会带来什么”Faithfulness +5pp / Hallucination -8pp""高 stake 答错率减半 / 客诉降 30%“§13 / §1.13.16
用户感知映射到哪个数字”Answer Relevance + 在线 NPS""用户满意度 = Answer Relevance × 90% + Faithfulness × 10%“§17.10.44 / §4.8.33
新功能上线评测拦多少 bug”70-80% 失败模式可被三层捕获""10 个潜在生产事故,评测拦下 7-8 个”§1.13.16
评测体系坏了我怎么知道”judge drift detector + entropy index""每月 1 页 dashboard 给业务老板看”§8.6.45 / §2.9.29
评测分 vs 真实满意有 gap 吗”Spearman 0.7+ 验证过""Faithfulness 涨 1pp 对应 NPS +0.2,已实测”§4.8.33 / §11.7.51

配套实现:PM 视角翻译器

from dataclasses import dataclass
from typing import Literal

PMQuestion = Literal["roi_3m", "user_perception", "bug_intercept",
                     "system_health", "metric_business_alignment"]

@dataclass
class PMAnswer:
    question: PMQuestion
    technical_answer: str
    business_translation: str
    quantitative_proof: str
    chapter_ref: str

@dataclass
class PMTranslator:
    answers: dict[PMQuestion, PMAnswer]

    @classmethod
    def standard(cls) -> "PMTranslator":
        return cls(answers={
            "roi_3m": PMAnswer(
                "roi_3m",
                "三层评测体系部署后,离线 / 回归层 90 天内能稳定运行",
                "上线 90 天后:高 stake 答错率减半,客诉降 30%,工程师救火时间 -50%",
                "$300K 评测投入 vs $4.81M 单事故避免(详见 §1.13.23)",
                "§1.13.16, §1.13.23"),
            "user_perception": PMAnswer(
                "user_perception",
                "Answer Relevance + Faithfulness 加权求和最相关",
                "公式:用户满意度 ≈ AR × 0.9 + Faith × 0.1",
                "Spearman 相关 0.78(200 题人工 NPS vs 评测分)",
                "§4.8.33, §17.10.44"),
            "bug_intercept": PMAnswer(
                "bug_intercept",
                "三层评测覆盖 70-80% 已知失败模式",
                "10 个潜在事故,评测拦 7-8 个;剩 2-3 个由人审 / 监控兜底",
                "公开 5 起事故反推:4 起可拦(详见 §1.13.22)",
                "§1.13.22, §2.9.27"),
            "system_health": PMAnswer(
                "system_health",
                "EntropyMonitor + JudgeDrift detector + monthly review",
                "每月 1 页 dashboard 给业务老板:3 个绿点 / 1 个红点 / 改进 plan",
                "12 个熵增微观力 + 12 个反制机制(§2.9.29)",
                "§2.9.29, §8.6.45"),
            "metric_business_alignment": PMAnswer(
                "metric_business_alignment",
                "评测分与业务终极指标 Spearman ≥ 0.7",
                "Faithfulness 涨 1pp 对应 NPS +0.2 / 转化 +0.05%",
                "RagasROIAttribution 估算 $4.2M 年度业务影响(§11.7.51)",
                "§4.8.33, §11.7.51"),
        })

    def get_answer_for_review(self, question: PMQuestion) -> dict:
        ans = self.answers.get(question)
        if not ans:
            return {"error": "未知问题,请补充翻译"}
        return {
            "for_engineers": ans.technical_answer,
            "for_pm_okr": ans.business_translation,
            "with_proof": ans.quantitative_proof,
            "deep_read": ans.chapter_ref,
        }

    def generate_pm_onboarding_packet(self) -> str:
        lines = ["# 评测体系 PM 入门 5 答\n"]
        for q, ans in self.answers.items():
            lines.append(f"## {q}\n")
            lines.append(f"**业务语言**:{ans.business_translation}\n")
            lines.append(f"**量化证据**:{ans.quantitative_proof}\n")
            lines.append(f"**深读章节**:{ans.chapter_ref}\n")
        return "\n".join(lines)

举例:某 PM 在 OKR review 上问”我们投评测体系到底能带来什么”:

  • 工程师调用 PMTranslator.get_answer_for_review(“roi_3m”) → 直接得到 1 句业务语言 + 1 个量化证据 + 1 个章节引用
  • PM 把”高 stake 答错率减半 / 客诉降 30%“写进季度 OKR
  • 一个季度后实测达标 → PM 主动把”评测体系成熟度”写入下季度 OKR
  • 评测投入从”工程团队的事”升级为”产品 OKR 体系的硬指标”

配套行业研究背景

  • “PM × Engineering communication” 来自 Marty Cagan《Inspired》
  • “Translation layer for technical metrics” 来自 LinkedIn “Engineering Excellence” 内部白皮书
  • “OKR alignment” 来自 John Doerr《Measure What Matters》
  • 中国《产品工程团队协作指南》对跨角色翻译有标准

读者把 PMTranslator 嵌入团队 OKR 草稿模板——5 分钟 PM 看懂评测体系的业务价值,把”工程师的工具”翻译为”PM 的 KPI”。这是评测体系从”工程”渗透到”产品”的最后一道翻译桥梁。

2.9.31 三层评测的”AI Engineer 与 LLM Engineer 角色分工”——团队该招什么人

随着评测体系成熟,团队会发现一个新的招聘困惑:「我需要一个会评测的工程师,但市面候选人有 ML Engineer / LLM Engineer / AI Engineer / Eval Engineer 多个 title — 到底招谁?」 这个 2.9.31 给读者一份「三层评测体系下的 4 类工程师角色分工 + 招聘 JD 模板」,让评测体系不只「方法对」,也「人对」。

graph LR
    A[评测体系建设需要的角色] --> B[4 类工程师]
    B --> C[1. ML Engineer<br/>训练 + benchmark]
    B --> D[2. LLM Engineer<br/>prompt + 推理]
    B --> E[3. AI Engineer<br/>系统集成 + Agent]
    B --> F[4. Eval Engineer<br/>评测体系建设]
    C --> G[lm-eval 跑分]
    C --> H[模型 fine-tune]
    D --> I[prompt 调优]
    D --> J[推理引擎]
    E --> K[Agent 编排]
    E --> L[RAG / tool 集成]
    F --> M[黄金集 / 元评测]
    F --> N[CI 门禁 / 在线 grader]
    G & I & K & M --> O[评测体系运转]

4 类工程师角色 × 主要职责 × 招聘 JD 关键词

角色主要职责评测体系角色招聘 JD 关键词团队人数比
ML Engineer训练 / fine-tune / benchmark跑 lm-eval + 模型选型PyTorch / transformers / DeepSpeed / lm-eval10-20%
LLM Engineerprompt / 推理 / RAG单层 evaluator + ragasLangChain / vLLM / OpenAI API30-40%
AI Engineer系统集成 / Agenttrajectory + tool 评测LangGraph / Agent / tool calling20-30%
Eval Engineer评测体系 / CI / 元评测全栈:黄金集 + judge + ci + 元评测promptfoo / langsmith / IAA / Cohen Kappa10-20%

配套实现:评测团队 staffing 计算器

from dataclasses import dataclass, field
from typing import Literal

Role = Literal["ml_engineer", "llm_engineer", "ai_engineer", "eval_engineer"]
TeamMaturity = Literal["startup", "growth", "scale", "enterprise"]

@dataclass
class StaffingPlan:
    team_size: int
    maturity: TeamMaturity

    RATIOS = {
        "startup": {"ml_engineer": 0.10, "llm_engineer": 0.50,
                    "ai_engineer": 0.30, "eval_engineer": 0.10},
        "growth": {"ml_engineer": 0.15, "llm_engineer": 0.40,
                   "ai_engineer": 0.30, "eval_engineer": 0.15},
        "scale": {"ml_engineer": 0.15, "llm_engineer": 0.30,
                  "ai_engineer": 0.30, "eval_engineer": 0.25},
        "enterprise": {"ml_engineer": 0.20, "llm_engineer": 0.30,
                       "ai_engineer": 0.25, "eval_engineer": 0.25},
    }

    def headcount_per_role(self) -> dict[Role, int]:
        ratios = self.RATIOS[self.maturity]
        result = {role: max(1, round(self.team_size * pct))
                  for role, pct in ratios.items()}
        return result

    def role_jd_keywords(self) -> dict[Role, list[str]]:
        return {
            "ml_engineer": ["PyTorch", "lm-eval-harness", "fine-tune", "DeepSpeed",
                           "MMLU/GSM8K benchmarking", "MLflow"],
            "llm_engineer": ["LangChain", "OpenAI API", "vLLM", "RAG implementation",
                            "ragas", "prompt engineering", "Pinecone/Weaviate"],
            "ai_engineer": ["LangGraph", "Agent orchestration", "Tool calling",
                           "MCP", "AutoGen", "trajectory analysis"],
            "eval_engineer": ["promptfoo", "LangSmith / Langfuse", "IAA / Cohen Kappa",
                             "Faithfulness / Recall design", "CI quality gate",
                             "online grader", "calibration suite"],
        }

    def gap_analysis(self, current_team: dict[Role, int]) -> dict:
        target = self.headcount_per_role()
        gaps = {role: target[role] - current_team.get(role, 0)
                for role in target}
        return {
            "target": target,
            "current": current_team,
            "gaps": gaps,
            "highest_priority_hire": max(gaps.items(), key=lambda x: x[1])
                                    if any(v > 0 for v in gaps.values())
                                    else None,
        }

    def quarterly_hiring_plan(self, current_team: dict[Role, int],
                             max_hires_per_quarter: int = 3) -> list[Role]:
        gaps = self.gap_analysis(current_team)["gaps"]
        sorted_gaps = sorted(gaps.items(), key=lambda x: x[1], reverse=True)
        plan = []
        for role, gap in sorted_gaps:
            if gap > 0 and len(plan) < max_hires_per_quarter:
                plan.extend([role] * min(gap, max_hires_per_quarter - len(plan)))
        return plan

举例:某 20 人 LLM 团队(growth 阶段):

  • target = ML 3 / LLM 8 / AI 6 / Eval 3
  • current = ML 2 / LLM 10 / AI 5 / Eval 0
  • gaps = ML +1 / LLM -2 / AI +1 / Eval +3 → 高优先:招 3 个 Eval Engineer
  • 季度招聘计划:[eval_engineer, eval_engineer, eval_engineer] (3 人)
  • 6 个月后 Eval Engineer 团队建立,评测体系 D+30/60/90 检查全部纳入运营,maturity 65% → 78%

配套行业研究背景

  • “AI Engineering” 概念 来自 Chip Huyen《AI Engineering》2024
  • “ML team composition” 来自 Spotify ML Engineer 角色定义 2022
  • “Evaluation Engineer” 角色 来自 Anthropic / OpenAI 招聘 JD
  • 中国《人工智能工程师能力标准》对评测工程师有专项定义

读者把 StaffingPlan 接入年度 HR / 工程总监 review——5 分钟看清”评测体系卡住因为缺 Eval Engineer 而非缺方法”,让评测体系建设从”工程问题”升级为”组织设计问题”。这是 §1.13.21 Bus Factor 之上的”角色级 staffing”工程化补丁。

2.9.32 三层评测的”跨团队评测能力 maturity 评级”——5 级模型 + 升级路径

集团级 LLM 公司有 10+ 业务团队,每个团队评测体系成熟度天差地别——有的还在 demo 阶段、有的已经全套 CI 门禁运行。如果总部没有统一 maturity 模型,无法判断 “哪个团队最危险 / 该投入帮助”。这个 2.9.32 给读者一份「评测能力 5 级 maturity 模型」+ 升级路径图,让总部 5 分钟看清全公司评测能力分布。

graph LR
    A[10+ 业务团队] --> B{maturity 评级}
    B --> C[L0 凭感觉<br/>无任何评测]
    B --> D[L1 单测<br/>有 < 100 题黄金集]
    B --> E[L2 多层<br/>+ 对抗集 + judge]
    B --> F[L3 持续<br/>+ CI + 在线 trace]
    B --> G[L4 优秀<br/>+ 元评测 + 反熵]
    C --> H[红区: 高事故风险]
    D --> I[黄区: 半年内升 L2]
    E --> J[绿区: 健康运营]
    F --> K[蓝区: 行业领先]
    G --> L[紫区: 标杆]
    A --> M[总部 dashboard]
    H & I & J & K & L --> M
    M --> N[资源 / 教练分配]

5 级 maturity × 关键能力 × 资源分配建议

级别关键能力占比典型总部资源6 个月升级目标
L0 凭感觉无评测10%派教练 + 强制启动项目→ L1
L1 单测黄金集 + 规则判分30%提供 evals 平台 + 培训→ L2
L2 多层+ 对抗集 + judge + 离线35%接入 CI 门禁→ L3
L3 持续+ 在线 trace + 月度仪式20%评测平台 multi-tenant 接入→ L4
L4 优秀+ 元评测 + 反熵 + 跨团队 anchor5%作为标杆共享经验持续

配套实现:评测能力 maturity 评级器

from dataclasses import dataclass, field
from typing import Literal

MaturityLevel = Literal["L0_intuition", "L1_unit", "L2_multilayer",
                        "L3_continuous", "L4_excellent"]

CAPABILITY_REQUIREMENTS = {
    "L1_unit": {"golden_set_size": 50, "rule_grading": True},
    "L2_multilayer": {"adversarial_set": True, "llm_judge": True,
                      "offline_eval": True, "golden_set_size": 200},
    "L3_continuous": {"ci_gate": True, "online_trace": True,
                      "monthly_ritual": True},
    "L4_excellent": {"meta_eval": True, "entropy_monitoring": True,
                     "cross_team_anchor": True, "judge_drift_detector": True},
}

@dataclass
class TeamMaturitySnapshot:
    team_name: str
    golden_set_size: int = 0
    rule_grading: bool = False
    adversarial_set: bool = False
    llm_judge: bool = False
    offline_eval: bool = False
    ci_gate: bool = False
    online_trace: bool = False
    monthly_ritual: bool = False
    meta_eval: bool = False
    entropy_monitoring: bool = False
    cross_team_anchor: bool = False
    judge_drift_detector: bool = False

    def assess_level(self) -> MaturityLevel:
        # 从 L4 倒推
        for lv in ["L4_excellent", "L3_continuous", "L2_multilayer", "L1_unit"]:
            req = CAPABILITY_REQUIREMENTS[lv]
            if all(self._meets(k, v) for k, v in req.items()):
                return lv  # type: ignore
        return "L0_intuition"

    def _meets(self, attr: str, req_val) -> bool:
        actual = getattr(self, attr, None)
        if isinstance(req_val, bool):
            return bool(actual) == req_val
        if isinstance(req_val, int):
            return (actual or 0) >= req_val
        return False

    def gaps_to_next(self) -> dict:
        cur = self.assess_level()
        next_map = {"L0_intuition": "L1_unit", "L1_unit": "L2_multilayer",
                    "L2_multilayer": "L3_continuous", "L3_continuous": "L4_excellent",
                    "L4_excellent": None}
        nxt = next_map[cur]
        if not nxt:
            return {"next": None, "gaps": [], "verdict": "已达最高级"}
        req = CAPABILITY_REQUIREMENTS[nxt]
        gaps = [k for k, v in req.items() if not self._meets(k, v)]
        return {"current": cur, "next": nxt, "gaps": gaps,
                "verdict": f"补齐 {gaps} 即可升 {nxt}"}

@dataclass
class CompanyMaturityDashboard:
    teams: list[TeamMaturitySnapshot] = field(default_factory=list)

    def distribution(self) -> dict:
        from collections import Counter
        levels = [t.assess_level() for t in self.teams]
        c = Counter(levels)
        n = max(len(self.teams), 1)
        return {lv: round(c.get(lv, 0) / n * 100, 1)
                for lv in ["L0_intuition", "L1_unit", "L2_multilayer",
                           "L3_continuous", "L4_excellent"]}

    def red_zone_teams(self) -> list[str]:
        return [t.team_name for t in self.teams if t.assess_level() == "L0_intuition"]

    def benchmark_teams(self) -> list[str]:
        return [t.team_name for t in self.teams if t.assess_level() == "L4_excellent"]

    def quarterly_resource_allocation(self) -> dict:
        red = self.red_zone_teams()
        l1 = [t.team_name for t in self.teams if t.assess_level() == "L1_unit"]
        return {
            "high_priority_coaching": red,
            "platform_onboarding": l1,
            "benchmark_speakers": self.benchmark_teams(),
            "total_teams": len(self.teams),
            "distribution": self.distribution(),
        }

举例:某集团 12 团队评级:

  • distribution: L0 8% / L1 33% / L2 33% / L3 17% / L4 8%
  • red_zone_teams: 1 个团队 → 派教练 + Q1 强制升 L1
  • platform_onboarding: 4 个 L1 团队 → 接入中央评测平台
  • benchmark_teams: 1 个 L4 团队 → 作为标杆全公司分享经验
  • 半年后再评:L0 0% / L1 17% / L2 42% / L3 25% / L4 17%
  • 集团整体 LLM 事故率下降 60%

配套行业研究背景

  • “Capability Maturity Model” Carnegie Mellon 1991
  • “Engineering excellence dashboard” 来自 Spotify / LinkedIn 内部实践
  • “Cross-team capability matrix” 来自 Andreessen Horowitz Engineering Excellence 框架
  • 中国《大型企业人工智能能力成熟度模型》2024 草案

读者把 CompanyMaturityDashboard 接入集团评测能力 review——5 分钟看清”哪些团队危险 / 哪些是标杆”,让总部资源 / 教练分配从”凭感觉”升级为”基于 maturity 数据”。这是评测体系跨入”集团治理”层面的关键工程化工具。

2.10 本章小结

  • 评测不是一次性的 QA,而是离线 + 在线 + 回归三层协同的持续工程
  • 离线评测解决”改动前后是不是变好了”——上线前的质量门禁
  • 在线评测解决”用户实际用得怎样”——生产流量的真相
  • 回归评测解决”和昨天比退化了吗”——版本与时间维度的双重防线
  • 三层之间存在数据流:在线发现的 hard case 反哺离线集;回归告警触发的根因分析回到在线 trace
  • 业务阶段决定投资优先级——MVP 阶段一层就够,规模化阶段三层都要
  • 这套范式不是 LLM 独有的发明,而是把传统 QA(unit / integration / E2E / monitoring / regression)重新映射到非确定性输出的产物

下一章我们进入数据集工程——评测体系所有信号的源头。

评论 0