当评测器比模型更不靠谱:Agent 评测管道的 18.5% 幻觉率与工程自救指南

最新研究发现 Agent 工具调用评测的误对齐率高达 18.5%,同一配置重复跑分差 18.9 个百分点。本文拆解评测器的五种失灵模式,给出从污染检测、可复现管道到分解式评测的三层工程自救方案。

工程实践 已修订 Agent评测 评测管道 Benchmark可靠性 工具调用 污染检测 工程实践

一、你看到的排行榜,有 1/5 是评测器的幻觉

2026 年 6 月 30 日,一篇来自 Bhat、Vaghasiya、Mohsin 和 Aali 的论文(arXiv:2607.02577)给 Agent 评测领域投下了一枚”元评测炸弹”。研究团队对 BFCL v4、τ2-Bench、LiveMCPBench 和 MCP-Atlas 四大主流工具调用评测基准进行了 496 个任务的人工复核审计,结论令人不安:

18.5% 的评测分数与人类专家判断不一致。

这不是个例,不是某个特定评测的 bug。这是当前 Agent 评测基础设施的系统性脆弱。

更令人震惊的是 LiveMCPBench 的发现:相同的 Agent + 相同的任务 + 相同的配置,重复运行 23 次,分数从 57.9% 波动到 76.8%——18.9 个百分点的差距,足以改变整个排行榜的排名结论。

想象一下这个场景:你花了两周时间调优 Agent,模型从 v1 换到 v2,benchmark 分数涨了 10 个百分点。你准备写周报了——但你可能不知道,这 10 个百分点的提升里,有一大半根本不是模型变强了,而是评测器那天心情好。

二、评测器失灵的五种模式

这篇论文的价值,不仅在于发现了问题,更在于它给出了系统性的故障分类学。评测器的失灵不是随机的——它有清晰可分类的模式。

确定性评测器的三种”硬伤”

1. 脆弱状态匹配(Brittle State Matching)

确定性评测器依赖于对 Agent 执行后系统状态的精确比对。问题在于,出题人定义”正确状态”时往往漏掉了合法但不同的实现路径。Agent 用一个稍有不同的文件名存储了结果——功能完全 OK,但字符串匹配失败,判负。

// 评测器预期的输出文件名
expected_output: "report_2026-07-23.csv"

// Agent 实际输出
actual_output: "report_20260723.csv"

// 判决:FAIL(功能正确,但命名格式不同)

2. 轨迹锁定(Trajectory Lock-in)

评测器预先定义了”正确”的工具调用序列。Agent 分两步调用 API 完成了任务(第一步查参数、第二步创建资源),而标注者预期的是三步(查参数、验证、创建)。缺少”验证”步骤导致整个任务被判失败——但两步方案在实际生产中更快且无误。

3. 基础事实错误(Incorrect Ground Truths)

连出题人自己也会把标注搞错。论文中发现多起 ground truth 本身无法完成任务的情况——Agent 不可能通过,因为”正确答案”本身就是错的。

LLM-Judge 评测器的两种”软偏”

4. 评分标准漂移(Rubric Drift)

LLM 做 Judge 时,对同一评分标准的理解会在不同运行时漂移。上次跑认为”基本可用”给 70 分,这次同样的输出给 55 分——因为 Judge 模型先看到了一个更好的输出做对比。

5. 幻觉补全(Hallucinated Completion)

LLM-Judge 在判断任务是否完成时,会”脑补”Agent 没有执行但应该已经完成的步骤。Agent 实际上只做了三件事,Judge 报告里写了五件——后两件是 Judge 自己合理推断出来的。这不是恶意,但结果完全不可靠。

三、工程自救方案一:污染检测前置

在相信任何公开评测分数之前,首先要确认一件事:模型有没有见过考题。

CoDeC:给模型做个”测谎”

CoDeC(Contamination Detection via Context,ICLR 2026)是目前最实用的检测方法。它的核心思想很简单:如果模型真的理解了一个题目,给它多一点上下文(比如一个相似的示例),它的置信度应该基本不变。但如果模型是背答案的,上下文会干扰它的记忆提取——置信度下降。

# CoDeC 的核心思路:对比有无上下文时的 token 概率
import numpy as np
from vllm import LLM, SamplingParams

def codec_score(model, benchmark_items, context_examples):
    """
    返回 0-100 的污染嫌疑度。
    > 80:高度可疑(可能被污染)
    60-80:灰色地带
    < 60:大概率干净
    """
    llm = LLM(model=model, dtype="bfloat16", max_model_len=4096)
    params = SamplingParams(max_tokens=1, prompt_logprobs=1)

    baseline = []
    with_context = []

    for item in benchmark_items:
        # 基线:只给题目
        b_out = llm.generate([item["prompt"]], params)
        baseline.append(_extract_confidence(b_out))

        # 加一个上下文示例再问
        ctx_prompt = context_examples[item["topic"]] + "\n" + item["prompt"]
        c_out = llm.generate([ctx_prompt], params)
        with_context.append(_extract_confidence(c_out))

    # 上下文导致置信度下降的比例
    drops = sum(1 for b, c in zip(baseline, with_context) if c < b * 0.9)
    return (drops / len(benchmark_items)) * 100

def _extract_confidence(outputs):
    """从 vLLM prompt_logprobs 提取平均置信度"""
    logprobs = outputs[0].prompt_logprobs
    # 跳过前 10 个 token(噪声大),取剩余的平均
    return np.exp(np.mean([lp[list(lp.keys())[0]].logprob
                           for lp in logprobs[10:] if lp]))

重要警告:CoDeC 对经过了 RL 后训练的推理模型可能失效。ASTRAL Group(UIUC + UW,Han Wang, Haoyu Li, Brian Ko, Huan Zhang)在 ICLR 2026 的研究(arXiv:2510.02386)表明,GRPO 强化学习可以”抹去”污染的检测信号——同一个模型在 RL 前 CoDeC 得分 85%(染污),RL 训练 64 步后降到 55%(看起来干净)。不要因为 CoDeC 没报警就放心。

CCV:让模型重做五遍

Cross-Context Verification(CCV)是污染检测的另一条路径:让模型在温度 0 下独立重复 5 次相同任务,检查解的多样性。

# 5 次独立运行的结果对比
Run 1: def solve(grid): return bfs(grid, (0,0), (n-1,m-1))
Run 2: def solve(grid): return bfs(grid, (0,0), (n-1,m-1))
Run 3: def solve(grid): return bfs(grid, (0,0), (n-1,m-1))
Run 4: def solve(grid): return bfs(grid, (0,0), (n-1,m-1))
Run 5: def solve(grid): return bfs(grid, (0,0), (n-1,m-1))

# AST 相似度: 99.8%(五份复制粘贴 → 极度可疑)

真实理解问题的模型应该会产生变量命名、实现路径上的自然变化。五份一模一样的输出是记忆复现,不是推理。

四、工程自救方案二:让评测可以复现

Tool-Veritas 论文提出的 Harness Lab 概念,给出了评测可复现性的工程解法。核心思想很简单——评测本身应该是一个可审计、可重放、可对比的系统,而不是一个”跑完出分就完事”的黑箱。

Harness Lab 的设计原则

┌─────────────────────────────────────────────────┐
│                  Harness Lab                      │
│                                                   │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │
│  │ 任务定义  │→│ 执行引擎  │→│ 评测器(多选) │  │
│  │ (YAML)   │  │ (Docker) │  │ - 确定性检查   │  │
│  │          │  │          │  │ - LLM Judge    │  │
│  │          │  │          │  │ - 人工复核标记  │  │
│  └──────────┘  └──────────┘  └───────────────┘  │
│                      ↓                ↓          │
│               ┌──────────────────────────┐       │
│               │  Trace Store(完整审计)   │       │
│               │  - 每个 step 的输入/输出   │       │
│               │  - 工具调用原始日志        │       │
│               │  - 评测器判断 + 置信度     │       │
│               │  - 运行环境 hash          │       │
│               └──────────────────────────┘       │
└─────────────────────────────────────────────────┘

最小可执行的评测管道

# eval-pipeline.yaml — 一个强调可复现性的评测定义
name: "agent-tool-calling-smoke"
version: "1.0.0"
environment:
  image: "eval-runner:v2.4"
  hash: "sha256:a1b2c3..."

tasks:
  - id: "mcp-file-read-001"
    description: "读取 /data/config.json 并解析 API endpoint"
    setup:
      docker: "mcp-testbed:file-ops"
      pre_state:
        files:
          - path: "/data/config.json"
            content_hash: "sha256:def456..."
    expected:
      # 分解式评估:分别检查工具调用、任务完成、结果验证
      tool_invocation:
        - tool: "read_file"
          args: { path: "/data/config.json" }
          required: true
      task_completion:
        evaluator: "deterministic"
        check:
          type: "json_contains_key"
          key: "api_endpoint"
          value_type: "string"
      outcome_verification:
        evaluator: "llm_judge"
        model: "claude-sonnet-4.5"
        prompt_template: "verify_config_read.jinja2"
        # 多次运行取均值,减少方差
        runs: 3

execution:
  repeats: 5               # 5 次重复运行
  timeout_per_task: 300    # 秒
  seed: 42                 # 固定种子——降方差
  capture_traces: true     # 保存完整执行链路

reporting:
  metrics:
    - "tool_invocation_accuracy"
    - "task_completion_rate"
    - "outcome_verification_agreement"
  output:
    format: ["json", "html"]
    include_trace: true

这里的关键设计选择是多次重复和分解式指标。单次运行的分数在大多数 Agent 评测中是不可靠的——5 次重复、取中位数、报告标准差应该成为标配。

评测管道的 CI 集成

# eval_ci.py — 在 CI 中加入评测可靠性校验
import json
import sys
from pathlib import Path

def check_eval_reliability(results_dir: str, threshold: float = 0.05):
    """
    校验评测结果的可靠性。
    如果评分方差超过阈值,CI 失败——评测本身不可信。
    """
    with open(Path(results_dir) / "summary.json") as f:
        data = json.load(f)

    for task_id, stats in data["tasks"].items():
        mean = stats["mean_score"]
        std = stats["std_score"]

        # 18.9pp 的方差是不可接受的——参考 Tool-Veritas 的发现
        if std > threshold * 100:
            print(f"❌ {task_id}: std={std:.1f}pp > {threshold*100:.1f}pp 阈值")
            print(f"   分数: {stats['scores']}")
            print(f"   该评测不可靠,CI 阻断。")
            sys.exit(1)

        # 额外检查:是否有 run-to-run 方向性差异
        if stats["max_score"] - stats["min_score"] > 15:
            print(f"⚠️  {task_id}: range={stats['max_score'] - stats['min_score']:.1f}pp")
            print(f"   评测方差过大,建议人工复核后再引用分数。")

    print("✅ 所有评测可靠性检查通过")

if __name__ == "__main__":
    check_eval_reliability(sys.argv[1])

把这段校验嵌入 CI 管道的一个直接效果是:你不会再被一个碰巧跑出高分的模型骗进生产环境。

五、工程自救方案三:分解式评测

Tool-Veritas 论文最核心的方法论贡献,是将单一的”通过/失败”评判决,拆分为三个独立可测的维度:

维度测的是什么评测方式典型陷阱
工具调用Agent 是否调用了正确的工具?参数是否正确?确定性匹配(工具名 + 参数签名)轨迹锁定——合法但不相同的调用序列被判错
任务完成Agent 是否达成了用户的最终目标?状态验证(环境状态检查点)状态匹配过于脆弱,忽略等效结果
结果验证输出是否满足定性要求?LLM Judge(有约束)+ 人工抽样评分标准漂移,单次运行不具代表性

拆分之后,你可以回答一些过去被单一大分数掩盖的关键问题:

  • 模型 A 的工具调用准确率 92%,但任务完成率只有 65%?→ 它每次都能调对工具,但是用错参数或调用顺序导致任务失败。
  • 模型 B 的任务完成率 80%,但结果验证只有 55%?→ 它能”形式上”完成任务,但输出质量达不到生产标准。

这类诊断,过去需要工程师手动翻 trace 才能发现。分解式指标让它自动化。

生产实践:评分模板

# 生产环境评测配置的关键片段
evaluators:
  tool_call:
    type: "deterministic"
    match_mode: "structural"     # 不强制精确顺序
    ignore_fields: ["timestamp", "request_id"]  # 忽略非确定性字段

  task_completion:
    type: "state_checkpoint"
    checks:
      - type: "file_exists"
        path: "/output/report.csv"
      - type: "file_contains"
        path: "/output/report.csv"
        pattern: "total_revenue"
      - type: "http_status"
        url: "http://api:8080/health"
        expected: 200

  outcome_quality:
    type: "llm_judge"
    model: "claude-sonnet-4.5"
    runs: 3                    # 跑三次取中位数
    rubric: |
      评估输出是否满足以下要求:
      1. CSV 文件包含表头
      2. 数值精度 ±1%
      3. 不包含调试信息或中间输出
      评分: 0-100
    constraints:
      - "如果 CSV 包含 Markdown 格式标记,扣 20 分"
      - "如果输出中引用了评测提示词原文,评分为 0"

六、开箱即用的评测可靠性自查清单

如果你现在负责一个 Agent 系统,以下是今天就能执行的 7 项自查:

  • 评测结果是否报告了标准差? 如果只报告均值不报告分布,分数无效。
  • 是否至少重复运行了 3 次? 单次运行的分数在大多数 Agent 评测中是不可靠的。Tool-Veritas 显示 LiveMCPBench 单次分数方差可达 18.9pp。
  • 是否对评测器本身做过人工审计抽样? 随机抽 50 个任务,让人看 Agent 的实际输出和评测器的判断——你会发现 15-20% 左右的误判率。
  • LLM-Judge 是否运行了多次并报告了一致性? 单次 LLM-Judge 判断受 position bias、verbosity bias 和 rubric drift 三重污染。
  • 是否有基于训练截止日期的污染检测? 如果模型声称在某个 benchmark 上表现优秀,先确认 benchmark 的题目发布时间是否在模型训练截止之后(LiveCodeBench 和 LiveBench 是黄金标准)。
  • 确定性评测的 ground truth 是否经过人工验证? 别信出题人的——OpenAI 审计发现 SWE-bench Pro 约 30% 的题目本身就有问题。
  • 评测环境是否 Docker 化且有版本 hash? 跑分结果无法复现的头号原因就是环境不一致。每一个评测管道都应该有一个 commit hash 绑定的 Docker 镜像。

七、结论:评测不是 QA 的最后一环,而是工程质量的一部分

Tool-Veritas 这篇论文给我们的教训远不止”有些 benchmark 有问题”这么浅。它在传达一个更深层的信号:

评测基础设施本身的工程质量,已经成为 Agent 研发流程中最被低估的技术债。

过去我们花 80% 的时间调模型,20% 的时间看分数。现在是时候把这个比例倒过来——在确认评测管道本身可靠之前,任何”涨点”都可能是评测器的随机抖动。

三个立即可执行的动作:

  1. 给每个评测管道加上 repeats: 5 和标准差报告——这是最小改动,最大收益。
  2. 从分解式评测的三个维度(工具调用/任务完成/结果验证)各抽 20 个 case 做人工审计——你会对自己过去信以为真的分数产生怀疑。
  3. 把 CoDeC 污染检测纳入新模型接入流程的必选步骤——别看排行榜,先测是否见过题。

评测不是为了证明自己做得对,而是为了发现自己错在哪里。当评测器自己都在犯错的时候,诚实比准确更重要。

关联内容

文章 ⬆️ 前置
别让评测分数骗了你:从静态刷榜到动态抗污染评测管道实战
当 78% 的 CodeForces 题目存在训练集软污染,当 NIST 闭源评测引发争议,传统静态基准的公信力正在崩塌。本文以 BeyondBench、StressEval、Claw-Eval-Live 为蓝本,给出从污染检测到动态生成评测管道的完整工程方案
文章 🔍 深入
你的 AI 评委靠得住吗?从 kappa 陷阱到几何中位数,LLM-as-a-Judge 可靠性审计与工程加固实战
21个评委、54万次判分的大规模审计揭示:LLM-as-a-Judge 的稳定性被系统性高估了约38个百分点,且高一致性与严重偏见可以共存。本文给出从审计到加固的完整工程方案。
文章 🔍 深入
LLM-as-Judge 不是魔法,是工程:从评测漂移到 CI 门禁,构建可信任的生产级评测管道
LLM-as-Judge 是 2026 年最主流的 AI 质量评测范式,但 80% 的团队低估了它的工程复杂度。本文给出从 Golden Dataset 构建、Rubric 工程化、Judge 校准到 CI/CD 门禁的完整工程方案,附可复制的代码模板和踩坑清单。
文章 ⬆️ 前置
Benchmark 数字越好看,越可能是幻觉
两篇独立论文不约而同地指向同一个结论:当前 LLM benchmark 分数的提升,很大一部分来自训练数据对测试集的软污染,而不是真实能力的进步。这对评估可信度是一次系统性打击。
文章 🔗 相关
修一个 Bug 容易,演进一个系统难:SWE 基准从静态到动态的范式跃迁
从 SWE-bench Verified 到 SWE-EVO、SWE-bench-Live、Vibe Code Bench,代码智能基准正从单 Issue 修复走向长周期软件演化和零到一应用构建,GPT-5 在 SWE-EVO 上仅 21% 解决率揭示能力鸿沟