Doc2Harness:不考模型写代码,考模型"造 Claude Code"——第一个确定性 Harness 工程基准

核心摘要

过去一年大家应该都有一个体感:同一个模型,套进 Claude Code、Codex CLI、OpenCode 这些不同的"外壳"里,跑出来的分数完全不一样——Composio 今年 8 月把同一个 DeepSeek V4 Flash 接进 8 个 harness 跑同一批任务,通过率从 46.7% 到 66.7% 不等。外壳(harness)的重要性没人否认,但所有现有基准都在固定 harness 测下游任务,没有人测过模型能不能自己写出一个合格的 harness。中科院计算所这篇 Doc2Harness 把这个空白补上了:给模型一份融合了 12 家前沿 harness 文档的开发规范和一副只剩接口的骨架,让它在容器里从零造一个完整的编程智能体外壳,然后用 328 个确定性测试用例、由脚本化的 mock 模型驱动着跑分。结果挺扎心——19 个前沿模型里最强的 GPT-6 Astra 也只过了 53.05%,最弱的 27.44%,失败高度集中在"横向功能该拆进几个模块"这种架构决策上。这不是又一张刷榜排行榜,是把"造工具的工具"这个能力第一次变成了可测量对象。


论文信息

  • 标题:Doc2Harness: Benchmarking LLMs on Long-Horizon Harness Engineering of Coding Agents
  • 作者:Yuyao Ge、Shenghua Liu、Peizhong Gao、Chendi Ge、Chenxu Liu、Yiwei Wang、Lingrui Mei、Baolong Bi、Jiayu Yao、Jiafeng Guo、Xueqi Cheng
  • 机构:中科院计算所、清华大学、北京大学、东南大学
  • 链接:https://arxiv.org/abs/2610.20344 (2026 年 10 月 14 日提交,17 页,5 图 4 表)
  • 项目页:geyuyao.com/doc2harness

为什么需要这个基准:我们到底在测模型,还是在测外壳?

先铺垫一下背景。所谓 harness,就是包在模型外面的那层运行时——管交互循环、工具执行、上下文压缩、权限、会话状态、失败恢复。模型只是引擎,harness 是变速箱和底盘。这个判断现在几乎是行业共识,DeepSeek 官方跑分表底下那行脚注(所有代码智能体测试都跑在自家 Harness 的 minimal mode 上)更是把"分数绑定外壳"这件事摆上了台面。

但现有的评测范式有个结构性盲区。SWE-bench、Commit0、NL2Repo-Bench 这些软件工程基准,全都在固定 harness 下测下游任务成功率。顺着这个思路往下想,有个更根本的问题没人量化过:让模型自己去写一个 harness,它写得好吗?

你可能会说,写 harness 不就是写软件吗,SWE 基准不就够了?说实话我第一反应也是这个。但作者给了两个挺硬的反驳:

  1. 数据源层面:SWE 基准用的开源仓库大多是成熟项目,训练语料里可能反复出现过。而 harness 是这两三年才冒出来的新物种、高频迭代,测的是更纯粹的外推泛化能力。
  2. 评估层面:普通单元测试通过固定接口观察行为,但 harness 里同一个功能可以有无数种实现位置;而且 harness 自己不完成下游任务,它的正确性必须靠"用一个模型驱动它"才能体现。

这两条其实指向同一个结论——harness 工程是一种现有基准捕获不到的泛化能力。而且它还连着递归自我改进(recursive self-improvement)这条线:智能体执行任务、观察结果、修改自己的 harness、再用新 harness 跑下一个任务。这个循环的上限,取决于模型"造 harness"的能力。

Doc2Harness 怎么做:两阶段,每阶段钉死一个变量

整个基准的设计哲学一句话能说完:每个阶段固定一个组件,把混淆变量挤出去。

Stage 1(构建):被测模型(build model)跑在一个固定的 builder harness 里——统一用开源的 OpenCode 1.17.0,而不用 Claude Code 或 Codex CLI。为什么?因为后两者是模型厂商自家开发的,会给 Claude 系和 GPT 系模型送主场优势。这个细节我很欣赏,说明作者真想过公平性问题。

被测模型拿到两样输入:

  • 开发文档(development document):从 12 个前沿 harness(Claude Code、Kimi Code、Gemini CLI、Qwen Code、OpenCode、OpenClaw、Codex CLI、Aider、OpenHands、Cline、Crush、Cursor)的用户文档里爬出特性清单,合并去重后写成一条条自洽的"条款"(clause),覆盖内置工具、命令与子命令、权限与角色模式、全局 flag、会话内命令、持久记忆、约定文件、退出码、自动化评测 API 等 11 章。写文档时还要做消歧——比如 Claude Code 管上下文压缩叫 /compact,Cursor 叫 /summarize,文档统一采用前者;再比如 "plan" 在 Claude Code 里是只读权限模式,在 Aider 里却是 architect/editor 两种角色的分工,文档把这两件事拆成独立条款。
  • 契约骨架(contract skeleton):从 mini-swe-agent 精简而来,只有 5 个 Python 文件、约 70 行,外加安装脚本和入口脚本。它只声明三个不可变的外部契约:CLI、无头入口(headless entry)、驱动自动化评测的 API。所有实现全部掏空。

模型要在隔离容器里(无外网,只有模型网关)把这个骨架养成一个完整的候选 harness。一个代表性的成品大约 30 个 Python 文件、约 16000 行代码——其中 99.6% 是模型自己写的。每次提交时契约检查(contract check)只验形式不验行为:能否编译安装、命令是否齐全、有没有占位 stub、三个契约接口在不在。过了就冻结,没过就打回,每次构建最多提交 3 次、24 轮对话、2 小时墙钟。

Stage 2(评测):冻结后的候选 harness 被搬进一批新容器,每个测试用例一个容器,由脚本化的 mock 模型按预设剧本驱动它跑完整会话。为什么不用真模型?因为真 LLM 的不稳定性会污染测量——这一钉,整个评测就变成确定性的了。

评分看四个可观察通道:发给模型的请求、磁盘上的文件、进程退出码、命令输出。

测试用例从哪来:三种挖掘方法交替使用

328 个测试用例不是拍脑袋写的,背后是一套"产出驱动"的合成流水线,三种方法轮着来,当前方法边际产出下降就换下一种。

三种测试用例合成方法

图:三种测试用例合成方法。上——公共 issue 挖掘:收集 4 个前沿 harness 公开仓库的全部 issue,按与开发文档的相关度排序、按现象聚类、过滤掉无法转成确定性断言的候选;中——跨 harness 对比:把候选 harness 和成熟 harness(如 Kimi Code、Qwen Code)的源码按文档特性切成同一组模块,逐模块 diff 差异并回溯到条款;下——单变量扩展:以已证明有区分度的用例为种子,每次只改一个变量(工具、输入形式、错误参数、结果返回位置),距离检查合格的留作新种子。

几个数字值得记住:公共 issue 挖掘这一路拉了 Claude Code、OpenCode、Gemini CLI、Kimi Code 四个仓库的全部 issue,去掉 PR 后分别有 9868、5438、4831、946 条,合计 21083 条,人工从相关度最高的开始审,审到边际产出覆盖不了成本就停。

阳性对照:防"真空通过"的机制,这是全文最精巧的设计

这里我得停下来多说两句,因为阳性对照(positive control)是这篇论文最值钱的方法论贡献。

设想一个路径逃逸测试:mock 模型让候选 harness 用 read 工具去读一个指向工作区外的符号链接,断言是"外部文件内容不得出现在发给模型的请求里"。问题来了——内容没出现,可能是 harness 正确拦截了越界读取,也可能是它的 read 工具压根读不了任何文件。两种情况断言都成立,后者就是"真空通过"(vacuous pass)。

作者的解法:给测试用例配阳性对照。对照与用例共享场景和 fixture,只改输入或访问路径,选一个不受被测条款约束的情形,断言一个"只要观察前提成立就必然发生"的结果。上面那个例子里,对照复用同一个符号链接、改用 bash 工具访问,断言外部文件确实可读。bash 命令不在路径逃逸条款的约束范围内,所以如果对照失败,说明这实现连合法访问都做不到,用例的"通过"就是假的,直接判 FAIL。

真空通过有三种成因,对应三类对照:

对照类别 排除的假象 例子
通道(Channel) 工具结果根本没进入被观察的通道 用 read 读普通文件,确认内容会出现在请求里
同形(Same form) 不受条款约束的访问也做不到 同一符号链接走 bash,确认可读
反向(Reverse) 实现一刀切拒绝所有同类输入 换成指向工作区内的合法符号链接,确认能读

328 个用例里 130 个被至少一个阳性对照门控(114 个被 1 个门控、15 个被 2 个、1 个被 3 个),覆盖面最广的一个对照管着 6 个用例。对照本身不计入通过率。文件操作和权限类的对照覆盖率最高,交互终端最低——这也合理,终端行为更容易直接观察。

实验结果:最强的模型也挂了近一半

19 个模型、11 家厂商、每个模型独立构建 3 次、共 57 个候选 harness,跑 328 个用例。

19 个模型的通过率排名

图:19 个模型在 Doc2Harness 上的通过率排名,每根柱子是 3 次独立构建的均值。GPT-6 Astra 以 53 分居首,Hy3 以 27 分垫底,头尾相差约 26 个百分点。

完整主表(按类别分解,数字是百分比,All 列为 3 次构建的均值 ± 标准差):

模型 权限 (61) 文件操作 (75) 会话状态 (84) 直接驱动 (33) 无头 (40) 交互终端 (20) 其他 (15) 总通过率
GPT-6 Astra 71.6 46.7 44.0 39.4 45.0 91.7 60.0 53.05 ± 0.25
Opus 5 63.4 39.6 38.1 17.2 43.3 91.7 75.6 46.65 ± 1.80
Qwen3.8 Max 61.2 38.2 43.3 15.2 32.5 93.3 75.6 45.83 ± 0.14
GPT-5.6-sol 47.5 48.9 29.4 32.3 39.2 83.3 68.9 43.80 ± 2.44
Grok 4.7 54.6 50.7 28.2 22.2 33.3 78.3 75.6 43.50 ± 2.49
GLM 5.3 51.4 41.8 33.3 18.2 31.7 85.0 77.8 42.07 ± 1.39
MiMo-V2.6-Flash 50.3 40.4 37.7 14.1 33.3 76.7 75.6 41.87 ± 3.29
Step 5 Preview 47.0 45.8 34.1 14.1 25.8 76.7 77.8 40.75 ± 3.29
DeepSeek V4.1 Flash 49.2 36.4 34.5 14.1 31.7 81.7 80.0 40.24 ± 0.86
Hy4 preview 44.8 38.2 35.7 10.1 27.5 78.3 77.8 38.92 ± 1.46
Kimi K3 43.2 40.9 25.4 18.2 32.5 86.7 73.3 38.31 ± 1.28
Opus 4.8 30.1 48.4 33.7 12.1 22.5 83.3 80.0 38.01 ± 0.87
GPT-5.5 36.1 36.4 25.0 24.2 20.8 76.7 77.8 34.65 ± 1.42
Doubao Seed 2.1 Pro 41.5 36.0 15.5 20.2 18.3 83.3 73.3 32.62 ± 1.51
Opus 4.7 36.1 29.8 25.8 12.1 16.7 85.0 80.0 32.22 ± 1.66
DeepSeek V4 Pro 29.5 33.3 21.0 23.2 21.7 78.3 80.0 31.91 ± 1.23
Opus 4.6 20.8 37.3 21.8 25.3 3.3 60.0 80.0 28.25 ± 0.14
GLM 5.2 23.0 32.9 21.8 12.1 12.5 63.3 80.0 27.64 ± 1.01
Hy3 28.4 31.1 19.4 13.1 9.2 65.0 80.0 27.44 ± 0.43

这张表里有几个地方让我挺意外的。

榜首之争的胜负手在边界场景。 常规场景下 Opus 5 其实领先——80.68% 对 GPT-6 Astra 的 72.46%;但到了边界场景,Astra 拿下 48.97%,Opus 5 掉到 37.02%。差距全在边界条款的处理上。这个分裂很有意思:常规场景考的是"见过没有",边界场景考的才是"懂了没有"。

没有饱和,也没有失去区分度。 最强 53.05%、最弱 27.44%,26 个点的差距;56 个用例没有任何候选 harness 通过(地板用例),16 个所有都过(天花板用例),剩下 256 个真正决定排名。KR-20 内部一致性系数 0.913,说明这套题测的确实是一个统一的底层能力。

各类别难度差异巨大。 直接驱动类平均通过率只有 18.8%,交互终端类 79.9% 已接近饱和。权限类的强弱差距最夸张——50.8 个点,61 个用例里 51 个有区分度,是这套题里最锋利的尺子。

排名在不同类别间剧烈漂移,最多跨 16 个位次。 Grok 4.7 在文件操作上排第 1、会话状态上排第 11;Opus 4.6 在直接驱动上排第 3、权限上排第 19(倒数第一);Hy4 preview 会话状态第 5、直接驱动第 19。但注意,六个主类别的两两相关系数全为正、均值 +0.521——所以这不是"各有所长"的 trade-off,而是一维能力上的整体强弱,只不过单一类别上的排名噪声很大。做选型的话,看总分比看单项靠谱。

最有意思的发现:高分 harness 的架构长什么样

作者把 57 个候选 harness 的源码全解析了一遍,这部分我读得最认真。

先说规模。各模型的平均代码量从 1271 行到 16681 行不等(全部 57 次构建的范围是 1062 到 19208 行)。关键是——代码量差异的 95.9% 来自模型之间,而不是同一模型三次构建之间(同一模型三次构建的变异系数只有 12.7%,跨模型是 60.1%)。也就是说,写多长是模型的"性格",不是运气。代码行数和通过率的相关系数是 0.614(57 次构建层面),多写确实有用。

但真正漂亮的是接下来这个分析。控制代码量之后,模块数与通过率的偏相关是 -0.423(留一模型检验下稳定在 -0.484 到 -0.309 之间);而每模块行数与通过率的偏相关是 +0.504。翻译成人话:同样写一万行,把横向功能(权限检查、路径解析、错误处理这类到处都要用的东西)塞进少数模块里的,得分更高;拆得七零八碎的,得分更低。

碎片化不是免费的。

一个极端样本是 GPT-5.5:只写了 1271 行、2.7 个模块,最大模块占了 78% 的代码——这不是"高内聚",是根本没写完,34.65% 的通过率也印证了这点。所以结论不是"模块越少越好",而是在达到足够实现规模的前提下,横向功能的安放位置是拉开差距的架构决策。

跟同期工作比,这篇的位置在哪

harness 评测今年其实挺热闹。Claw-SWE-Bench 固定 prompt、预算、工作区,发现换 harness 能让分数移动 27.4 个点——和换模型(29.4 个点)几乎一样大;Harness-Bench 在 106 个沙箱任务上得出结论:能力应该按"模型–harness 配置"组合来报告。还有一派让模型基于现有 harness 和执行反馈去改造它,但收益无法和"多给点测试时搜索"分离开。

这些工作有一个共同点:都是从下游任务反推 harness 质量。下游任务要预先准备,驱动任务的真模型能力还会污染分数。Doc2Harness 换了条路——不测下游,直接逐条款裁决 harness 自身的行为是否符合规范,用 mock 模型把评测变成完全确定性。就我所知,这确实是第一个这么干的。

局限也得说清楚。协议测的是单次构建,不是"构建–评估–修复"的迭代循环;契约检查只验形式,早提交的模型拿到的通过率是下界而不是上界;单变量扩展产生的变体之间仍然相关,一个缺陷可能连带挂掉好几个用例。这些作者自己都承认了,还算坦诚。另外我自己有一点保留:开发文档是从 12 家现有 harness 的文档融合出来的,条款分布不可避免地偏向"现有 harness 已经做过的事",对全新形态的 harness 设计(比如多智能体编排、自我修改插件)覆盖不到。当然这可能是下一个基准的事了。

我的判断

这篇论文的价值不在排行榜本身——榜单半年就会过时——而在它把三个东西变成了可测量对象:

  1. 长程构建能力。16000 行、30 个文件、2 小时、24 轮对话的完整工程产出,比单函数补全或单 issue 修复更接近真实的软件工程。
  2. 确定性评测范式。mock 模型 + 四通道观察 + 阳性对照,这套组合拳对任何想测"工具类软件"的人都会有用。阳性对照这个设计尤其值得抄——它解决的"断言成立但功能没跑"问题,在几乎所有基于否定断言的测试里都存在。
  3. 架构质量的代理指标。模块碎片化与通过率的负相关,是给"什么样的代码结构算好"这个老问题添了一块实证砖头。

如果你在做 coding agent 相关产品,两个直接启发:一是别迷信单项基准排名,模型在不同能力面上的位次波动极大;二是如果你让模型生成大型工程代码,关注它怎么切模块——这个信号比代码量更可靠。

至于"模型造 harness"这条线能不能接上递归自我改进的叙事——现在下结论还早。53% 的通过率说明路还很长,但至少,我们终于有了一把尺子。


觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我