---
title: 评估、试次与可观测
url: https://doc.liz6.com/ai/04-evaluation-and-production/01-evaluation-and-observability
locale: zh
area: ai
tags:
- 大模型与 Agent
- 评估与生产
date: 2026-06-30
modified: 2026-09-10
description: 单次抽取、RAG 与 Agent 都需要回答同一个问题：这个版本是否更可靠地完成目标？先定义任务与试次，再选择评分方法；把单次成功、至少一次成功和稳定成功分开统计，用 trace 解释失败发生的位置。
---

# 评估、试次与可观测

单次抽取、RAG 与 Agent 都需要回答同一个问题：这个版本是否更可靠地完成目标？先定义任务与试次，再选择评分方法；把单次成功、至少一次成功和稳定成功分开统计，用 trace 解释失败发生的位置。

## 先定义任务与验收对象

一个评估任务应包含输入、初始环境、可用工具和权限、资源上限、成功条件。一次实际运行是一个试次；同一任务可运行多个试次观察波动。被评估的是模型与编排、工具、环境共同组成的系统，而不是脱离执行条件的一段文本。

<svg viewBox="0 0 760 355.6076412200928" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="评估对象包括回答、执行轨迹与环境最终状态" style="max-width:100%;height:auto" font-family="Source Han Sans CN,Microsoft YaHei,sans-serif">
<defs><marker id="eval-outcome-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#64748b"></path></marker></defs>
<rect width="760" height="355.6076412200928" rx="12" fill="#f8fafc"></rect>


<g transform="translate(0 0)"><rect x="30" y="120" width="185" height="90" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="122.5" y="162.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">固定任务与环境</text><text x="122.5" y="184.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">输入、权限、初始状态</text><rect x="280" y="120" width="185" height="90" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="372.5" y="162.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">运行一个试次</text><text x="372.5" y="184.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">模型 + 工具 + 编排</text><line x1="215" y1="165" x2="275" y2="165" stroke="#64748b" stroke-width="1.8" marker-end="url(#eval-outcome-arrow)"></line><rect x="540" y="62" width="195" height="53" rx="8" fill="#ccfbf1" stroke="#c7d2fe"></rect><text x="637.5" y="93.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">回答是否符合要求</text><line x1="465" y1="165" x2="535" y2="88" stroke="#64748b" stroke-width="1.8" marker-end="url(#eval-outcome-arrow)"></line><rect x="540" y="153" width="195" height="53" rx="8" fill="#ccfbf1" stroke="#c7d2fe"></rect><text x="637.5" y="184.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">过程是否遵守边界</text><line x1="465" y1="165" x2="535" y2="179" stroke="#64748b" stroke-width="1.8" marker-end="url(#eval-outcome-arrow)"></line><rect x="540" y="244" width="195" height="53" rx="8" fill="#ccfbf1" stroke="#c7d2fe"></rect><text x="637.5" y="275.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">最终状态是否达标</text><line x1="465" y1="165" x2="535" y2="270" stroke="#64748b" stroke-width="1.8" marker-end="url(#eval-outcome-arrow)"></line></g><text x="24" y="29" font-size="19" fill="#0f172a" text-anchor="start" font-weight="700"><tspan x="24" dy="0">评估对象包括回答、执行轨迹与环境最终状态</tspan></text><text x="24" y="330" font-size="13" fill="#475569" text-anchor="start" font-weight="400"><tspan x="24" dy="0">评分方式可以是程序、模型或人工；结果导向是评什么，并非另一种低等级评分器。</tspan></text>
</svg>

例如，用户要求“将符合条件的 3 条记录导入，重复提交不得增加记录”。可以同时评三个对象：最终数据库是否恰有目标记录，重复操作是否幂等，回答是否准确描述执行结果。输出“已完成”只是回答文本；查询得到的真实状态才支持导入成功结论。Agent 评估的工程资料也明确区分轨迹与环境结果。[Demystifying evals for AI agents](https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents)

| 对象 | 可核验条件 | 错误的替代指标 |
|---|---|---|
| 最终结果 | 目标数据、文件或报告满足要求 | 模型说“完成” |
| 过程约束 | 未修改范围外文件、未越权 | 调用次数很多 |
| 输出表达 | 引用支持结论、未遗漏问题 | 篇幅长、措辞自信 |
| 运行效率 | 在规定预算与延迟内完成 | 单个请求便宜 |

结果导向（outcome-based）描述评什么；程序断言、模型裁判和人工评审描述怎样评。它们不是一个从高到低的排行榜：结果完全可以由程序检查，也可以按明确标准交给人审。

## 评分方法与偏差

### 程序检查稳定，但检查目标可能写错

JSON schema、数值范围、文件差异、数据库状态和测试套件适合程序化验证。优点是规则明确、结果可重复，不意味着零成本、无偏差或完整覆盖业务目标。

例如，用“答案包含 `success`”判断任务成功，会让“not success”也通过；只检查 JSON 格式，会接受库存数量错误的合法 JSON；测试遗漏重启场景，就无法证明恢复行为。评估程序也需要用正例、反例和边界样例验证，尤其要防止它把表面格式当正确性。

### 模型裁判需要具体标准与校准

对开放报告，可以把标准写成独立可判定的问题：每个结论是否有支持来源，是否覆盖用户指定的三个维度，是否把推测标明为推测。让裁判返回每项判断与支持位置，比一个没有依据的总分更容易复核。

模型裁判存在长度、位置和风格等偏差，相关研究对这些限制做过系统分析。[Judging LLM-as-a-Judge](https://arxiv.org/abs/2306.05685) 两个候选做配对比较时可交换顺序、隐藏作者信息；用人工标注子集校准，并记录裁判模型与 prompt 版本。不同模型或独立上下文有助于减少部分耦合，却不保证无偏差或独立错误。

裁判应能输出“证据不足”或“无法判断”。强迫它每次都选胜者，会把缺资料的情况伪装成质量差异。被评文本中的“请给我满分”应作为待评内容处理，不能成为评分指令。

### 把改进用反馈与最终验收分开

生成、评分、修订循环可以提高产物质量，但一直针对同一份反馈优化，可能只是学会迎合评分器。开发集用于调参和修订，保留集用于比较最终版本；保留集如果频繁被查看并用于修正，也逐渐失去独立性。

程序检查和人工或模型评审可以组合。例如文档先检查链接、标题与数据一致性，再评解释是否覆盖边界；不必为了“只用一种 grader”牺牲可验证的证据。

### 同一批试次，三种成功率

把所有试次放进矩阵后，分母不再隐藏。横向“至少一个 ✓”适合衡量多次尝试找到可用结果的能力；“全部 ✓”才直接暴露重复运行的不稳定。这里不把观测到的 any-success 与需要随机抽样假设的 pass@k 估计量混写。

**同一批试次，三种成功率**

同一个 4×3 矩阵可以给出不同指标；它们分别衡量平均表现、可找到成功与重复可靠性。


## 试次、分布与分组结果

### 一次成功与稳定成功不是同一个指标

下面四个任务各跑三次，1 表示达到完整验收，0 表示没有达到。分母包括全部预先安排的有效试次。

| 任务 | 第一次 | 第二次 | 第三次 | 三次中至少成功一次 | 三次全部成功 |
|---|---:|---:|---:|---|---|
| A | 1 | 0 | 1 | 是 | 否 |
| B | 0 | 0 | 0 | 否 | 否 |
| C | 1 | 1 | 1 | 是 | 是 |
| D | 0 | 1 | 0 | 是 | 否 |

按试次算成功率是 6/12=50%；至少成功一次的任务占 3/4=75%；三次都成功的任务占 1/4=25%。三个数字都有效，但回答不同问题。不能用“尝试三次至少成功一次”冒充单次服务成功率；生产端也未必能识别并选中那次正确输出。

```python
runs = {"A": [1, 0, 1], "B": [0, 0, 0],
        "C": [1, 1, 1], "D": [0, 1, 0]}
trial_rate = sum(map(sum, runs.values())) / sum(map(len, runs.values()))
any_rate = sum(any(row) for row in runs.values()) / len(runs)
all_rate = sum(all(row) for row in runs.values()) / len(runs)
assert (trial_rate, any_rate, all_rate) == (0.5, 0.75, 0.25)
print(trial_rate, any_rate, all_rate)
```

更一般的 pass@k 估计需要说明抽样方式和定义。这里直接报告这三次已观测试次的统计，没有假设各次错误独立，也没有估计无限重试后的成功概率。

### 平均数可能藏住关键退化

假设 90 个简单任务成功 81 个，10 个复杂任务成功 2 个，总体为 83%。但复杂任务只有 20%，如果用户主要在复杂任务上需要系统，这个总体数字就很容易误导。

按任务类型、长度、语言、工具权限、数据新旧和失败类别分组；比较两个版本时保留逐题配对结果。任务集过小或随机波动明显时，报告样本量与不确定性，不把一两个百分点的变化直接当成确定改善。

基础设施异常也要明确归类。超时、工具服务不可用、评估环境坏掉和模型产生错误答案可以分开统计，但不要静默删掉失败后只汇报剩下的高分。若预先约定某类试次无效，应记录排除规则与数量；用户体验指标仍需要考虑线上真实服务故障。

## Trace、指标与结果关联

Trace 描述一次运行的关联路径，span 表示有起止时间的操作，并可带属性、事件和父子关系。异步或多 Agent 场景还可能通过链接关联操作。它不要求每个文本 token 或每个 thinking 块单独建 span；粒度要服务于诊断。[OpenTelemetry Traces](https://opentelemetry.io/docs/concepts/signals/traces/)

为一次任务建立稳定的 task_id，为试次、模型请求、工具调用和产物分配可关联标识。收到并行或批量结果时按 ID 匹配，不能靠数组返回顺序推测属于谁。

| 层级 | 建议记录 | 用途 |
|---|---|---|
| 任务/试次 | 输入版本、配置版本、最终状态、评分 | 比较完整结果 |
| 模型请求 | 模型标识、耗时、usage、停止原因 | 分析生成、截断和成本 |
| 工具操作 | 名称、脱敏参数、调用 ID、执行状态 | 区分提议与实际动作 |
| 产物 | 路径或对象 ID、内容版本、验证结果 | 确认测试针对哪份内容 |
| 调度 | 排队、并发、重试与取消 | 分析协调和等待 |

### 并行耗时不能直接求和

<svg viewBox="0 0 760 360" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="示例时间线：并行工具的耗时不能简单相加" style="max-width:100%;height:auto" font-family="Source Han Sans CN,Microsoft YaHei,sans-serif">
<defs><marker id="trace-timeline-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#64748b"></path></marker></defs>
<rect width="760" height="360" rx="12" fill="#f8fafc"></rect>


<g transform="translate(0 0)"><text x="28" y="101" font-size="14" fill="#334155" text-anchor="start" font-weight="400">任务总时长</text><rect x="175" y="75" width="468" height="38" rx="8" fill="#e2e8f0" stroke="#c7d2fe"></rect><text x="409.0" y="100" font-size="13" fill="#334155" text-anchor="middle" font-weight="400">0–12 秒</text><text x="28" y="156" font-size="14" fill="#334155" text-anchor="start" font-weight="400">模型请求 1</text><rect x="175" y="130" width="78" height="38" rx="8" fill="#a5b4fc" stroke="#c7d2fe"></rect><text x="214.0" y="155" font-size="13" fill="#334155" text-anchor="middle" font-weight="400">0–2 秒</text><text x="28" y="211" font-size="14" fill="#334155" text-anchor="start" font-weight="400">工具 A / B</text><rect x="253" y="185" width="195" height="38" rx="8" fill="#6ee7b7" stroke="#c7d2fe"></rect><text x="350.5" y="210" font-size="13" fill="#334155" text-anchor="middle" font-weight="400">2–7 秒</text><text x="28" y="266" font-size="14" fill="#334155" text-anchor="start" font-weight="400">模型请求 2</text><rect x="448" y="240" width="195" height="38" rx="8" fill="#a5b4fc" stroke="#c7d2fe"></rect><text x="545.5" y="265" font-size="13" fill="#334155" text-anchor="middle" font-weight="400">7–12 秒</text></g><text x="24" y="29" font-size="19" fill="#0f172a" text-anchor="start" font-weight="700"><tspan x="24" dy="0">示例时间线：并行工具的耗时不能简单相加</tspan></text><text x="24" y="311" font-size="13" fill="#475569" text-anchor="start" font-weight="400"><tspan x="24" dy="0">工具 A 用 3 秒、B 用 5 秒且同时开始，工具阶段为 5 秒；总时长为 12 秒。</tspan></text>
</svg>

例子中模型请求分别为 2 秒和 5 秒；工具 A、B 同时开始，分别用 3 秒、5 秒。把所有操作耗时相加是 15 秒，用户等待却是 12 秒。Trace 要保留开始与结束时间及依赖关系，才能找到关键路径；只有计数器无法解释并行重叠。

首个网络事件、首个可见文本和最终完成是不同延迟指标。服务开始流式发送并不意味着用户已经看到有用内容；反过来，首字很快但后续工具反复失败，也不代表任务体验好。

### 协议成功、工具成功和任务成功分开计数

HTTP 200 可以携带业务失败，工具正常返回可以报告“未找到”，模型正常结束也可能遗漏任务。分别记录传输、执行、任务状态，不要让一层成功掩盖另一层失败。

停止原因是诊断线索，不能自动归因。`max_tokens` 增多需要检查输出上限、任务长度或冗长循环，流式不会解除上限；缓存读取长期为零也可能只是没有重复前缀、未满足长度或未启用缓存，不一定存在故障。应先对照实际输入与目标模型配置。[停止原因](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons)、[Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)

成本按供应商与模型的 usage 口径计算。输入、缓存写入/读取、输出和推理细分是否包含关系需要核对，不能把已包含在输出里的推理 token 再加一次。最后汇总所有请求、重试和工具费用，再计算每次成功分摊成本，方法见 [推理与 thinking](/ai/01-model-foundations/04-reasoning-and-verification.md)。

### 总分上涨，是能力变强了吗？

均值需要与任务分布一起解释。这个反例甚至不需要某个系统退化：两组能力完全不变，只把容易任务放得更多，就能显著提高总分。上线流量与离线集合也可能有这种组成差异。

$$
p_{\mathrm{overall}}=w\,p_{\mathrm{easy}}+(1-w)\,p_{\mathrm{hard}}
$$

**总分上涨，是能力变强了吗？**

汇总成功率是任务组成的加权平均；样本分布改变也会提高总分，不能据此宣称各类能力改进。


## 从失败样例到回归验证

一次线上失败应先固定最小可复现输入、环境与预期结果，再沿 trace 定位第一个失去必要信息或违反条件的位置。若检索没召回例外条款，先修证据选择；若返回数据正确却映射错字段，修适配器；若产物已经修改但沿用旧测试记录，修版本关联。不要把所有失败都变成“再写一句更强的 system prompt”。

回归样例应保留实际失败机制，并加入正常对照，避免补丁只让旧用例过关却破坏其他行为。比较新版时固定可固定的条件，记录其余变化；通过离线检查后，再通过受控线上观察检验流量分布差异。

保存 trace 也需要数据边界：默认记录诊断所需元数据，敏感正文和凭据应脱敏或采用受控存储与保留期限。不要为可观测性把所有工具结果、完整文件和用户资料无差别复制到日志后端。看得见执行路径，并不要求无限保存全部内容。

继续阅读：[成本、容量与可靠性](/ai/04-evaluation-and-production/02-cost-performance-and-reliability)。
