Hugging Face · 官方博客

你的工具上开源模型的自主性基准测试

Is it agentic enough? Benchmarking open models on your own tooling

二〇二六年六月十八日 · 英文原文

Hugging Face 团队(Lysandre、Nathan Habib、Pedro Cuenca)发布了一个面向 agent 使用场景的软件测试框架 `agent-eval`,以 `transformers` 库为案例,测量 agent 完成任务的过程成本(轮次、token、时间、错误率)而非仅最终答案。该框架在三种层级(bare、clone、skill)下运行,通过 Hugging Face Jobs 在相同硬件上并行扫描模型×版本×任务组合。实验发现,为大型开源模型(如Kimi、GLM)添加CLI+Skill可节省时间,但会降低小型模型(如Qwen3-4B/14B)的匹配率,甚至导致任务失败。框架支持自定义配置文件,结果和轨迹存入Hub。

](https://huggingface.co/lysandre)

Image 2: Nathan Habib 的头像

Image 3: Pedro Cuenca 的头像

Image 4: 在不同指标上对 transformers 版本进行基准测试

在不同指标上对 transformers 版本进行基准测试

这是一篇由人类撰写的、聚焦 agent 的博文。

编码 agent 越来越多地代替我们使用软件:描述一个任务,agent 就会选择库、编写调用、运行它们,并调试自己的错误。当库成为障碍时,它会愉快地绕过它,从头重写逻辑。这给库开发引入了一个新概念:代码不仅应该正确和快速,还应该设计成能让 agent 有效地驱动它。笨拙的 API 或过时的文档会困扰我们开发者,现在它也会让 agent 走上一条更长、更昂贵的路径。

大多数 benchmark 只看最终答案。而我们想要的是整个过程:不仅仅是 agent 是否做对了,还包括它花了多少功夫才达到目标,以及这些功夫如何随模型、库版本和任务而变化。我们以 transformers 为案例,精确测量了这一点。

在这里,我们将介绍一个专注于答案如何被找到的工具特定 benchmark,并提供这样一个测试框架的简单实现,完全运行在由 pi 编码 agent 驱动的开源模型上,并将模型 × 版本 × 任务的完整扫描分布在 Hugging Face Jobs 上,以确保每次运行都使用相同的硬件。

但是,如何为 agent 优化软件?

我们坚信以下两个软件原则:

在面向 agent 优化的工具领域,这一点同样适用,而且这一次,这两者直接相互关联。

你希望你的工具对 agent 来说是存在的:它需要是可发现的。API 需要清晰,文档需要详尽。它们需要以 agent 能够快速访问有用文件和示例的方式组织。如果你希望你的工具能为 agent 工作,那么你应该为 agent 使用场景测试它。

为 agent 使用场景测试软件

我们将在整篇博文中以 transformers 为例:agent _使用_它来解决 ML 任务(对文本进行分类、为图像生成标题、转录音频),而不是向它贡献代码;尽管该测试框架被设计为能与任何可以从命令行操作的工具一起工作。

我们对 transformers 的直觉是,通过一些改动可以极大地简化使用:一个 CLI、一个 Skill,以及自包含的、特定于任务的示例。这与最近应用于 hf CLI(已重新设计为面向 agent 优化) 的方法相同,agent 使用的 token 减少了 1.3–1.8 倍(最多 6 倍)。我们想知道这种优势是否具有普遍性,以及它是否也能对 transformers 有用。

直觉是一个强大的工具,但在我们向像 transformers 这样广泛使用的代码库提交增加数千行代码的 PR 之前,我们想要更多的证据。我们开始衡量成功是什么样的。

并非所有成功都等价

两个 agent 都可以为情感分类任务产生正确的标签,但其中一个:

而另一个

两者都得到了 POSITIVE (0.9999),以下是 agent 在这个确切任务上实际采取的两条路径:

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

- # 一个 agent:将脚本通过管道传给 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

+ # 另一个 agent:一条命令
+ transformers classify \
+   --model distilbert/distilbert-base-uncased-finetuned-sst-2-english \
+   --text "I absolutely loved the movie, it was fantastic!"

两种方法都达到了相同的结果。但它们在成本、延迟、token 使用量和失败率方面有着非常不同的特征。

如果你的评估只检查最终字符串,那么你对这些以及你发布到库的更改(CLI 改进、更好的错误消息、一个 Skill)是否真正帮助了 agent 都视而不见。

我们使用这个测试框架的目标是评估 agent 完成给定任务需要做多少工作,以及库的更改是否提高了性能。

我们如何运行评估?

简单说一下我们将如何在这里评估 agent。

我们在三种变体(或“层级”)下运行每个任务;agent 可以接触 transformers 的三种不同方式:

bare     仅 pip install transformers,没有其他
clone    完整的 transformers 源代码,检出在工作目录中
skill    一个打包好的 Skill:CLI 的文档 + 任务示例,加载到上下文中

这些不是嵌套的:skill 不包含 clone(它提供精选的文档,而不是源代码树),两者也不是严格包含关系,每种方式都给 agent 提供了不同类型的帮助。正如我们将看到的,一个模型有时在 clone 上比在 skill 上表现更好。

还有几个选择:

应该以哪些模型作为基准?

并非所有驱动 agent 的模型都是等价的,它们的差异会改变你在运行它们时应该关注的内容。

大型开源模型

一方面,你有最大、能力最强的开源模型。在相当常见的任务上,这些模型最终应该能得到正确答案。对于它们来说,任务完成率接近 100%,不再能告诉你太多关于工具的信息;一个更相关的 benchmark 是 agent 达到目标所付出的努力:花费了多少轮次、token 和秒数,以及它们是否走了一条干净的路径,还是使用了已弃用的 API。

本地模型

本地模型的大小差异很大,它们的能力也是如此。诸如 “匹配率 %” 之类的指标比大型模型更相关,因为你可以看到模型大小/能力如何影响你在特定工具上的结果。

这个测试框架不仅为库维护者提供了如何改进仓库以利于 agent 交互的指导,还有助于评估不同的 agent 和模型在用户关心的任务上的表现。

该测试框架在多个维度上对每次运行进行评分,以便你可以询问对于每类模型什么才是真正重要的:

所有这些都汇总到一个你可以直接查看的报告中:

实时报告:概览、覆盖率和结果,全部在客户端。

并且因为它捕获了每次运行的原生 agent 轨迹,数字只是开始:你可以逐条命令地精确阅读 agent 做了什么。这些轨迹可以通过 Hub 的 agent-traces viewer 共享:

Image 5: 在 Hub 的 agent-traces viewer 中渲染的一次运行:MiniMax-M2.7 在 answer-question 任务上

在 Hub 的 agent-traces viewer 中渲染的一次运行:MiniMax-M2.7 在 answer-question 任务上。

在 Hub 上打开此轨迹 ↗

在结果之前,快速回顾一下设置。每次运行变化四个东西:驱动 agent 的模型、它运行的 transformers 版本任务以及层级bare / clone / skill)。正如讨论过的,我们针对两种不同的模型类别查看不同的指标。

大型开源模型:固定模型,变化版本

由于大型开源模型通常能得到正确结果,你真正衡量的是达到结果所需的努力。它花了十轮还是一轮?它是否因为信任过时的文档而遵循了你已弃用的 API 路径?它是否遇到了你未曾预见的错误?

自然的实验是固定一个强大的模型,变化工具的版本:我们测试的 transformers 的连续 git 版本,从像 v5.8.0v5.9.0 这样的发布标签,到引入 CLI 和 Skill 的特定提交。我们想观察它给 agent 带来的负担是增加还是减少。我们在 transformers 上使用该测试框架来检查添加专用 CLI 和 Skill 是否确实减轻了 agent 的工作。

对于我们测试中使用的三个大型模型,所有任务的平均时间表明,Skill 提交导致完成任务所需的时间更少:

Image 6: 按层级划分的每个版本的中位时间

按层级划分的每个版本的中位时间:skill 提交(绿点)是最快的。

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

Image 7: 按层级划分的每个版本的中位新 token 数

按层级划分的每个版本的中位新 token 数:一旦 CLI 进入仓库,clone 变体就会跳升。

阅读 clone 变体的轨迹可以解释原因。该提交添加了一个命令,但它也将 CLI 的实现和一组 cli/agentic/*.py 使用示例直接放入了仓库。

clone 变体上,agent 面前有一个完整的 transformers 检出,大约三分之一的运行会去读取新的表面(/cli/ 树和示例脚本)以了解接口,然后再调用它。这将中位输入从约 4k token 提高到约 6.4k token。

这两个图表是一个权衡的两个方面:该提交为大型模型节省了时间(它们使用 CLI 而不是调试 Python),但代价是更多的 token(它们阅读了教会它们 CLI 的代码)。在合并 PR 之前,这是一个值得了解的权衡。

不过,有一个因素对 CLI 有利,但尚未被基准测试:阅读它的成本会随着后续运行而摊销。我们的设置是为一次性实验构建的。每次运行都是一个全新的 agent,从头重新发现 CLI,因此它每次都支付发现成本。在实际使用中,agent 学习一次接口,然后在同一个会话中一个接一个地解决任务,将成本分摊到许多请求中。我们在这里测量的 token 增加更接近最坏情况,而不是用户日常会看到的情况。

小型模型:固定版本,变化模型

开源模型让我们可以精细控制这里最重要的变量:大小、配置、量化、提供者、训练,以及任何模型之间可能不同的东西。它们也是好的工具表面最重要的地方:一个被要求在一个 bare 环境中“使用 transformers 做 X”的小型模型可能会猜测一个在几个版本前已经改变的 API,可能会进行不必要的工具调用,并且可能得到错误的答案。

所以这里的实验与上面相反:固定版本,扫描模型。这有助于查看哪些模型实际上处理了任务,不仅仅是 token 数量和时间,而是深入到哪些模型不能可靠地处理工具调用。我们的直觉是,模型越小,工具使用和任务都越困难;我们跨一系列模型大小运行了测试框架来精确测试这一点:

Image 8: 按层级划分的跨模型的匹配率 %

按层级划分的跨模型的匹配率 %:skill 层级提升了大型模型,但降低了小型模型。

这似乎也与摄入的 token 数量相关

Image 9: 按层级划分的跨模型的中位新 token 数

按层级划分的跨模型的中位新 token 数。

关于公平比较的说明:当覆盖率不均匀时(一个只完成了快速任务的模型看起来很快),简单地跨任务平均会产生误导。报告有一个 “仅共享任务” 切换(跨模型和/或版本),以便你可以进行同类比较,还有一个 覆盖率 热图,以便你可以精确查看哪些任务 × 版本 × 模型单元实际运行了。

调整工具:标记与结果

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

什么是标记?

匹配率、token 和时间告诉你运行的成本,但并不能告诉你太多关于幕后发生的事情。

这就是我们引入标记(marker)概念的原因。标记是一个命名的模式,配置文件(profile,一个小的、针对特定工具的插件,它教会测试框架如何构建和驱动给定的库)会针对一次运行进行匹配。

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

对于 transformers,我们声明了几个标记,但我们只会看两个最相关的:

这些是我们观察一个更改是否真正改变了 agent 行为的东西。有趣的是,在这里,模型越大,它越倾向于利用新的上下文而不是使用其记忆;因此更倾向于利用新引入的 CLI。

Image 10: 按层级划分的跨模型的 CLI 采纳率

按层级划分的跨模型的 CLI 采纳率:只有 skill 层级会使用它,并且随着模型变大而增加。

CLI 采纳是新的:CLI 在一个单独的提交中落地,不在任何模型的训练数据中,并且只有少量文档。效果很明显:是 Skill 变体,即提供 CLI 文档的那个,实际使用了它,采纳率为 55.3%。

CLI + Skill 提交有帮助吗?

跨模型大小比较该提交,CLI + Skill 帮助了较大的模型:在 skill 层级上,Kimi 和其他大型 agent 使用了 CLI 并在更少的轮次内完成。(在 clone 上,它们首先花费了_更多_的输入 token 来阅读新的 CLI 代码,正如我们上面看到的,所以优势体现在时间和轮次上,而不是原始 token 上。)

Image 11: Kimi-K2.6、GLM-5.1 和 MiniMax-M2.7 跨版本的表现

Kimi-K2.6、GLM-5.1 和 MiniMax-M2.7 跨版本的表现

但在一些较小模型的设置中,它似乎损害了性能。一个合理的解释是,小型模型依赖于记忆的 API 模式,重现它们在训练数据中看到的 pipeline(...) 代码片段。新概念对它们来说是一个更大的出错面。你可以直接在测试框架上观察到这一点:更低的匹配率,更多的重试,cli 标记几乎不触发。这在 Qwen3-4B 模型上尤其引人注目:Skill 几乎没有改变其匹配率,但其成本分布却受到了显著影响。

几乎所有这些都是由 clone 层级引起的。检出现在包含 CLI 的实现和 cli/agentic/*.py 示例,4B agent 大量读取它们:其中位新 token 数从约 2.4k 跃升至约 23k,时间和输出也急剧上升,而准确率却没有提高。

Image 12: Qwen3-4B 跨版本的成本分布:已用时间、新 token、重复 token、输出 token

Qwen3-4B 跨版本的表现。CLI + Skill 提交使成本分布大幅扩散,在 clone 层级上,agent 大量读取新发布的 CLI 源代码(新 token 数增加约 10 倍),而匹配率没有提高。(重复 token 保持平稳:此设置不使用 prompt 缓存。)

然而,有时 Skill 会直接破坏正确性。阅读轨迹显示了原因,例如对于 Qwen3-14B:添加 Skill 将其整体匹配率从 67%(bare)降至 43%,在最简单的任务上,崩溃非常明显:classify-sentimentclone 变体的 100% 降至使用 Skill 时的 0%

Image 13: Qwen3-14B 在 classify-sentiment 任务上按层级划分的跨版本匹配率 %

Qwen3-14B 在 classify-sentiment 任务上,按层级划分:clone(蓝色)跨版本保持在 100%,但 Skill 变体(绿色)在 CLI + Skill 版本处崩溃至 0%。

查看轨迹,模型将 CLI 误认为是_一个可以直接调用的工具_(就像 agentic 测试框架中的工具,例如 web-search)。Skill 不是一个可执行工具:它是加载到 agent 上下文中的文档,而 transformers CLI 只应通过 shell(使用 bash)运行;所以这行不通。

Qwen3-14B 读取了 Skill,在其 56 次 Skill 运行中,有 39 次要么发出了一个 transformers(command="classify", ...) 工具调用(一个从未注册过的工具),要么在其 read/bash/edit/write 工具中找不到类似的东西,从而得出结论它_无法_运行模型并放弃。无论哪种方式,它都没有回退到在 clone 检出中得分 100% 的一行 pipeline(...),而是宣布任务不可能完成。

Image 14: Qwen3-14B 在 Skill 变体下放弃 classify-sentiment 任务

Qwen3-14B 在 classify-sentiment 任务上(Skill 变体):它推理出 read/bash/edit/write 无法运行模型,然后放弃。

这正是我们构建测试框架要捕捉的东西:加速大型模型的同一个更改最终却破坏了小型模型,这起初对我们来说似乎有点反直觉,而且我们很可能会按原样发布。给维护者的启示:面向 agent 的 API 应该跨模型大小进行评估,因为一个新的功能可以减少强模型的工作量,同时为较小的模型增加歧义。 这也暗示了一个修复方法:与其手写一个 Skill 并在事后检查,你可以预先针对较弱的模型生成并验证一个。

这正是 Upskill 所做的:只有当它可衡量地帮助较小模型时,它才会将强模型的解决方案转化为 Skill。

亲自尝试

该测试框架是一个 CLI,agent-eval。安装它,运行一个测试套件,将其分布在 HF Jobs 上的模型 × 版本上,并将报告发布为 Hugging Face Space。

仅限受信任的本地使用。 该测试框架会运行一个具有绕过权限的编码 agent,并执行你指向的任何版本的代码,轨迹可能包含 prompt、输出和本地路径。在将其指向不是你编写的代码或共享结果之前,请参阅 SECURITY.md

完整且保持最新的设置和使用说明位于 README 中。

结语

检查最终答案可以告诉你 agent _能否_使用你的库。它不会告诉你成本是多少:轮次、token、错误以及它达到目标所走的路径。这个测试框架可以衡量这些,在你选择的版本和模型上。

transformers 上,它捕捉到了我们本会凭信念发布的东西:CLI + Skill 帮助了最大的开源模型,却伤害了最小的模型。在合并之前值得了解!

它是基于配置文件的,并且设计为可适应:将其指向你自己的库,定义几个任务及其预期答案,就能得到相同的报告。代码和任务在 repo 中,轨迹在 Hub 上。如果你在你的项目中使用它,请告诉我们!

致谢

这个测试框架完全建立在 pi 之上,即 Mario Zechner 的编码 agent CLI:它驱动每一次开源模型运行,只需要一个 HF_TOKEN 来服务模型,这使得开源模型扫描变得切实可行。

感谢我们扫描的模型背后的模型构建者和推理提供商。总的来说,它们的表现远高于 bare 基线所暗示的水平。

译自 Hugging Face · 官方博客 · 录于 二〇二六年六月十八日