前五篇里,我一直在说"实测":
引用越界 30.4%
延伸段带引用 75% → 0%
k=3 之后召回饱和
R@1 从 0.61 提到 0.74
你可能想问:这些数字是怎么来的?我凭什么信?
更关键的是——如果换个时间重跑,数字会不会变?
这一篇讲的就是这件事。它是整个系列的收口:没有评测,前面五篇的所有优化都只是"感觉"。
第 9 题问:Agent 效果怎么评?看哪些指标?
很多人会答"任务完成率"。但这个数字太粗——它只能告诉你"成了没成",不告诉你"哪一步坏了"。
必须分三层,因为三层的失败模式不同,修法也不同:
| 层 | 指标 | 怎么测 | 坏了说明什么 |
|---|---|---|---|
| 单步 | 工具选择准确率、参数正确率 | 构造"给定上下文应选哪个工具 + 什么参数"的样本 | 提示词/工具描述写得不好 |
| 链路 | 任务完成率、平均步数、平均耗时、token 成本 | 端到端跑任务 | 编排逻辑有问题 |
| 端到端 | 人工评分、追问率、放弃率、点踩率 | 标注 + 行为信号 | 体验有问题 |
为什么要分层: 因为一个"任务完成率 70%"可能来自完全不同的原因:
原因 A:每次工具都选对了,但第 5 步超时了 → 工程问题(第二篇)
原因 B:工具有时候选错,多绕了 3 步才成功 → 提示词问题(第二篇)
原因 C:任务压根没完成,但用户没追问,直接走了 → 产品问题
只看总完成率,你永远不知道该修哪里。
虽然第 9 题问的是 Agent,但 RAG 侧也必须分层(第四、五篇提到过):
| 层 | 指标 |
|---|---|
| 检索 | Hit@k、Recall@k、MRR、负样本正确过滤率 |
| 生成 | 引用越界率、忠实度、结构完整性、拒答率 |
一个必须遵守的原则:检索和生成分开测。 因为它们的失败模式完全不同:
检索 95% + 生成 80% → 问题在生成,去改提示词/加校验
检索 60% + 生成 90% → 问题在检索,去改切分/换向量模型
混在一起测一个"答案准确率",你分不清该动哪一头。
① 真实 badcase:线上出过的问题,一条一条攒 ← 最有价值
② 合成样本:按意图分类,每类造几条,保证覆盖面
③ 边界样本:空输入、超长输入、工具失败、权限不足、多意图混合
第 ① 类最重要,但很多人不做。因为它需要你先有线上日志——而很多项目连日志都没留全。
检索测试集里,必须有一部分是改写后的问法(不含原关键词):
原题:取消重复请求怎么做
改写:同一个接口被连着调了两次,怎么处理
为什么必须有: 如果测试题都含关键词,那么关键词检索一路就能全中,向量检索的价值根本测不出来。你会得出"混合检索没比关键词好"的错误结论。
这条在 RAG 评测里最容易被漏:
只测「该答对的答对了吗」
不测「该拒答的拒了吗」
结果就是:正样本分数一路涨,负样本开始在编,而你看不见。
一个真实的过时案例:
负样本里有题:「如何训练一个大语言模型」
当时博客里确实没有相关内容 → 好题,应该拒答
后来博客发了两篇(幻觉、微调)→ 这题现在能答了
但它还留在负样本里 → 每次评测都报「漏拒答 12.5%」
这个 12.5% 是假警报,不是 bug。
测试集是会过期的。 每季度都要重新过一遍负样本,看它们是不是还是"知识库外"的问题。
这是整个系列里我最想说的一点,也是第 9 题真正的深度所在。
同一道题、同一个模型、同一段提示词,连跑 3 次:
第 1 次 延伸段引用:无
第 2 次 延伸段引用:无
第 3 次 延伸段引用:[1] ← 出问题了
所以我第一次跑全量得到的"13/23 题有问题",不能当基线。 换个时间重跑可能变成 8 题。
假设你有一个 20 题的测试集,每题真实失败率 30%(这是个"不太行"的系统)。
跑一次,得到"几题不合格"。这个数字服从二项分布:
期望值 20 × 0.3 = 6.0
标准差 sqrt(20 × 0.3 × 0.7) = 2.05
90% 的区间 3 ~ 9
这意味着什么:
今天跑出 3 题不合格
明天跑出 9 题不合格
系统一行代码没改
你如果拿单次结果当基线,会得出完全错误的结论。 "昨天还好好的,今天怎么坏了?"——它没坏,是采样波动。
❌ 旧口径:这题有没有问题(跑 1 次)
✅ 新口径:跑 N 次里有几次出问题(跑 3 次)
同一个系统,两种口径的结论可能完全不同:
| 题目 | 跑 1 次 | 跑 3 次 |
|---|---|---|
| 题 A | 有问题 | 3/3(稳定复现,真 bug) |
| 题 B | 有问题 | 1/3(偶发,可能只是采样) |
| 题 C | 没问题 | 2/3(藏着的 bug,单次跑漏了) |
只有"触发率"这个口径才稳定、可比。 而且它能区分两类完全不同的东西:
3/3 全中 → 系统性行为,改代码能修
1/3 偶发 → 概率问题,只能降低频率,不能消除
这个区分非常重要。 因为"3/3 的题"才是真正值得花时间的——修一道,收益是确定的。
"跑 3 次至少错 1 次"和"至少错 2 次"是两个不同的指标。
用二项分布算一下(真实失败率 10% 的"好题"):
| 口径 | 被误判为坏题的概率 |
|---|---|
| 跑 3 次,至少错 1 次 | 27.1% |
| 跑 3 次,至少错 2 次 | 2.8% |
第一个口径会把 27% 的好题冤枉成坏题。
所以:改口径必须重跑全部基线,不能拿新口径的数字和旧口径的数字比。
第 9 题最后半句:线上 badcase 怎么回流?
很多人答"收集起来加到测试集"。这不够,中间漏了最关键的一步:归因。
① 线上 trace 全量落库
问题 / 改写后的问题 / 召回了哪些 chunk / 调了什么工具 / 输出 / 耗时 / token
↓
② 人工过一遍,标出哪一步错了
↓
③ 归因到四类
检索错 / 工具选错 / 参数错 / 生成错
↓
④ 按归因补进对应的测试集(不是混在一起)
↓
⑤ 修完跑「同一批」对比
因为不同原因要补到不同测试集:
| 归因 | 补到哪 | 修哪里 |
|---|---|---|
| 检索错 | 检索测试集 | 切分 / 向量模型 / 混合权重 |
| 工具选错 | 工具选择集 | 工具描述 / 路由规则 |
| 参数错 | 参数抽取集 | schema / few-shot |
| 生成错 | 生成质量集 | 提示词 / 校验层 |
混在一起会怎样:
你把 10 条 badcase 一股脑加进一个"总测试集"
修完提示词,总分从 70% 涨到 75%
→ 你以为修好了
→ 实际上:生成那 6 条修好了,检索那 4 条还是错的
(4 条 × 0 分 被 6 条 × 满分 平均掉了)
分开测,你才知道自己到底修好了哪一块。
❌ 改完 prompt,重新生成一批题测 → 数字变好
(可能是题目变了,可能是采样运气)
✅ 改完 prompt,跑与基线完全相同的那批题 → 数字变好
(才说明是改动带来的)
这就是为什么基线要"固定"下来——固定题目、固定重复次数、固定模型版本。任何一个变了,数字就不可比。
能辅助,不能当准。
用户没有追问 ≠ 答对了
(他可能直接放弃了)
用户点了踩 → 基本可以确定答错了
用户点了赞 → 不一定对(可能只是格式好看)
所以行为信号用来看趋势、找异常(比如某天追问率突然翻倍),不用来判对错。
现在回头看,前五篇的每个结论其实都是评测支撑的:
| 篇 | 结论 | 靠什么验证 |
|---|---|---|
| 一 | 纯 Agent 不可控 | 路径不可复现 + 调用次数分布 |
| 二 | 长链路失败是常态 | 50 步 × 98% = 36% |
| 三 | 改写不是每轮都要做 | 条件触发能砍掉 60~80% 调用 |
| 四 | 切分要按结构 | 碎片率 / 超长率 / 检索命中后 chunk 是否完整 |
| 五 | 阈值比 K 重要 | K–Recall 曲线在 k=3 饱和 |
| 五 | 校验必须用代码 | 75% → 0%(12 次生成) |
| 六 | 单次评测不可信 | 二项分布:标准差 2.05,区间 3~9 |
如果没有第六篇,前五篇全是观点。
第一篇开头我说:模型是概率的,业务是确定的。
六篇下来,这句话变成了六个具体的动作:
因为它是概率的
→ 所以划边界(哪些不交给它) 篇一
→ 所以长链路要能续跑(每步都可能失败) 篇二
→ 所以记忆要按需取(全塞会稀释) 篇三
→ 所以资料要建库(它不记得你的私有数据)篇四
→ 所以生成后要校验(提示词管不住采样) 篇五
→ 所以要有评测(否则不知道修好没) 篇六
这六件事没有一件是"让模型变准",全都是"让不确定性可控"。
这就是我对 AI 应用开发这个岗位的理解:
它不是一个"调模型"的活,是一个在概率之上建确定性的活。
模型负责理解模糊的输入、生成自然的语言;其余的——判定、计算、状态、时效、边界——都得靠代码兜住。
而"兜没兜住",只能靠评测回答。
第 9 题:Agent 效果怎么评?看哪些指标,测试集怎么搭,线上 badcase 怎么回流?
答题骨架: