核心判断

Benchmark 分数是在特定协议下得到的一次观察,不是脱离以下条件的模型固有属性:

  • 数据集和 Split;
  • Prompt 和示例;
  • Model 与 Decoding 配置;
  • Tool 或 Retrieval Context;
  • Scorer 与聚合方式;
  • 时间、软件、硬件和价格;
  • 污染与样本选择效应。

真正应该问的不是“哪个模型分数最高”,而是:

在可复现测试下,哪个模型满足当前工作负载的质量、安全、延迟、成本和治理约束?

分数为什么会误导

污染与记忆

公开测试题可能出现在预训练、微调、Prompt 示例、网络讨论或评估代码中。精确重合检测并不充分,因为改写题目或派生解法可以保留答案而不保留原文。

使用分层证据:

  1. 记录数据来源和发布日期;
  2. 在风险允许时保留私有或新编写的 Holdout;
  3. 测试改写、扰动和过程变化;
  4. 比较熟悉题目与新任务切片;
  5. 说明检查过什么、仍然不知道什么。

不要把字符串重合启发式包装成精确污染率。

天花板与饱和

当许多系统在小型测试上接近满分时,细小差异可能只是抽样噪声。报告题目级结果、置信区间或 Bootstrap 范围和切片表现。没有不确定性的排名很容易被过度解读。

题目与标签质量

评估可能测到标注不一致、题干歧义、错误参考答案或文化假设,而非模型能力。抽样审核题目,记录仲裁规则,并允许使用“无效”或“歧义”标签。

Goodhart 效应

分数成为目标后,团队可能优化分数而没有改善任务:

  • 训练 Benchmark 相似样本;
  • 选择更有利的 Prompt 或 Model Snapshot;
  • 针对 Scorer 调整输出格式;
  • 只报告最佳运行;
  • 不断修改测试直到达到目标。

应预先登记协议,保留锁定 Holdout,报告相关运行,并分开探索性与确认性评估。

可复现的评估记录

每次结果都保存 Manifest:

json
{
  "model": "provider/model@version",
  "evaluation_revision": "2026-07-01",
  "dataset": {
    "name": "support-holdout",
    "split": "test",
    "digest": "sha256:pinned"
  },
  "prompt_revision": "prompt-12",
  "decoding": {
    "temperature": 0,
    "seed": 17,
    "max_output_tokens": 512
  },
  "tools": "disabled",
  "retrieval": "fixed-fixture-v3",
  "repetitions": 3,
  "metrics": ["task_success", "policy_violation", "p95_latency", "cost_per_success"],
  "environment": {
    "runner": "pinned-runner",
    "region": "recorded-region"
  }
}

Manifest 不是安全策略,而是让结果可审计、可比较的记录。

构建任务集

一个有用的任务案例应包含:

json
{
  "case_id": "refund-042",
  "input": "redacted user request",
  "context": ["approved evidence fixture"],
  "expected": {
    "outcome": "deny_and_explain",
    "required_facts": ["refund_window"],
    "forbidden_actions": ["refund_without_authorization"]
  },
  "oracle": "policy_and_business_rules_v2",
  "risk": "high",
  "provenance": "human-authored-and-reviewed"
}

根据工作负载加入正常、边界、歧义、对抗、多语言、长上下文、失败恢复和跨 Tenant 案例。生产数据应最小化并受访问控制。

按任务类型评分

不要把所有任务强行压成一个指标:

任务 更有力的证据
精确分类 Accuracy、Macro-F1、Calibration、切片 Recall
结构化抽取 Schema 有效性、字段 Precision/Recall、Abstention
Retrieval/RAG Evidence Recall、Citation Support、答案正确性
编码 Tests、安全检查、Patch 范围、Review 结果
客服 Policy 正确性、解决率、升级率、语气复核
Tool Use Tool、参数、授权、副作用、幂等
文本生成 事实声明、量表维度、人工复核、拒答行为
交互 Agent 任务成功、恢复、成本、延迟、重复动作

BLEU、ROUGE、Exact Match 或 Judge 分数可以作为切片,但都不自动等于业务结果。

先使用确定性 Oracle

能用代码或可信参考验证的地方,优先使用确定性检查:

python
from dataclasses import dataclass
from typing import Mapping

@dataclass(frozen=True)
class Case:
    expected_status: str
    expected_fields: Mapping[str, str]

def check_result(case: Case, result: Mapping[str, object]) -> tuple[bool, list[str]]:
    errors: list[str] = []
    if result.get("status") != case.expected_status:
        errors.append("status_mismatch")
    for key, value in case.expected_fields.items():
        if result.get(key) != value:
            errors.append(f"field_mismatch:{key}")
    return not errors, errors

高影响动作的 Oracle 应验证授权、对象所有权、Tenant、审批、幂等和副作用状态,而不只是最终文本。

谨慎使用 LLM-as-a-Judge

Judge 可以分类开放式回答,但应作为测量工具:

  • 只提供量表所需的证据;
  • 对 Pairwise 顺序进行盲化和随机化;
  • 允许平局和“证据不足”;
  • 在代表性切片上与人工标注比较;
  • 报告一致性、分歧和置信信息;
  • 测试 Judge Model、Prompt 和量表变化;
  • 隔离敏感标识和无关风格信号。

不要用 Judge 分数作为授权、安全、删除或金融动作的唯一门禁。流畅但错误的回答也可能得到高分。

评估 Harness

评估 Runner 应同时产生原始样本和报告:

python
from dataclasses import dataclass
from time import monotonic
from typing import Callable, Iterable

@dataclass(frozen=True)
class Outcome:
    case_id: str
    passed: bool
    errors: tuple[str, ...]
    latency_ms: int
    estimated_cost: float | None

def run_case(
    case_id: str,
    invoke: Callable[[], dict[str, object]],
    check: Callable[[dict[str, object]], tuple[bool, list[str]]],
) -> Outcome:
    started = monotonic()
    try:
        result = invoke()
        passed, errors = check(result)
        return Outcome(
            case_id,
            passed,
            tuple(errors),
            round((monotonic() - started) * 1000),
            None,
        )
    except TimeoutError:
        return Outcome(case_id, False, ("timeout",), round((monotonic() - started) * 1000), None)
    except Exception:
        return Outcome(case_id, False, ("unexpected_error",), round((monotonic() - started) * 1000), None)

def summarize(outcomes: Iterable[Outcome]) -> dict[str, object]:
    items = list(outcomes)
    return {
        "cases": len(items),
        "passed": sum(item.passed for item in items),
        "errors": sorted({error for item in items for error in item.errors}),
        "latency_ms": [item.latency_ms for item in items],
    }

这是结构性 Harness 片段,不是完整的 Model Provider 集成。应将原始结果、环境 Metadata 和报告生成分开,避免摘要掩盖失败。

公平比较模型

对于每个候选模型:

  1. 使用相同的任务版本、Context、Tools 和 Policy;
  2. 固定或记录 Model 和 SDK 版本;
  3. 按已声明协议预热并重复;
  4. 保存原始结果,而不只保存平均值;
  5. 报告置信区间或 Bootstrap 范围;
  6. 同时比较质量、安全、p95/p99 延迟、每个成功任务成本和运营负担;
  7. 在宣布胜者前做切片分析。

某模型可能平均质量最高,却在高风险切片失败。便宜模型如果需要更多重试或人工修正,也可能有更高的成功任务成本。

发布门禁

使用面向工作负载的流程:

text
contract -> scenario -> replay -> abuse -> human review
          -> cost/latency -> approval -> canary -> rollback

门禁应表达不变量和可接受风险,而不是通用百分比。例如:

  • Abuse Suite 中不得发生跨 Tenant 访问;
  • 不得产生未授权外部写入;
  • 所有必需结构化字段有效;
  • 任务成功不超过预先声明的回归区间;
  • p95 延迟和成本保持在工作负载预算内;
  • 每个失败案例都被分类或升级。

公开 Leaderboard 与厂商声明

公开排名适合发现和提出假设。做决策前核查:

  • 评估日期和 Model 版本;
  • Prompt、Tools 和 System Instruction;
  • 抽样与平局处理;
  • 参与者选择和缺失结果;
  • 置信区间与切片报告;
  • Scorer 或 Provider 是否存在利益冲突;
  • 测试工作负载是否接近你的场景。

不要把 Leaderboard 描述成中立真相,也不要在缺少协议和来源时复述厂商结果。

企业运营实践

  • 版本化任务集、Prompt、Policy、Runner 和 Model;
  • 保留锁定 Holdout,持续加入新编写案例;
  • 将重大线上事故加入失败分析;
  • 分开探索性调优和发布评估;
  • 对评估数据执行删除和访问控制;
  • 仅在记录用途后抽样生产结果;
  • 审查 Judge Drift 和 Oracle Drift;
  • 发布包含原始样本引用和已知限制的评估报告。

总结

Benchmark 不是因为某一个分数不好就“失效”。当来源、不确定性、任务匹配、评分有效性和激励机制被隐藏时,它才会变成弱证据。把公开测试作为有边界的基线,再建立版本化工作负载评估、确定性 Oracle、校准的人类或模型复核、滥用案例、成本/延迟账本和发布门禁,模型选型才会成为工程决策,而不是排行榜竞赛。

延伸阅读

一手来源