Agent 评测与线上运营:离线全绿,线上照坏
四格矩阵 · Golden Set 与指标口径 · 轨迹级 judge · span 树归因 · 影子与灰度
🧠 一句话记忆锚点
评测按「离线/在线 × 单步/全轨迹」四格排布,冲突时以在线全轨迹为准(它离用户最近)。成功率的分母比分子难——超时与熔断样本必须显式归类,部分成功要拆成关键步达成率。轨迹 judge 的死穴是正确路径不唯一,所以判关键步而不是比参考轨迹。可观测埋点看 span 树:任务→轮→模型/工具,最小属性集里"终止原因"最值钱。上线走回放→影子→灰度三级,回滚看人工接管率而不是成功率。失败归因四分类:模型决策 / 工具执行 / 上下文缺失 / 护栏误拦。
名词速查
首次阅读可跳过本表,直接看「场景问题」,遇到生词再回查。
本篇是评测里「Agent 多步轨迹」子域的名词归属页。总论(基准污染、judge 偏差、Cohen's kappa)归 评测方法论,本表只收轨迹特有的词。
一句话说清本篇解决什么:单次问答只要判"答对没有",多步轨迹要判"这一路走得对不对"——而正确的路往往不止一条。类比过来就是:从断言返回值,升级到审计整条调用链。
| 名词 | 后台类比 | 在系统里干什么 | 类比失效边界 |
|---|---|---|---|
| 四格评测矩阵 | 监控的「离线压测 / 线上拨测 × 单接口 / 全链路」四格 | 按「离线/在线 × 单步/全轨迹」排布评测手段,冲突时以在线全轨迹为准(它离用户最近) | 压测与拨测结果通常互相印证;这四格经常互相矛盾(离线单步全绿、线上全轨迹却失败率高),因为单步正确不代表串起来正确。矩阵的价值是提醒你"别只看一格",而不是给出统一分数 |
| 成功率分母 | SLI 的统计口径——分母里算不算超时、算不算主动降级的请求 | Agent 成功率里比分子更难的部分:超时、预算熔断、护栏拦截、人工接管的样本各自归到哪里 | 常规 SLI 的口径一旦定下就稳定适用;Agent 的"失败"种类多且性质不同——被护栏拦住其实是系统正常工作,算失败会低估实际质量;不算又会掩盖护栏误拦。所以必须显式分类统计而不是压成一个数,任何单一的成功率数字都不可比 |
| 部分成功(Partial Success) | 批处理里的部分成功——100 条里成了 87 条,不是二值结果 | 多步任务常常只完成一部分,需要拆成"关键步达成率"而非只判最终成败 | 批处理的成功条数是精确可数的;这里"哪几步算关键"是人为定义的,定义变了指标就变了。且步骤之间有依赖,第 3 步失败导致后续全跳过,不能简单按"完成步数/总步数"算比例 |
| 轨迹级 judge | 审计整条调用链而非单个响应——看流程走得对不对 | 让模型评判整条执行轨迹的合理性,而不只看最终答案 | 链路审计有标准流程图可比对,偏离即异常;Agent 的正确路径不唯一——先查日志再查配置、还是反过来,可能都对。所以不能拿参考轨迹逐步比对,只能判关键步是否达成,这是本篇与单步评测最本质的分野 |
| span 树 | 分布式追踪的调用链树(trace → span)——你在 Jaeger 里看的那个 | 把一次任务的执行结构化为树:任务 → 轮次 → 模型调用/工具调用,用于定位"是哪一步坏的" | 语义和结构几乎一致,这是本表最贴近后台原物的概念。差别在最值钱的属性不同:常规 span 看耗时与错误码,Agent span 最关键的属性是"终止原因"(正常完成/轮数耗尽/预算耗尽/无进展中止)——没有这个字段,你分不清"跑完了"和"被掐掉了"。属性命名建议对齐 gen_ai.* 语义约定,清单见 AI 研发工程化 |
| 步级归因(Step Attribution) | 故障根因定位——从链路里找出第一个出错的环节 | 把失败归到具体某一步,并分类为:模型决策错 / 工具执行错 / 上下文缺失 / 护栏误拦 | 链路里的错误有明确的错误码与堆栈可定位;这里第一个"错"的步骤往往不报错——模型决策错误表现为一次格式完全合法的工具调用,只是选错了工具。所以归因不能靠扫错误日志,必须靠轨迹重读与关键步断言 |
| 失败四分类 | 故障分类(代码缺陷 / 依赖故障 / 配置缺失 / 限流误伤) | 把失败归入四类以指向不同的修复动作:改 prompt / 修工具 / 补检索 / 调护栏 | 常规故障分类边界清晰,一个故障通常只属于一类;这四类经常同时成立且互为因果——上下文缺失导致模型决策失误,你既可以补检索也可以改 prompt。分类的价值是收敛修复方向,不是精确定责 |
| 回放(Replay) | 流量录制回放——用历史真实请求跑新版本 | 上线三级的第一级:拿历史轨迹重跑,看关键步达成率是否退化 | 常规回放能精确重放(同样输入同样依赖);Agent 回放无法重现当时的外部世界——工具依赖的数据已经变了,同一个查询现在返回不同结果,所以"失败"可能只是环境变了。需要把工具返回值一起录制回放(mock 化)才有可比性 |
| 影子(Shadow) | 双跑不生效——新版本吃真实流量但结果不返回用户 | 上线三级的第二级:真实流量并行验证,无用户风险 | 常规影子只消耗算力;Agent 影子会真实调用工具——如果工具有副作用(发消息、改数据、扣款),影子跑一遍就出事了。必须先把副作用工具替换成 mock,这一步做不到就不能上影子 |
| 灰度(Canary) | 按比例放量的金丝雀发布 | 上线三级的第三级:小比例真实用户,观察后逐步放量 | 常规灰度的回滚判据是错误率与延迟,秒级可见;Agent 的质量退化往往要等到用户抱怨或人工接管率上升才显现(延迟到小时级)。所以观察窗必须比常规灰度长,不能按"跑了 10 分钟没报错"就放量 |
| 人工接管率(Human Takeover Rate) | 自动化流程转人工的比例——最诚实的那个健康指标 | 灰度与回滚的首要判据:用户或运营多久要接手一次。比成功率更早、更真实地反映退化 | 常规转人工率主要反映流程覆盖度;这里它是质量的代理指标——但它有滞后性(用户忍一会儿才求人)和场景偏差(不同业务的容忍度天差地别)。所以要看它的相对变化趋势,不能设一个跨业务的绝对阈值 |
本篇引用的其他上下文名词
| 名词 | 在本篇里的角色 | 完整解释去哪读 |
|---|---|---|
| 基准污染 / LLM-as-judge / 位置偏差 / 长度偏差 / Cohen's kappa / Golden Set / 人类在环 | 通用评测方法论总论,本篇不重复展开 | 评测方法论 · 名词速查 |
| rubric 打分 / 一致性断言 / 人评抽样 | 无 golden answer 的创作类评测,归创作篇 | 创作 Agent · 名词速查 |
| 调度状态机 / 四条终止线 / 预算熔断 / 护栏 | 被观测的那套运行时机制("终止原因"字段的来源) | Agent 运行时深水区 · 名词速查 |
gen_ai.* 语义约定 / span 属性清单 | span 命名对齐的标准,属性清单归工程化篇 | AI 研发工程化 · 名词速查 |
| MAS 特有失效 / 级联幻觉 | 多 Agent 轨迹归因时要区分的失效类型 | Multi-Agent 协作 · 名词速查 |
场景问题
一个客服 Agent 上线前,Golden Set 200 条跑到 94% 成功率,红队测了三轮没越权,团队签字发布。上线第四天,客服组反馈「机器人答得挺好但用户投诉暴涨」。
拉数据才看清:Agent 的回答质量确实没问题,但它有 11% 的会话在第 3~4 轮后触发了工具超时,然后礼貌地道歉并结束会话——离线评测里这类样本被判定为"未完成,不计入分母",于是 94% 是在 89% 的样本上算出来的。真实的用户视角成功率是 83%。
指标没错,口径错了。而离线评测的四格里,这个 Agent 只被测过一格。
阅读边界
本篇只讲 Agent 多步轨迹特有的评测与运营问题。通用 LLM 评测方法论——指标分类、基准选择与污染、LLM-as-judge 的可靠性总论与偏差校正、人类在环与标注一致性——是 评测方法论 的地盘,本篇不重复展开,只在轨迹场景下补它的特有变化。护栏与预算的实现见 Agent 运行时深水区;本篇讲这些护栏怎么被量化验证。
另有一类任务本篇不覆盖:无 golden answer 的创作类任务(剧本、视频不存在唯一正确版本,评测的数学基础从分类问题变成打分排序问题)。rubric 的分档设计、可自动化的一致性断言、按风险分层的人评抽样,归 互动影游与长视频创作 Agent。本篇的成功率口径与 Golden Set 方法论以「任务有可判定的正确结果」为前提。
实现方案
一、主骨架:离线/在线 × 单步/全轨迹 四格
Agent 评测容易乱,是因为「Golden Set / 红队 / 回放 / 影子流量」这四个词不在同一个维度上。摊到两个正交维度上就清楚了:
| 单步(一次决策 / 一次工具调用) | 全轨迹(一个完整任务) | |
|---|---|---|
| 离线 | ① 决策单测:固定上下文快照,断言模型该选哪个工具、参数是否合法。跑得快、能进 CI | ② Golden Set 回归:200~500 条任务跑完整轨迹,看成功率/步数/成本/p95 延迟 |
| 在线 | ③ 实时护栏与步级监控:每步的 Schema 校验失败率、工具错误率、护栏拦截率、单步延迟 | ④ 影子 / 灰度 / 真实轨迹分析:与旧版并跑对比,看真实完成率与人工接管率 |
四格各自的必要性在于它们拦的东西不重叠:
- 只有 ① 会漏掉「每步都对、组合起来跑偏」;
- 只有 ② 会漏掉开篇那种「分母口径掩盖的问题」,因为 Golden Set 的任务分布不等于真实分布;
- 只有 ③ 会漏掉「护栏没拦但结果就是错」;
- 只有 ④ 则完全没有发布闸门,等于拿用户当测试集。
维度冲突时以哪个为准
这是四格模型必须给出的裁决规则,否则每次数据打架都要吵一轮:
离线指标与在线指标背离时,以在线全轨迹(第 ④ 格)为准,且必须回头修离线。 理由是 ④ 离用户最近,而离线与在线背离本身就是一个 bug 信号——它说明 Golden Set 的分布或口径与真实场景脱节。修法不是调整对 ④ 的解读,而是把背离的真实样本回捞进 Golden Set,让 ② 下次能拦住。
单步与全轨迹背离时(每步都对但任务失败),以全轨迹为准。 单步全绿而轨迹失败,通常意味着问题在步与步之间:上下文丢失、观察被误读、或规划层面的错误。这类问题不该靠加严单步断言解决。
二、Golden Set 与指标:分母比分子难
打个比方:Golden Set 就是你的回归测试集——典型用例、边界用例、每次线上事故的最小复现,跑一遍看通过率。而"分母比分子难"这件事你也遇过:统计接口成功率时,超时算不算失败、5xx 和 4xx 要不要分开、被限流拒掉的算不算一次请求——口径不同,同一份日志能算出差好几倍的成功率。类比失效边界:回归测试的通过判定是断言相等,非黑即白;Agent 的一次运行可能"跑完了、格式合法、但答得不对",也可能"答对了但绕了 30 步"。所以这里的判定必须先归类终止方式再谈对错,且要看指标结构而非单一数字——只看成功率会奖励"多试几次凑对"。
样本怎么构造
三类样本,配比经验值 典型 60% / 边界 25% / 已知失败 15%:
- 典型:真实分布里最高频的任务形态。来源应当是线上轨迹回捞而不是人工想象——人工构造的"典型"通常过于干净。
- 边界:参数极值、空结果、多义指令、超长输入、工具不可用。
- 已知失败:每次线上事故的最小复现样本。这一类只增不减,是 Golden Set 里最值钱的部分。
回捞流程:从线上按分层抽样(按任务类型 × 结果状态),失败与慢轨迹要过采样。抽出来的轨迹需要人工确认「正确结果应该是什么」才能进 Golden Set——这一步无法省。
成功的可判定定义
「成功率」这个词在 Agent 上有歧义,必须拆开声明:
| 断言类型 | 判定对象 | 适用 |
|---|---|---|
| 终态断言 | 最终答案/世界状态是否符合预期 | 有唯一正确结果的任务(查询类、计算类) |
| 过程断言 | 是否调用了必需的工具、是否避开了禁止操作 | 有合规要求的任务(必须查过库才能答) |
| 关键步达成 | 若干关键里程碑各自是否达成 | 长任务、部分成功有意义的场景 |
部分成功怎么计分:不要用 0/1。定义 k 个关键步,得分 = 达成数 / k,同时单独报「完全成功率」(k/k)。开篇的客服案例如果早就这么报,「答得好但会话中断」会立刻显形为「关键步达成 0.75、完全成功率 0.83」。
核心指标族与分母陷阱
六个指标,缺一个都会有盲区:
- 任务成功率(按上面的定义,分完全/部分两档报)
- 平均步数(同时看 p95——长尾步数暴涨是跑偏的先行信号)
- 平均 token 与费用(分模型报,路由到小模型的部分单价不同)
- p95 端到端延迟(不是平均值,用户体感在长尾)
- 工具调用失败率(分工具报,能直接定位坏工具)
- 人工接管率(有 human-in-the-loop 时最重要的一个,见下文回滚判据)
分母陷阱——这是开篇事故的直接根因:
# ✗ 错:超时/熔断样本被悄悄踢出分母,成功率虚高
success_rate = n_success / (n_total - n_timeout - n_aborted)
# ✓ 对:所有样本进分母,异常单独归类并显式报出
@dataclass
class EvalReport:
total: int
success_full: int # k/k 关键步全达成
success_partial: int # 0 < 达成 < k
failed: int # 跑完了但结果错
aborted_budget: int # 预算/轮数熔断
aborted_tool: int # 工具不可恢复错误
timeout: int
@property
def success_rate(self) -> float:
return self.success_full / self.total # 分母永远是 total
def assert_accounted(self) -> None:
# 每个样本必须落进且只落进一个桶,防止口径漂移
assert (self.success_full + self.success_partial + self.failed
+ self.aborted_budget + self.aborted_tool + self.timeout) == self.total
assert_accounted 这一行是防口径漂移的关键:每个样本必须落进且只落进一个桶。加了新的中止原因却忘了加桶,断言会立刻炸,而不是静默地把样本从分母里蒸发掉。
打分一条轨迹的骨架
def score_trace(trace: Trace, case: GoldenCase) -> TraceScore:
# ① 先归类终止方式 —— 决定这条样本进哪个桶,必须最先做
if trace.status == "timeout":
return TraceScore(bucket="timeout", steps=len(trace.turns), cost=trace.cost)
if trace.status == "aborted":
bucket = "aborted_budget" if trace.abort_reason in BUDGET_REASONS else "aborted_tool"
return TraceScore(bucket=bucket, steps=len(trace.turns), cost=trace.cost)
# ② 关键步达成:按里程碑而非参考轨迹比对(路径不唯一)
hit = [m for m in case.milestones if m.reached(trace)]
ratio = len(hit) / len(case.milestones)
# ③ 过程约束:必需工具调过没有、禁止操作碰过没有
violations = [c for c in case.constraints if not c.holds(trace)]
# ④ 终态断言(若该 case 有唯一正确答案)
terminal_ok = case.assert_terminal(trace.answer) if case.has_terminal else True
bucket = ("success_full" if ratio == 1.0 and terminal_ok and not violations
else "success_partial" if ratio > 0 and not violations
else "failed")
return TraceScore(
bucket=bucket, milestone_ratio=ratio, violations=violations,
steps=len(trace.turns), cost=trace.cost,
latency_p95_step=percentile([t.duration for t in trace.turns], 95),
tool_failures={t.name: t.failures for t in trace.tool_calls},
first_divergence=find_first_divergence(trace, case), # 步级归因锚点
)
注意 ① 的位置——先归类终止方式再谈对错。反过来写(先判对错、再看是否超时)就会写出上面那个错误的分母。
过拟合 Golden Set 的表现与防范
表现:Golden Set 成功率持续爬升到 97%+,但线上完成率不动或下降。原因通常是 prompt 被反复调到「刚好能过这 200 条」。
三条防范:
- holdout 集:留 20% 从不用于调优,只在发布前跑一次。两者差距 >5pp 就是过拟合信号。
- 定期轮换:每季度用新回捞的线上样本替换 30% 的典型样本。
- 看指标结构而非单一数字:成功率涨但平均步数也涨,往往是「靠多试几次凑对」而非真的变强。
这一节记住三句话
- 分母比分子难。先归类终止方式(正常终止 / 超时 / 熔断 / 崩溃)再谈对错——反过来写"先判对错再看是否超时"就会算出一个虚高的成功率。
- Golden Set 的样本要从线上回捞,不能人工想象——人工构造的"典型"过于干净。三类配比约 60% 典型 / 25% 边界 / 15% 已知失败,而已知失败那一类只增不减,是最值钱的部分。
- 过拟合的信号是"Golden Set 涨、线上不动"。三条防范:20% holdout 只在发布前跑(差距 >5pp 即报警)、每季度轮换 30% 典型样本、看指标结构——成功率涨而平均步数也涨,是靠多试几次凑对。
三、轨迹级 judge:正确路径不唯一
评测方法论 讲了 LLM-as-judge 的通用可靠性问题。轨迹场景多出一个更硬的困难:
同一个任务有多条合法路径。 查一个玩家的异常,先看日志再看指标、还是先看指标再看日志,都对。所以「与参考轨迹比对相似度」这个思路从根上不成立——它会把一条更聪明的短路径判为偏离。
替代方案是判关键步而非判路径:
| 判分粒度 | 成本 | 归因能力 | 适用 |
|---|---|---|---|
| 终态判分 | 最低(1 次 judge 调用) | 无(只知道错了) | 有唯一答案、轨迹短 |
| 关键步判分 | 中(k 次断言,多为规则而非 judge) | 强(知道哪个里程碑没达成) | 默认选择 |
| 逐步判分 | 高(每轮 1 次 judge 调用) | 最强 | 只在排查特定坏 case 时开 |
「关键步判分」之所以是默认——里程碑通常能写成规则断言("调用过 query_player_log 且返回非空"),根本不需要 judge,既省钱又没有 judge 偏差。judge 只用在无法规则化的地方("回答是否体现了对用户情绪的回应")。
步级归因:找到 first_divergence——第一个偏离所有合法路径的步骤。实现上不必穷举合法路径,用一个弱得多的判据就够:从哪一步开始,后续再也没有新里程碑达成。那一步通常就是跑偏点。
judge 偏差在长轨迹上的放大:
- 位置偏好:judge 对轨迹开头和结尾的步骤给分明显不同于中间步骤——和「迷失在中间」同源。校正手段是分段判分(把长轨迹切成若干段分别判)而不是整条喂进去。
- 长度偏好:judge 倾向给更长、更"努力"的轨迹更高分,即使结果一样。校正手段是把步数从 judge 的输入里剥离——judge 只看关键步是否达成,步数作为独立指标单独报。
什么时候必须退回人工:新任务类型上线的第一批样本(还没有可靠的里程碑定义时)、judge 与规则断言结论冲突的样本、以及所有进「已知失败」类的事故样本。judge 是用来扩大人工标注的覆盖面,不是替代它。
四、轨迹级可观测:span 树与最小属性集
一次 Agent 任务的 span 树形态是固定的三层:
最小属性集——少一个就有排查不了的场景:
| 层 | 属性 | 少了它排查不了 |
|---|---|---|
| task | task_id、总耗时、终止原因、总 token、总费用 | 「为什么停了」——终止原因是全表最值钱的一个字段 |
| turn | 轮次序号、本轮状态、上下文 token 数 | 上下文膨胀曲线、压缩是否按预期触发 |
| llm | 模型名、input/output token、是否命中 prompt 缓存、耗时 | 成本归因、缓存失效 |
| tool | 工具名、危险级别、参数指纹、耗时、错误码、返回是否被截断 | 打转检测复盘、坏工具定位 |
gen_ai.* 语义约定的定位:属性名不必自己发明——OpenTelemetry 的 GenAI 语义约定已经给了 span 操作类型与属性名的完整清单(含 agent / tool / MCP span),详见 AI 研发工程化 · OpenTelemetry GenAI 语义约定,本页不重复罗列。这里只说它对 Agent 评测的意义:对齐它,换 APM 后端时不用重埋点;但它仍在 Development 阶段、属性名改过一次,所以务实做法是在自己代码里过一层常量映射,约定变更只改一处。
采样策略:全采样在 Agent 场景比普通 API 贵得多——一次任务几十个 span,每个 llm span 还想带上 prompt 摘要。分层采样:
- 失败与中止轨迹:100% 采样(这是排查的主要材料)
- p95 以上慢轨迹:100%
- 成功轨迹:1~5% 抽样(用来看常态分布)
- 成本超阈值的轨迹:100%
从 span 树反推「这次为什么失败」
固定的四步排查路径:
- 看 task span 的终止原因 —— 直接分流到四类归因之一。
- 若是 aborted/无进展:看 tool span 的参数指纹序列,找重复段,确认是否指纹计算漏剔了 nonce。
- 若是 failed(跑完但错):找
first_divergence那一轮,读该轮的 llm span 输入——通常是上下文里少了某个事实,或工具返回被截断截掉了关键部分(这就是 tool span 要记「是否被截断」的原因)。 - 若是 timeout:看各 tool span 耗时分布,区分「某个工具慢」与「轮数太多累加慢」——两者修法完全不同。
打个比方:span 树像快递的物流轨迹——包裹(任务)每经一个网点(轮次)留一条记录,每条记录里有揽收人(模型调用)和运输方式(工具调用)。收不到货时你不是重寄一遍,而是顺着轨迹找最后一个正常节点。类比在哪失效:物流轨迹上每个节点的「正常」是客观的(签收了就是签收了),而 Agent 的每一步都可能看起来正常但语义上已经跑偏——模型成功调用了工具、拿到了结果、给出了下一步,每个 span 都是绿的,但它查的是错的那张表。所以 span 树只能定位「在哪一步开始偏」,不能自动告诉你「偏了」——后者仍要靠里程碑断言。
五、从离线到线上:回放 → 影子 → 灰度
离线回放
录制真实轨迹,换 prompt / 换模型后重放,对比结果。回放的不可复现来源有四个,每个都要显式固定:
| 不可复现来源 | 固定手段 |
|---|---|
| 模型采样随机性 | temperature=0 + 固定 seed(注意:仍不保证跨版本一致) |
| 工具返回随时间变化 | 回放时用录制的工具返回而非真实调用(工具打桩) |
| 并行工具回填顺序 | 按发起顺序回填(见 运行时) |
| 时间/随机数进 prompt | 冻结时钟,nonce 走可注入的 provider |
工具打桩有个边界:换了 prompt 后模型可能调用录制里没有的工具。这时回放只能停在这里并标记「轨迹分叉」——分叉率本身是个有用的指标(分叉率高说明改动影响面大)。
影子流量
真实请求同时打给新旧两版,只有旧版的结果对用户生效。要点两条:
- 副作用必须隔离:影子版的所有 L1/L2 工具(见 运行时的危险分级)必须走打桩或只读副本,否则一次影子跑就会产生两笔真实扣款。这是影子流量最容易出事的地方。
- 成本要预先算:影子等于双倍模型成本。通常只对 5~10% 流量开影子,且优先覆盖高价值/高风险任务类型。
灰度与回滚判据
按流量比例(1% → 5% → 20% → 100%)或按用户分层(内部员工 → 低价值场景 → 全量)推进。
回滚看什么指标——这里有个反直觉的结论:不要只看成功率。成功率是滞后指标,且被口径影响。优先级更高的三个:
- 人工接管率(有 HITL 时的首选)——它对质量下降最敏感,且不受分母口径影响。
- 护栏拦截率突增——说明新版在尝试它不该做的事。
- p95 步数 / 成本漂移——说明它在靠多试凑答案,是质量下降的先行指标。
阈值设成相对旧版的偏移而非绝对值(人工接管率相对基线上升 >30% 即回滚),因为绝对值随任务分布波动。
六、线上失败归因四分类
每个失败必须落进且只落进一类,否则修复责任无法分派:
| 归因 | 典型信号 | 修法归属 |
|---|---|---|
| 模型决策错 | 工具调用都成功、上下文里信息齐全,但选了错的工具或误读了结果 | Prompt / few-shot / 换模型;补进 Golden Set「已知失败」 |
| 工具执行错 | tool span 有错误码或超时;同一工具失败率异常 | 修工具本身(分页、超时、错误消息可读性) |
| 上下文缺失 | first_divergence 那轮的 llm span 输入里缺关键事实;或工具返回被截断截掉了关键段 | 记忆层(压缩水位、pin、截断策略)→ 运行时 |
| 护栏误拦 | 护栏拦截日志 + 用户本意合法 | 调护栏阈值或分级,见下文代价不对称 |
护栏误拦 vs 放过的代价不对称如何定阈值:
两类错误的代价通常差几个数量级——放过一次 L2 不可逆操作(误删生产数据、错误扣款)的代价,可能是误拦一百次的一百倍以上。所以:
- 对 L2 不可逆动作:阈值调到宁可多拦(高召回、低精确),误拦的代价只是一次人工确认。
- 对 L0 只读动作:阈值调到宁可少拦,误拦会让 Agent 完全无法工作。
- 判据是「误拦的补救成本」:能被一次人工点击补救的,就往严;补救需要走工单流程的,就要权衡。
这也解释了为什么护栏拦截率突增要当回滚信号——它可能意味着新版正在批量触碰不该碰的东西。
为什么这么做
为什么四格里 ② 是发布闸门而不是 ①
单步决策单测跑得快、能进 CI,看起来是最理想的闸门。但它有个结构性局限:它验证的是「给定这个上下文,模型该做什么」,而 Agent 的失败大多来自上下文本身是错的。上下文快照是人工准备的,天然干净;真实运行时的上下文经过了压缩、截断、召回,可能已经缺了关键事实。
所以 ① 的定位是快速回归(改 prompt 后 30 秒内知道有没有明显破坏),② 才是闸门。两者是漏斗关系,不是替代关系。
为什么人工接管率比成功率更适合做回滚判据
三个理由:
- 不受分母口径影响——它是「人接手的会话数 / 总会话数」,没有"该不该计入分母"的争议。
- 对质量下降更敏感——用户或客服在体感变差时会更早接手,早于自动判定的失败。
- 它直接对应人力成本——业务方能理解「接管率涨 30% 意味着客服要多招人」,比「成功率跌 2pp」更容易达成回滚共识。
局限:没有 HITL 的全自动场景用不了这个指标,此时退回「护栏拦截率 + 成本漂移」组合。
为什么里程碑断言优于 judge
成本与偏差两头都赢:里程碑多数能写成规则(调过某工具、返回非空、终态含某字段),零 judge 调用、零 judge 偏差、可重复。judge 的位置应该是规则写不出来的那部分(语气、是否体现共情、解释是否充分),而这部分通常只占判据的一小半。
一个常见的反模式是「整条轨迹丢给 judge,让它给 1-10 分」——这个分数既不可归因(不知道扣分在哪)又不稳定(同一轨迹两次判分能差 2 分)。
为什么别的选择不行
「用 RAGAS 那套指标评 Agent」
RAGAS 的 faithfulness / context precision-recall 是单轮检索质量的度量(详见 RAG),它假设了「一次检索 → 一次生成」。Agent 的问题是多步的:第 3 轮检索质量很好但第 5 轮基于它做了错误推论,RAGAS 全绿而任务失败。
它能用在 Agent 的某一个检索步骤上(作为里程碑断言的一部分),但不能作为轨迹的评价体系。
「只做在线 A/B,不要离线评测」
在线 A/B 的统计功效需要样本量。一个日活不高的内部 Agent,要检测 2pp 的成功率差异可能需要跑两周——而这两周里坏版本一直在影响用户。更要紧的是 A/B 无法覆盖低频高危场景(一个月出现两次的删除操作),这类只能靠 Golden Set 的「已知失败」类样本守。
「让 judge 对比新旧两条轨迹,选更好的那条」
成对比较(pairwise)确实比绝对打分稳定,但在轨迹上有两个坑:位置偏好(judge 系统性偏好放在前面或后面的那条,必须做位置交换取平均)和无法归因(知道 B 比 A 好,但不知道好在哪一步,改不动)。
它适合做发布前的最终 sanity check,不适合作为日常回归的主指标。
三种上线推进方式的对照
| 方式 | 成本 | 能发现的问题 | 盲区 |
|---|---|---|---|
| 离线回放 | 低(无真实流量) | prompt/模型改动的直接影响、分叉率 | 工具打桩后的真实工具行为、真实分布 |
| 影子流量 | 高(双倍模型成本 + 副作用隔离复杂度) | 真实分布下的成本/延迟/分叉 | 用户反馈(结果不生效,拿不到接管率) |
| 灰度 | 中 | 全部(含用户反馈与接管率) | 已经在影响真实用户,需要快速回滚能力 |
三者是递进的:回放筛掉明显退化 → 影子验证成本与稳定性 → 灰度收真实反馈。跳过任一级都会把该级的盲区留到下一级去暴露,代价递增。
沉淀结论
面试常见问题清单
评测框架
- Q:Agent 评测怎么组织? A:按「离线/在线 × 单步/全轨迹」四格——决策单测(快、进 CI)/ Golden Set 回归(发布闸门)/ 步级监控 / 影子灰度。四格拦的东西不重叠。
- Q:离线和在线指标背离时听谁的? A:以在线全轨迹为准(离用户最近),但必须把背离样本回捞进 Golden Set 修离线——背离本身就是「离线分布或口径脱节」的 bug 信号。
指标口径
- Q:Agent 的成功率怎么定义才不骗人? A:分母永远是全样本;超时/预算熔断/工具中止各自单独归桶并显式报出;部分成功按关键步达成比例计,同时单报完全成功率。加一个「所有桶之和 == total」的断言防口径漂移。
- Q:怎么发现 Golden Set 被过拟合了? A:holdout 集与主集差距 >5pp;或成功率涨但平均步数也涨(靠多试凑对)。防范靠 holdout + 每季度轮换 30% 典型样本。
轨迹 judge
- Q:为什么不能拿参考轨迹比相似度? A:同一任务有多条合法路径(先查日志还是先查指标都对),比相似度会把更聪明的短路径判成偏离。改判关键步里程碑。
- Q:judge 在长轨迹上有哪些偏差? A:位置偏好(对首尾步骤给分不同于中间,与"迷失在中间"同源,靠分段判分校正)、长度偏好(偏好更"努力"的轨迹,靠把步数从 judge 输入剥离、单独报指标校正)。
可观测与运营
- Q:Agent 的 span 树怎么埋?最值钱的字段是什么? A:三层——task → turn → llm/tool span。最值钱的是 task span 的终止原因,它直接把排查分流到四类归因。tool span 要记「返回是否被截断」,否则查不出上下文缺失。
- Q:影子流量最容易出什么事故? A:副作用没隔离——影子版的写操作打到真实系统,一次影子跑产生两笔扣款。所有 L1/L2 工具必须打桩或走只读副本。
- Q:灰度回滚该看什么指标? A:不要只看成功率(滞后且受口径影响)。优先看人工接管率(最敏感、无口径争议、直接对应人力成本)、护栏拦截率突增、p95 步数/成本漂移。阈值设成相对基线的偏移而非绝对值。
心法总结
Agent 评测的难点不在算指标,在于定口径和分归因。 一个没写清「超时算不算失败」的成功率,比没有成功率更危险——它给了团队一个签字发布的理由。
本域延伸
- 枢纽结论与四种评测手段的定位:Agent 开发
- 被评测的那些护栏、预算、幂等怎么实现:Agent 运行时深水区
- 通用 LLM 评测方法论(基准污染、judge 可靠性总论、标注一致性):评测方法论
- 无 golden answer 的创作类任务评测(rubric 分档、一致性断言、风险分层抽样):互动影游与长视频创作 Agent
- 多 Agent 的跨 trace 归因与冲突裁决:Multi-Agent 系统
- 单步检索质量的度量(RAGAS)与它的适用边界:RAG
- token 计价与延迟拆解,成本漂移的口径:成本与延迟
- 红队与对抗测试:LLM 应用安全
记忆口诀
- 四格矩阵:离线/在线 × 单步/全轨迹 —— 决策单测 / Golden Set / 步级监控 / 影子灰度
- 裁决规则:背离时听在线全轨迹,然后回捞样本修离线
- 样本三类:典型 60(线上回捞)/ 边界 25 / 已知失败 15(只增不减)
- 分母铁律:分母永远是 total,异常单独归桶 + 桶和断言
- 判分三粒度:终态(省)/ 关键步(默认) / 逐步(排查才开)
- judge 两偏差:位置偏好(分段判分)/ 长度偏好(剥离步数)
- span 三层:task → turn → llm+tool;最值钱字段 = 终止原因
- 采样分层:失败与慢 100%,成功 1~5%
- 上线三级:回放 → 影子(副作用必隔离)→ 灰度
- 回滚首选:人工接管率(不是成功率),阈值用相对基线偏移
- 归因四分类:模型决策 / 工具执行 / 上下文缺失 / 护栏误拦
内容来源
综合整理:OpenTelemetry GenAI 语义约定(gen_ai.*,Development 阶段)、LangSmith / 主流 Agent 可观测方案的 span 模型、AI 巡检与客服 Agent 自研落地经验(2026-08)。
文中配比与阈值(60/25/15 样本配比、5pp 过拟合信号、接管率相对上升 30% 回滚)为经验值,需按自身任务分布与业务代价结构重新标定。
gen_ai.*约定仍在演进,属性名请以官方最新版本为准。
自测:合上资料能说清楚吗?
- Agent 评测的四格矩阵是哪两个维度?为什么不能只留 Golden Set 这一格?
参考答案
两维是 离线/在线 × 单步/全轨迹,四格 = 决策单测 / Golden Set 回归 / 步级监控 / 影子灰度。不能只留 Golden Set:它拦不住「护栏没拦但结果就是错」(要靠③步级监控),也拦不住「Golden Set 分布不等于真实分布」导致的口径盲区(要靠④在线轨迹分析);而缺了①决策单测就没有 30 秒级的快速回归。四格拦的东西不重叠。
- 「成功率 94%」这个数字可能怎么骗人?正确的口径怎么定?
参考答案
骗法:把超时 / 预算熔断 / 工具中止的样本从分母里剔除(n_success / (n_total - n_timeout - n_aborted)),于是 94% 是在 89% 的样本上算的,真实用户视角只有 83%。正确口径:分母永远是 total;异常各自单独归桶(timeout / aborted_budget / aborted_tool)并显式报出;部分成功按关键步达成比例计分,同时单报完全成功率。加一条「所有桶之和 == total」的断言,防止新增中止原因时样本静默蒸发。
- 为什么轨迹评测不能用「与参考轨迹比相似度」?替代方案是什么?
参考答案
因为同一任务的合法路径不唯一——先查日志再查指标、还是反过来,都对。比相似度会把一条更聪明的短路径判为偏离。替代:判关键步里程碑(默认粒度),且里程碑多数能写成规则断言(调过某工具、返回非空、终态含某字段),零 judge 调用、零偏差、可重复;judge 只用在规则写不出来的部分(语气、共情)。步级归因用 first_divergence——从哪一步起后续再无新里程碑达成。
- 对比 影子流量 与 灰度发布:成本、能发现的问题、各自盲区?影子最容易出什么事故?
参考答案
影子:成本高(双倍模型费 + 副作用隔离复杂度),能发现真实分布下的成本/延迟/分叉,盲区是拿不到用户反馈与接管率(结果不生效)。灰度:成本中,能发现全部问题含用户反馈,盲区是已在影响真实用户、需要快速回滚能力。影子最容易出的事故:副作用没隔离——影子版的 L1/L2 写操作打到真实系统,一次影子跑产生两笔真实扣款。所有写操作必须打桩或走只读副本。
- 灰度回滚为什么不该只看成功率?该看什么?阈值怎么设?
参考答案
成功率是滞后指标且受分母口径影响。优先看:① 人工接管率(对质量下降最敏感、无口径争议、直接对应人力成本,业务方容易达成回滚共识);② 护栏拦截率突增(说明新版在批量触碰不该碰的);③ p95 步数/成本漂移(靠多试凑答案,是质量下降的先行指标)。阈值设成相对旧版基线的偏移(如接管率相对上升 >30%)而非绝对值,因为绝对值随任务分布波动。全自动无 HITL 场景用不了接管率,退回「护栏拦截率 + 成本漂移」组合。
- 线上失败的四类归因分别是什么?各自的典型信号与修法归属?
参考答案
① 模型决策错:工具都成功、上下文齐全但选错工具/误读结果 → 改 Prompt/few-shot/换模型,并补进 Golden Set 已知失败类。② 工具执行错:tool span 有错误码或超时、某工具失败率异常 → 修工具本身。③ 上下文缺失:first_divergence 那轮的 llm span 输入缺关键事实,或工具返回被截断截掉关键段 → 修记忆层(压缩水位/pin/截断策略)。④ 护栏误拦:拦截日志 + 用户本意合法 → 按「误拦补救成本」调阈值,L2 不可逆动作宁可多拦、L0 只读宁可少拦。每个失败必须落进且只落进一类,否则修复责任无法分派。