Skip to content

Agent 评测与可观测

单轮 LLM 应用评「回答对不对」,Agent 要评一整条轨迹:想了什么、调了什么工具、结果怎么用、最终产物成不成、花了多少钱。这是 Agent 上线最难也最必需的一环——没有它,「偶尔很聪明、经常很抽风」的 Agent 无法迭代。本页给出轨迹级评测方法论(评测集/验证器/judge)、可观测设计(trace/指标/失败分析)与「评测-可观测-门禁」闭环;评测的技术底座与单步概念见 RAG 与 AgentPrompt 工程AI 工程化

一、为什么 Agent 的评测是「新问题」

维度单轮问答评测Agent 评测
评测对象一段回答文本轨迹(推理 + 工具调用序列 + 外部副作用) + 终态产物
确定性低随机性采样随机 + 工具时序随机,同任务两次跑结果可能不同
答案空间可预设金标任务开放,无法穷举「正确答案」
状态副作用无(纯生成)会写库/发信/改文件,不可随意重放
关键指标正确性正确性之外还有:步数效率、成本、安全合规
  • 结论:Agent 评测 = 结果评测(终态对不对)+ 过程评测(轨迹好不好)+ 副作用评测(环境被改对了吗),三者缺一不可。

二、评测指标的分层

结果层:任务到底成了没有

指标含义备注
任务成功率 / pass@1一次运行完成且产物通过验证最基本指标
pass@kk 次独立运行至少成功一次衡量「上限能力」,与成本相关
部分成功(partial)完成部分子步骤需要子任务拆解定义
产物验证通过率终态产物被程序化检查判定正确优先用验证器,别听模型自评

过程层:轨迹质量(决定可维护性)

  • 工具选择正确率:该调 A 时有没有调 A;
  • 无效/冗余调用率:幻觉工具名、重复调同一工具、无用探测;
  • 步数效率:达到同等结果的平均步数(越少越好,直接关联成本);
  • 计划合理性:计划与实际执行的偏差、是否频繁返工(再规划次数);
  • 约束违反率:是否做了任务明确禁止的动作(如「只读任务」里去改了数据)。

安全与健壮层

  • 注入/越狱逃逸率(工具描述被恶意内容污染时是否会越权,方法见 Prompt 工程 安全节);
  • 失败恢复率:工具报错后能否正确换路,而不是原地重试到耗尽;
  • 越权动作拦截率:权限墙外动作是否都被拦下(见 AI 工程化 权限墙)。

效率层

  • 每任务成本(token 用量)、每任务耗时、预算达成率——Agent 的 token 账单是单轮的 N 倍,成本必须进评测指标(计量方法见 AI 工程化 成本节)。

三、评测集怎么搭:让结果「可自动判定」

一句话原则:能用确定性验证器判定的,不要用 LLM judge

  • 第一步,把任务模板化:
text
任务模板:给定仓库 {repo} 与需求 {req},产出满足 {tests} 的补丁
实例化 1:repo=mini-calc, req=支持负数相加, tests=[calculator_test.py]
实例化 2:repo=mini-calc, req=输出改为 JSON,  tests=[calculator_test.py]

模板 + 参数实例化 → 一套模板生成几十个任务,保覆盖且可扩展;

  • 第二步,为每类任务定义验证器(verifier),验证器优先检查「环境被改成了什么」而非「模型说了什么」:
任务类型验证器示例(断言对象是产物/环境)
代码修改跑测试套件通过
数据操作数据库事务回滚后检查目标行数据变化
发消息/通知注入 mock 邮件/消息网关,断言发送对象与内容
写文件断言输出文件存在、内容满足规则
  • 第三步,状态重置与沙箱(可重复的前提):
    • 每个任务用干净的临时环境:数据库事务包裹回滚、网络 API 打桩、临时目录隔离;
    • 同一任务跑 N 次(测 pass@k / 测稳定性)时必须每次从同一起点开始;
  • 第四步,准备参考轨迹(golden trajectory):人工或人工修正的「最优路径」,用于过程评测与对 judge 的校准;
  • 第五步,分集管理:冒烟集(十几个,每次改动秒级回归)→ 回归集(覆盖主要能力,发布门禁用)→ 扩展集(持续从线上失败案例回流,见第七节)。

四、LLM-as-judge:只有没法规则化时才用

  • 适用的地方:开放式任务的结果质量(方案好坏)、轨迹的过程评价(步骤是否合理)、人类偏好对齐;
  • 关键:judge 只看「判定所需的证据」——给完整轨迹会太长且引入噪声;先抽取证据(终态产物 + 关键工具调用 + 成本/步数)再判;
text
# 结果级 judge prompt 骨架
任务:{task}
参考验收标准:{rubric 里的可判定条件}
Agent 最终产物:{product}
请逐条按 rubric 给出 通过/不通过 与一句话理由。
注意:只依据给出的产物文本判断,不要脑补产物没有的内容。

# 过程级 judge prompt 骨架(可选)
轨迹摘要:{agent 的关键动作序列,已脱敏}
判定维度:工具选择是否合理 / 是否有效推进 / 是否违反约束
输出:每条维度 pass/fail + 证据行号
  • Rubric 化:先写「什么算对」的可判定条件(如:包含编号引用、金额格式正确、调用了且只调用了该调的工具),避免 judge 凭感觉;
  • 双判与校准:两个不同 prompt/模型判不一致时人工复核;定期用已知样本测 judge 自身的稳定性;
  • judge 陷阱:偏好更长更花哨的输出、对自我生成轨迹有偏、容忍模型「复述正确结论但过程瞎编」。

4.1 可运行的 judge 示例(deepseek-chat, 2026-09-04)

把上面的 prompt 骨架落成可运行脚本并实测:4 个构造样例(1 合格 / 3 各有缺陷),judge 对 rubric 逐条判 pass/fail 并给一句话理由,与人工期望 4/4 一致。执行: export DEEPSEEK_API_KEY=sk-... && python3 本文件

python
# LLM-as-judge 最小实现 —— 真实可跑版(DeepSeek deepseek-chat)
# 依赖: python3 + requests; 环境变量 DEEPSEEK_API_KEY
import os, json, requests
API = "https://api.deepseek.com/chat/completions"
MODEL = os.environ.get("DEEPSEEK_MODEL", "deepseek-chat")

TASK = "给客户写一封邮件, 解释上月账单多收 58 元是系统汇率四舍五入所致, 并为造成困扰致歉。"
RUBRIC = [
    "解释了多收 58 元的具体原因(汇率/四舍五入)",
    "对造成的困扰表达了道歉",
    "给出了明确的下一步行动(如自动抵扣/退款/联系支持)",
    "金额一致(58 元), 未出现与事实矛盾的数字",
]

def judge(product):
    content = ("你是质检评测员。任务:\n" + TASK +
               "\n验收标准(rubric, 请逐条判定 通过/不通过):\n" + json.dumps(RUBRIC, ensure_ascii=False) +
               "\nAgent 最终产物:\n" + product +
               "\n只依据上面产物文本判断, 不要脑补产物没有的内容。输出 JSON(不要其它文字):\n"
               '{"items":[{"no":1,"pass":true,"reason":"一句话理由"},...],"verdict":"pass"}'
               ' 其中 items 与 rubric 顺序一一对应, verdict 为 "pass"(全部通过)或 "fail"(任一不通过)。')
    r = requests.post(API,
                      headers={"Authorization": "Bearer " + os.environ["DEEPSEEK_API_KEY"]},
                      json={"model": MODEL,
                            "messages": [{"role": "user", "content": content}],
                            "response_format": {"type": "json_object"}, "stream": False}, timeout=60)
    r.raise_for_status()
    body = r.json()
    return json.loads(body["choices"][0]["message"]["content"]), body["usage"]["total_tokens"]

# (name, 产物, 人工期望): 三个缺陷样例各自只漏/错一个要素
CASES = [
    ("合格样例", "尊敬的客户:经核查,多收的58元系汇率四舍五入所致。给您带来困扰深表歉意,该款3个工作日内自动退回,如有疑问拨打400-000。", "pass"),
    ("未解释原因", "尊敬的客户:非常抱歉给您带来困扰,我们已记录您的反馈并会尽快处理,感谢理解。", "fail"),
    ("无致歉无下一步", "经查,系统在汇率换算环节存在四舍五入误差,导致本次账单金额偏高。", "fail"),
    ("金额写错", "您好!关于多收的50元,是系统汇率四舍五入造成的,为此致歉,下月账单自动抵扣。", "fail"),
]

if __name__ == "__main__":
    assert os.environ.get("DEEPSEEK_API_KEY"), "请先 export DEEPSEEK_API_KEY=sk-..."
    agree = 0
    for name, product, expected in CASES:
        j, toks = judge(product)
        v = j.get("verdict", "?")
        # 容错: json_object 只保证合法 JSON, 字段名可能换词(no→?, reason→explanation...)
        rows = []
        for it in j.get("items", j.get("rubric", [])):
            ok = it.get("pass", it.get("passed", it.get("result") == "pass"))
            rows.append(("✓" if ok else "✗") + " " + str(it.get("reason", it.get("explanation", ""))))
        agree += (v == expected)
        print(f"== {name} | judge={v} 期望={expected} {'一致' if v == expected else '不一致'} | tokens={toks}")
        for r in rows:
            print("   ", r)
    print(f"一致性: {agree}/{len(CASES)}")

# ---- 实测输出(2026-09-04, deepseek-chat) ----
# 合格样例  judge=pass  期望=pass   (四条 rubric 全 ✓, tokens≈384)
# 未解释原因 judge=fail  期望=fail   (✗未解释原因; ✓道歉; ✗无下一步行动)
# 无致歉无下一步 judge=fail 期望=fail (✓解释; ✗无道歉; ✗无下一步)
# 金额写错   judge=fail  期望=fail   (✗写 50 元而非 58 元 → 金额矛盾)
# 一致性: 4/4
  • 实测观察:①json 模式只保证「合法 JSON」不保证字段命名,judge 的解析必须容错,别假设 no/reason/verdict 一定原样返回;②rubric 里把「金额写错」这种机器可判的硬条件放进判定清单很有效——它等价于给 judge 一把「事实核查尺」,比让 judge 凭感觉打质量分稳得多;③正式使用时仍需双判 + 已知样本校准(见上文),单次样例的 4/4 只能说明构造的用例区分度高,不能当作 judge 稳定性的结论。

五、面向 Agent 的基准与工具(了解现状即可)

  • 用公开基准看自己 Agent 的「段位」,但别刷榜当 KPI——基准环境 ≠ 你的生产环境:
基准评测什么你的 Agent 是否适用
GAIA通用助手任务的难度分级通用任务粗定位
AgentBench多环境(网页/代码/游戏等)通用智能体跨域粗定位
tau-bench(τ-bench)真实业务场景的工具操作(客服/零售)贴近工具型 Agent,可借鉴其「用户模拟器」设计
SWE-bench真实 GitHub issue → 代码补丁代码 Agent 必看
WebArena / Mind2Web网页操作与浏览浏览器 Agent 参考

基准榜单排名会持续变动,选基准前先查其最新维护状态与提交方式;最重要的是借鉴它们的评测设计(任务构造、验证器、重置策略),而不是照搬榜单。

  • 评测/追踪工具的形态:市面上有轨迹追踪 + 评测集成平台(如 LangSmith、Langfuse 等),概念上一律等价于「记录 trace + 跑 verifier/judge + 出报表」——先用 OpenTelemetry GenAI 语义约定 打底避免被平台锁定。

六、可观测:让「看不见的轨迹」可见

6.1 只记「一层调用」是不够的

  • 单步日志能看出「这步调了哪个模型」,看不出「这次任务为什么绕了 30 步」;
  • Agent 必须有任务级 trace:从任务开始到结束,串起规划、每一步 LLM 决策、每一次工具调用的因果关系与时间线。

6.2 事件模型与 span 设计

Span 类型记录什么关键字段
task span一次任务全生命周期task_id、目标、终止原因、耗时、成本
plan span计划生成/再规划计划内容、再规划次数、触发原因
llm span每次模型决策模型、输入摘要、输出、token 用量
tool span每次工具调用工具名、参数摘要与结果摘要(截断),成功/失败、耗时
  • 记录策略:
    • 脱敏先行:prompt、工具参数/结果可能含 PII——记录前按字段脱敏 + 截断,详情只留摘要(见 AI 工程化 日志策略);
    • 因果关系:用 span 父子关系把「这次思考 → 那次调用 → 结果回填」串成树,这是复盘的最小单元;
    • 成本累计:在 task span 上聚合 token 与金额,一眼看出「哪个任务烧钱」。

6.3 指标与日志

  • 聚合指标:成功率、平均步数与步数分布、工具错误率 Top N、成本/任务、超时率、预算耗尽率(终止原因分布);
  • 关键事件日志:任务起点与目标、终止原因(validated / validation_failed / step_budget_exhausted / tool_error_max / human_abort / refused)、产物哈希——「终止原因分布」是 Agent 健康度的第一仪表盘。

6.4 失败分析工作流

  1. 从线上 trace 捞失败样本,按失败类型聚类(超步数 / 验证不过 / 工具错 / 越权拦截);
  2. 对代表性样本重放 trace,定位断点:是目标理解错、工具没选对、结果没用好、还是验证器太严;
  3. 把典型失败样本回流到评测集(第七节),修一次防一类;
  4. 定期人工抽样复核 trace,防「指标在涨、体验没涨」——评测与真实用户感受的偏差只能靠抽查校准。

七、评测-可观测-门禁:上线闭环

开发期:冒烟集/回归集 + verifier/judge
        ↓ 通过
发布期:canary 小流量 → 对比成功率/成本/终止原因 → 逐步放量
        ↓ 持续
运行期:线上 trace + 指标监控 → 失败聚类
        ↓ 回流
        失败样本进回归集 → 下次改动必须先过它

模型升级 / prompt 变更 / 新增工具 / 框架升级 —— 全部重跑回归集,过不了不让上线
  • 门禁要覆盖的四类改动:换模型、改系统提示/工具描述、加删工具、升框架——任何一类都可能静默改变轨迹行为;
  • 线上漂移:任务分布会变,定期用线上真实样本重标注扩充评测集(见 AI 工程化 评测门禁);
  • 成本护栏与门禁联动:每任务成本预算当二级门禁——质量过了但烧钱翻倍的改动一样要打回。

八、踩坑清单

  1. 只评终态不评过程:结果对但乱调 50 个工具的 Agent,上线后维护与账单都会爆;
  2. 验证器缺位,全上 judge:没有确定性断言,评测结果跟模型抽风一样随机;
  3. 任务不可重放:评测时污染了共享状态,同任务两次结果没法比;
  4. 判「完成」听模型自评:Agent 说完成就记成功——必须有产物级验证;
  5. judge 不给证据只丢全轨迹:判不准又费 token;先抽证据、写 rubric;
  6. 没有冒烟集:每次改 prompt 全量跑回归,迭代慢到放弃评测;
  7. 可观测只到单步:出了问题无法重放整条轨迹,等于没有日志;
  8. trace 裸存敏感数据:工具参数/用户文本直接落盘,合规事故;
  9. 终止原因不统计:只报成功率,不知道失败是「预算耗尽」还是「验证不过」,无从下手;
  10. 失败样本不回流:同一个坑反复踩,评测集和线上真实分布渐行渐远;
  11. 拿基准榜单当上线标准:SWE-bench 高分不等于你的业务任务好——评测集永远要贴合自己的任务分布。

关联阅读:Agent 怎么设计才能被评测、被救回,见 Agent 架构通论;单轮答案评测与 LLM-as-judge 基础见 Prompt 工程;评测门禁与可观测基建(OTel GenAI、成本计量、权限审计)见 AI 工程化;结果用 RLHF/DPO 等反馈信号进模型本身的路线见 LLM 原理 对齐节与 模型微调

基于 VitePress 构建 · 内容以知识共享方式沉淀