可观测:trace、线上质量监控与数据飞轮

T5-3模块 5 · 评测与可观测面试权重 更新于 2026-08-19

这篇学完你能回答什么

  1. 一次多轮 Agent 请求的 trace 里应该记哪些东西?prompt 原文存不存?
  2. 没有标准答案的线上场景,怎么定质量监控指标?
  3. 线上质量掉了,第一个动作是什么?点赞点踩怎么用起来?

从一个真实故障讲起

一个客服助手,线上有采样 LLM judge,每天出一条「回答有据率」曲线。周二早上这条曲线从 0.86 掉到 0.81,掉了 5 个点,告警响了。

值班同学做了教科书式的处置:查最近变更,发现周一晚上上游模型做过一次小版本升级,回滚

分数没回来。

接着回滚了周一改的 prompt。还是没回来。

折腾到下午才有人去看输入端:周一上午市场部把一个新渠道接进来了,那个渠道的用户问题平均长度是原来的 1.8 倍,且大量涉及退款和赔付政策——一类本来就答得最差的问题。新渠道占了当天 14% 的流量。

系统一点没变坏,是进来的问题变难了。 而这半天里,团队回滚了两个其实没问题的东西,其中一个回滚本身还引入了新的缓存不一致。

这个故障的教训是一句可以直接背的话: 线上质量分数掉了,第一个要回答的问题不是「哪里坏了」,而是「到底是系统变差了,还是进来的问题变难了」。 这两个的处置方向完全相反,而分数本身分不出来。

后面会给出解这个问题的最小工具:金丝雀集


系统没变坏,是进来的问题变难了
周二早上有据率从 0.86 掉到 0.81,值班同学做了教科书式的处置:回滚

  1. 有据率 0.86 → 0.81

    采样 judge 的日曲线,告警响了

  2. 查最近变更

    周一晚上上游模型做过小版本升级

  3. 连回滚两次:模型、prompt断点

    分数都没回来,还引入了缓存不一致

  4. 下午才去看输入端

    周一上午市场部接进来一个新渠道

  5. 新渠道占当天 14% 流量

    问题平均长 1.8 倍,多涉退款赔付

「回答有据率」曲线0.860.81 —— 掉 5 个点,告警响
新渠道的问题原来的长度1.8 倍长,大量涉退款与赔付
新渠道流量周一上午才接进来当天占 14%,足够拉掉全局 5 个点
半天里做了什么回滚模型 + 回滚 prompt两个都没问题,还引入新的缓存不一致
分数本身分不出这两者,因为分子分母都在动。造一个分母不动的参照物就行 —— 金丝雀集:20–50 条固定输入按小时跑同一条链路,它不动就是流量变了,它也掉才是系统变了。

核心概念:先打比方,再给定义

打比方监控是仪表盘上的水温灯,可观测是能把发动机拆开看的能力。水温灯告诉你「有问题」,可观测让你回答「一个你上线时没想到的问题」。

LLM 应用之所以对可观测的要求比传统服务高,是因为它的失败大多不抛异常——HTTP 200、延迟正常、日志干净,但答案是错的。传统监控的全部手段在这里同时失效。

术语(首次出现,中英对照 + 人话):

  • trace(链路 / 调用链):一次完整请求从进到出的全过程记录。人话:这一单从头到尾发生了什么。
  • span(跨度 / 节点):trace 里的一个步骤,可以嵌套。人话:其中的一步。
  • 语义约定(semantic conventions):大家约好某个字段该叫什么名字。人话:字段命名的行业普通话。
  • 金丝雀集(canary set):一小撮固定输入,在线上按固定频率跑,用来当参照物。人话:随身带的那把尺子。
  • 数据飞轮(data flywheel):线上失败 → 变成评测用例 → 门禁变严 → 同类不再复发。人话:让每次事故都留下点资产。

水温灯只报警,拆开才能查
LLM 应用的失败大多不抛异常:HTTP 200、延迟正常、日志干净,答案是错的

仪表盘上的水温灯

它只告诉你「有问题」

至于是哪一步、为什么,它一个字也答不出

对应
监控 Monitoring

看预先设好的那几条曲线

曲线动了你知道出事了,但它答不出这一单坏在哪一步

告警阈值P95 分位错误率
能把发动机拆开看

能回答上线时没想到的问题

前提是这台机器本来就拆得开 —— 事后装不上去

对应
可观测 Observability

trace 记下这一单的全过程

span 是其中的一步、可以嵌套;语义约定 = 字段命名的行业普通话

trace / spangen_ai.conversation.id语义约定
还有两个术语后面会反复用:金丝雀集是随身带的那把尺子,数据飞轮是让每次事故都留下点资产 —— 前者用来归因,后者用来收尾。

原理拆解

trace 记得全不算数,能重放才算
OTel GenAI 给的默认骨架:invoke_agentchatexecute_tool

invoke_agent最外层:gen_ai.agent.name / gen_ai.agent.idgen_ai.conversation.id 是把多轮串起来的那把钥匙 —— 没有它,多轮问题永远查不了
chat一次模型调用:gen_ai.request.model / provider.name子 span usageinput_tokens / output_tokens;模型 ID 要含小版本,上游静默升级只有这一栏能证明
execute_tool一次工具调用:gen_ai.tool.name / .call.id / .arguments工具的真实入参与返回是离线复现的前提;只记 prompt 和 response,这一栏就是空的
chat(第二次)带着工具结果再调一次,挂在同一棵树上继续长结束原因要记死:正常结束 / 打到 max_tokens 被截断 / 工具报错 / 超时
指标与评测挂树上gen_ai.client.token.usagegen_ai.evaluation.score.value评测结果本身就是遥测数据:挂在 trace 上才能从曲线一键下钻到具体 span;另有 gen_ai.client.operation.duration
四类必记(判据:没有它就查不出问题)
类别必记什么没有它会怎样
身份与串联trace_id、conversation / session id、user id(脱敏)、请求入口与渠道开篇那个故障永远定位不到「新渠道」
可重放的输入系统提示的版本号、检索命中文档 ID + 分数、工具真实入参与返回、上下文总长度、是否触发过压缩能不能把一条线上请求原样搬到离线跑,全看这一栏
模型与配置模型 ID(含小版本)、effort 档位、max_tokens、是否命中 prompt 缓存、是否走了降级路由上游静默升级、降级到小模型,只有这一栏能证明
结果与判定结束原因(正常 / 打到 max_tokens 截断 / 工具报错 / 超时)、结构化输出是否解析成功、评测分数与 judge 版本截断和解析失败最容易被当成「模型变笨」

飞轮怎么转起来:线上 trace → 用点踩 / 转人工 / 重问挑出候选失败 → 开放编码后归并成 5–10 个失败类别 → 每个新模式补 1 条最小可复现用例(不是整条 trace)→ 门禁变严。每次事故都要以「评测集加了一条」收尾。

点踩是采样器,不是标注。反馈率通常只有个位数百分比,且重度偏向不满的用户;更值钱的是隐式负反馈 —— 立刻重问、改写再问、中途放弃、转人工,覆盖率高一个数量级。反馈事件必须带 trace_id 落库。

存不存 prompt 原文是本篇最真实的一道取舍题:存,PII 合规风险加存储大头;不存,出问题只能看到「有个 span 慢了」。收敛位置是:默认脱敏 + 按采样存原文 + 敏感字段哈希 + 保留期分层(原文 7–30 天,指标与评分长期)。

一、一次 Agent 请求的 trace 长什么样

OpenTelemetry 的 GenAI 语义约定给了一套通用骨架,值得当成默认结构来记:

原文示意
invoke_agent                      gen_ai.agent.name / gen_ai.agent.id
│                                 gen_ai.conversation.id  ← 把多轮串起来的那把钥匙
├── chat                          gen_ai.operation.name / gen_ai.provider.name
│   │                             gen_ai.request.model
│   └─ usage                      gen_ai.usage.input_tokens / output_tokens
├── execute_tool                  gen_ai.tool.name / gen_ai.tool.type
│   │                             gen_ai.tool.call.id / .arguments / .result
├── chat                          (带着工具结果的第二次调用)
└── ...
指标:gen_ai.client.token.usage、gen_ai.client.operation.duration
评测:gen_ai.evaluation.name / .score.value / .score.label / .explanation

最后一行是最容易被忽略、也最重要的一行:评测结果本身就是遥测数据。把分数挂在 trace 上,你才能从「这周有据率掉了 3 个点」一键下钻到具体是哪几条 span;分数活在另一个系统里,你就只能靠人工对时间戳。

二、trace 里必须记什么

按「没有它就查不出问题」的标准,分四类:

类别 必记 为什么
身份与串联 trace_id、conversation/session id、user id(脱敏)、请求入口/渠道 没有会话 ID,多轮问题永远查不了;没有渠道,开篇那个故障永远定位不到
可重放的输入 系统提示的版本号、检索命中的文档 ID + 分数、工具的真实入参与返回、上下文总长度、是否触发过压缩 能不能把一条线上请求原样搬到离线跑,全看这一栏记全没有(承接 Q5-02 第一刀)
模型与配置 模型 ID(含小版本)、effort 档位、max_tokens、是否命中 prompt 缓存、是否走了降级路由 上游静默升级、降级到小模型,都只有这一栏能证明
结果与判定 结束原因(正常结束 / 打到 max_tokens 被截断 / 工具报错 / 超时)、结构化输出是否解析成功、评测分数与 judge 版本 截断和解析失败是两个最高发、最容易被当成「模型变笨」的假象

判据:trace 的验收标准不是「记得全不全」,是「能不能据此把这条请求在离线复现出来」。 做不到,它就只是一堆好看的瀑布图。

存不存 prompt / completion 原文,是本节最真实的一道取舍题:

存原文 不存原文
收益 能排障、能做数据飞轮、能补评测用例
代价 PII 合规风险 + 存储成本的绝对大头 出问题只能看到「有个 span 慢了」

实践口径:默认脱敏 + 按采样存原文 + 敏感字段哈希 + 保留期分层(原文 7–30 天,指标与评分长期保留)。这条几乎是所有做过合规评审的团队最后收敛到的位置。

三、线上无标注监控的三层,**顺序不能反**

原文示意
① 不需要模型的信号  ← 先把这一层建全
   用户侧:重问率/改写率、复制率、点踩率、会话中途放弃率、转人工率
   系统侧:结构化输出解析失败率、工具报错率、超时率、重试率、
           拒答率、截断率(打到 max_tokens)、上下文超限率
   特点:不需要标注、不需要 judge、不会漂移、成本近乎为零
      ▼
② 需要模型但不需要标准答案
   采样 judge(有据性 / 拒答合理性 / 格式合规)
   输入侧 embedding 聚类:出现新簇 = 新意图或新渠道
   约束:基线采样约 2%,高风险或刚改过的路由提到 20%;
        必须异步、在响应返回之后跑,绝不进关键路径
      ▼
③ 业务终审
   转化率、工单解决率、人工复核通过率、退款/投诉率
   滞后,但唯一真实

硬判据:没把第 ① 层建全就先上 LLM judge 的团队,是在用一把会漂的尺子,去量一个从来没测过的东西。 第 ① 层里最被低估的三个指标是转人工率、重问率、截断率——它们零成本、零标注、且和用户真实不满的相关性通常高于任何 judge 分数。

还有一条约束要说死:线上 judge 的分数只能看趋势和分布,不能看绝对值,因为线上不是固定集合,输入分布每天在动。也正因如此,它绝对不能当团队 KPI——一旦考核它,团队就会开始对着 judge 的已知偏差优化。

四、【本篇最关键】归因第一刀:金丝雀集

回到开篇的故障。分数掉了,可能是系统变差,也可能是输入变难,而线上分数天然分不出这两者——因为分子分母都在动。

解法是造一个分母不动的参照物

原文示意
金丝雀集 = 20–50 条固定输入,覆盖主要意图与高风险场景
运行方式 = 在生产环境、走同一条链路、同一份 prompt、同一个 judge,
           按固定频率(如每小时)跑一遍,单独出一条曲线

  线上曲线掉  +  金丝雀曲线不动   →  输入分布变了(新渠道/新活动/更难的问题)
                                     系统没坏,该改的是覆盖面,不是回滚
  线上曲线掉  +  金丝雀曲线也掉   →  系统真的变了
                                     再往下切:模型版本?prompt?索引?工具依赖?

它和 RAG 排障里「手工把正确文档塞进 prompt」是同一个手法:固定住一个变量,把问题一分为二。 前者固定「文档」以切开检索侧与生成侧,这里固定「输入」以切开流量侧与系统侧。

成本极低(几十条 × 每小时一次),但它把开篇那半天的盲目回滚压缩成一次看板对比。如果只允许你在本章带走一个工程实践,带这个。

五、数据飞轮:让每次失败都留下资产

原文示意
线上 trace
   │
   ├─ 采样 + 用户信号(点踩、转人工、重问)挑出候选失败
   │
   ├─ 错误分析:开放编码(每条失败写一句话)→ 轴心编码(归并成 5–10 个失败类别)
   │
   ├─ 每个新失败模式补 1 条评测用例(最小可复现输入,不是整条 trace)
   │
   └─ 评测集增长 → 门禁变严 → 同类问题不再复发

关于点赞点踩,有三件事必须讲清楚,否则这条飞轮转不起来:

  1. 点踩率是趋势信号,不是标注。 反馈率通常只有个位数百分比,且重度偏向不满的用户——直接拿它当质量指标会系统性偏低。它的正确用法是当采样器:把点踩样本优先送进人工复核队列。
  2. 点踩必须能定位到 trace。 反馈事件要带 trace_id 和 conversation_id 落库,否则你只知道「有人不满意」,不知道他不满意什么。这是产品和工程最常见的接口漏配。
  3. 点赞几乎没有信息量,点踩才有。 更值钱的是隐式负反馈:立刻重问、改写问题再问一次、中途放弃、转人工——这些不需要用户动手,覆盖率高一个数量级。

两条可背判据: ① 每一次线上事故都必须以「评测集里新增了一条用例」结束。 只回滚、只改 prompt、不补用例的,这个问题一定会换个形式回来。 ② 评测集不是只增不减。 定期把「最近所有版本都 100% 通过」的用例降级到冒烟集——它们已经失去区分度,只在消耗预算。


工程实践(截至 2026-08)

OpenTelemetry GenAI 语义约定的真实状态

这是本章最硬的时效锚点,也是一个很好的分水岭题材:

  • 到 2026-08,没有任何一个 GenAI span / event / metric / attribute 达到 Stable,全部处于 Development。
  • 2026-06 的 semantic-conventions v1.42.0 把全部 gen_ai.* 内容移出核心仓库,迁到独立仓库,该仓库尚无版本化 release。
  • 它改过好几轮:prompt_tokensinput_tokens(v1.27.0,2024-08);gen_ai.systemgen_ai.provider.name(v1.37.0,2025-08);新增评测结果事件(v1.38.0,2025-10);检索 span 与缓存 token(v1.40.0,2026-02);agent span 拆分与 reasoning tokens(v1.41.0,2026-04)。

工程后果:同一条链路上不同框架很可能同时发多代属性(既发 gen_ai.system 又发 gen_ai.provider.name)。

对策:在采集层(Collector)做属性归一化,别让看板直接吃上游语义;并把语义约定版本当依赖钉死。 分水岭:只会说「我们用 OTel」→ 读过;能说「gen_ai.* 还没稳定、改过好几轮,所以我们在 Collector 里做了映射」→ 上过生产。

平台选型(本教程唯一一次点名,截至 2026-08,会变)

代表 定位
指标 / 断言库(进 CI) DeepEval、RAGAS、promptfoo 写在代码里,跟单测一起跑
评测 + 可观测一体 Langfuse(开源可自托管)、LangSmith、Braintrust、Arize AX + Phoenix trace 与评测同一份数据
传统可观测栈延伸 Datadog LLM Observability、MLflow、W&B Weave 与既有 APM / 实验管理打通
云厂商托管 Vertex Gen AI Evaluation Service、Azure AI Evaluation 与自家 Agent 运行时耦合

只记三条选型判据,产品会换、判据不会:

  1. 评测数据和 trace 数据必须在同一个系统里——否则「这条掉分的请求到底哪一步错了」永远要跨系统人工拼。
  2. 能自托管的优先——评测集是团队唯一 100% 保值的资产,不该锁在别人的 SaaS 里。
  3. CI 里跑的那一层必须是代码,不是平台 UI——UI 里点出来的评测无法 code review、无法回滚、无法跟着分支走。

避坑清单

  • 别把 judge 判分放进关键路径。 同步判分会直接把 P99 翻倍。异步、离线、采样,三选一。
  • 别只看均值。 质量问题几乎总是先出现在 P95 和某个细分渠道上,全局均值是最后才动的。开篇故障里 14% 的新渠道流量,就足以把全局曲线拉掉 5 个点。
  • 别忘了记「结束原因」。 截断(打到 max_tokens)伪装成「模型变笨」,是最高发的假象之一。
  • 别让 trace 采样率对所有路由一刀切。 高风险路由(涉钱、涉权限、涉外发)应该全采。
  • 别把点踩当标注用,它是采样器不是真值。
  • 告警要按细分维度设,不要只设全局阈值。 按渠道 / 意图 / 模型版本分组,才能在被稀释之前发现问题。

评测和 trace 不同库,就只能人工对时间戳
产品会换、判据不会 —— 这是本教程唯一一次点名,截至 2026-08,会变

平台选型(截至 2026-08)
代表定位
指标 / 断言库(进 CI)DeepEval、RAGAS、promptfoo写在代码里,跟单测一起跑
评测 + 可观测一体第一条判据指向这层Langfuse(开源可自托管)、LangSmith、Braintrust、Arize AX + Phoenixtrace 与评测同一份数据
传统可观测栈延伸Datadog LLM Observability、MLflow、W&B Weave与既有 APM / 实验管理打通
云厂商托管Vertex Gen AI Evaluation Service、Azure AI Evaluation与自家 Agent 运行时耦合
监控纪律与时效锚点
三层的顺序
① 不要模型的信号 → ② 采样 judge → ③ 业务终审。顺序不能反,第 ① 层零标注零成本
第 ① 层最被低估
转人工率、重问率、截断率 —— 和用户真实不满的相关性通常高于任何 judge 分数
judge 怎么跑
基线采样约 2%,高风险或刚改过的路由提到 20%;必须异步,同步判分直接把 P99 翻倍
judge 分数怎么用
只看趋势和分布,不看绝对值;绝不能当 KPI —— 一考核就会对着它的已知偏差优化
OTel GenAI 现状
到 2026-08 gen_ai.* 全部 Development,无一 Stable;v1.42.0(2026-06)已移出核心仓库
所以要归一化
一条链路可能同时发 gen_ai.systemgen_ai.provider.name,在 Collector 层做映射
只记三条判据:评测数据和 trace 必须在同一个系统里;能自托管的优先,评测集是团队唯一 100% 保值的资产;CI 里跑的那一层必须是代码,UI 里点出来的评测无法 code review、无法跟着分支走。

面试视角

答题的顺序,就是排障的顺序
四问连着来:trace 记什么 → 没标注怎么监控 → 掉分先干什么 → 点踩怎么用

  1. 先给结构trace 骨架 invoke_agentchat / execute_tool,会话 ID 串多轮
  2. 再给判据记录的验收标准 = 能不能离线原样复现
  3. 讲监控分层先建不要模型的信号,再上采样 judge,最后对齐业务
  4. 给归因动作金丝雀集:切开「输入变难」与「系统变差」
  5. 谈闭环与取舍失败 → 补用例 → 门禁变严;存不存原文、采样率、PII
只读过:这些回答会暴露你
  • 「我们接了 XX 平台,能看到 trace」—— 只有工具,没有字段清单
  • 线上直接上 LLM judge 打分做监控 —— 跳过了零成本的第一层
  • 「分数掉了就先回滚」—— 没有归因动作,开篇故障的标准复现
  • 「点踩率是我们的质量指标」—— 把重度有偏的稀疏信号当真值
  • trace 只记 prompt 和 response,不记小版本、结束原因、文档 ID
真做过:这些细节骗不了人
  • 说得出验收标准是「能离线原样重放」,以及为此必须记哪几栏
  • 先建零成本信号再上 judge,报得出 2% / 20% 这两个采样档
  • 第一动作是金丝雀集,并说清它切开的是流量侧还是系统侧
  • 知道隐式负反馈(重问、改写、放弃、转人工)覆盖率高一个数量级
  • 说得出 gen_ai.* 还没 Stable,自己在 Collector 里做了归一化
四问里区分度最高的是第二问「线上没有标准答案,你怎么知道质量变差了」—— 一问就看得出你是先建了零成本信号,还是上来就把 judge 当尺子。配套题目:Q5-06Q5-07Q5-09Q5-10

面试官会怎么问

  • 「一次 Agent 请求的 trace 你会记哪些字段?」(工程向二面高频)
  • 「线上没有标准答案,你怎么知道质量变差了?」(区分度最高的一问)
  • 「线上分数掉了,你先干什么?」(考的是归因次序,不是知识点)
  • 「用户点踩的数据你们怎么用?」(三面 / 业务负责人常问)

答题结构建议

原文示意
① 先给结构    trace 骨架(invoke_agent → chat / execute_tool),会话 ID 串多轮
② 再给判据    记录的验收标准 = 能不能离线原样复现
③ 讲监控分层  先建不要模型的信号,再上采样 judge,最后对齐业务指标
④ 给归因动作  金丝雀集:切开「输入变难」和「系统变差」
⑤ 谈闭环      失败 → 错误分析 → 补评测用例 → 门禁变严
⑥ 谈取舍      存不存原文、采样率、成本、PII

分水岭信号

暴露只读过:

  • 「我们接了 XX 平台,能看到 trace」——只有工具,没有字段清单,也没有判据。
  • 「线上用 LLM judge 打分做监控」——跳过了零成本的第一层信号。
  • 「分数掉了就先回滚」——没有归因动作,开篇故障的标准复现。
  • 「点踩率是我们的质量指标」——把一个重度有偏的稀疏信号当真值。
  • 记 trace 只记 prompt 和 response,不记模型小版本、结束原因、检索文档 ID。

证明真做过:

  • 说得出**「trace 要能支持离线原样重放」**这条验收标准,以及为此必须记哪几栏。
  • 说得出存不存原文的取舍,并给出脱敏 + 采样 + 保留期分层的具体口径。
  • 说得出金丝雀集(或任何等价的固定参照物),并解释它切开的是哪两件事。
  • 说得出 gen_ai.* 还没 Stable、改过好几轮,以及自己在采集层做了归一化。
  • 说得出隐式负反馈(重问、改写、放弃、转人工)比点踩覆盖率高一个数量级
  • 说得出「评测分数应该挂在 trace 上」,而不是活在另一个系统里。

小结与延伸

三句话小结:

  1. LLM 应用的失败大多不抛异常,所以 trace 的验收标准不是「记得全」,而是「能不能据此在离线原样复现这条请求」。
  2. 线上监控分三层且顺序不能反:先建零成本的用户与系统信号,再上采样 judge(异步、2%/20%),最后对齐业务指标;judge 分数只看趋势,不看绝对值,更不能当 KPI。
  3. 分数掉了的第一个动作是用金丝雀集切开「输入变难」和「系统变差」;每次事故的收尾是往评测集里补一条用例。

延伸阅读(本教程内):

  • T5-1(评测集与三条线):线 B、线 C 的定义与它们之间背离的诊断表。
  • T5-2(LLM-as-Judge):线上采样判分背后那个评委本身可不可信。
  • T3-11(Agent 评测与 trace):轨迹层面的评测指标。
  • T1-4(压缩清理与 prompt 缓存):trace 里「是否触发压缩」「是否命中缓存」这两栏的来历。

继续深入

本篇归属第 5 章「评测与可观测」,去做这一章的题