本页目录
评估与可观测
agent 难评估:输出开放式、过程非确定、链路多步。但没有评估就没法迭代——评估(离线衡量质量)和可观测(在线看清运行时)两者互补,才能把 agent 从"看着还行"变成可度量、可改进。
概述
agent 难评估,难在三点:输出开放式(没有唯一正确答案)、过程非确定(同 prompt 两次结果不同)、链路多步(一个任务中间走了二十次工具调用)。但没有评估就没法迭代——改了 prompt、换了模型、加了工具,凭什么说"变好了"而不是"换了个错法"?这篇讲两件互补的事:评估(eval)——离线、可重复地衡量质量;可观测(observability)——在线看清 agent 运行时到底在干嘛。前者回答"整体变好没",后者回答"哪一步出了问题"。两者接进迭代循环,agent 才能从"看着还行"变成"可度量、可改进"。
评估:离线衡量质量
flowchart LR
D["eval 集<br/>代表性任务 + 期望/rubric"] --> R["跑当前版本"]
R --> J["打分"]
J -->|可编程?| P["代码断言"]
J -->|开放式?| L["LLM-as-judge"]
J -->|有完成标准?| O["outcome 评分"]
P --> I["聚合 & 对比"]
L --> I
O --> I
I -->|改 prompt/模型/工具| R
打分方式按可靠性从高到低用,能用前者就别用后者:
1. 可编程校验(最可靠)
能用代码判定的,绝不交给模型。"返回 JSON 通过 schema""CSV 有数值列 price""测试通过""含某关键字段"——直接断言。确定、零成本、无偏差。结构化输出(output_config.format / strict: true)能让这类校验稳定可做(见 提示工程与结构化输出)。
2. LLM-as-judge(开放式才用)
摘要质量、回答是否切题这类无法编程判定的,用一个模型当裁判。要点全在降偏差:
- 给明确 rubric:把"好不好"拆成可独立判定的分条("是否引用了来源""是否回答了全部三个子问题"),而不是让裁判凭感觉打一个总分。模糊 rubric → 噪声分。
- 裁判与被测分离:别让同一个模型评自己的输出(自我背书)。用不同模型、或至少独立上下文当裁判。
- 警惕已知偏差:裁判偏爱更长的答案、偏爱自己家模型的风格、受选项顺序影响——rubric 越具体,这些偏差影响越小。
- 把打分本身也评一评:抽样人工核对裁判判分,确认裁判靠谱再规模化。
3. outcome-based(有完成标准时)
Managed Agents 把"结果导向评估"做成了原语。发一个 user.define_outcome 事件,带:
description(任务)、rubric({type:"text"|"file"},必填,写可独立判定的标准)、max_iterations(默认 3,最大 20)。
平台用一个独立上下文的 grader 跑 iterate → grade → revise 循环,每轮发 span.outcome_evaluation_start/ongoing/end,end.result ∈ {satisfied, needs_revision, max_iterations_reached, failed, interrupted},直到达标或到上限。这等于把"评估 + 自动改进"内建进运行。适合产出有明确"完成长什么样"的任务(报告、模型、文档)。
4. regression(回归)
每发现一个失败,把它固化成一条 eval case。这是 eval 集随时间变强的方式——线上每翻一次车,就在离线多一道防线。
最重要的实践:先有一个小而真实的 eval 集再开始调。哪怕只有 20 条代表性任务(含已知失败/边界,不只 happy path),也远胜凭单条手感拍脑袋。
可观测:在线看清运行时
agent 上线后要能回答:它走了哪几步?哪步慢/失败?烧了多少 token?为什么停?
-
trace / span:把一次运行拆成可见步骤——每个
tool_use/tool_result/thinking/模型调用是一个 span。Managed Agents 直接给事件流,span.model_request_end还带model_usage(本次推理的 token 明细),用于逐步归因延迟与成本。 -
usage(成本 + 缓存健康):每次响应的
usage给:字段 看什么 input_tokens未缓存输入(全价) output_tokens输出 cache_read_input_tokens命中缓存(~0.1x);长期为 0 = 有静默失效器(见 上下文工程) cache_creation_input_tokens写缓存(~1.25x) 注意:
input_tokens只是未缓存的余量,总量 = 三者之和——别只看一个字段就以为没花 token。 -
stop_reason 分布:
end_turn/tool_use/max_tokens/refusal的占比是健康仪表盘。max_tokens偏高 = 输出被截断(调大或流式);refusal异常升高 = prompt 触发了拒绝;pause_turn多 = 服务端工具循环在续。 -
OpenTelemetry:把 trace/metric 导到标准后端统一看。
真实例子:本站点的静态生成器构建时就初始化了 OTel——构建日志里那行
OTel tracer initialised, exporting to ...就是。同一套"trace 每一步、量化每一次"的思路,LLM 应用照搬即可。
把两者接进循环
可观测回答"哪步出了问题",评估回答"整体变好没"。缺前者,不知道为什么错;缺后者,不知道改动是不是真改进。两者缺一,迭代就是盲改。
最佳实践
- 先有小而真实的 eval 集再开始调。 哪怕 20 条代表性任务(含已知失败/边界),也远胜凭单条手感拍脑袋。
- 打分按可靠性从高到低用。 可编程校验 > LLM-as-judge > outcome 评分;能
assert的绝不交给模型(结构化输出 让这类校验稳定可做)。 - 裁判与被测分离,给可独立判定的 rubric。 别自评自背书;把"好不好"拆成可逐条判定的分项,降噪降偏差。
- 每翻一次车,固化成一条回归 eval。 线上每个失败变离线一道防线,eval 集随时间变强。
- trace 每一步,量化每一次。 一次运行拆成 span,
usage/stop_reason逐步归因延迟与成本;OTel 导标准后端统一看。 - 缓存命中率进监控。
cache_read_input_tokens长期为 0 = 有静默失效器,成本会悄悄翻倍。 - 非确定系统看聚合,别被单次手感带偏。 通过率、token 分布、
stop_reason占比;批量结果按custom_id/trace id 归因,绝不靠顺序。
权衡与失败模式
- 只测 happy path:线上一遇边界就崩 → eval 集必须含失败/边界,并随事故增长(回归)。
- 裁判 = 被测:自评自背书 → 裁判与被测分离;对应 agent 工程的"编写与审查分两条 lane"(见 多 Agent 编排)。
- rubric 含糊:"看起来不错"无法稳定打分 → 写可独立判定的分条。
- 只看单条不看分布:非确定系统要看聚合(通过率、token 分布、stop_reason 占比),别被单次手感带偏。
- 不归因:批量/并行结果对不上是哪条 → 用
custom_id/ trace id 归因,绝不靠顺序(Batches 结果本就乱序)。 - 缓存命中率不监控:成本悄悄翻几倍才发现 → 把
cache_read_input_tokens进监控面板。
参考
- Anthropic 官方文档: Building Evals、Managed Agents Define Outcomes & Observability、Handling Stop Reasons(platform.claude.com)
Keywords: eval, eval set, programmatic check, structured outputs, LLM-as-judge, rubric, judge bias, outcome-based, user.define_outcome, grader, span.outcome_evaluation, max_iterations, regression, observability, trace, span, model_usage, usage, input_tokens, output_tokens, cache_read_input_tokens, stop_reason 分布, OpenTelemetry, OTel, authoring vs review lane, custom_id