终端智能体评测中脚手架为何比模型更影响分数
最近我把一批终端智能体任务在同一组评测样本上重新跑了一遍。原本预期是“模型越强,分数越高”,但实际结果让我修正了这个判断:当我把同一个模型从基础版换成头部旗舰版,分数只上涨了几个百分点;而当我把脚手架从“让模型自由发挥命令”改成“显式声明动作空间+结构化环境反馈”,同一模型在同一批任务上的分数直接拉高了一个档位。
这不是在否定模型能力。终端智能体是长在终端里的 AI 代理,模型负责理解和决策,但真正决定它能不能把决策变成正确动作的,是外面的那层脚手架。这也是为什么同样的模型跑同一个评测,不同框架的分数能有很大差异。如果你也在跑终端智能体评测,或者正准备接入这类工具,我建议你先别急着追新模型,停下来看看脚手架。
1. 先搞清楚终端智能体到底在测什么
1.1 终端智能体的核心链路
要理解脚手架为什么重要,先要拆开终端智能体的工作链路。一个典型的终端智能体任务,往往是这样走的:
- 收到一个自然语言任务,比如“修一下项目里所有测试失败的用例”。
- 模型把任务拆成若干步,生成对应的命令、脚本或代码。
- 脚手架把这些输出转换成终端可执行的命令。
- 终端执行命令,返回输出、退出码、报错信息。
- 脚手架把反馈整理后送回给模型。
- 模型根据反馈决定下一步动作。
- 循环直到任务完成或达到最大步数。
在这个链路里,模型负责的是第 2 步和第 6 步,也就是“决策”。其他环节,尤其是命令执行和反馈整理,都由脚手架负责。评测分数恰恰是整个链路的综合结果。任何一个环节出错,最终都会表现为任务失败或分数下降。
1.2 评测分数由四层变量共同决定
很多人把终端智能体评测看成“模型排行榜”,但实际分数背后至少有四层变量:
| 变量层 | 具体内容 | 对分数的影响方式 |
|---|---|---|
| 模型能力 | 语义理解、指令跟随、代码生成、推理深度 | 决定任务方向的正确性 |
| 脚手架 | 动作空间、反馈解析、上下文管理、错误恢复 | 决定决策能否转化为正确执行 |
| 评测环境 | 容器、系统依赖、网络、初始文件状态 | 决定命令是否可稳定运行 |
| 评估指标 | 任务成功率、子步骤完成、耗时、成本 | 决定什么算“完成” |
目前多数终端智能体评测只输出一个最终成功率,很难区分失败到底来自哪一层。所以当模型 A 的分数比模型 B 高,不一定说明模型 A 更强,也可能只是脚手架对模型 A 的输出格式更友好。
1.3 一个反直觉现象:模型升级掩盖了脚手架的短板
我在评测中反复看到一种情况:同一个脚手架上,换更强的模型,分数提升并不明显。原因不是模型没有变强,而是模型的大部分能力被脚手架的限制浪费了。
举个例子:如果脚手架只支持把模型输出当作纯字符串命令去执行,那么模型生成再合理的命令,也可能因为参数格式、转义、当前目录、环境变量等问题执行失败。哪怕模型知道应该先进入某个目录再运行测试,脚手架没有把目录切换状态保持住,模型还是会在下一轮丢上下文。
反过来,如果脚手架把动作空间限定得很清晰,比如明确告诉模型当前有哪些工具、每个工具接收什么参数、终端反馈怎么结构化返回,那么哪怕模型能力不是最强,也能稳定完成任务。强模型可以弥补部分脚手架缺陷,但它无法修改脚手架本身。
所以我的核心判断是:在终端智能体评测里,模型是决策引擎,脚手架是执行系统。执行系统的设计缺陷,会直接吞掉模型能力的提升。
2. 为什么脚手架比模型更影响分数
2.1 脚手架是终端智能体的操作系统
把模型比作大脑,脚手架就是手、眼睛和神经系统。模型本身不具备打开 shell、读取文件、执行命令的能力,它输出的只是一段文本。脚手架负责把这端文本变成终端里的真实动作。
这个角色很像操作系统。操作系统不决定程序要解决什么问题,但它决定程序能不能稳定调用 CPU、内存、文件系统和网络。终端智能体的脚手架也一样,它不决定任务目标,但决定模型能不能稳定地读取环境、执行命令、看到结果。
如果你的终端智能体经常卡在“命令执行失败”或者“模型反复生成同样的错误命令”,问题大概率不在模型,而在脚手架没有把环境状态准确传给模型。
2.2 三种关键的脚手架设计
从评测角度,我一般会重点看三种脚手架设计:
第一种:动作空间的设计。 脚手架允许模型用哪些命令?是给一个完整的 bash,还是限定成一组安全工具?如果给完整 bash,模型灵活度高,但出错面也大;如果限定成工具集合,模型更容易被引导到正确的操作路径。评测里,后者往往表现更稳定。
第二种:反馈压缩与结构化。 终端输出可能几千行,模型上下文有限,全塞进去会稀释注意力。好的脚手架会截断、摘要、高亮错误信息,甚至把退出码、当前目录、关键日志单独拎出来。这一步直接影响模型能否定位失败原因。
第三种:恢复机制。 一次命令失败后,脚手架是直接返回一个错误字符串,还是把错误上下文带回给模型?是让模型无限重试,还是设置最大尝试次数?这些机制决定了任务能否从错误中恢复。
2.3 评测中的“隐式脚手架”
还有一个容易忽略的点:很多评测差异不来自显式框架,而来自环境初始化脚本、路径预置、依赖预装、可用的 Python 包和系统工具。这些都可以算作隐式脚手架。
两个终端智能体跑同一个 SWE-bench 任务,如果环境 A 预装了 pytest,环境 B 没有,那么即使模型能力完全相同,环境 A 的分数也会明显更高。评测报告里通常不会写这些差异,但它们真实地影响分数。
所以当你看到某篇文章说“我们换了模型后分数提升 x%”,先不要急着复现。先问清楚:脚手架是什么?环境怎么初始化?评测脚本怎么判断成功?
3. 同一个模型,分数差距从哪里来——影响评测分数的四个关键细节
3.1 命令输出不是越长越好
终端执行命令后,stdout 和 stderr 可能很长。如果脚手架直接把完整输出全部塞给模型,模型很容易被大量无关日志干扰,反而看不到最关键的错误信息。
我在评测里见过一个很典型的例子:模型运行测试后,终端输出 500 行,其中有 480 行是正常日志,20 行是失败堆栈。如果脚手架把 500 行全部送回上下文,模型经常会忽略位于中间的失败信息;如果脚手架只保留最后的报错段,并明确标注“以下为错误输出”,模型就能很快定位问题。
命令输出的压缩不是简单的截断,而是一种信息筛选。好的脚手架会保留退出码、错误行、最近相关文件变化,并丢弃与任务无关的日志。这个设计看起来不起眼,但对评测分数的影响非常大。
3.2 一次失败后的重试策略决定上限
终端智能体评测里,几乎没有哪条任务是一次命令跑通的。失败后的处理方式,往往决定了最终是成功还是彻底卡住。
如果脚手架只返回“Command failed with exit code 1”,模型拿到的信息量太少,下一轮大概率还是猜。如果脚手架能返回错误行、stderr 摘要、最近一次文件修改记录,模型才有机会真正修正命令。
这里要特别提一个高频现象:模型反复生成同一条错误命令。很多评测失败不是模型能力不够,而是脚手架没有把失败原因解释清楚,导致模型在同一个坑里来回踩。你可以把记录打开看,如果发现模型的三步操作几乎一样,基本可以断定是反馈信息不足,而不是模型没有理解任务。
3.3 上下文窗口里的文件与状态管理
终端智能体经常需要在多个目录、多个文件之间切换。模型能不能在下一轮明确知道“当前在哪个目录”“刚改过哪个文件”“环境变量是否生效”,直接决定后续命令是否正确。
脚手架如果只记录命令输出,却不跟踪状态,就会出现一种很奇怪的现象:模型明明已经知道要进入某个目录,但下一轮生成命令时还是用绝对路径,或者把旧路径和新命令混在一起。问题不在模型记忆,而在脚手架没有把状态同步到上下文。
好的脚手架会在每轮反馈中附带当前工作目录、最近文件变更、关键环境变量。这样模型不需要从历史消息里“回忆”状态,而是每次都能拿到一个快照。难度会低很多。
3.4 评测基准的“隐藏分”:检查点与评估粒度
以 SWE-bench 这类以任务完成度为核心的评测为例,最终分数只看测试用例有没有通过。这个设计非常依赖终端智能体“能否运行测试并理解测试结果”的能力。
模型生成一个补丁很容易,难的是确认补丁是否正确、测试是否通过、有没有引入新的失败。这一整套验证行为,几乎都由脚手架决定。如果脚手架支持“自动运行相关测试并返回编译错误”,模型就能快速调整;如果脚手架只能让模型自己猜测试命令,分数就会低很多。
所以评测分数里其实藏着大量“隐藏分”。这些分数不是来自模型是否聪明,而是来自脚手架是否提供了必要的验证回路。很多人换模型后分数上不去,就是因为隐藏分被脚手架锁死了。
4. 实操:如何用最低成本优化脚手架,提升终端智能体评测分数
4.1 先记录每一次动作和判定
优化脚手架之前,先建一份运行日志。我一般会记录这些信息:
- 任务编号
- 模型名称
- 脚手架版本
- 输入提示词
- 每一步动作:命令、当前目录、输入输出摘要
- 每一步判定:成功、失败、超时、格式错误
- 最终分数
- 任务耗时
不要只记最终分数。没有动作序列,你根本不知道分数是在哪一步丢的。很多评测框架默认不会把中间过程存下来,但我们自己跑的时候一定要在脚手架层加钩子,把所有输入输出和状态变化留下来。
4.2 建议的最小对比实验
要判断“当前瓶颈是模型还是脚手架”,我建议做一个 2×2 对比实验:
| 实验组 | 模型 | 脚手架 |
|---|---|---|
| A1 | 模型 X | 脚手架 1 |
| A2 | 模型 Y | 脚手架 1 |
| B1 | 模型 X | 脚手架 2 |
| B2 | 模型 Y | 脚手架 2 |
先固定脚手架,换模型,看分数变化幅度;再固定模型,换脚手架,看分数变化幅度。通常你会看到,对大多数任务来说,B1 到 B2 的差距比 A1 到 A2 更大。这说明脚手架在当前阶段更值得投入。
如果你不希望一次跑太多,可以先跑 A1 和 B1,用同一个模型分别跑两套脚手架,从中看到反馈信息对任务成功率的影响。
4.3 三个最具性价比的脚手架改造点
我建议把有限的优化时间优先投到下面三个地方:
第一,结构化输出解析。 把模型输出切分成“动作类型”“目标文件”“命令参数”“预期结果”等字段。不要把模型输出直接当命令执行,而是先解析、校验、再执行。这样可以减少因格式问题导致的无效命令。
第二,终端输出压缩。 保留最后 50 行错误输出,提取退出码和关键报错,删除重复日志。如果命令输出特别长,先做摘要再送回模型。这样模型每次看到的都是“可理解的信息”,而不是一坨日志。
第三,失败反馈增强。 命令失败时,不要只回传 exit code。把错误行、stderr、当前目录、最近文件变更一起组装成结构化反馈。同时保留最大重试次数,避免模型无限循环。
这三个改造点都不需要换模型,也不涉及大规模框架重写,但效果通常很明显。
4.4 一套排查链路:分数不涨时按这个顺序检查
如果你已经调了模型,分数还是上不去,我建议按下面这个顺序排查:
- 先看日志,定位失败发生在哪一步。 是任务理解阶段错了,还是执行命令阶段错了。
- 再看模型当时拿到的反馈信息。 如果反馈信息里看不到错误原因,那就是脚手架的信息传递问题。
- 检查脚手架是否保留了关键状态。 比如当前目录、文件变更、环境变量。如果状态丢了一段,模型后续很容易迷路。
- 检查命令执行本身的稳定性。 临时目录冲突、并发执行、依赖缺失,这些环境问题也经常被误判为模型问题。
- 最后再考虑换模型。 模型只在“语义理解不足”或“方案设计错误”时才是主要的优化方向。
这个排查链路解决过很多“看起来像模型问题”的评测失败。比如有用户反馈终端智能体进不去,先看环境是否初始化成功,再看脚手架是否正确捕获启动错误,最后才判断是不是接入本身的问题。顺着这个顺序查,通常能少走很多弯路。
5. 什么时候该换模型,什么时候该调脚手架
5.1 用错误类型做判断
一个简单有效的判断方法:把失败案例按错误类型归类。
| 错误类型 | 典型表现 | 优先优化方向 |
|---|---|---|
| 语义理解错误 | 模型把需求理解偏,任务方向不对 | 换模型或改进提示词 |
| 推理错误 | 理解正确,但方案设计有问题 | 换模型或增加中间反思 |
| 动作格式错误 | 模型输出无法被脚手架解析 | 优化脚手架解析器 |
| 反馈信息不足 | 模型重复生成相同错误命令 | 优化脚手架反馈结构 |
| 环境状态丢失 | 模型忘记当前目录或文件变化 | 优化脚手架状态管理 |
| 环境不稳定 | 命令执行结果不一致 | 修复评测环境 |
如果大部分失败来自前两行,换模型通常有效。如果来自后面四行,调脚手架才是根本解法。
5.2 模型能力仍然是天花板
脚手架的作用不能被无限放大。如果模型完全没有能力处理某个复杂推理任务,脚手架做得再多,分数还是上不去。它不能把一个不会写代码的模型变成能修 bug 的程序员。
但更常见的情况是:模型能力已经足够,只是在执行链路中反复丢分。脚手架的作用就是把“有能力完成”变成“稳定地完成”。模型的推理质量越高,脚手架越能放大它的稳定输出;反过来,脚手架越完善,对模型能力的下限要求就越低。
所以模型和脚手架不是对立关系,而是一条转化链:模型决定潜力,脚手架决定转化率。评测分数是最二者的乘积。
5.3 给不同人群的实用建议
如果你是个体开发者,刚开始用终端智能体。 不要一上来就定制脚手架。先选择脚手架设计成熟的终端智能体,理解它默认的反馈机制,再去调个别参数。很多工具默认配置已经处理好了命令解析和环境反馈,额外改装未必划算。
如果你是要做内部测评或工具选型。 建议建立一个自带的评测集,固定跑同一套任务,记录每个候选方案的中间动作。不要只看最终分数,要把失败步骤和模型当时的输入输出一起记录。这样才能判断分数差异是模型带来的,还是脚手架带来的。
如果你是做终端智能体研发的团队。 把日志、可观测性、错误分类当成基础设施来建设。脚手架不是一次性实现完就结束的功能,它需要随着评测数据持续迭代。每次失败案例都是一次脚手架优化的切入点。
5.4 一个值得长期关注的方向
终端智能体的评测还在快速演进。未来评测基准会越来越细化,从最终成功率扩展到成功率、稳定性、成本、安全、可解释性。到那时候,脚手架的作用会进一步凸显,因为单靠模型无法解释为什么这一步会失败,而脚手架能把执行链路每个环节的状态都暴露出来。
从长期看,终端智能体评测的最大价值不是告诉我们“哪个模型最聪明”,而是帮我们发现决策与执行之间的断层在哪里。模型负责想,脚手架负责做。真正拉高分数的,往往是让“做”这一层变得更可控。
如果你现在手头有一批终端智能体评测任务,分数还没有达到预期,我的建议很直接:先别急着换最新的模型。把一次失败任务从头到尾打开,看看模型到底看到了什么、缺了什么、在哪个位置反复出错。答案大概率写在脚手架里,而不是模型参数里。
很多提升不在别处,就在那层最容易忽略的脚手架上。