背景与动机 SWE-Bench Verified 是用于评估代码型智能体的重要基准,包含大量容器化执行环境用于生成可复现的测试与修复轨迹。原始的预构建镜像集合由500个实例镜像组成,总计展开后的存储消耗达到约240 GiB。对于希望在云端或临时主机上大规模运行评估或生成训练数据的团队来说,这种体量既昂贵又耗时,尤其在受限于 Docker Hub 拉取速率时,可能需要数小时到三十小时不等才能完成初次准备。 面临的实际问题不仅仅是体积本身,更是多实例、多层次镜像构建逻辑导致的大量重复数据。我们的目标是将这个基准压缩到能够在分钟级完成下载和加载,并能被广泛分发到成百上千台短生命周期云实例上。最终目标是降低成本、提高可重复性并加速大规模 trace 生成流程。
镜像分层的根源问题 容器镜像基于分层文件系统的设计带来了共享与缓存优势。多个镜像可以共享相同的基础层,例如 ubuntu:22.04,从而避免重复存储。但分层机制也有一个重要的副作用:任何被修改的文件都会在新层中完全复制一次。SWE-Bench 的镜像构建流程是基于三层结构:统一的基础系统层(如 Ubuntu、Conda 等)、若干环境层(大约63种不同的环境依赖组合),以及500个实例层 - - 每个实例层包含对应仓库在某次提交下的工作树和构建产物。 虽然仓库在不同提交之间变化有限,环境层之间共享大量依赖,但常规构建仍然把每次 checkout 的完整副本放进单独的层中,导致成百上千个近乎重复的层文件被存储与分发,最终将展开后的镜像总量拉升到240 GiB。 delta layering:按时间链建立增量层 为了解决重复复制问题,我们提出并实现了称为 delta layering 的技术。
核心思想是按仓库时间顺序将实例串联成链:每个实例镜像不再包含仓库的完整副本,而只包含相对于前一个实例的差异(delta)。通过将每次提交引入的真实变更作为独立小层来组织,镜像的新增数据量从数百兆甚至数 GiB 降到了数兆或几十兆,使得整体镜像集合的冗余大幅减少。 实现细节包括为每个仓库建立按时间排序的实例链。由于 Docker(或 OCI)在镜像层数量上存在硬性限制,我们对实例数非常多的仓库(例如 Django)拆分成两条链以绕过单镜像层数上限 125 的限制。所有这些链共享一个统一的基础层,基础层包含真实全局通用的组件(如 Ubuntu 22.04、Conda 安装器、系统级依赖)。 为何不能仅用单一 checkout 并切换提交? 一种看似简单但不可行的替代方案是把仓库克隆到某个最新提交,然后在镜像运行时 checkout 到所需的旧提交。
然而这会在 .git 中保留未来提交的历史记录,而智能体在评估中可能通过读取完整历史来"作弊" - - 获取未来变更信息,从而破坏评估的公平性。因此镜像必须只包含到问题创建时止的历史数据,不能把未来提交信息暴露给评测主体。 git 历史与 packfile 的陷阱 Git 将对象存储为提交、树和 blob 的键值数据库。理论上,每次新增提交只会新增少量对象。如果所有对象以松散格式(loose objects)存储,那么 delta layering 完全可以仅捕获新增的对象文件即可。然而真实世界中 git 为了效率会生成 packfile,将成千上万的对象打包并进行跨对象压缩保存。
packfile 的存在使得很小的历史新增可能触发整个 packfile 的重新打包,从文件系统层面看就是生成了一个新的、体积巨大的文件。对于 Docker 层模型来说,这意味着你得把整个大 packfile 当作一个完整新文件写入层,进而抹杀掉增量层带来的优势。 为了克服这个问题,我们对 packfile 的组织方式进行了重构。具体做法是为每个实例生成一个单独的 packfile,仅包含该实例相对于前一个实例的新对象。这样虽然牺牲了部分 git 自身在跨对象压缩上的收益,但换来了按实例精细增量的层级,从总体上显著缩小了针对每个实例的新增数据量。 构建产物与缓存清理 许多镜像在构建时会遗留大量运行时其实并不需要的文件:安装器二进制(例如 Miniconda 安装器每个镜像多出约136 MB)、pip 和 conda 的缓存文件、临时构建产物等。
清理这些文件在节省空间上极其高效且成本极低。我们在镜像后处理阶段删除安装器、清空包管理器缓存、移除未被程序在运行时使用的构建临时文件,从而进一步减少了镜像层大小。 跨层压缩与排序优化 即便每个层都尽可能小,Docker 的层模型仍然会在单个文件发生少量变化时把整个文件复制到新层。压缩算法擅长发现跨输入的重复模式,因此跨层压缩(把所有层作为一个连续数据流来压缩)能够把原本在文件级别重复的内容"重叠"起来,从而实现更高压缩比。 我们选择 zstd 作为压缩工具,原因是其速度快、并行度好,并支持非常长的压缩窗口,有利于在大数据流中发现跨层的相似性。为便于压缩器发现相近内容,把层按照它们在时间链中的顺序排序,让几乎相同的层在输入流中相邻出现。
最终结果是:原本展开后约240 GiB 的镜像数据经过处理和重打包后,全部内容能够被压缩成一个只有约5 GiB 的档案。 压缩与解压性能 使用约100核并行压缩时,整个压缩流程大约需要十分钟;而解压却非常快,单核解压大概需要四十秒左右。这种快速解压属性对云端大规模部署非常重要:下载完成后快速解包并加载镜像能够将实例准备时间缩到几分钟之内。 我们实际使用的压缩命令示例(压缩时)为: zstd --T100 -19 --long=31 layers.tar 最终数据对比 压缩与重打包前后的体积对比很直观:原始的未压缩 Podman/OCI 层合计约240 GiB。经过重组和清理后,未压缩但重新组织的镜像集合约31 GiB。存入镜像仓库并进行普通压缩后约12.4 GiB,进一步用 zstd 做跨层压缩后最终档案约5.0 GiB。
完整流程与快速部署 为了方便用户在任意机器上快速完成准备,我们在 Hugging Face 上托管了压缩档案,并提供了加载脚本。由于 Docker 和 Podman 并不能原生加载经过 zstd 直接压缩的整包,我们提供了 helper 脚本来把解压输出直接交给 docker load。 最简的快速部署命令如下: curl -L -# https://huggingface.co/LogicStar/SWE-Bench-Verified-Compressed/resolve/main/saved.tar.zst?download=true | zstd -d --long=31 --stdout | docker load 在一台现代机器上,这个流程包括下载和解压通常可以在不到五分钟内完成,让你可以立即在云端或本地开始大规模评测或轨迹采集。 工程权衡与安全考虑 在实施这些优化时,我们做出了一些有意识的权衡。把 git packfile 拆分成每实例的 packfile 会牺牲一部分 git 对象间重用带来的压缩率,但能显著降低镜像层的冗余体积。这个权衡在我们的场景中是合理的:减小分发体量与加快部署速度为首要目标,而 git 内部压缩的微小收益远不及重复 packfile 带来的巨大成本。
另一个关键点是保持镜像中仅包含到目标时间点为止的 git 历史,防止智能体在评估过程中利用未来提交信息作弊。我们通过只将到问题创建时止的对象写入实例 packfile 来保证历史隔离。 可移植性与复用价值 delta layering 并非 SWE-Bench 专属的技巧,它适用于任何由大量相似快照构成的容器镜像集合。典型应用场景包括需要分发大量版本化的执行环境、课程与教学镜像库、测试矩阵中大量近似环境的场景等。通过重构镜像的层级结构、谨慎管理 VCS 数据与删除不必要的构建缓存,几乎任何大型镜像库都能获得显著的体积与分发效率改善。 另外,跨层压缩与层排序的做法对任何涉及静态镜像仓库归档备份的场景都适用:当你能够控制镜像打包的顺序以及全局压缩策略时,zstd 之类具备长压缩窗口的算法会带来额外收益。
实践建议 在尝试迁移类似方案时,以下实践可以减少试错成本并提高成功率:确保基础层尽量包含真正通用且不常变动的依赖;按仓库时间构建实例链以最小化每层新增;在构建后阶段清理所有安装器与包管理缓存;对 git 对象做显式管理以避免大 packfile 的重新打包;在进行最终归档前将层按链顺序整理以便跨层压缩最大化收益。对于极端多实例的仓库,考虑水平拆分链以规避镜像层数量上限。 影响与未来方向 通过这些工程优化,我们把 SWE-Bench Verified 的可分发体积从原本需要数小时才能准备的240 GiB(展开后)降到了一个单文件的5 GiB 档案,下载与加载时间大幅降低到几分钟。这项改进不仅让评估更加高效和友好,也为大规模训练数据生成铺平了道路。因为更低的分发成本与更快的部署速度,使得在短生命周期云实例上并行生成大量 agent 轨迹成为现实,从而扩展了可用于监督学习和强化学习微调的高质量数据源。 未来我们计划把这些方法论推广到更多基准与私有镜像库,提供更通用的工具链来自动化 packfile 重组、层差分打包与跨层压缩,同时探索可验证的历史隔离机制以在保证评估严谨性的前提下进一步减少冗余。
结语 将大型容器化基准的分发成本降到可被广泛接受的水平既是一个工程挑战,也是一个系统设计问题。通过理解镜像分层与 git 存储的低层行为、采取增量化的 delta layering、对 packfile 进行重构、清理不必要的构建产物并使用高效的跨层压缩,我们实现了从240 GiB到5 GiB的体量飞跃。这个结果不仅提升了 SWE-Bench 的可用性,也为其他需要分发大量近似环境的项目提供了明确且可复用的路径。 若需了解更多实现细节或获取我们的 helper 脚本与压缩包,请访问我们在 Hugging Face 的存储库或 GitHub 仓库,开始在你的环境中快速部署与评测。 。