一、从方案到工程化
③ 评测思维框架的决策树到"评测方案确定"就停了:
看到系统 -> 问题1粒度 -> 问题2时机 -> 问题3来源 -> 问题4目的 -> 方案确定
│
▼
停了
方案确定后,还有四步工程化工作:
评测方案确定
│
▼
① 测试集管理 -- "拿什么来评?"
│
▼
② 执行管道 -- "跑一次还是持续跑?"
│
▼
③ 回归保护 -- "怎么防止退化?"
│
▼
④ 在线评测 -- "线上有没有出问题?"
判分实现(规则校验/LLM-as-judge/两层配方)已在④ 评测实践中展开,本文不重复。本文聚焦"怎么持续跑"。
二、测试集管理
测试集是评测的输入--"拿什么来评"。没有测试集,方案和决策树都是空的。
来源:三种途径
┌──────────────────┬──────────────────┬──────────────────┐
│ 真实日志 │ 合成数据 │ 人工构造 │
├──────────────────┼──────────────────┼──────────────────┤
│ 从生产环境采集 │ 用 LLM 生成变体 │ 手写边缘用例 │
│ 最贴近实际 │ 可规模化 │ 覆盖极端情况 │
│ 但有隐私/合规问题 │ 但可能不代表真实 │ 但不可规模化 │
└──────────────────┴──────────────────┴──────────────────┘
建议:阶段 0 用真实日志 10 条起步(最贴近实际,量小无隐私压力)。阶段 1 扩展时混合三种:真实日志为主,合成数据补量,人工构造补边缘用例。
数量:多少够用?
阶段 0: 10 条 -- 手动跑通,验证评测逻辑能跑
阶段 1: 50-100条 -- 统计有意义,能看出通过率分布
阶段 2: 200+ 条 -- 回归保护,CI 触发时足够稳定
不要等"够了"才开始。10 条就能验证评测逻辑是否可用。评测集是渐进的,不是一次定终身的。
版本化与演化策略
测试集会演化--加用例、改用例、删过时用例。版本化的最低要求:
v1: 初始 10 条 (阶段 0)
v2: 扩展到 50 条 (阶段 1)
v3: 删除 3 条过时用例, 加 5 条边缘用例
...
每个版本记录:哪些用例、什么时候加的、为什么加。这样当通过率变化时,能区分"是系统变好了"还是"测试集变了"。
演化策略:
- 定期审查:每月检查一次,删除过时用例(业务场景已变的),补充新发现的问题模式
- 缺陷驱动:线上发现的新问题,转化为测试用例加入集
- 难度分级:标注用例难度(简单/中等/困难),避免测试集全是简单用例导致通过率虚高
三、执行管道
测试集有了,下一步是"怎么跑、怎么读结果"。
跑一次 vs 持续跑
跑一次 (阶段 0-1):
"这次评测通过率多少?"
-> 手动触发, 看结果, 改系统, 再跑
-> 目的: 验证系统当前能力
持续跑 (阶段 2):
"系统有没有退化?"
-> CI 触发, 每次提交自动跑
-> 目的: 回归保护, 防止改 A 破坏 B
阶段 0-1 是"跑一次"。手动跑评测脚本,看结果,改系统,再跑。这够了。不需要 CI,不需要自动化。等评测集稳定到 100+ 条、评测逻辑成熟后,再上 CI。
结果聚合:分数意味着什么
跑完评测,会得到一堆分数。怎么读?
A 层 (代码评测) 的聚合:
忠实度通过率 = 通过用例数 / 总用例数
例: 85/100 = 85%
含义: 85% 的用例中, 节点3 忠实渲染了节点2 的观测值
15% 的用例有谎报/失真/编造 -> 这些是缺陷, 要修
B 层 (LLM-as-judge) 的聚合:
质量均分 = 所有用例的质量均分
例: 3.8/5.0
含义: 通过忠实度的用例, 语义质量平均 3.8 分
低于 3.0 的用例是质量问题 -> 看裁判的理由, 改提示词
两个分数分开看,不要合成一个。忠实度 85% + 质量 3.8/5 不能合成"综合分 78.5"--它们测的是不同评测维度,合成会掩盖问题。忠实度失败是缺陷(要修),质量低是优化(要调提示词)。
评测本身的健康度
评测也可能有问题。这些信号说明评测本身需要检查:
信号 1: 忠实度通过率 = 100%
-> 可能规则太松 (所有用例都过, 没区分力)
-> 检查: 手动看几条输出, 确认规则在校验对的东西
信号 2: 质量分数全是 3 分
-> 可能裁判提示词太模糊 (LLM 不敢给极端分)
-> 检查: 加锚点用例 (已知应该是 1 分和 5 分的用例)
信号 3: 同一用例跑 3 次, 分数差 2 分以上
-> 裁判不稳定
-> 检查: 换裁判模型, 或加少样本示例稳定判分
信号 4: 通过率突然跳变 (85% -> 50%)
-> 要么系统坏了, 要么测试集变了
-> 检查: 先确认测试集版本没变, 再查系统
评测不是"设好就忘"的。定期检查评测本身的健康度,否则在用一把弯的尺子量东西。
四、回归保护
持续跑评测的核心目的是回归保护--防止改 A 悄悄破坏 B。
退化检测
正常波动: ±2% (LLM 非确定性导致)
退化信号: 通过率下降 >5%
严重退化: 通过率下降 >15%
检测方法:
- 每次评测结果与上一版本对比
- 下降 >5% 触发告警,下降 >15% 阻止发布
- 区分"整体退化"和"个别用例退化":整体退化是系统问题,个别用例退化可能是测试集问题
告警阈值设计
┌──────────────────┬──────────────────────────────┐
│ 通过率变化 │ 动作 │
├──────────────────┼──────────────────────────────┤
│ ±2% 以内 │ 正常波动,无需干预 │
│ 下降 2-5% │ 记录,观察趋势 │
│ 下降 5-15% │ 告警,人工审查 │
│ 下降 >15% │ 阻止发布,必须修复 │
│ 上升 >5% │ 警惕(可能是测试集变简单了) │
└──────────────────┴──────────────────────────────┘
注意:通过率突然大幅上升也要警惕--可能是测试集泄露(模型见过答案),或测试集被改简单了。
CI 集成
PR 提交 -> 自动跑评测 -> 与基线对比
│
├─ 通过率下降 <5% -> CI 通过
├─ 通过率下降 5-15% -> CI 通过但告警
└─ 通过率下降 >15% -> CI 失败, 阻止合并
五、在线评测实践
离线评测测能力上限,在线评测测线上健康。智能体的静默失败(看起来正常但结果偏离预期)比传统服务严重得多,需要在线评测及时发现。
工具能力
Langfuse、Phoenix、DeepEval 均支持在线评测:
Langfuse:
对生产 trace 实时打分
支持 LLM-as-a-judge、code evaluators、user feedback
在 Dashboard 中展示评测趋势
Phoenix:
对生产 trace 打分
支持 LLM-based evaluations、human annotations
组件级评测(span 级)
DeepEval:
专门的 Online Evals 功能
对 trace/span/thread 三级打分
集成 Confident AI 平台做监控
静默失败检测
传统监控: 延迟升高、错误率上升 -> 告警
在线评测: 输出偏离预期 -> 告警
区别:
传统监控看"系统有没有报错"
在线评测看"输出质量有没有下降"
智能体的静默失败:
系统正常返回 200, 没有报错
但输出内容偏离了预期(编造信息、遗漏关键字段、语义跑偏)
只有在线评测能发现
采样策略
在线评测对每条生产流量打分成本太高(LLM-as-judge 需要 LLM 调用)。采样策略:
全量打分: 代码评测(便宜,可对每条流量跑)
采样打分: LLM-as-judge(贵,采样 1-10% 跑)
触发打分: 只对异常 trace 打分(如延迟超阈值、用户反馈负面)
在线评测不替代离线评测。离线评测用测试集测能力上限,在线评测用真实流量测线上健康。两者互补。
六、从手动到自动
不需要一开始就建评测管道。从手动开始,渐进自动化。
三阶段路径
阶段 0: 手动 (本周就能开始)
┌──────────────────────────────────────────────┐
│ - 10 条用例 (从真实日志选) │
│ - 评测脚本: 一个 Python 文件 │
│ - 执行: 手动跑 python eval.py │
│ - 结果: 打印到终端 / 写 CSV │
│ - 频率: 改完系统跑一次 │
│ │
│ 目标: 验证评测逻辑能跑, 能区分好/坏用例 │
│ 成功标准: 能指着某条用例说"这个该失败" │
│ 且评测确实给了失败 │
└──────────────────────────────────────────────┘
│ (评测逻辑稳定后, 2-3 周)
▼
阶段 1: 脚本化
┌──────────────────────────────────────────────┐
│ - 50-100 条用例 │
│ - 评测脚本: 包化的模块, 可导入 │
│ - 执行: 一键脚本 run_eval.sh │
│ - 结果: 写 DB / 写评测平台分数 │
│ - 频率: 每周跑一次 / 版本发布前跑 │
│ │
│ 目标: 评测可重复, 结果可追踪 │
│ 成功标准: 两次跑同一版本, 通过率一致 (±2%) │
└──────────────────────────────────────────────┘
│ (评测稳定后, 4-6 周)
▼
阶段 2: CI 触发 + 在线评测
┌──────────────────────────────────────────────┐
│ - 200+ 条用例 │
│ - 评测脚本: CI 流水线的一部分 │
│ - 执行: 每次 PR / 每次合并自动触发 │
│ - 结果: 写 DB + 告警 (通过率下降 >5% 时) │
│ - 在线: 对生产 trace 采样打分 │
│ - 频率: 每次代码变更 + 线上持续 │
│ │
│ 目标: 回归保护 + 线上质量监控 │
│ 成功标准: 有一次 CI 拦住了一个退化 │
│ 或在线评测发现了一次静默失败 │
└──────────────────────────────────────────────┘
关键原则
不等管道,本周就能开始阶段 0。
最常见的错误是"等评测基础设施建好再开始评测"。这是倒着的--不知道评测什么,怎么建基础设施?阶段 0 的 10 条手动评测就能告诉你:真值逻辑对不对、裁判提示词稳不稳定、维度选择准不准。这些信息反过来指导管道建设。
阶段 0 不需要任何工具。 一个 Python 文件、10 条用例、print() 输出结果。够了。工具是阶段 1-2 的事。
每个阶段的退出条件是"评测逻辑稳定",不是"时间到了"。 如果阶段 0 跑了 2 周还在改评测逻辑(规则校验老是误判、裁判提示词不稳定),不要急着进阶段 1。先让评测逻辑稳定。
决策:评测流水线架构
读完本文,画出你的评测流水线:
┌─────────────────────────────────────────────────────┐
│ 评测流水线架构 │
├─────────────────────────────────────────────────────┤
│ 测试集: □ 10条 □ 50-100条 □ 200+条 │
│ 执行方式: □ 手动 □ 脚本化 □ CI触发 │
│ 回归保护: □ 无 □ 告警 □ 阻止发布 │
│ 在线评测: □ 无 □ 采样打分 □ 全量打分 │
├─────────────────────────────────────────────────────┤
│ 当前阶段: □ 阶段0 □ 阶段1 □ 阶段2 │
│ 下一步: │
│ (基于当前位置,写出下一步行动) │
└─────────────────────────────────────────────────────┘