Agent 评测

专栏:Agent 工程 · 第 13 / 18 篇
Agent评测Evals

阶段六从”能跑”走向”可靠”。第一课不是可观测性也不是多 Agent,而是评测——没有评测,改提示词、换模型、调工具描述全凭感觉,你无法回答”这次改动是变好还是变坏”。

:::info 学习目标 完成本篇后你能够:搭建一个带轨迹记录的最小评测框架;区分三层评测各自的适用场景;从会话日志里持续供给评测集。 前置:第 4~12 篇的 mini-agent 可用。预计时长:60 分钟。 :::

:::note 本章术语速查(新手建议先读)

  • Eval(评测):给 agent 出一套固定考题,自动判分——没有评测,改提示词就是”凭感觉”。
  • 回归测试(Regression):改动后重跑全部考题,确保没把原来会的改不会。
  • LLM-as-judge:主观题没法写死判分规则,就让(另一个更强的)模型按评分标准打分。
  • 轨迹(Trajectory):agent 的完整解题过程记录(每步调了什么工具)——评测和排障都靠它。 :::

评测的三层

第一层:单步评测(最便宜,先做)。 固定输入(用户消息 + 场景),断言单步行为:该调哪个工具?参数对不对?这层不需要跑完整任务,速度快、定位准。20 条固定 case 就能覆盖你 agent 的主要能力面。

第二层:端到端评测(任务级)。 给完整任务,断言最终产物:测试通过了吗?文件改对了吗?答案包含要求的要点吗?这层贵(每条跑一次完整 agent),但测的是用户真正关心的东西。

第三层:LLM-as-judge(无法程序化断言时)。 “总结得好不好”这类主观判断,让一个(更强的)模型按评分标准打分。要点:评分标准写成明确的 rubric、给 judge 少量人工标注的锚点样例、定期抽查 judge 与人工判断的一致性。

你的评测集从哪来:会话日志就是金矿

这是两个解剖对象的日志设计在此刻的回报——线上真实失败案例就是评测集的持续供给

  • Codex 的 rollout JSONL 完整记录每轮的输入、工具调用、TokenUsageRecord、压缩事件——用户中断(Esc)的那些轮次,就是”模型走错路”的真实样本;codex exec 的无头形态更是天生适合批量评测(退出码 0/1 即任务成败信号);
  • dsh 的 session 日志带 turn/end 的原因枚举(completed/blocked/error/max-tokens),加上 session-query 的全文检索——筛出所有 error 轮次就是现成的改进清单。

做法:每周从日志里挑 5~10 个失败/被中断的 case,脱敏后进回归集。评测集只增不减,任何改动(提示词、模型、工具描述)跑一遍全量,命中率不许回退。

评测的闭环

图表(agent-evals.md)

第二层半:轨迹评测

只断言最终结果会漏掉一类问题:结果对但过程危险(删了整个目录重建 vs 精确修复)。轨迹评测断言中间过程:

  • 工具调用序列符合预期模式(先探索后修改);
  • 禁用的工具从未被调用;
  • 步数 / token 消耗在预算内;
  • 失败后没有重复同一失败调用超过 N 次。

实现上就是把第 4 篇的循环加一个”轨迹收集器”(每步 append 工具名+参数摘要),评测时对轨迹做模式断言。两个解剖对象天然支持这件事——dsh 的 session 事件流与 Codex 的 rollout JSONL 就是完整轨迹(第 13 篇的日志设计在这里闭环)。

一个最小评测框架

一个最小评测框架

50 行内搭起来:

CASES = [  # (任务, 判定函数)
    ("统计 src 下的 Python 行数",
     lambda r: "总行数" in r and any(c.isdigit() for c in r)),
    ("README.md 里提到 license 吗",
     lambda r: ("提到" in r and "MIT" in r) or "没有提到" in r),  # 两种正确答案都算
    # ……从日志里持续补充
]

def run_eval(agent_run, cases=CASES):
    passed = 0
    for task, judge in cases:
        try:
            result = agent_run(task)
            ok = judge(result)
        except Exception as e:
            result, ok = f"Error: {e}", False
        passed += ok
        print(("PASS" if ok else "FAIL"), task[:40], "|", result[:60])
    print(f"\n{passed}/{len(cases)} passed ({passed/len(cases):.0%})")
    return passed / len(cases)

要点:固定温度(低且一致)、每个 case 独立干净的工作目录记录每条的完整轨迹(不只最终答案——轨迹能告诉你它绕了多少弯)、判定函数宁可粗糙也要先有。

与 CI 集成

评测的最终归宿是每次改动自动运行:

# .github/workflows/eval.yml(示意)
on: [pull_request]
jobs:
  eval:
    runs-on: self-hosted        # 需要 API key 与网络
    steps:
      - run: python eval.py --baseline 0.7 --min 0.7
        # 命中率低于基线即 fail,评论到 PR

--min 参数让”命中率不许回退”成为机器强制的门禁,而不是口头约定。

JUDGE_PROMPT = """你是评审。根据以下标准对回答打分(1-5):
- 是否完成了任务要求
- 是否有多余/有害操作
- 表述是否清晰
任务:{task}
回答:{answer}
只输出 JSON:{{"score": n, "reason": "一句话"}}"""

4 分以上算过。同一 case 跑 3 次取多数(agent 有随机性,单次判定噪音大)。

成本也是被评测对象

评测不只测对错。每次跑评测顺手记录:步数、token 用量、耗时——max-tokens 结局的 case 要特别标记(预算设计缺陷的信号,第 6 篇的 TokenUsageRecord 思路直接搬过来)。一个”更准但贵三倍”的改动是好是坏,只有数字能回答。

随堂练习(带验收标准)

  1. 基线:从 mini-agent 的真实使用里挑 10 个任务建成 CASES,跑出基线命中率。验收:一条命令可重复执行、输出单一数字;
  2. A/B:把第 5 篇的工具描述改动拿来做对照实验。验收:两组命中率有数字,且能说出差异是否在噪音范围内(每组跑 3 次取多数);
  3. 轨迹:记录每步的工具调用序列存 JSON,挑一个失败 case 的轨迹,用三句话说出它从哪一步开始走偏——你会开始”看轨迹诊断 agent”,这是评测带来的最大能力跃迁。

← 返回文章列表