回归门禁:分数跌了,不许上线
实测拦截一次"更便宜但更差"的改动——全课最该抄走的 50 行
前面几章你建了评测集、学会了判分。但评测只有变成"卡住上线的闸门",才真正产生价值——否则它只是一个"改完可以跑一下、忘了也没人管"的脚本。
这一节把评测做成门禁:分数跌了,退出码非零,CI 直接红灯。
实测里它立刻救了一次场:一个"看起来更灵活、成本还低了 52%"的提示词改动,被门禁拦下了——因为分数从 17.0 掉到了 14.0。
体检报告放在抽屉里,和体检不合格不许开车,是两种东西。
前者是"信息",后者是"制度"。AP5 前四节给你信息,这一节把它变成制度。
实测:门禁拦了一次"看起来更好"的改动
基线(v2):规则版提示词,20 条评测集跑 3 轮
✅ 已写入基线:17.0/20(版本 v2)
错例 3 条(正品质疑判成商品咨询、医保卡问题、温和抱怨)
改动(bad):把优先级规则删掉,换成"请综合考虑用户的整体意图和情绪,灵活判断"——听起来更聪明、提示词更短更便宜。
本次得分:14.0/20(标准差 0.00)
平均 token:1330 | 耗时 12s
错例(6 条):
「我买的鞋子小了,能换个大一码吗?」 应=退款 实=商品咨询
「收到的杯子碎了!!!要求赔偿」 应=投诉 实=退款
…
门禁判定
基线(v2):17.0/20 本次(bad):14.0/20
分差:-3.0 噪声地板:0.50 token 变化:-52%
❌ 拦截:分数下跌 3.0 超过噪声地板 —— 不许上线
注意那个 -52%:这次改动便宜了一半。如果只看成本仪表(ap1.6),你会以为这是一次成功的优化。只有评测门禁能告诉你:你用 3 分准确率换了这一半成本。
门禁的核心逻辑(50 行)
base = json.load(open("eval_baseline.json")) # 基线快照
mean, sd, tok, errs = run_eval(rounds=3) # 本次跑分(多轮取均值)
noise = max(base["sd"], sd, 0.5) # 噪声地板(ap2.5)
delta = mean - base["mean"]
if delta < -noise:
print("❌ 拦截:分数下跌超过噪声地板")
sys.exit(1) # ← CI 因非零退出码而红灯
elif delta > noise:
print("✅ 放行:真提升")
if tok/base["tok"] - 1 > 0.2:
print("⚠️ 但成本涨了 20% 以上,确认划算吗?")
else:
print("⚖️ 打平") # 打平时看成本决定去留
四个设计要点:
- 多轮取均值(3 轮):单轮波动会误伤(ap2.5 的噪声地板);
- 噪声地板兜底(至少 0.5):防止基线记录的标准差偏小导致误拦;
- 同时看成本:分数持平但成本涨 20%,应该回退(ap2.4 的教训);
sys.exit(1):这一行才是"门禁"——没有它,就只是个报告。
挂进 CI
# .github/workflows/eval.yml(示意)
- name: 运行评测门禁
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
run: python3 evals/gate.py # 分数跌了 → 非零退出 → PR 红灯
配套的三条团队规矩(比技术更重要):
- 改提示词/换模型/改检索参数的 PR,必须跑门禁;
- 基线更新要单独提交(
--update-baseline),并在 commit message 里写清"为什么允许这次变化"; - 门禁红了不许强行合并——要么修,要么明确说明为何可接受(比如"用 3 分准确率换 50% 成本,产品决定接受")并记录在案。
门禁要卡哪些指标
| 指标 | 来源 | 建议阈值 |
|---|---|---|
| 主任务准确率 | ap2.4 评测集 | 不得低于基线 - 噪声地板 |
| 检索命中率(RAG) | ap4.4 | 同上 |
| 幻觉率 | ap5.4 | 只许降不许升 |
| 格式合格率 | ap3.2 校验器 | 应接近 100%,跌破即红灯 |
| 单次成本 | ap1.6 流水 | 涨幅超过 20% 需人工确认 |
| P95 延迟 | ap1.6 流水 | 超阈值告警(不一定拦截) |
别一次性卡太多:先卡 1-2 个核心指标(准确率 + 成本),跑顺了再加——门禁误报太多,大家就会开始绕过它。
🔧 动手做:给你的项目装上门禁(15 分钟)
- 跑本节代码:先
python3 gate.py --update-baseline建基线,再PROMPT_VERSION=bad python3 gate.py看它拦截。 - 换成你的评测集和提示词,建立你的基线快照(提交进 Git,基线是代码的一部分)。
- 故意做一次坏改动(删掉一条关键规则),跑门禁——它拦住了吗?
- 挂进 CI:GitHub Actions / GitLab CI 里加一步跑门禁(key 走 secrets,ap1.5 的规矩)。
- 写进项目 README:"改提示词的 PR 必须门禁绿灯"——把制度写下来。
✅ 做到这里你应该有:一个会 exit(1) 的门禁脚本 + 提交进 Git 的基线快照 + CI 配置 + 一条写下来的团队规矩。
为什么:🎯 这是全课最该抄走的 50 行代码。它把前面所有章节的努力(评测集、判分、指标)变成不可绕过的制度——从此"我觉得这样更好"必须先过一遍数字。
本课两次"白改"教训(ap2.4 加规则没用、ap2.5 加步骤没用)+ 这次"更便宜但更差"的拦截,合起来就是门禁存在的全部理由。
最常见的门禁失效方式:门禁红了,开发者顺手跑一下 --update-baseline,红灯变绿灯,继续合并。这就是把门拆了。
三条防御:① 基线文件的改动必须单独 PR 并有人审(和代码一样);② commit message 里必须写清为什么允许这次下跌;③ 定期回看基线历史——如果你的基线一路在降,说明你在温水煮青蛙。技术上再完善的门禁,也挡不住一个决定绕过它的团队;制度和技术要一起立。
我的评测集在【路径/格式】,主要指标有【准确率/幻觉率/成本】。请帮我写一个可挂进 CI 的门禁脚本:1) 多轮跑分取均值 + 噪声地板判定;2) 与 Git 里的基线快照比较;3) 分数跌了 exit(1),打平时看成本给建议;4) 输出适合 CI 日志的摘要(含错例);5) 给出 GitHub Actions 配置示例(key 走 secrets)。
门禁三句话:分数跌了 exit(1)(这一行才是门禁)、多轮取均值 + 噪声地板防误伤、成本和质量一起卡;基线更新要单独审——别自己把门拆了。
AP5 最后一节讲评测体系的"活水":线上真实的失败样本怎么回流进评测集——让你的评测越用越准,而不是停留在上线那天的想象。
🔎 来源与核验· 1 条,点开核对
