CodeRescue:代码报错不必换大模型,35%成本超越全量升级
CodeRescue: Budget-Calibrated Recovery Routing for Coding Agents
在软件工程智能体(Coding Agent)的研究与落地过程中,开发者正在经历从“单轮生成”到“环境交互”的范式转变。当智能体接管了阅读代码库、修改文件、调用终端与运行测试的完整链路后,代码生成不再是一锤子买卖。然而,伴随这一转变而来的,是极其沉重的推理成本负担。
ArXiv URL:https://arxiv.org/abs/2607.19338v1
传统的大模型降本策略大多依赖“模型级联”(Model Cascades):遇到任务先派轻量的小模型尝试,一旦失败或置信度偏低,就把整个任务移交给更强但也昂贵得多的大模型。这种直觉在传统问答或分类任务中十分有效,但在代码执行环境中却忽略了一个极其关键的事实——代码运行失败时反馈的并不是一个抽象的“错误标记”,而是包含编译器报错、断言失败、栈追踪(stderr)以及运行超时的结构化诊断信息。
代码报错并不一定意味着小模型能力不够,很多时候它只是欠缺了一次针对局部边缘条件的微调。如果一遇到报错就立即将任务上送给昂贵的大模型,往往不仅浪费了宝贵的执行反馈,也让大量的推理预算付诸东流。
针对这一症结,来自亚马逊、字节跳动、纽约大学与华盛顿大学的研究团队提出了名为 CodeRescue 的后失败恢复路由框架。该工作颠覆了“失败即升级”的固有认知,将代码执行失败后的应对机制建模为跨异构动作的决策路由,并引入共形风险控制(Conformal Risk Control, CRC)实现动态预算调节。实验结果显示,在 GPT-5.4-nano 与 GPT-5.4 的评测组合中,校准后的恢复策略仅花费大模型全量调用 35% 的平均成本,其任务解决率反而超越了“总是升级到大模型”的基准线。

代码报错之后:三种互补的救场路径
为了把计算资源花在刀刃上,首先需要理清代码失败之后智能体究竟能做些什么。以往的研究在不同局部方向上展开过探索,例如依靠报错信息进行自反思微调的 Self-Debugging 与 Reflexion,通过增加采样次数提升命中率的测试时计算扩展(Test-Time Compute Scaling),以及传统的模型级联。
CodeRescue 将这些零散的策略收敛为一个简洁但互补的最小一阶动作集合 $\mathcal{A} = {\texttt{reflect}, \texttt{replan}, \texttt{escalate}}$:
-
reflect(局部修复):保持小模型不变,将失败的代码、测试反馈以及标准错误流(stderr)重新输入给小模型,引导其定位异常并打补丁。这一动作开销极低,最适合语法小疏漏或特定边界条件的遗漏。
-
replan(重构思路):同样使用小模型,但彻底抛弃当前的实现路径,从小模型中重新采样生成一份全新的解决计划与代码。这种做法对应于小模型陷入了局部逻辑死胡同,但题目本身的算法难度并没有超出其能力边界的情况。
-
escalate(升级模型):将任务连同既有上下文移交给更强的大模型重新求解。这一动作成本最高,专门用来攻克小模型本身存在底层理解缺陷或算法复杂度瓶颈的硬骨头。
深入分析执行轨迹可以发现,这三者在解题空间上并不是简单的层级包含关系。换言之,动作的成功集合 $S(x)$ 并不是沿着成本单调扩展的梯子。在某些案例中,小模型在获得了明确的 stderr 堆栈反馈后,精准地修补了一处越界索引,成功通过测试;反而是直接升级后的大模型,由于重写了全部逻辑,反而意外写出了超时(TLE)的代码。正因为不同动作各有所长且开销悬殊,后失败决策的核心问题便从“该不该换模型”转化为了“当前上下文最适合哪一种恢复动作”。
监督路由器:从真实执行轨迹中学习选择
明确了动作空间后,接下来的挑战是如何训练一个能够根据报错特征做出明智裁决的轻量级路由器。
研究团队收集了涵盖 APPS、TACO、BigCodeBench、LiveCodeBench 以及 CodeContests 五大基准的 27,300 道编程题目,统一使用 GPT-5.4-nano 作为基础小模型进行初次求解尝试。经过沙盒评估环境的过滤,大约有一半题目在首轮便成功解出;另有大约 30% 的题目难度极高,后续尝试任何恢复动作均无法通过。剩下的则是极具价值的“可挽回失败样本”。
在这些失败样本上,研究团队分别运行了 reflect、replan(均由 GPT-5.4-nano 执行)以及 escalate(由更昂贵的 GPT-5.4 执行),从而观测到每个样本在各个动作下的真实成功状态集 $S(x) \subseteq \mathcal{A}$。
在离线训练阶段,样本的监督标签被定义为“能够解出题目的最廉价动作”:
\[a^{\dagger}(x) \in \arg\min_{a \in S(x)} c(a,x)\]如果某个错误通过便宜的 reflect 就能修好,即便 escalate 也能做对,系统也坚决将标签标注为 reflect,以此倒逼模型在能省则省的前提下完成修复。
路由器本身由一个参数量仅为 4B 的 Qwen3.5-4B 经全参数微调构建而成。输入给路由器的上下文 $x = (q, v_0, e_0)$ 经过了紧凑设计:除了包含原始问题陈述 $q$、首轮执行结果判据 $v_0$ 以及错误堆栈 $e_0$ 外,还拼接了一个简短的元数据头,注明题目来源、预估难度与算法类别标签。实验证明,这个元数据前缀对于判断任务难度上限至关重要,特别是面对那些没有输出详细报错、仅仅显示“Wrong Output”的测试用例,元数据能为路由器提供关键的全局先验。
路由器通过计算各个动作对应标签的平均 Token 对数似然,并经由 Softmax 归一化后,输出每个动作的预测得分 $s_\theta(a \mid x)$。未受预算约束的初始路由器,直接选择得分最高的动作:
\[\pi_0(x) = \arg\max_{a \in \mathcal{A}} s_\theta(a \mid x)\]然而,简单的最高分选择只能对应单一的成本与质量权衡点。在实际工程落地时,用户的预算是动态浮动的——业务低谷时可能希望追求极致的低成本,而在关键发布前夕则可能愿意为了提高 1% 的成功率不计代价。重新微调模型既不可行也无法实时响应,这便引出了预算控制层面的创新。
免重训的预算控制:引入 Conformal Risk Control
为了在不重新训练路由器的前提下任意滑动成本开关,CodeRescue 引入了标量成本惩罚因子 $\lambda \geq 0$。带惩罚的决策策略被重构为:
\[\pi_\lambda(x) = \arg\max_{a \in \mathcal{A}} \left\{ s_\theta(a \mid x) - \lambda c(a,x) \right\}\]在这个公式中,$\lambda$ 扮演了“紧缩杠杆”的角色:当 $\lambda = 0$ 时,模型追求纯粹的准确率;而随着 $\lambda$ 的增大,高成本动作(如 escalate)的净收益被迅速削减,决策被强力推向 reflect 和 replan 这类廉价动作。
显然,对于任意给定的输入样本 $x$,动作的选择成本 $C_\lambda(x) = c(\pi_\lambda(x), x)$ 关于 $\lambda$ 是单调非递增的。但这种单调性仅仅是定性的,在现实系统中,运维人员需要的是定量承诺:如果给定的每个任务恢复预算上限是 $B$ 毫美元(m$),系统应该把 $\lambda$ 设定在多少,才能确保平均支出不超标?
传统强化学习或带约束优化方案通常需要把预算 $B$ 硬编码在训练目标中,预算一旦变化就必须重新优化。而 CodeRescue 巧妙地借助了统计学中的共形风险控制(Conformal Risk Control, CRC)框架,利用一个留出的校准集(Calibration Set),在线性计算复杂度内完成了精确校准。
具体而言,假设有 $n$ 个独立同分布(或满足可交换性)的留出失败样本 $x_1, \dots, x_n$。对于候选网格中的每一个惩罚参数 $\lambda$,系统计算其在校准集上的经验平均成本:
\[\widehat{C}_n(\lambda) = \frac{1}{n} \sum_{i=1}^n c(\pi_\lambda(x_i), x_i)\]随后,CRC 选取满足如下保守上界条件的最小惩罚参数 $\widehat{\lambda}_{\mathrm{CRC}}$:
\[\widehat{\lambda}_{\mathrm{CRC}} = \min \left\{ \lambda \in \Lambda : \frac{n \widehat{C}_n(\lambda) + c_{\max}}{n + 1} \leq B \right\}\]该机制在理论上提供了严格的期望成本控制保证。定理表明,只要测试样本与校准样本满足可交换性假设,在部署阶段将惩罚项固定为 $\widehat{\lambda}_{\mathrm{CRC}}$,系统在新样本上的边际期望恢复成本绝不会超过预设的预算 $B$:
\[\mathbb{E}\left[ C_{\widehat{\lambda}_{\mathrm{CRC}}}(X_{\mathrm{test}}) \right] \leq B\]这意味着工程师只需要在部署前做一次离线网格扫描,建立一张“预算—惩罚因子”映射表。当上层应用的预算配额改变时,系统只需在内存表中做一次常数级查询,就能瞬间切换运行模式,完全不需要更新神经网络的任何权重。
实验评测:为什么小模型自救能反超大模型?
在严格留出的 360 个测试样本上,研究团队将 CRC 校准的 CodeRescue 路由器与固定动作基线、基于 Prompt 的零样本路由器以及传统的二元模型级联(Binary Cascade)进行了横向对比。
评估的核心指标包括两项:解决率(Solve Rate) 与 平均恢复成本(以毫美元 m$ 计)。需要特别指出的是,解决率并不等同于路由分类准确率——只要路由器选取的动作能够使测试用例最终通过,哪怕该动作并非全剧成本最低的动作,系统也会判定为成功解决。这种评价方式如实反映了软件工程对“交付可用代码”的终极诉求。
实验数据揭示出了一组极具说服力的对比结果:
在没有细粒度动作划分的二元级联模式下,系统只能在“小模型盲试”与“转交大模型”之间做二选一。然而,通过引入包含了局部修补与重新规划的三动作路由器,成本效益边界被彻底改写。
在平均每题 $2.56$ m$ 的适中预算下,CodeRescue 实现了 0.717 的测试解决率。对比之下,同等成本附近的二元级联基线解决率仅有 0.636。更关键的对照来自全量升级策略:如果无脑地将所有失败样本全部移交给强力的大模型 GPT-5.4 处理,其平均解决率为 0.686,而单题恢复成本却高达 $7.30$ m$。
也就是说,CodeRescue 在仅消耗大模型调用 35% 成本 的前提下,整体解题率反而高出了 3.1 个百分点。
这种“花更少钱、办更好事”的反常识现象,正是执行环境独有特性的体现。当一个小模型写的代码遭遇了断言错误时,带有时序和栈信息的报错实际上构成了极佳的上下文引导。小模型沿着原有的正确骨干结构打补丁,其成功率往往高于让大模型另起炉灶重新写一套全新代码。无脑升级大模型,实际上白白丢掉了前一轮试错所换来的信息资产。
消融实验进一步验证了路由器设计的各个细节:
如果移除输入上下文中的元数据前缀(题目来源、难度等),测试集上的解决率会从 0.697 骤降至 0.656,证明宏观先验与微观报错的结合至关重要;在底座选择上,全参数微调的 Qwen3.5-4B 显著优于 LoRA 微调版本以及早期架构,展现出了对长文本代码与错误日志更优秀的表征能力;而那些不经微调、仅靠 Prompt 驱动通用大模型充当路由器的基线,其表现甚至无法稳定超越固定的重写策略,证明了从真实 Rollout 轨迹中学习隐式错误模式的不可替代性。
从单向级联到测试时动作分配
CodeRescue 的价值不仅在于给出了一个高性价比的代码修补工具,更在于它向当前的 Agent 架构设计提供了一种全新的思路。
在过去的很长一段时间里,工业界处理复杂任务的直觉是“把模型做大”或“在推理链断裂时换更大的模型”。然而,随着测试时计算(Test-Time Compute)理论的演进,研究界越来越清晰地认识到:推理阶段的计算预算,既可以用来购买“更宽的参数”,也可以用来购买“更多的交互轮次”或“更精准的局部干预”。
在代码这类具备强执行验证机制的垂直领域中,计算维度的灵活性更为明显。编译器和沙盒不是冷冰冰的裁判,而是持续提供梯度替代信号的信息源泉。合理的软件智能体架构,应当把“大模型升级”视为昂贵且低频的保底手段,把廉价模型与诊断反馈的高频互动作为首选阵地。
当然,这项研究仍然具有明确的探索边界。目前的 CodeRescue 仅仅将恢复过程抽象为“单次失败后的单步裁决”,而在真实的软件工程 Agent(例如 SWE-bench 类的长程任务)中,修复动作往往是多轮交织的复杂动力学过程。此外,共形风险控制提供的是无偏的边际期望控制,并不能保证每一批次成本的绝对绝对刚性不超标。将这种细粒度的异构动作路由机制扩展到长程多步规划,并引入更加鲁棒的高概率安全界,将是下一阶段代码智能体走向生产环境的核心看点。