基准测试开源模型的智能工具使用

Hugging Face Blog··作者 Lysandre

关键信息

这个基准测试是面向工具的,不只看最终输出,还看答案是如何被找到的;它在相同运行环境下比较模型、库版本和任务。作者认为,面向智能体的软件应当可发现、API 清晰、文档充分、示例结构化,并建议为 transformers 采用 CLI、Skill 和独立的任务示例这套方案。

资讯摘要

这篇博客的核心观点是,编码智能体越来越不是“帮人写代码”,而是在自己与软件交互:它会自己选择库、写调用、运行代码,并在出错后自行调试。也正因为如此,一个库不再只需要正确和快速,还必须让智能体能够高效地驱动它。作者指出,如果 API 很笨重或者文档过时,智能体可能会干脆绕开这个库,重新从头实现逻辑,这会让软件使用变得更复杂、更昂贵。为了研究这一点,他们设计了一个基准测试,不只看智能体是否给出正确答案,还看它为得到答案付出了多少工作量。

文章把 transformers 作为案例,用它来处理文本分类、图像描述和语音转写等机器学习任务。作者介绍了一个运行框架,可以在由 pi coding agent 驱动的开源模型上执行测试,并通过 Hugging Face Jobs 把模型、版本和任务的完整扫描分发到相同硬件上运行,确保比较公平。文章还把这一思路与他们之前对 hf CLI 的重设计联系起来,称那个面向智能体优化的版本让智能体使用的 token 数减少了 1.3–1.8 倍,最高可达 6 倍。最后,作者用情感分类任务举例说明差异:一种智能体会写一段多行 Python 脚本、导入 transformers、调试形状错误并多次重试;另一种则只需调用专门设计的 `transformers classify` 命令,一步就能完成。

基准测试开源模型的智能工具使用

资讯正文

跨不同指标对 transformers 修订版进行基准测试

这是一篇人工撰写、面向 agent 的博客文章。

编码 agent 越来越多地与我们的软件协作,而不是由我们亲自操作:你描述一个任务,agent 就会选择库、编写调用、运行它们,并调试自己犯下的错误。当库妨碍它时,它会很乐意绕过库,重新从头实现逻辑。这为库开发引入了一个新概念:代码不仅要正确、快速,还要设计成能让 agent 有效驱动。糟糕的 API 或过时的文档会让开发者不快,但现在它们也会把 agent 引向一条更长、成本更高的路径。

大多数基准测试只看最终答案。我们想看的则是整个过程:不仅要看 agent 是否答对,还要看它为此付出了多少工作,以及这种代价如何随着模型、库修订版和任务而变化。我们正是用 transformers 作为案例研究来测量这一点。

在这里,我们将介绍一个专注于答案是如何被找到的、针对工具的基准测试,并提供这样一个 harness 的简单实现;它完全运行在由 pi coding agent 驱动的开源模型上,而 models × revisions × tasks 的完整扫描则通过 Hugging Face Jobs 分发出去,从而确保每次运行都使用相同的硬件。

不过,如何为 agent 优化软件呢?

我们坚定地相信以下两条软件原则:

- 如果没有测试,那它就不起作用

- 如果没有文档,那它就不存在

在面向 agent 优化的工具领域,这一点依然成立,而且难得的是,这两者现在直接相互关联。

你希望你的工具对 agent 来说是“存在”的:它需要是可发现的。API 需要清晰,文档需要详尽。它们还需要以一种结构化的方式组织,让 agent 能快速获取有用的文件和示例。如果你希望你的工具能为 agent 所用,那么你就应该针对 agentic-use 对它进行测试。

在整篇博客中,我们都会以 transformers 为例:让 agent 使用它来解决机器学习任务(文本分类、图像描述、音频转录),而不是向其中贡献代码;不过,这个 harness 的设计能够适用于任何可以从命令行操作的工具。

我们对 transformers 的直觉是,只需做几处改动,使用就能大幅简化:增加一个 CLI、一个 Skill,以及自包含的、面向具体任务的示例。这与最近应用于 hf CLI 的做法相同——该 CLI 已被重新设计为面向 agent 优化,agent 使用的 token 数减少了 1.3–1.8 倍(最高可达 6 倍)。我们想知道,这种收益是否具有普适性,以及它是否也能对 transformers 有用。

直觉是个强大的工具,但在我们为像 transformers 这样被广泛使用的代码库提交会新增数千行代码的 PR 之前,我们还想要更多证据。我们着手测量,成功究竟是什么样子。

在情感分类任务中,两个 agent 都可能给出正确标签,但其中一个:

- 编写一个 40 行的 Python 脚本,导入 transformers,调试一个形状错误,重新运行两次,最后打印答案;

而另一个则

- 输入 transformers classify --model ... --text "..."

然后一次调用就完成。

都达到了 POSITIVE(0.9999),下面是代理在这个确切任务上实际采取的两条路径:

# 任务:对“I absolutely loved the movie, it was fantastic!”进行情感分类

- # 一个代理:把脚本通过管道传给 python 并解析输出

- python - <<'PY'

- from transformers import AutoTokenizer, AutoModelForSequenceClassification

- import torch

- import torch.nn.functional as F

- model = AutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased-finetuned-sst-2-english")

- tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased-finetuned-sst-2-english")

- inputs = tokenizer("I absolutely loved the movie, it was fantastic!", return_tensors="pt")

- with torch.no_grad():

- logits = model(**inputs).logits

- probs = F.softmax(logits, dim=1)

- idx = torch.argmax(probs, dim=1).item()

- print(model.config.id2label[idx], probs[0][idx].item())

- PY

+ # 另一个代理:一条命令

+ transformers classify \

+ --model distilbert/distilbert-base-uncased-finetuned-sst-2-english \

+ --text "I absolutely loved the movie, it was fantastic!"

两种方法都得到了相同结果。但它们在成本、延迟、token 使用量和失败模式上却有很大不同。

如果你的评估只检查最终字符串,那么你也看不到这些差异,更看不到你为库推出的某项改动——比如 CLI 改进、更好的错误信息,或者一个 Skill——是否真的帮助了代理。

我们搭建这个基准测试框架的目标,是评估代理完成某项任务需要做多少工作,以及对库的改动是否提升了性能。

下面简要说明我们将如何在这里评估代理。

我们会在三种变体(或称“层级”)下运行每个任务;也就是代理接触 transformers 的三种不同方式:

仅执行 bare pip install transformers,不做其他任何事

克隆完整的 transformers 源码,并在工作目录中检出

skill,一个打包好的 Skill:CLI 文档 + 任务示例,会在上下文中加载

这些并不是嵌套关系:skill

不包含 clone

(它提供的是精选文档,而不是源码树),而且二者也

并不严格包含彼此,每一种都会给代理提供不同类型的帮助。正如我们将看到的那样,模型有时在 clone

上的表现会比在 skill

上更好

还有几点选择:

- 目前我们只关注可以提供精确匹配的确定性任务,因为它们为实验提供了非常好的基础。对于其他任务,显然下一步应该考虑 model-as-a-judge 和其他方案。

- 每次运行都是一个独立的 Hugging Face Job:针对每个(model × revision × task)各运行一个,因此整个扫描会在相同硬件上并行执行,这使得大规模比较保持公平。

- 结果和 trace 会落到一个 Hugging Face Bucket 中:速度快、不需要版本控制,并且能够处理非常高的写入并发。

并非所有驱动代理的模型都一样,它们之间的差异会改变你在运行时应该关注什么。

大型开源模型

一端是规模最大、能力最强的开源模型。对于相当常见的任务,它们最终应该能给出正确答案。对这些模型来说,任务完成率很快就会接近 100%,不再能告诉你多少关于工具本身的信息;更相关的基准是代理为了达到这个结果付出了多少努力:它花了多少轮对话、多少 token、多少秒,以及它们走的是不是一条干净的路径,还是用了已经弃用的 API。

本地模型在规模上差异很大,能力也同样如此。像“匹配率”这样的指标就比对更大模型更有意义,因为你可以看到模型规模和能力如何影响它在你自己的特定工具上的结果。

这个 harness 不仅为库维护者提供了如何改进仓库以适配 agent 交互的指导,也有助于评估不同 agent 和模型在用户真正关心的任务上的表现。

该 harness 会从多个维度对每次运行打分,因此你可以针对不同类别的模型,去问到底什么才真正重要:

- 匹配率:最终答案是否包含预期结果(按任务区分,大小写不敏感的子串 / 正则 / 精确匹配,报告中都会明确给出);

- 中位时间和中位 token 数(新的 / 缓存的 / 生成的);

- 出错运行百分比:包括一个守卫机制,用来标记那些什么都没有产出的运行(0 个输出 token、没有工具调用、没有答案),这样静默失败就不会伪装成“0”;

- 标记采用率:由工具定义的行为标记;下文会解释这指的是什么。

所有结果都会进入一份你可以直接查看的报告:

实时报告:Overview、Coverage 和 Results,全部在客户端渲染。

而且由于它捕捉了每次运行的原生 agent 轨迹,数字只是起点:你可以逐条、逐命令地准确查看 agent 做了什么。这些轨迹可以通过 Hub 的 agent-traces 查看器共享:

在 Hub 的 agent-traces 查看器中呈现的一次运行:MiniMax-M2.7 在 answer-question 任务上的表现。

在 Hub 上打开这条轨迹 ↗

在看结果之前,先快速回顾一下设置。每次运行都会变化四个因素:驱动 agent 的模型,

transformers

所运行的 revision,任务,以及 tier(bare

/ clone

/ skill

如前所述,我们会针对两类不同的模型采用不同的指标。

由于一个强大的开源模型通常会得到正确结果,因此你真正衡量的是它为此付出了多少努力。它花了十个回合还是一个回合?它是否遵循了一条你已经因为过时文档而弃用的 API 路径?它是否遇到了你未预见到的错误?

一个自然的实验是固定一个强模型,然后变化工具的

revisions:也就是我们进行测试时所用的 transformers

连续 git 版本,从像

v5.8.0

v5.9.0

这样的发布标签,到引入 CLI 和 Skill 的那个特定 commit。我们想观察它给 agent 带来的负担是增加了还是减少了。我们使用 harness 在 transformers

上做测试,以验证增加一个专门的 CLI 和 Skill 是否真的减轻了 agent 的工作量。

在我们测试中使用的三个大型模型里,所有任务的平均耗时表明,Skill commit 使完成任务所花费的时间更少:

按 tier 分、按 revision 统计的每次任务中位时间:skill commit(绿色圆点)最快。

另一方面,在我们克隆了仓库的那些实验中,可以看到,由于引入 CLI 和示例的那个提交,令牌消耗显著增加,稍后我们会看到这一点。

按层级划分的每次修订新增令牌中位数:一旦 CLI 进入仓库,clone 版本就会跃升。

阅读 clone 版本的轨迹就能解释原因。这个提交新增了一个命令,但它也把 CLI 的实现以及一组 cli/agentic/*.py 使用示例直接一并放进了仓库。

在 clone 版本中,agent 面前有完整的 transformers 检出代码可供查看,大约三分之一的运行会先去读取新的表层内容(/cli/ 目录和示例脚本)来学习接口,然后才调用它。这使得输入中位数从约 4k 个令牌上升到约 6.4k 个令牌。

于是这两张图就成了同一种权衡的两面:这个提交让大模型少花时间(它们会转而使用 CLI,而不是调试 Python),代价则是更多的令牌(它们读取了教会自己使用 CLI 的代码)。在合并 PR 之前,这种权衡值得了解。

不过,有一点对 CLI 有利,但目前还没有纳入基准测试:读取它的成本会随着后续运行被摊薄。我们的设置是为一次性实验构建的。每次运行都是一个全新的 agent,它从零开始重新发现 CLI,因此每次都要支付发现成本。在真实使用中,agent 只需学习一次接口,随后就在同一会话中一次又一次地解决任务,把这部分成本摊到许多请求上。我们这里测到的令牌增幅,更接近最坏情况,而不是用户在日常中会看到的情况。

开源模型让我们能够对这里最重要的变量进行细粒度控制:模型大小、配置、量化、提供方、训练方式,以及任何在不同模型之间会有所差异的因素。它们也是工具界面最重要的地方:对于一个小模型来说,在一个纯净环境里被要求“用 transformers 做 X”,它可能会猜测一个在几个版本前就已变动的 API,可能会做出不必要的工具调用,并且可能得到错误答案。

因此,这里的实验与上面的相反:固定修订版本,扫描模型。这有助于看出哪些模型真正能把任务做好,不只是看令牌数和时间,还能细致到哪些模型根本无法稳定处理工具调用。我们的直觉是,模型越小,工具使用和任务本身都越难;我们在一系列模型规模上运行了这个 harness,正是为了测试这一点:

按模型划分的匹配率,按层级:技能层级提升了较大的模型,但拉低了较小的模型。

这似乎也与摄入的令牌数量相关。

按模型划分的新增令牌中位数,按层级。

关于公平比较,有一点需要说明:当覆盖不均衡时,简单地对任务求平均会产生误导(只完成了快速任务的模型看起来会很快)。报告里有一个“仅共享任务”切换项(可按模型和/或修订版本),这样你可以做同类比较;还有一个 Coverage 热力图,让你能清楚看到哪些 task × revision × model 单元格 वास्तव上运行过。

这里有两件事结合在一起:如何超越 agent 是否成功,去看它做了什么以及它是如何做的;以及我们从这个 harness 中提取出的第一批结果。

Match %、tokens 和时间只能告诉你一次运行的成本,却不能告诉你底层到底发生了什么。

这就是我们引入 marker 概念的原因。marker 是一种命名模式,由 profile(一个针对单个工具的插件,用于教 harness 如何构建并驱动某个特定库)来匹配一次运行。

它是一种你关心的行为的一行标签,会根据 agent 运行的 shell 命令、它编写的代码、读取的文件,或最终答案进行检查。一次运行可以触发多个 marker,也可以一个都不触发;报告会按模型和版本分别显示每个 marker 的触发频率。

对于 transformers

我们定义了几个,但这里只看其中两个最相关的:

cli

:agent 调用了 transformers

命令行工具(例如 transformers classify …

),而不是编写 Python。pipeline

:它转而使用高层的 pipeline(...)

Python API。

这些是我们用来观察某项改动是否真的改变了 agent 行为的指标。有意思的是,在这里,模型越大,就越会利用新上下文,而不是依赖记忆;因此它会更多地使用新引入的 CLI。

按模型层级划分的 CLI 采用情况:只有 skill 层级会去调用,而且模型越大越明显。

CLI 采用是新的:CLI 是在一个单独的提交里加入的,不在任何模型的训练数据中,而且文档也只有很少一部分。效果很明显:真正会去用它的是 Skill 变体,也就是那一版随 CLI 文档一并发布的模型,触发率达到 55.3%。

把这个提交与不同模型规模进行比较,CLI + Skill 对更大的模型是有帮助的:在 skill

tier 上,Kimi 和其他大型 agent 会去调用 CLI,并且用更少的轮次完成任务。(在 clone

tier 上,它们会先花更多输入 tokens 去读取新的 CLI 代码,正如上面所看到的,所以收益体现在时间和轮次上,而不是原始 tokens 上。)

Kimi-K2.6、GLM-5.1 和 MiniMax-M2.7 在不同版本中的表现

但在一些较小模型的设置中,它似乎会拖累性能。一个合理的解释是,小

模型更依赖记忆中的 API 模式,会复现它们在训练数据里见过的 pipeline(...)

片段。

新的概念随后就成了它们更容易出错的表面。你可以在 harness 上直接看到这一点:match % 更低、重试更多,cli

marker 几乎不触发。Qwen3-4B 模型上的表现尤其明显:

Skill 几乎没有改变它的 match rate,但成本分布却受到了显著影响。

几乎所有变化都来自 clone

tier。现在的检出内容包含

CLI 的实现和 cli/agentic/*.py

示例,而 4B agent 会大量读取这些内容:它的中位新增

tokens 从约 2.4k 跃升到约 23k,时间和输出也随之飙升,但准确率却没有任何提升。

Qwen3-4B 在不同版本中的表现。CLI + Skill 提交把成本分布彻底拉开,在 clone

tier 上,agent 会大量读取新发布的 CLI 源码(新增 tokens 约为 10 倍),但 match % 并没有任何提升。(repeat tokens

保持不变:这个设置没有使用 prompt caching。)

不过有时,Skill 也会直接破坏正确性。查看 traces 可以发现,例如在 Qwen3-14B 上:

加入 Skill 会把总体 match rate 从 67%(bare)降到 43%,而在最简单的任务上,这种崩塌非常

visible:classify-sentiment

在克隆版本上得分从 100% 变为使用 Skill 时的 0%。

Qwen3-14B 在 classify-sentiment 上,按版本划分:克隆版(蓝色)在各次修订中一直保持 100%,但 Skill 版本(绿色)在 CLI + Skill 修订中直接崩到 0%。

查看轨迹可以发现,模型把 CLI 误认为是一个可以直接调用的工具(就像 agentic-harness 工具,比如 web-search 那样)。但 Skill 并不是可执行工具:它只是加载到 agent 的上下文中的文档,而 transformers CLI 也始终只打算通过 shell(经由 bash)运行;所以这行不通。

Qwen3-14B 读取 Skill 后,在 56 次 Skill 运行中的 39 次里,要么发出一个 transformers(command="classify", ...) 的工具调用(这个工具从未被注册过),要么在自己能读到的 /bash /edit /write 工具里找不到类似能力,于是得出无法运行模型的结论并放弃。无论哪种情况,它都没有退回到在克隆版 checkout 上拿到 100% 的那条单行 pipeline(...),而是直接断定任务不可能完成。

Qwen3-14B 在 classify-sentiment(Skill 版本)上的表现:它推断 read/bash/edit/write 不能运行模型,于是放弃了。

这正是我们搭建这个 harness 想捕捉的东西:同样的改动在加速大模型的同时,反而会把小模型搞坏——起初这让我们觉得有点反直觉,而且很可能会被我们就这样原样发布出去。对维护者来说,结论是:面向 agent 的 API 应该跨模型规模进行评估,因为一种新的能力可能会减少强模型的工作量,却给较小模型增加歧义。这也提示了一个修复方向:与其手工编写一个 Skill 然后事后检查,不如一开始就针对较弱模型生成并验证它。

这正是 Upskill 所做的:只有当某个强模型的解法能实实在在帮助更小的模型时,它才把这个解法转换成 Skill。

这个 harness 就是一个 CLI:agent-eval。安装它,运行一套测试,在 HF Jobs 上跨模型 × 修订版本分发执行,并将报告发布为一个 Hugging Face Space。

仅限可信的本地使用。这个 harness 会运行一个绕过权限的编码 agent,并执行你指向的任意修订版本中的代码,而且轨迹里可能包含提示词、输出和本地路径。在把它指向不是你自己写的代码或分享结果之前,请先查看 SECURITY.md。

最新、完整的安装与使用说明都在 README 里。

检查最终答案只能告诉你 agent 是否能使用你的库,却不会告诉你代价是什么:它花了多少轮、多少 token、出了多少错,以及走了什么路径才到达那里。这个 harness 会在你选择的修订版本和模型范围内测量这些东西。

在 transformers 上,它捕捉到了我们原本可能会盲信地合并的东西:CLI + Skill 对最大的开源模型有帮助,却会伤害最小的模型。在合并之前知道这一点很重要!

它基于 profile 设计,并且可适配:把它指向你自己的库,定义几个任务及其期望答案,就能得到同样的报告。代码和任务都在仓库里,轨迹在 Hub 上。如果你把它用在自己的项目里,欢迎告诉我们!

这个 harness 完全建立在 Mario Zechner 的 coding-agent CLI 之上:它驱动每一次开源模型运行,而且只需要一个 HF_TOKEN。

用于为模型提供服务,这正是让这次开放模型的全面评测得以实际开展的原因。

这要感谢我们所评测的这些模型背后的模型构建者和推理服务提供商。总体来看,它们的表现都远远高于最基础的基线所能暗示的水平。

来源与参考

  1. 原始链接
  2. Is it agentic enough? Benchmarking open models on your own tooling

收录于 2026-06-19