跳到正文

可观测与评测:让 Agent 的改进可被证明 ​

一句话:Agent 的可观测不是“多打日志”,而是把一次执行还原成“第几步、看了什么、调了什么、花了多少”的结构化证据;它换来可定位与可回归,代价是分层存储、采样策略和一套会自我校准的评估体系。

适用:2026 年主流做法是 OpenTelemetry 语义约定加专用评测框架;具体字段与能力以官方文档为准。

为什么需要 ​

线上出现“昨天答得对,今天答错了”。传统日志里只有一条 request 和一条 response,你无法回答四个问题:模型在第几步偏航、它当时看到的上下文是哪一版、它调用了哪个工具、这次多花了多少 token。日志是扁平的,Agent 的执行是一棵决策树,二者结构不匹配。

传统 APM 能回答“哪个服务慢”,回答不了“哪一步想错了”。Agent 的失败常常没有异常栈:HTTP 200、工具成功、答案流畅,但结论是错的。没有轨迹和上下文快照,这类失败只能等用户投诉;没有离线评测集,每次改 Prompt 都只能凭感觉。

核心机制 ​

轨迹:最小字段决定可回溯性 ​

一个 span 至少要记录:trace_id、step、span_type(llm / tool / retrieve)、模型名与版本、prompt_hash、context_ref、工具名与参数摘要、token(输入 / 缓存 / 输出)、延迟、decision、error_class。少一项,事后就补不回来。Session 关联一段连续交互,Trace 承载一次外部输入触发的活跃执行,Span 是基本单元;把步骤整理成可读的行为主干,才是 Trajectory。

采样要按“是否异常”分层,而不是均匀随机:正常轨迹的边际价值很低,失败轨迹才是排障素材。把全文与热存储分离,才能同时保住可回溯性和成本上限;否则完整 Prompt 动辄数万 token,观测成本会超过推理成本。

成本:先分清花在谁身上 ​

Trace 只回答“发生了什么”,成本还要回答“该找谁”。三级归因的最小字段如下,缺一项就会退化成猜:

归因维度回答的问题最小字段典型用途
Agent哪个 Agent 花得最多agent_id、模型版本、输入/缓存/输出 token、工具调用数模型路由、预算分配
用户谁在花、是否异常tenant_id / user_id、任务数、单任务成本配额、异常用户
任务哪类任务最贵task_type、步数、重试数、单位成功成本优化流程、定价

只看 Agent 会把成本推给“模型贵”,只看用户会把成本推给“大客户”,只有落到任务类型才知道该改流程还是改模型。最小公共字段是 trace_id、agent_id、user_id、task_type、模型版本、三档 token、工具调用次数、重试次数。

缓存账要写清假设。命中部分按未命中输入价的 10% 计(示意价,按实际价格替换),设命中率 h,单位输入成本约为 1 - 0.9h:h=90% 时 0.19,h=50% 时 0.55,比值约 2.9,不是 3–5 倍。3–5 倍只在命中价明显低于 10%(例如 5%,约 3.6 倍)时近似成立;若比较 100% 与 0%,按 10% 计价差距约 10 倍。把 10% 命中价下的 2.9 倍写成 3–5 倍,是把不同假设混在了一起。

三层评估:不要合成一个“准确率” ​

检索评估问“该看到的文档有没有被找到”,用 Recall@k、nDCG、context precision;生成评估问“答案是否只依据给定上下文”,用 faithfulness、answer relevance、citation support;端到端任务评估问“用户的事有没有办成”,用任务成功率、约束通过率、人工接管率、单任务成本。

层核心指标典型失败首先检查
检索Recall@k、nDCG召不回、召回过宽切分、embedding、混合检索
生成faithfulness、引用支持率编造、答非所问上下文顺序、约束措辞
任务成功率、接管率、成本多步任务半途而废工具契约、循环上限

三层是漏斗关系:检索 Recall 低时,生成指标再高也没有意义,因为模型只能从错误材料里作答。反过来,检索全对而答案仍错,问题在上下文组装或生成约束。把三者混成一个数,就无法判断该改哪一层。还有三个独立结论不能压扁:一次运行完成、任务判为成功、允许发布;执行状态、环境有效性、证据完整性、任务质量要分开记。

LLM-as-Judge:四类偏差、三道校准 ​

Judge 的偏差来自“同源”。如果 Judge 与被测模型同家族,它们会共享同一批盲区,模型犯的错 Judge 也认为合理,分数很高而问题仍在。四类偏差与对应校准如下:

偏差表现校准
自我偏好同家族互打高分,经验上抬 5–15%换模型家族,多 Judge 投票
位置偏好排在前或后的答案候选顺序随机,多次取中位数
长度偏好更长、更“像样”的回答rubric 约束要点覆盖,不奖励字数
风格偏好某种文风结构化 rubric + 人工对齐

三道校准要一起用,而不是只做一道:多 Judge 用不同家族投票(3:0 / 2:1 / 1:2),分歧大的样本进人工;人工对齐每月抽 30–50 条(经验值),一致率低于 85% 就重写 rubric;位置随机化并对同一例跑 3 次取中位数,压掉顺序和单次波动。能用代码断言的(字段是否存在、引用 ID 是否合法、是否越权)一律不用 Judge;Judge 还应被允许输出“证据不足”,强制二选一会把缺失证据变成猜测。

回归门禁:QS 只保底线 ​

Quality Score 把四个维度压成一个可比较的数:QS = 完成率×0.5 + 工具效率×0.2 + 安全×0.2 + 成本×0.1。以 0.80 / 0.70 / 1.00 / 0.60 为例:0.40 + 0.14 + 0.20 + 0.06 = 0.80。三档是 ≥0.75 允许上线、0.60–0.75 人工 Review、<0.60 阻止。

但绝对阈值只保底线,不能证明这次改动有效。上线还要同时满足两条:相对基线有显著提升——同一用例跑 3 次取中位数,新旧版本做配对检验,p < 0.05;以及护栏不退化——P95 延迟、成本、安全拦截率都在可接受范围。新版 QS 0.80,如果基线也是 0.80,提升为 0;如果只从 0.78 涨到 0.80 但配对检验不显著,仍然不放行;如果延迟翻倍,也不放行。

门禁的价值在于把“感觉变好了”变成“可复现地变好了”,但它只覆盖已知分布。生产采样回流的失败案例,才是让评测集跟上真实用户的那条回路;没有这条回路,门禁会越来越像自我确认。

评测集分三层:黄金集(人工标注、稳定、每次必跑,经验起点 50–100 条)、对抗集(历史事故与边界、只增不减,经验起点 20–50 条)、生产采样(每周补充)。数字是经验起点,不是硬门槛;任务族多、风险高时按覆盖度扩,不是凑数。黄金集要留出不参与调参的 hold-out,否则优化的是测试集而不是系统。样本量也要算清楚:n 次对抗测试零违规,违规率的单侧 95% 置信上界约 3/n——300 次零违规也只说明上限约 1%,不是零风险。

实战场景 ​

场景:Badcase 48 小时闭环实录——一条“答非所问”从进收到进回归集 ​

背景:退款场景客服 Agent 被用户投诉“问的是部分退款时限,答的是退款路径”。表象是“质量变差”,闭环要产出的是可行动的相邻事实。

时间动作产出
0–2h拉全量 trace,定位 step 4 decision=answer_directly,取 context_ref 快照确认 step 3 检索返回 2 篇文档,“时限”段落在其中
2–6h按三层归因逐层排除:环境 → Harness → 模型无工具超时与错误码聚集,环境排除;上下文快照显示截断点落在时限段落之前,指向 Harness
6–20h回放同轨迹,一轮实验只改一项:调整截断策略同一输入 3 次跑 2 次答对且命中约束,判为 Harness 层,模型排除
20–36h修复发布 + 黄金集全量回归QS 过底线、配对检验 p<0.05、护栏(P95 延迟 / 成本 / 安全拦截率)不退化
36–48h该 case 写清输入、期望约束、失败层,入黄金集回归桶此后任何改动触发同样截断,门禁直接拦下

设计决定:

  • 异常轨迹全量保留是这条时间线成立的前提——若按正常轨迹 1%–5% 采样,48 小时后已取不到 step 3 的上下文快照。
  • 修复按“从最便宜层动手”:改截断策略的成本远低于动模型;归因记录必须写明“模型层为何被排除”,否则下次故障仍先怀疑模型。
  • 用例只增不删:该 case 入黄金集的同时成为显著性门禁基线的一部分,下一版发布必须证明在它上面不退化。

场景:LLM-as-Judge 上线一个月后评分漂移——被抬升 0.3 分掩盖的真实退化 ​

现象:黄金集 faithfulness 月均 6.4,第三周跳到 6.7,QS 各档整体上移、无告警;同期人工抽检的误判数与线上追问率却在上涨。分数与现实脱钩。

排查路径:

  1. 先排评测集:黄金集与对抗集版本号未变、题目零增删,排除。
  2. 再排被评对象:模型、Prompt、索引三个版本的绑定字段全部一致,排除。
  3. 查评分侧:judge 配置记录的评审模型版本,被基础设施例行升级静默更换;该变更不在发布清单里,也未触发任何回归。
  4. 用旧版 judge 对同一批回答重打分:抬升几乎全部来自评审侧,被测系统实际轻微退化。

修法:

  • 评审器与被评对象同版本管理:judge 模型版本写入 trace 与绑定字段,与模型 / Prompt / 索引版本同契约;评审器换版必须走发布与回归,不允许基础设施静默升级。
  • 建 judge 自身的校准集:固化 500 条已人工对齐的历史样本(经验值),评审器每次换版前后各跑一遍,一致率跌破 85% 即阻止换版。
  • 发布决策改用成对比较:同一用例、新旧版本回答、同一 judge 打分;绝对分只用于看趋势,不作为放行依据——judge 换版造成的整体平移在成对设计下基本抵消。
  • 人工校准抽审维持每月 30–50 条;漂移嫌疑期临时加到 80–100 条,直至一致率回到 85% 以上。

教训:judge 是评估体系的一部分,不是背景设施。凡能改变分数的组件都要版本化;“分数上涨但没有任何被评对象变更”本身就是告警条件。

场景:Trace 字段设计事故——“检索没召回”与“召回了没用好”共用一份日志 ​

现象:答疑机器人准确率下滑,值班拉起 trace 只看到 step 3 tool=search_docs, status=ok——召回了几篇、排序如何、最终答案实际用了哪几段都不知道。周会一小时无法裁决该改检索还是改上下文组装。

事后补齐字段、重跑当周失败样本:约 60% 是“召回了没用好”(关键段落排在截断点之后或被埋没),约 20% 才是真召回失败。两类问题的责任模块与修法完全不同,旧 trace 却无法支撑这个区分。

最小字段集清单(新工具 span 的 schema 评审按此收口,缺一项不合入):

字段回答的问题缺失的后果
result_count / top_k实际召回几篇、给了几个槽位分不清“没召回”还是“召回了没进上下文”
score_digest各篇相关性分数判不了“阈值过高截掉”还是“排序错误”
key_facts_hit参考答案要点是否出现在返回摘要中只能回对象存储取全文快照人肉比对
consumed_doc_ids最终答案实际引用了哪几篇判不了“召回了没用好”,引用支持率算不出来

设计决定:进热存储的是摘要与指纹,不是检索原文——全文仍走 context_ref 落冷存储;否则动辄数万 token 的检索返回会把观测成本推高到超过推理成本,违背热 / 冷分层设计的初衷。

最小上手 ​

先不接平台,用一个 JSON 结构把一次执行串起来(示意):

json
{
  "trace_id": "t-9f2c", "session_id": "s-17", "step": 3,
  "span_type": "tool", "agent_id": "a-2", "user_id": "u-8", "task_type": "refund",
  "model": "example-model@2026-05", "prompt_hash": "sha256:1a2b...",
  "context_ref": "obj://ctx/9f2c-3.json",
  "tool": {"name": "search_docs", "args_digest": "sha256:7c1e...", "status": "ok", "ms": 230},
  "tokens": {"input": 4500, "cached": 3800, "output": 320},
  "decision": "call_tool", "error_class": null
}

跑通判据:任取一条线上失败会话,不看应用日志,只看 trace 能回答五件事——第几步、看了哪一版上下文、调了什么工具、花了多少 token、在哪一步偏离预期。答不全,说明字段还不够。

生产动作 ​

  • 采样:异常轨迹全量,正常轨迹按 1%–5% 采样(经验值);按天分层,避免故障日把存储打满。
  • 看板:任务成功率、P95 端到端、首 token(TTFT)与每 token 输出时间(TPOT)、每任务 token(区分输入 / 缓存 / 输出)、人工接管率、失败分类 Top 5;指标能按模型、Prompt、索引版本下钻。
  • 失败分类:固定一套枚举(召回为空、工具报错、选错工具、上下文冲突、违反指令、超时、主动拒答),每周看占比变化;分类不稳定,排障就会退化成个案分析。
  • 成本哨兵:单任务 token 超过基线 3 倍、输出 token 占比超过 50%(疑似空转)、缓存命中率跌破 70%(经验值),分别触发不同告警。
  • 评测节奏:每次 Prompt / 模型 / 检索变更跑黄金集与对抗集;每周跑全量并比对趋势;每月做一次人工校准。安全性回归零容忍,其他指标看显著性与护栏。
  • 黄金集不是“题目集”:每题要有参考答案或 rubric、场景与来源;选题覆盖高频、已发生、边界高风险、原本正常、表述/多轮五类。参与调优的题与独立验证的题必须分开。
  • Badcase 归因落到相邻事实:不要写“回答质量不好”,要写“用户要操作步骤 → 检索返回了步骤 → 回答遗漏步骤”;一轮实验只改一项,被评分的必须是新生成的回答与新轨迹。
  • 闭环:每个线上事故在 48 小时内变成一条用例,写清输入、期望约束、失败层;用例只增不删,删除等于允许复发。

边界 ​

  • 适合有稳定任务定义、改动频繁的 Agent;不适合一次性 Demo——搭评测的成本高于收益。没有明确的“任务完成”定义时,先定义任务,再谈指标。
  • Trace 适合定位“哪一步出错”,不适合证明“用户是否满意”;后者要靠产品信号(追问率、复制率、转人工率),两者不能互相替代。
  • Judge 适合语义质量与风格,不适合数值精确、权限、合规与引用真实性;这些要用确定性检查或外部事实源核验。
  • 门禁适合防退化,不适合发现新能力;黄金集只覆盖已知分布,新场景要靠生产采样和探索性评估。
  • 常见误区:把 trace 当访问日志全量存,最后被存储成本反噬;把 Judge 分数当绝对真值,忽略同源偏差;把 QS 绝对阈值当成上线充分条件,忽略相对基线与护栏。
  • “AI 审 AI”的边界:Judge 能扩展评估规模,但不能提供独立事实。凡是需要“对错”而非“像不像”的判断,必须有人工样本或可执行断言兜底。

讲清楚 ​

可观测与评测解决的是同一个问题的两面:前者让一次执行可回放,后者让一组执行可比。做法是把 Agent 的每一步记成带步号、上下文指纹、工具与 token 的 span,热存储留摘要、冷存储留全文,成本按 Agent / 用户 / 任务三级归因。评估分检索、生成、任务三层,任何一层塌了都往下游传导,所以不能合成一个准确率。Judge 能扩大评估规模,但它有自我偏好、位置、长度、风格四类偏差,必须用多 Judge、人评对齐、位置随机加多次中位数三道校准。最后把评测接进合并门禁:QS 过底线只是必要条件,还要相对基线显著提升、护栏不退化才放行,线上失败再回流成用例。

适用:2026 年 Agent 可观测以 OpenTelemetry 语义约定为主、评测以黄金集 + LLM-as-Judge + 门禁为主 | 初稿 2026-10

把 AI 工程讲到既懂也会用