换个脚手架就掉 29 个点?DCAS:问题不在模型,在它没学会"怎么规划"
你有没有碰到过这种怪事:一个开源编程智能体模型,榜单上 SWE-bench Verified 刷了 52 分,看着挺能打。结果你把它从 OpenHands 搬到 Claude Code 或者 mini-swe-agent 里跑,分数直接崩掉——最狠的一个从 49.7% 掉到 20.4%,跌了 29 个点。
更诡异的是,没微调过的基座模型反而不这样。Qwen3-32B 基座在四个脚手架之间晃悠,分数也就上下浮动几个点,稳得很。
也就是说,这个"换环境就崩"的毛病,是微调亲手装进去的。模型没变笨,它只是把训练时那个脚手架的"做事套路"刻进了权重,换个套路就水土不服。
这篇 ASE '26 的论文(arXiv: 2608.06113)干的事,就是把这个现象掰开:套路里到底哪部分在起作用?答案是规划结构。然后作者造了一个叫 DCAS 的拦截层,在不改动任何脚手架的前提下,把任意后端模型塞进任意 CLI 智能体里做实验,最后用区区 576 条轨迹微调,就让模型学会了跨脚手架通用的规划能力。
核心摘要
开源编程智能体的训练轨迹几乎全在 OpenHands 单一脚手架下采集,导致微调模型换个脚手架就大幅掉分,而基座模型没这毛病。论文提出假设:脚手架之间最要命的差异是规划结构,并把它拆成两层——显式规划(动手前先产出一份计划)和隐式规划(agent loop 里每一轮的执行惯例)。作者实现 DCAS 拦截层做受控实验:只换计划来源,Pass@1 最多涨 15 个点;只用 576 条规划感知轨迹微调 Qwen3-Coder-30B,在没见过的 OpenCode 和 mini-swe-agent 上分别涨 3.4 和 7.0 个点。我的判断:这不是一个新模型或新 benchmark,而是一篇"诊断 + 药方"式的工作,诊断部分(跨脚手架掉分是微调造成的)比药方部分更有价值,576 条轨迹能涨这个幅度,数据效率确实漂亮。
论文信息
- 标题:DCAS: Decoupling CLI Agent Scaffolding to Internalize Planning across Scaffolds
- 作者:Kishanthan Thangarajah、Boyuan Chen(Centre for Software Excellence, Huawei Canada)、Ahmed E. Hassan(Queen's University)
- 发表:ASE '26,2026 年 8 月 6 日提交 arXiv
- 链接:https://arxiv.org/abs/2608.06113
📖 先搞清楚:脚手架到底是个啥
CLI 智能体脚手架,说人话就是把一个语言模型包装成"自动修 bug 的智能体"的那层外壳:它管理 agent loop、暴露工具集、组织多轮对话、决定什么时候收工。模型本身只负责"给定上下文输出下一个动作",剩下所有结构性的事都是脚手架定的。
论文里选了四个脚手架,刚好横跨了设计光谱:
| 脚手架 | 特点 | 规划方式 |
|---|---|---|
| OpenHands | 开源生态的事实标准,主流轨迹数据集(SWE-Gym、Nebius、SWE-Lego、CoderForge)都在它下面采集 | 规划分散在 CodeAct 循环里 |
| Claude Code | 闭源,和 Anthropic 模型深度绑定 | 有独立规划阶段(Plan Mode) |
| OpenCode | 开源、provider 无关的终端智能体 | 有专门的 Plan agent |
| mini-swe-agent | 约 100 行的极简脚手架,只有 bash 交互 | 完全没有规划结构 |
注意最后一行。mini-swe-agent 存在的意义就是个"压力测试"——它和 Claude Code 在结构上几乎处处相反,任何"跨脚手架泛化"的宣称都得在它上面站住脚才算数。
🔥 动机实验:掉分是微调装进去的
作者先做了一个很直接的测量:拿四个 30B–32B 级别的开源模型,在四个脚手架上各跑一遍 SWE-bench Verified(Pass@1,最多 100 轮),同时跑它们对应的未微调基座。
| 模型 | OpenHands | Claude Code | OpenCode | mini-swe-agent |
|---|---|---|---|---|
| Qwen3-32B(基座) | 29.0 | 23.2 | 18.4 | 8.0 |
| SWE-Lego-Qwen3-32B | 52.6 | 44.2 | 8.4 | 37.6 |
| SERA-32B | 54.2 | 33.4 | 35.8 | 26.4 |
| Qwen3-32B-Nex-N1 | 50.5 | 50.8 | 26.2 | 15.6 |
| Qwen3-30B-A3B-Instruct-2507(基座) | 25.2 | 26.8 | 27.6 | 14.0 |
| Nebius-SWE-Rebench-30B | 49.7 | 46.0 | 35.8 | 20.4 |
这张表里有三种翻车姿势,值得逐个看。
第一种是随脚手架距离衰减,最常见。Nebius-SWE-Rebench-30B 从 OpenHands 的 49.7% 一路掉到 mini-swe-agent 的 20.4%,29 个点的塌方。
第二种是工具调用格式不兼容,这个更惨。SWE-Lego-Qwen3-32B 在 OpenCode 上只有 8.4%——不是模型不会修 bug,是它输出的工具调用格式 OpenCode 的解析器根本认不出来,大部分轨迹里一个可解析的动作都没有。说实话看到这种死法我第一反应是:这到底是"能力问题"还是"接口问题"?作者自己也承认这块和规划是正交的,没完全剥离干净,这是后话。
第三种是上下文长度撞墙。SERA-32B 和 Qwen3-32B-Nex-N1 微调时用的是 32,768 token 的上下文,而 Claude Code 内部有个硬编码的上限,请求直接失败。作者用 YaRN RoPE scaling 做了长度外推才跑出分数。
关键的对比在两个基座模型上:Qwen3-32B 基座四档分数 29.0/23.2/18.4/8.0,Qwen3-30B-A3B 基座 25.2/26.8/27.6/14.0。每一行的内部波动都远小于任何一行的微调模型。这个对照实验做得干净——分歧是训练安装的,不是模型固有的。
还有一个反例值得单独说:Qwen3-32B-Nex-N1 是在多格式混合轨迹上训练的,不是单一脚手架。它在 Claude Code 上确实扛住了(50.8%,和 OpenHands 持平),但在 OpenCode 和 mini-swe-agent 上照样掉到 26.2% 和 15.6%。
"把多个脚手架的数据混在一起训"这个直觉解法,不 work。
🧠 核心假设:规划结构是承重墙
现象清楚了,接下来问为什么。作者的推理链条是这样的:
脚手架之间表面千差万别,但掀开看,大家跑的都是 ReAct 式的 act/observe 循环——模型出动作或规划,脚手架返回观察,循环往复直到完事。既然底层循环是共享的,一个在某个脚手架下训出来的模型,理论上具备在别的脚手架下运行的能力,缺的只是对那个脚手架"做事惯例"的熟悉度。
那惯例里哪部分最关键?作者引了 Liu 等人的一个旁证:对 16,991 条 CLI 智能体轨迹的分析显示,计划质量和计划遵循度是解决率的首要驱动因素。于是锁定规划结构,并且做了一个全文最重要的概念拆分:
- 显式规划(explicit planning):动手前先产出一份计划,作为一等公民的产物。Claude Code 的 Plan Mode、OpenCode 的 Plan agent 都是这个路数。
- 隐式规划(implicit planning):脚手架强加在 agent loop 每一轮上的结构性惯例——工作怎么拆成子步骤、什么时候停止探索开始动手、工具调用怎么串、失败了怎么重新规划。
这个拆分听着有点学究,但后面的实验证明它真的能拆出东西来。先记着这两个词。
hypothesis 一句话版:单脚手架微调把那个脚手架特有的"显式 + 隐式"规划组合刻进了模型,换个脚手架,这套组合就成了错配的枷锁。要解套,就得把规划从"脚手架的固定产物"变成"模型学会的能力"。
🏗️ DCAS:不动脚手架,换掉它背后的模型
要验证这个假设,需要三件当时不存在的能力:同一个后端模型能接进任意脚手架做受控对比;能在任意脚手架下采集"规划可分离"的轨迹;微调不碰脚手架本身。
DCAS(Decoupling CLI Agent Scaffolding)就是干这个的——一个后端替换拦截层,架在 CLI 脚手架和模型 API 之间,翻译请求和响应格式,让任何模型都能顶替原来的后端,而脚手架完全无感知。架构很直白:
Docker 容器(每个任务实例隔离)
└── SWE-bench 环境(克隆的 repo + 官方镜像依赖)
└── CLI 智能体脚手架(Claude Code / OpenCode / ...)
└── DCAS 拦截层(Interceptor/Router + Trace Logger)
└── 任意后端模型 API
轨迹记录用的是 claude-trace 这个开源工具,把 Claude Code 和后端模型之间的全部 HTTP 流量落盘成轨迹文件。DCAS 本身是在 Jim McMillan 开源的 SWE-bench runner 基础上扩展的,加了规划阶段分离、结构化轨迹记录和多脚手架支持。
坦白说,这个拦截层本身谈不上多大的技术发明——社区里早就有各种把 Claude Code 接到开源模型上的代理工具。作者的贡献是第一个把这套能力系统性地变成研究方法:用同构的环境做受控变量实验。论文里一句话说得很到位:既然每个 RQ 内部脚手架完全一致,观察到的任何性能差都只能归因于模型或计划,而不是脚手架。
🧪 RQ1:光换计划,能值多少分?
第一个问题刻意问得很"便宜":先不训练,只操纵显式规划这一个变量——保持执行模型和脚手架不动,只换计划的来源。如果杠杆很小,后面的大动作就不值得做了。
实验设定:执行模型固定为 Qwen3-Coder-30B-A3B-Instruct,脚手架固定 Claude Code 2.0.76,跑 SWE-bench Verified。三种条件:不给计划(no-plan)、模型自己先生成计划再执行(self-plan,计划限八步以内)、外部更强的模型生成计划喂给它(external-plan)。
| 计划配置 | Pass@1 |
|---|---|
| 无计划(基线) | 42.8% |
| 自己生成计划 | 48.2%(+5.4) |
| Qwen3-Coder-480B 做规划器 | 49.2%(+6.4) |
| Claude Opus 4.5 做规划器 | 54.2%–56.0% |
| Claude Sonnet 4.5 做规划器 | 57.8%(+15.0) |
看到 57.8% 这个数的时候我确实愣了一下。执行模型一行代码没改,只是开工前读了一份 Sonnet 4.5 写的计划,分数从 42.8% 蹿到 57.8%——15 个点的摆动,全部来自计划质量这一个变量。对比一下,前面观察到的跨脚手架掉分(比如 SWE-Lego 从 OpenHands 到 Claude Code 掉 8.4 个点)还没这个杠杆大。
还有个反直觉的发现:Opus 4.5 是更强的通用模型,但当规划器却输给了 Sonnet 4.5(57.8% 对 54.2–56.0%)。作者给的解释挺有道理——规划器和执行器之间需要 校准 这件事。更强的模型写计划时会不自觉地假设执行方有和自己相当的推理能力,30B 的执行器跟不上这种计划;Sonnet 的计划更步骤化、更直白,反而适配。另外 Claude Code 本身就是围绕 Sonnet 级模型调的,提示结构和轮次管理天然更搭。
这个发现对工程实践挺值钱:搭 planner-executor 架构时,别默认"规划器越强越好",规划和执行的能力档位的匹配度可能更重要。
🔧 RQ2:规划能不能训进权重里?
RQ1 证明了显式规划值钱,但那是推理时外挂的。RQ2 问:能不能让模型自己长出来?
数据采集:用 GLM-4.7(当时最强的开源权重模型,刻意不选前沿闭源模型,避免"蒸馏闭源能力"的混淆)通过 DCAS 接进 Claude Code,在 SWE-Lego Real Data(约 5,000 条已对 SWE-bench Verified 去污染的 GitHub issue)上以"先规划后执行"两阶段跑轨迹,拒绝采样只留补丁验证通过的,最后剩下 576 条轨迹。
576 条。对比 SWE-Gym、Nebius、CoderForge 那种几千到几万条的规模,小了一个数量级以上。
然后构造两个数据集变体,专门用来把两种规划拆开:
- PlanOnly:只用规划阶段的轮次(14,162 个样本)。测试光有规划惯例能不能迁移。
- Plan+Exec:规划 + 执行全要(36,259 个样本)。每条轨迹都是"显式规划 → 照着执行"的完整两阶段。
全参数 SFT 微调 Qwen3-Coder-30B-A3B-Instruct(LLaMA-Factory,学习率 4.0e-6,65,536 token 上下文),然后在 Claude Code 下测:
| 微调变体 | 推理时是否自规划 | Pass@1 |
|---|---|---|
| 基座(不微调) | 否 | 42.8% |
| PlanOnly | 否 | 53.8%(+11.0) |
| PlanOnly | 是 | 53.2%(+10.4) |
| Plan+Exec | 否 | 52.8%(+10.0) |
| Plan+Exec | 是 | 55.8%(+13.0) |
这张表是全文最漂亮的一张,两种规划真的被拆开了。
看 PlanOnly:训练数据里只有规划轮次,结果不给计划时涨了 11 个点——模型根本没机会用显式规划,涨的只能是隐式规划:子步骤怎么拆、工具怎么串、探索什么时候收手,这些惯例已经融进了它每一轮的行为里。反过来,给它自规划的机会反而没用(53.2% vs 53.8%),因为训练从没教过它"拿到一份计划之后该怎么用"。
看 Plan+Exec:不给计划涨 10 个点(隐式规划同样装进去了),给自规划再加 3 个点冲到 55.8%——因为训练数据里有完整的"计划 → 照做"示范,模型学会了把显式规划变成生产力。
你想想看这个对应关系:两个变体的不给计划成绩几乎一样(都装了隐式规划),只有 Plan+Exec 能从自规划中再榨出 3 个点(只有它的数据教了显式规划的用法)。两种规划在数据层面干净利落地分开了。这个实验设计我挺喜欢,用数据构成当手术刀,比事后归因的 analysis 硬气得多。
还有一组对比值得拎出来看:Plan+Exec 自规划的 55.8%,已经摸到了 RQ1 里 Opus 4.5 外挂计划的 56.0%,离 Sonnet 4.5 的 57.8% 也只差 2 个点——推理时不再需要外挂任何前沿规划器。
🚀 RQ3:学到的是"技能"还是"背书"?
这是证伪假设的最后一关。如果微调学到的是 Claude Code 2.0.76 的表面惯例,那换个版本、换个脚手架就该现原形;如果学到的是结构性的规划技能,就应该迁移。
作者把 Plan+Exec 模型(只在 CC 2.0.76 轨迹上训过)丢进三个从未见过的环境:
| 脚手架 | 条件 | 基座 | SFT 后 |
|---|---|---|---|
| CC 2.0.76(训练版本) | 无计划 / 自规划 | 42.8 / 48.2 | 52.8 / 55.8 |
| CC 2.1.73(更新版本) | 无计划 / 自规划 | — | 54.6 / 57.2 |
| OpenCode | 无计划 / 自规划 | 46.0 / 47.4 | 47.6 / 49.4 |
| mini-swe-agent | 无计划 / 自规划 | 35.4 / 37.2 | 39.2 / 42.4 |
两个维度都扛住了。
版本泛化:CC 2.1.73 换了工具 schema 和轮次管理,分数不降反升,自规划 57.2% 是全文不靠外部规划器的最高成绩。说明学到的不是 2.0.76 的表面细节,而是两个版本共享的底层规划结构。
跨脚手架泛化:OpenCode 涨 3.4 个点(自规划对基座无计划),mini-swe-agent 涨 7.0 个点。注意 mini-swe-agent 这个结果的分量——一个约 100 行、连工具调用接口都没有的纯 bash 脚手架,结构上跟 Claude Code 几乎处处相反,模型从没见过它,照样涨。一个只会"背脚手架表面惯例"的模型在这种环境下应该掉分,而不是涨分。
当然也有梯度:Claude Code 上涨幅最大(+13.0 到 +14.4),OpenCode 和 mini-swe-agent 上小一些。作者的解读是,结构性技能整体能带走,但能发挥多少取决于目标脚手架的惯例和模型内化的风格有多匹配。这个说法自洽,而且和模式吻合。
顺带一提一个诚实的失败记录:作者试了 Codex CLI v0.118.0,翻译代理所有调用都返回 200,但 Codex 二进制在 10 到 18 个来回后永久卡死,一个补丁都没产出,只能排除。这种"我们试了但不行"的细节写进论文,我反而更信其余的结果。
📊 我的判断
最值钱的是诊断,然后是那把手术刀。
这篇论文我最看重的地方不是 55.8% 这个分数——Kimi K2 靠大规模 agentic RL 能到 65.8%,绝对数值上这不是 SOTA。真正有价值的是三件事:
其一,把"单脚手架评测"打成了方法学缺陷。表 1 那个对照(基座模型跨脚手架波动小、微调模型波动剧烈)设计得干净利落,直接说明现在榜单上大量"模型 X 在 SWE-bench 上 Y 分"的宣称是不完整的——不报脚手架的分数,就像不报测试集分布的准确率。Qwen3-Coder-Next 的技术报告独立发现了同样的跨脚手架迁移问题,可以交叉印证。
其二,显式/隐式规划的拆分不是文字游戏,是可实验分离的。PlanOnly 和 Plan+Exec 那组对照,用数据构成做变量,把两种规划的贡献拆得清清楚楚。这种"机制级别"的分析在 agent 训练论文里不多见,大部分工作还停留在"数据多了分就涨"的层面。
其三,576 条轨迹的数据效率。如果结论成立——跨脚手架可迁移的信号主要由轨迹的规划结构承载,而不是数据量——那轨迹采集的经济学就变了:与其在规划贫瘠的脚手架下采几万条,不如在规划结构丰富的脚手架下采几百条。对预算有限的团队,选采集脚手架和选采集规模一样重要。说实话这个结论我很喜欢,但也要泼点冷水:576 条轨迹 + 30B 单模型 + 单 benchmark,这个组合下得出的"小数据够用"结论能不能外推到更大规模和更多任务类型,论文自己也承认是开放问题。
问题也有。 跨脚手架比较里,规划和工具 schema、轮次管理、上下文处理是纠缠在一起的,作者没有把这些维度逐一消融掉(SWE-Lego 在 OpenCode 上 8.4% 那种解析器级翻车,说明格式问题真实存在且不完全正交)。PlanOnly 的 no-plan 提升里,有多少来自"接触了成功轨迹"这种泛泛的域内曝光,缺一个 execution-only 的对照组。另外所有核心数字都建立在 Qwen3-Coder-30B 这一个执行器上,换家族换规模会不会重演,没人知道。
工程上怎么用? 如果你在做编程智能体相关的事,三条可以直接拿走:发布模型分数时至少报一个训练分布外的脚手架;搭 planner-executor 时把"规划器与执行器的能力校准"当成一等设计变量,别默认堆最强的规划器;采轨迹时优先考虑脚手架的规划结构丰富度,而不是只盯着数据量。DCAS 的代码、SFT 权重和 GLM-4.7 轨迹数据集都在 Zenodo 和 HuggingFace 上公开了,复现门槛不高。
回到开头那个 29 个点的塌方。这篇论文给的答案挺优雅:模型没有变笨,它只是只学过一种"做事的章法"。而章法这个东西,几百条好轨迹就能教会——前提是,你得先有个能看清章法的实验台。
觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我