前面几篇都在讨论离线测评如何证明价值,但真正的系统不会停在离线结论里。线上失败一旦发生,如果不能回流成 regression 用例,测评集就会慢慢和真实风险脱节。
测评集不是写完就不动的静态资产。真正有用的测评集,应该随着线上问题一起成长。
这篇回答一个很具体的问题:用户在线上踩过的坑,怎么变成以后每次发布都会自动执行的 regression 用例?
核心链路是:
text
线上负反馈 → trace 归因 → 生成 regression case → 加入 inputs/ → 下次发布前自动拦截
你有没有遇到过这种情况
三月份,你的报销 Skill 出了一个 bug:用户说"帮我报差旅费",Skill 却走了日常报销流程。你查了半天,发现是路由规则的边界条件没写清楚。
你修了 Skill,手动验证通过,上线。
五月份,同事改了一下 Skill 的另一个规则。改完跑了测评,通过率 91%,顺利上线。
六月份,又有用户反馈:同样的问题------"差旅费"走了日常流程。
三月份修的 bug,六月份又复现了。 因为五月份的改动不小心影响了路由逻辑,而测评集里没有这个场景的用例。
这就是"死的测评集"的代价:它只能防住设计时想到的问题,无法防住已经发生过、修过、但没有被固化为用例的问题。
核心概念:测评集的"活"与"死"
| 特征 | 死的测评集 | 活的测评集 |
|---|---|---|
| 用例来源 | 设计时一次性编写 | 设计 + 线上回流 + 定期补充 |
| 更新频率 | 基本不更新 | 每次修 bug 后自动新增 |
| 覆盖范围 | 设计者能想到的 | 设计者能想到的 + 用户帮你发现的 |
| 防护能力 | 只防新问题 | 防新问题 + 防旧问题复现 |
| 规模趋势 | 停滞 | 持续增长(但有上限控制) |
活水机制:让线上真实失败自动转化为 regression 用例,每修一个 bug 就在测评集里"钉一颗钉子"------确保这个问题永远不会第二次出现。
反馈闭环全链路
markdown
线上用户触发 Skill
↓ 执行结果不符合预期
用户投诉 / 在线监控检测到失败
↓ 记录为失败 trace
失败 trace 归因分析
↓ 分类为具体失败模式
达到阈值(同一模式 ≥ 3 次)
↓ 触发回流
自动生成 regression 用例
↓ 写入 inputs/<skill>/
人工审核确认
↓ 纳入正式用例库
下次 SkillSentry 测评自动执行
↓ 验证修复效果
永久防护(每次变更都会回归这条用例)
第一步:失败归因------知道"坏在哪"
不是所有失败都值得回流。需要先归因,判断这是不是一个"值得固化为用例"的失败。
失败模式分类
| 模式 | 定义 | 是否回流 | 原因 |
|---|---|---|---|
| 路由错误 | 用户说A,Skill理解成B | ✅ 回流 | 规则边界不清晰,需要测试覆盖 |
| 参数错误 | 传错字段值 | ✅ 回流 | 精确断言可固化 |
| 流程中断 | 某步骤失败后没有降级处理 | ✅ 回流 | 异常路径未覆盖 |
| 新场景 | 测评集从未覆盖的输入类型 | ✅ 回流 | 扩大覆盖范围 |
| 上游超时 | MCP接口响应慢导致超时 | ❌ 不回流 | 基础设施问题,非Skill问题 |
| 用户误操作 | 用户自己取消了操作 | ❌ 不回流 | 非Skill质量问题 |
| 偶发抖动 | 单次失败,后续恢复 | ⚠️ 观察 | 可能是随机性,累积3次再回流 |
归因方法
python
def classify_failure(trace):
"""对失败trace做自动归因"""
if trace.tools_called != trace.expected_tools:
return "routing_error" # 路由错误:调了不该调的工具
for call in trace.tools_called:
if call.args_mismatch:
return "param_error" # 参数错误:工具对了,参数错了
if trace.has_unhandled_error:
return "flow_break" # 流程中断:报错但没降级
if trace.user_input_type not in known_input_types:
return "new_scenario" # 新场景:从未见过的输入模式
if trace.upstream_timeout:
return "infra_issue" # 基础设施问题:不回流
return "unknown" # 需人工判断
第二步:生成 regression 用例------从 trace 到 cases.md
自动生成模板
对于确认要回流的失败,自动从 trace 中提取信息生成用例:
markdown
# [线上回流] 差旅费被误路由到日常报销
> 帮我报差旅费,上周出差北京的机票和酒店
- [ ] 触发差旅报销流程(非日常报销)
- [ ] 调用 queryTravelExpense(非 queryExpenseItems)
- [ ] response 中包含"差旅"或"出差"相关引导
<!--
source: online_trace
trace_ids: [tr_20260615_012, tr_20260617_005, tr_20260618_031]
failure_mode: routing_error
first_seen: 2026-06-15
last_seen: 2026-06-18
frequency: 3/120 (2.5%)
severity: high(影响核心流程)
fixed_in: v2.3(待验证)
-->
用例质量要求
回流用例和设计时的用例同等标准:
| 要素 | 要求 |
|---|---|
| Prompt | 使用真实用户的表述(脱敏后) |
| 断言 | 至少 1 条 exact_match(精确可验证) |
| rule_ref | 标注对应 SKILL.md 中的哪条规则 |
| 覆盖新路径 | 确认这条用例覆盖了之前未覆盖的场景 |
存储位置
javascript
inputs/<skill名>/
├── golden.cases.md ← 人工设计的核心用例
├── online_failures.cases.md ← 线上回流用例(本文的产出)
└── cases.cache.json ← cases 步骤的缓存
第三步:纳入 SkillSentry 测评------永久防护
regression 模式自动执行
SkillSentry 的 regression 模式专门为此设计:
bash
触发条件:SKILL.md hash 匹配(Skill 规则没变)+ 存在外部用例
执行范围:只跑 source="external" 且 tag="golden" 或 tag="regression" 的用例
执行速度:快(不需要重新设计用例,直接执行+评审)
预计耗时:~5分钟
每次 Skill 变更后跑 regression,确保:
- 之前修过的 bug 没有复现
- 新改动没有破坏已有功能
与其他模式的关系
| 模式 | 用例来源 | 用途 |
|---|---|---|
| smoke | AI快速生成 | 快速冒烟,看基本面 |
| quick | AI生成 + 缓存 | 常规验证 |
| regression | 外部用例(golden + 回流) | 防止旧问题复现 |
| standard/full | AI生成 + 外部 + 全量 | 完整评估 |
第四步:用例生命周期管理------避免无限膨胀
问题:用例越积越多怎么办?
如果每个线上失败都回流,半年后可能有 200+ 条 regression 用例。全跑一遍可能要 30 分钟。
解决方案:分层 + 淘汰
markdown
Tier 1:核心防护(永不淘汰)
- 涉及资金/安全的失败
- 出现过 ≥ 3 次的失败模式
- 被标记为 severity=high 的
Tier 2:常规防护(年度审视)
- 出现过 1-2 次的失败
- severity=medium
- 如果连续 5 次测评都通过,可考虑降级或合并
Tier 3:观察区(3个月无复现则淘汰)
- 单次出现的偶发问题
- severity=low
- 补充的变体用例
淘汰规则
python
def should_retire(case):
"""判断用例是否可以退役"""
if case.tier == 1:
return False # 核心防护永不退役
if case.tier == 2:
if case.consecutive_passes >= 5 and case.age_months >= 6:
return True # 连续5次通过且超过6个月
if case.tier == 3:
if case.age_months >= 3 and case.last_failure is None:
return True # 3个月无复现
return False
退役不是删除,而是移到 archive/ 目录。如果问题复现,可以恢复。
完整闭环示例
以"差旅费误路由"为例,走一遍完整流程:
arduino
6月15日:用户反馈"差旅费走了日常流程"
↓
6月15日:在线监控检测到 routing_error,trace_id=tr_20260615_012
↓
6月18日:同一模式第3次出现,触发回流阈值
↓
6月18日:自动生成用例 → online_failures.cases.md
↓
6月19日:开发者修复 Skill 路由规则
↓
6月19日:跑 SkillSentry regression 模式
↓
结果:新用例通过 ✅ + 其他 regression 用例全通过 ✅
↓
6月19日:修复上线
↓
7月份:另一个同事调整了 Skill
↓
7月份:跑 regression,"差旅费误路由"用例自动执行 → 通过 ✅
↓
永久防护生效:这个 bug 不会再悄悄复现
投入产出分析
| 投入 | 产出 |
|---|---|
| 归因脚本开发:1天 | 自动分类失败模式 |
| 用例模板配置:0.5天 | 自动生成 cases.md |
| 人工审核:每周5分钟 | 确认用例质量 |
| regression 执行:自动 | 每次变更 5 分钟跑完 |
ROI:投入 2 天搭建,之后每修一个 bug 自动积累一条防护。半年后你会有一个"经历过实战检验"的用例库------比任何人工设计的测评集都更贴近真实场景。
常见问题
Q:如果我还没有在线监控,能做反馈闭环吗?
能,手动版。 不需要自动化在线监控:
- 用户反馈问题时,手动写一条用例
- 修复后,手动跑 regression 验证
- 把用例放入
inputs/<skill>/online_failures.cases.md
这就是最简版的反馈闭环。自动化只是让这个过程更高效。
Q:回流用例的 prompt 要用原始用户输入吗?
建议脱敏后使用原始表述。 原因:
- AI 生成的"等价prompt"可能绕过了原始问题的触发条件
- 真实用户的表述方式往往是你设计时想不到的
- 脱敏规则:去除姓名、金额具体数字可保留量级(如"3500"改为"3000+")
Q:怎么判断一个用例该是 Tier 1 还是 Tier 2?
简单规则:
- 涉及资源/安全/合规 → Tier 1
- 出现过 ≥ 3 次 → Tier 1
- 其他 → 先 Tier 2,观察
总结
| 步骤 | 核心动作 | 产出 |
|---|---|---|
| 归因 | 对失败 trace 分类 | 知道"坏在哪" |
| 生成 | 从 trace 提取为 cases.md | 可执行的用例 |
| 纳入 | 放入 inputs/ 目录 | regression 自动执行 |
| 管理 | 分层 + 淘汰 | 控制规模,保持有效 |
一句话记住:每修一个 bug,就应该多一个 regression 用例。测评集不是一次性产物,而是一套会随着线上失败不断更新的防护网。