AI 博客

207,489 条开放轨迹买到的是脚手架,不是能力

开放的智能体轨迹语料是社区有史以来最好的微调数据,而几乎没有人在读它们里面到底装了什么:一条轨迹记录的是模型、执行框架与工具词汇表三者共同行动的结果,因此真正迁移过去的,很大程度上是那个框架的习惯。拿 OpenHands 的轨迹去微调,你得到的是一个在 OpenHands 里更强的模型——这和「更强的智能体」不是同一个论断,而你的评测分不出这两者。

作者 智能体 AI 维基 23 分钟读完

本周 NVIDIA 的 Open-SWE-Traces 又进了一批新的智能体轨迹,人的反射动作是把 207,489 条录制下来的运行读成 207,489 份智能体能力。它们不是。一条轨迹记录的是模型、执行框架与工具词汇表三者共同行动的过程,而其中大部分 token 是框架贡献的——所以拿它去微调,你稳稳买到的是一个在那个框架里更强的模型。这是一件真实且有用的东西。但它不是发布说明里说你买到的那件东西,而你的基准测试分不出这两者。

先看全貌

如今在编码智能体的有监督微调这一端,占据主导地位的是三套开放语料,全部由 NVIDIA 发布在 Hugging Face 上,条款允许商用。

语料轨迹数执行框架用途
SWE-Zero318kOpenHands面向问题修复循环的大批量指令微调。
Open-SWE-Traces207,489OpenHands + SWE-agent双模式:带显式思考的轨迹与不带思考的轨迹。
SWE-Hero34kOpenHands规模更小、筛选更严的一套。
Published trajectory counts in three open coding-agent corpora Horizontal bar chart. SWE-Zero trajectories: 318 thousand. Open-SWE-Traces: 207 thousand. SWE-Hero trajectories: 34 thousand. Below, a note that Open-SWE-Traces draws its 207 thousand trajectories from roughly 20 thousand source pull requests, so the count of distinct environments is an order of magnitude smaller than the count of trajectories. Recorded trajectories per corpus (thousands) SWE-Zero 318k Open-SWE-Traces 207k SWE-Hero 34k 0 80k 160k 240k 320k Trajectories are not environments Open-SWE-Traces: ~20,000 source pull requests → 207k trajectories Roughly ten recorded runs per environment. Diversity is measured on the smaller number.
标题上的数字是运行次数。真正框定这套语料能教什么的,是它下面那个「不同环境」的数量。

值得细读的是 Open-SWE-Traces,因为它的构造过程被记录得异常详细。它的 issue 陈述取自 SWE-rebench-V2,使用宽松许可证——MIT、Apache-2.0、BSD-2-Clause、BSD-3-Clause——覆盖约 20,000 个真实 pull request,横跨九种语言(Python、Go、TypeScript、JavaScript、Rust、Java、PHP、C 与 C++)。轨迹本身是合成的:它们是把另外一些模型放进 OpenHands 与 SWE-agent 框架里跑出来的,采用混合推理的分工——一个模型贡献带显式思考通道的轨迹,另一个贡献不带思考的轨迹。2026 年 8 月 26 日新增的那一批,又是由一对更新的开放权重模型生成的。

在往下走之前,先注意这种构造意味着什么。二十万条轨迹对应两万个 pull request,大约是每个环境十次录制。这套语料的多样性远低于它的标题数字,而真正要紧的那种多样性——不同的仓库、不同的构建系统、不同的失败形状——受限于那个较小的数。

一条轨迹里究竟装了什么

What a recorded agent trajectory contains One trajectory step decomposes into three layers. The task layer holds the issue statement and the repository state. The strategy layer holds reproduce, localise, patch and verify, and is portable across harnesses. The harness layer holds tool names and argument schemas, observation formatting and truncation, the scratchpad convention and the stop rule, and is bound to the harness that produced the trace. One step of a recorded trajectory Task layer Issue statement · repository state · gold patch Sourced from real pull requests. Reusable anywhere. Strategy layer — portable Reproduce the failure before editing Localise from the stack trace, not the filename Minimal patch, then run the suite This is the part you wanted to buy. Harness layer — bound to the recorder Tool names and argument schemas Observation formatting and truncation markers Scratchpad / thinking convention Retry, re-read and stop rules This is most of the tokens. Fine-tune, then run in the same harness Both layers land where they were recorded. Action tokens match the parser exactly. Benchmark score moves. Reported as: the model got better. Actually measured: the pair got better. Fine-tune, then run in your harness Strategy layer transfers. Harness layer becomes a wrong prior: tool names that do not exist here, argument shapes your parser rejects, a stop rule tuned to another loop. Reads on your dashboard as: a capability regression.
中间那层才是你想要的。底下那层才是你多半会拿到的。

打开一条轨迹,把它当文档读,而不是当数据集里的一行。你会看到三样东西的长串交替:模型生成的文字、它发出的动作,以及框架递回来的观测。任务——issue 文本与仓库——占据开头几百个 token。此后的一切都是框架的世界。

其中有些内容确实可以迁移。那套调试策略是:动手改之前先复现失败、从堆栈跟踪而不是文件名去定位、做出可能奏效的最小改动、跑测试、读清楚哪里坏了。这套策略是一项真实的能力,也正是你希望蒸出来的东西,而且它用的是普通散文,换到任何框架里含义都不变。

其余的部分不可迁移,而其余的部分占了绝大多数 token:

  • 工具名与参数 schema。一条轨迹记录的是 str_replace_editor 连同它确切的参数名——或者那个框架给自己的文件编辑器起的任何名字。在几千条这样的样本上微调过的模型,会形成一个强烈的先验:就发那一个调用。
  • 观测格式。文件内容回来时带不带行号、截断怎么标记、一段 400 行的测试失败被框架砍到 50 行之后长什么样。模型学会读的是某一种特定方言的输出。
  • 控制流约定。什么时候重读一个文件、一条命令失败后重试几次才换思路,以及最要命的——什么时候算干完了。停止规则是框架的属性,而模型把它当习惯吸收了进去。
  • 思考约定。Open-SWE-Traces 的双模式划分明确就是冲着这一点:一部分轨迹带显式推理通道,一部分不带,两者教出来的生成形态并不相同。

这些都不是对该数据集的批评。它就是任何智能体轨迹的诚实内容,因为你没有办法在不记录「智能体透过什么行动」的前提下记录它的行动。这也正是本站一再强调执行框架是一个自带行为的组件、而不是管道的原因;也是「一个不写明框架名的分数不构成对模型的论断」的原因。

它会导致的失败模式

你在 20 万条 OpenHands 轨迹上微调了一个开放权重模型。你把它放进自己的循环——那里有六个用你自己命名的工具,观测按你自己的格式返回。它的得分比基座模型还差,而失败看起来就像无能:畸形的工具调用、形状不对的参数、本该继续时却提前收工、偶尔还吐出一个它根本没有的工具的文本。

把这读成能力问题,你就会回头去折腾训练配方。这不是能力问题。这是一个把某种方言学得很流利的模型,如今被要求去讲隔壁那种方言;修法在接口,不在权重里。这与我们此前写过的一个头条智能体分数其实把脚手架也算了进去是同一个混淆:测量的单位是「模型 + 框架」这一对,而在这里,训练的单位同样是这一对。

污染的界线已经挪到了环境上

What each decontamination check can and cannot see Three columns. Answer overlap compares the gold patch against the training set and catches verbatim leakage. Solution overlap compares the approach and catches paraphrased fixes. Environment overlap asks whether the model has already navigated this repository, and no standard string check detects it — only held-out repositories do. Three things a benchmark task can leak Answer overlap The gold patch itself appears in training text. Detected by n-gram or substring comparison. Everyone checks this one. Solution overlap The same approach, in different words. Detected unreliably, by embedding similarity. Some teams check this one. Environment overlap The model has already navigated this repository: layout, test names, failure strings. No string check sees it. Held-out repos, or nothing. A trajectory corpus adds mainly to the third column, and the third column is the one nobody reports on.
轨迹语料压倒性地贡献在第三栏,而第三栏正是没人汇报的那一栏。

标准的去污染做法,是拿基准的答案去比对训练语料。当风险是「模型背下了某个 gold patch」时,这个设计是合理的。轨迹数据打破了这个前提,因为泄漏出去的并不是答案。

一条跑在某个仓库上的轨迹,教的是这个仓库。它教目录结构、测试文件的名字、构建报错的形状、哪个模块抛哪个异常,以及断言信息的具体措辞。一个读过某个项目十次录制运行的模型,等于被人带着逛了一遍这个项目——而它一次也没见过你即将用来评测的那个留出 issue 的补丁。你跑的每一项 n-gram 检查都会显示干净,而你得到的分数相对于一个模型从未见过的仓库仍然是虚高的。

这正是我们基准污染与泄漏那一页以一般形式给出的论点——污染是(模型,基准,日期)这个三元组的属性,而不是基准的属性——而开放轨迹语料是当下最锋利的一个例子,因为这次泄漏是一件合法、有许可、且被刻意公开发布的产物。

由此得出的是一套测试纪律,而不是一项指控:

  • 留出仓库,而不是留出任务。一个来自「已在你的轨迹配比里出现过的仓库」的留出 issue,在任何有意义的意义上都没有被留出。
  • 把交集报出来。公布哪些仓库同时出现在你的训练配比与评测集里。目前没人这么做,而它只需要一个脚本。
  • 留一片截止日之后的切片。来自「在你的语料快照之后才合入」的 pull request 的任务,是唯一在结构上干净的测量;而它每个月都在贬值,所以要为刷新它留预算。

法务真正会问的是来源

还有一个二阶问题,是许可证字段覆盖不到的,值得和技术论证清楚地分开来说。

Open-SWE-Traces 里的 issue 陈述带的是宽松开源许可证,数据集卡片说「可商用」时讲的就是这件事。而轨迹是另一件产物,有另一套来历:它们是把另外一些模型跑出来的。那些教师模型的条款关于「用其输出训练竞争模型」怎么说,就适用于这些轨迹 token,而这与底层 pull request 上的许可证不是同一个问题。当教师本身就是以宽松条款发布的开放权重模型——就像这里——答案通常是让人安心的;但那句话里的「通常」是在承重的,而这项检查只要五分钟,多数团队却因为数据集卡片说出了那个咒语而跳过了它。

同一观察的战略版本更有意思。如果开放智能体生态主要是在「由少数几个模型透过两个框架合成出来的轨迹」上训练的,那么由此产生的开放智能体多样性,会比模型发布的数量所暗示的要窄得多。所有人都继承同一套工具使用惯用法、同一套恢复习惯,以及同一批盲区——包括那些还没有人刻画出来的盲区。训练数据的单一栽培不会自我宣告;它会在数年之后,以「每一家厂商都有的一个相关联的失败」的形式浮现出来。

什么情况选什么

要决定的不是这份数据好不好。它很好。要决定的是你拿框架层怎么办。

你的处境这么做原因
你原样运行 OpenHands 或 SWE-agent直接用这套语料框架层不是税;它恰恰就是你想让模型学会的东西。
你跑的是带自有工具的自研循环训练前先把动作重渲染到你的动作空间一次机械的「名字 + schema」映射能收回大部分价值;这是一个脚本,不是一个研究项目。
你的工具在语料里找不到对应物在自己的框架里生成自有轨迹;开放语料只用来凑量,且配比要低重渲染需要一个渲染目标,而一个错误的映射比不映射更糟。
你是在评测而不是在训练把任何用这些数据训过的模型,都视为在 SWE-bench 系列仓库上受到了环境污染你干净的信号是「截止日之后、且仓库在语料之外」的任务。

无论你落在哪一行,汇报规则都一样,而且很便宜:每一次都把框架名写在分数旁边。一个不带框架名的数字,是对一「对」东西的测量,却把其中一半省略掉了。

常见问题

在开放轨迹语料上微调是个坏主意吗?

不是——它是当下性价比最高的智能体有监督微调数据;而对于「运行的正是录制这些轨迹的那个框架」的团队来说,它几乎是白送的能力。本文的论点是「得到的那个数字意味着什么」,而不是「该不该做」。

我能把框架层从轨迹里剥掉吗?

能剥掉一部分。工具名与参数 schema 可以机械地重映射到你的动作空间,这能收回大部分可迁移价值。观测格式与停止规则的习惯弥散在散文里,无法干净地分离出来。

这是不是说,微调开放模型公布的 SWE-bench 系列分数都是错的?

不是错,而是比读起来要窄。它们是对「模型 + 框架」的联合测量,且发生在训练数据通常造访过的仓库上。这两件事都会让数字相对于你那个全新仓库虚高,而两者通常都不会被披露。

对于不是我训练的模型,怎么检测环境污染?

在难度匹配的前提下,比较它在「出现于公开轨迹语料的仓库」上的得分与在「未出现的仓库」上的得分。差距大就是信号。这比审计训练集要弱,但它是你真正跑得起来的那一个。

207,489 条轨迹算多吗?

论运行次数,算多。论环境,那大约是 20,000 个 pull request——每个约十次运行。把运行次数堆过这个点,买到的是在已知环境里的稳健性,而不是对新环境的覆盖。

延伸阅读

本站相关:

信息来源: