Think Anywhere in Code Generation

Think Anywhere in Code Generation

论文pdf:https://arxiv.org/html/2603.29957v2

1. 这篇论文在讲什么?

一句话版:
这篇论文想解决一个很直观的问题:很多“会思考”的代码大模型,都是先想一大段,再开始写代码;但真实写代码时,难点往往会在写到一半才冒出来。于是作者提出:让模型可以在代码生成过程中的任意位置停下来想一下,而不是只在开头想一次。(arXiv)

作者给这个机制起的名字: Think-Anywhere
它的核心不是“让模型想得更多”,而是“让模型在该想的时候再想,而且只在需要的局部想”。论文把传统做法叫 upfront thinking,也就是“前置思考”;而 Think-Anywhere 是“边写边按需思考”。(arXiv)


2. 背景:为什么以前的“先想再写”不够好?

作者认为,代码生成和普通问答不太一样,至少有两个问题:

  • 问题 1:前置思考不够用。
    很多代码题在真正实现时,才会暴露细节坑点,比如边界条件、变量更新、返回值处理等。只在一开始做总体规划,往往不够。(arXiv)

  • 问题 2:思考资源分配不均。
    代码里有些地方很简单,只是样板;有些地方却特别难。如果所有思考都堆在开头,就没法精准把“算力”和“推理长度”放到真正困难的位置。(arXiv)

用人类写代码来类比:
人写代码时不会只在开头想一次。我们通常会先有一个大致方案,然后在写到关键语句、复杂表达式、返回逻辑时,再临时停下来想。这篇论文想让模型学会的,就是这种节奏。(arXiv)


3. 核心想法:Think-Anywhere 到底是怎么工作的?

3.1 基本形式

作者定义了两类“思考块”:

  • 开头的整体规划块:<think> ... </think>

  • 插在代码中间的局部思考块:<thinkanywhere> ... </thinkanywhere> (arXiv)

模型生成的不是“纯代码”,而是“代码段 + 思考段”混合在一起的序列。
最后要运行程序时,只需要把所有思考块删掉,剩下的就是最终代码。(arXiv)

3.2 训练模板在强制什么?

论文设计了一个模板,要求模型:

  1. 先输出一个初始思考块;

  2. 然后开始写代码;

  3. 如果写到某个位置需要推理,就在代码中插入 \<thinkanywhere> 块;

  4. 去掉这些块后,代码仍然要是合法、可执行的。(arXiv)

很适合小白的理解:
这有点像给模型装了一个“写代码时临时自言自语”的按钮,而且这个按钮不能乱按,按完还不能把程序骨架弄坏。


4. 理论方法:论文是怎么把这个能力教给模型的?

4.1 第一阶段:Cold Start(冷启动)

作者先用强推理模型自动造数据,得到大约 5000 条带 Think-Anywhere 结构的训练样本,再用这些样本做监督微调(SFT / LoRA),先把“会在代码里插入思考块”这件事教会模型。(arXiv)

这一步的意义不是让模型已经学会“最优地在哪儿想”,而是先让它具备“原来我可以在代码中途停下来想”的基本能力。(arXiv)

4.2 第二阶段:RLVR / GRPO(强化学习)

光靠模仿还不够,因为“该在哪儿想”这件事,本质上需要模型自己探索。
所以作者第二步用了 GRPO 做强化学习,让模型通过奖励去学会:

  • 什么时候该插入思考块;

  • 插在什么位置更有效;

  • 思考多长更合适。(arXiv)

4.3 奖励函数怎么设计?

奖励分成两部分:

  • 结构奖励:检查输出有没有遵守 Think-Anywhere 的格式,比如有没有初始 \<think>,以及代码里是否真的插入了 \<thinkanywhere>。(arXiv)

  • 正确性奖励:把去掉思考块后的代码拿去跑测试,如果通过测试就给奖励。论文中这个部分直接由 PassAllTests 决定。(arXiv)

论文给结构奖励设置了一个较小权重,文中写的是 (\alpha=0.1)。直觉上就是:先保证你真的在按这种格式思考,但最终仍然要以代码能不能做对题为主。 (arXiv)


5. 一个容易忽略但挺巧的点:专用触发 token

作者还尝试了一个变体:
不直接把 \<thinkanywhere> 当普通文本,而是做成一个专门的特殊 token(论文里记作 Think-Anywhere*),并且用“语义 + 分隔符角色”的方式初始化它,再分两步训练 embedding。

结果上,这个特殊 token 版本和普通文本版差不多,平均分 70.0 vs 70.3;但作者观察到,特殊 token 版在“思考插入位置”上更自然,不会总在很刻板的位置触发。不过因为后训练数据还不够多,它的潜力没有完全释放出来。(arXiv)


6. 实验设置

6.1 训练设置

  • 默认基座模型:Qwen2.5-Coder-7B-Instruct。(arXiv)

  • 训练语料:Skywork 数据集中的 14K 编程题。(arXiv)

  • 强化学习框架:VeRL。(arXiv)

  • 关键训练参数:batch size 128,mini-batch 64,学习率 (1\times 10^{-6}),训练 2 个 epoch;每题 rollout 8 个样本,最长 4096 token。(arXiv)

  • 硬件:8 张 A100 40G。冷启动数据由 Gemini 2.5 Flash 合成。(arXiv)

6.2 评测设置

论文评测了四个常见代码基准:

  • HumanEval

  • MBPP

  • LeetCode

  • LiveCodeBench (arXiv)

主指标是 (pass@1),也就是“只生成 1 个答案时,一次就写对的比例”。
为了统一比较,作者使用 greedy decoding,温度固定为 0。(arXiv)

6.3 对比基线

论文比较了两大类方法:

  • 推理增强方法:CoT、Self-Planning、Interleaved Thinking、GRPO。(arXiv)

  • 代码后训练方法:OlympicCoder、OCR-Qwen-7B、CodePRM、CodeBoost、CodeRL+。(arXiv)


7. 实验结果:最重要的表

7.1 主结果(Table 2)

下面是最值得记的几个模型结果:

方法

LeetCode

LiveCodeBench

HumanEval

MBPP

平均

Base Model

50.6

34.3

88.4

70.7

61.0

CodeRL+

63.3

36.9

90.9

76.2

66.8

GRPO

67.3

36.0

88.6

81.7

68.4

Think-Anywhere (Ours)

69.4

37.2

91.5

82.9

70.3

数据摘自论文主表。(arXiv)

7.2 该怎么解读这张表?

  • 相比 base model,Think-Anywhere 平均提升 +9.3。(arXiv)

  • 相比同样走 RL 路线的 GRPO,平均再高 +1.9。(arXiv)

  • 相比后训练方法里最强的 CodeRL+,平均高 +3.5。(arXiv)

  • 分 benchmark 看,提升尤其明显的是 LeetCode(+18.8)MBPP(+12.2),说明它对更需要中途推理和实现细节控制的任务很有帮助。这个差值是由表中数值直接计算得到的。(arXiv)

7.3 一个很关键的结论:光靠提示词不行

论文里有两个很醒目的对照:

  • Think-Anywhere (Prompting) 平均只有 56.9

  • Think-Anywhere (SFT) 平均 60.6

  • 真正完整版本 Think-Anywhere (Ours)70.3 (arXiv)

这说明这件事不是“提示词写漂亮一点”就能学会的。
模型必须经过先模仿、再强化,才能学会“在哪儿思考最有用”。(arXiv)


8. 消融实验:哪些部分真的有用?

论文在 LeetCode 上做了消融,结果如下:

变体

Pass@1

相比完整方法

Think-Anywhere

69.4

-

Only Cold Start

47.9

-21.5

Only RLVR

63.4

-6.0

Line-level Thinking

67.2

-2.2

No Upfront Thinking

66.6

-2.8

Padding Thinking

67.6

-1.8

数据摘自 Table 5。(arXiv)

这张表很有信息量:

  • Cold Start 和 RL 都重要。
    只做冷启动会掉很多;只做 RL 也不如完整版本,说明冷启动能帮 RL 稳住起点。(arXiv)

  • token-level 比 line-level 更好。
    也就是“允许在任意 token 位置思考”比“只能在整行边界思考”更强,说明作者的设计不是花哨,而是真的更细粒度。(arXiv)

  • 真正起主要作用的是代码中途的思考。
    去掉最开始的 upfront thinking,只掉了 2.8 分,说明最大的收益来自“写到中间时能停下来想”。(arXiv)

  • 思考内容本身有价值。
    \<thinkanywhere> 里的内容替换成 padding,性能还是会下降,说明不是“插个标记就行”,里面的推理内容确实在帮忙。(arXiv)


9. 进一步分析:模型会在什么地方触发思考?

9.1 它更喜欢在“不确定”的地方思考

论文做了一个 token entropy 分析。
这里可以把 entropy 粗略理解成“模型有多拿不准”。作者发现,Think-Anywhere 往往会在高熵位置插入思考块,也就是在更难、更不确定的地方暂停推理。(arXiv)

9.2 它更喜欢在什么语法结构附近思考?

Figure 2 显示,模型最常在:

  • 赋值语句(assignment)

  • return 语句

附近触发思考。作者的解释是:赋值常常涉及复杂计算或变量更新;而 return 前往往要再次确认最终输出是否正确。(arXiv)


10. 效率:它会不会更费 token?

很有意思,论文结论是:不会,反而更省。
因为 Think-Anywhere 缩短了开头那段很长的“预先思考”,虽然多插入了一些中途思考块,但总 token 数依然更少。(arXiv)

附录里给了更细的 token 成本拆分:

方法

HumanEval

MBPP

LeetCode

GRPO

309.4

325.2

440.7

CoT

348.8

372.0

577.0

Think-Anywhere

215.6 + 22.5

183.2 + 23.2

283.0 + 22.9

这里 Think-Anywhere 的两个数字分别表示:
前置思考长度 + 中途 \<thinkanywhere> 思考长度。(arXiv)

直觉解释:
与其一开始把所有可能的坑都脑补一遍,不如写到坑边上再想。像水流拐弯时才改变方向,比一出发就把整条河道都绕一圈,要聪明得多呀,博士。


11. 泛化能力:只在代码上训练,能迁移到数学吗?

论文还拿只做过代码训练的模型,去测了 AIME 2024 / AIME 2025 / HMMT 2025 这三个数学推理基准。结果 (pass@1) 分别是:

  • AIME 2024:17.3

  • AIME 2025:17.7

  • HMMT 2025:14.4

而 base model 对应是 5.3 / 4.0 / 0.0,GRPO 是 6.0 / 4.7 / 0.3。(arXiv)

这说明作者学到的东西不只是“代码技巧”,更像是一种按需触发推理的能力。(arXiv)


12. 跨模型泛化:换底座还行不行?

作者还在三种不同模型上做了实验,平均分如下:

模型

Base

GRPO

Think-Anywhere

Qwen2.5-Coder-7B-Instruct

61.0

68.4

70.3

Qwen2.5-Coder-1.5B-Instruct

40.6

51.9

54.5

LLaMA-3.1-8B-Instruct

38.4

42.0

43.8

数据摘自 Table 4。(arXiv)

其中小模型 Qwen2.5-Coder-1.5B 提升最明显,说明这种“把推理资源放到关键位置”的思路,对容量更紧张的小模型尤其有价值。(arXiv)


13. 我觉得这篇论文真正重要的地方

13.1 它改变的不是“会不会推理”,而是“推理的时间表”

过去很多方法是在比“谁开头想得更长”;
这篇论文在改的是:模型能不能把思考插到真正需要的位置。 (arXiv)

13.2 它把“推理”从一整段,变成了局部、可观察的行为

因为你能看到模型到底在哪些代码位置触发 \<thinkanywhere>,所以它的行为更容易分析,也更接近“可解释的动态计算”。(arXiv)

13.3 它对“推理成本”这个问题很有启发

很多人默认觉得“想得更多 = 更强”。
这篇论文的意思更接近:想得准,比想得长更重要。 这点从 token 成本下降但性能上升,体现得很清楚。(arXiv)


14. 这篇论文的局限,我会怎么理解?

14.1 “SOTA”要加前提

论文里的“最好结果”是建立在他们这套实验设定上:默认基座主要是 Qwen2.5-Coder-7B-Instruct,主指标是四个 benchmark 上的 (pass@1)。所以更准确的理解是:在这套比较里,它是最强的。 (arXiv)

14.2 效率只测了 token,没有直接测真实延迟

论文效率分析比较的是生成 token 数,这当然很重要,但它不等于端到端 wall-clock latency,也不完全等于真实部署成本。这个点论文没有展开得特别多。(arXiv)

14.3 方法依赖训练,不是“提示词魔法”

Prompting 版和只做 SFT 的版本都不强,说明这个能力需要额外训练成本。换句话说,这更像一种训练范式,而不是一个“随手可加的 prompt trick”。(arXiv)

14.4 特殊 token 版本还没完全长开

作者自己也承认,特殊 token 版没充分发挥,可能因为后训练数据有限。他们认为如果把这类 token 原生放进大规模预训练,潜力还会更大。(arXiv)


15. 论文自己给出的未来方向

作者在结论里明确提到两个未来方向:

  • 把 Think-Anywhere 扩展到代码之外的领域

  • 研究模型如何学会 “什么不该想”,也就是进一步优化“推理深度”和“计算成本”之间的平衡。(arXiv)


16. 从这篇论文出发,我觉得可以长出哪些新 idea?

下面这些是我的判断,不是论文原文。

Idea 1:做一个“预算感知版 Think-Anywhere”

让模型在推理时带着明确预算,比如:

  • 最多只能插 3 次 \<thinkanywhere>

  • 或者总 thinking token 不能超过 80

这样可以把“按需思考”进一步做成“按预算按需思考”。
这会更接近真实部署环境,也正好呼应论文最后提到的“学会什么不该想”。这个方向我觉得非常值得做。(arXiv)

Idea 2:把触发依据从“隐式学到”变成“显式混合控制”

现在论文观察到模型会在高 entropy 位置思考。下一步可以直接把:

  • token entropy

  • AST 节点类型

  • 单元测试反馈

  • 静态分析告警

这些信号合起来,做成一个显式的 think controller
也就是:模型不只“会想”,还知道“为什么这里该想”。这会让可解释性更强。(arXiv)

Idea 3:把 Think-Anywhere 和工具调用结合

现在它是在代码里插思考块。下一步可以让模型在某些位置不是只“想”,而是:

  • 调用静态分析器

  • 运行局部测试

  • 查 API 文档

  • 看历史相似代码

也就是从 think-anywhere 走向 tool-anywhere
一旦某个位置不确定度高,就触发“思考 or 工具”,这会很像真正的程序员工作流。

Idea 4:做“多尺度思考”

论文已经证明 token-level 比 line-level 好。
那可以继续往前走,做成三层:

  • token-level:处理局部表达式

  • statement-level:处理一条语句

  • function-level:处理整个函数策略

模型动态决定该开哪个层级。这样可能比单一粒度更稳,也更适合长代码场景。(arXiv)

Idea 5:把 Think-Anywhere 蒸馏给小模型

论文已经看到小模型收益很大。那就可以进一步:

  1. 先让大模型产生高质量 think-anywhere 轨迹;

  2. 再蒸馏给更小的 coder model;

  3. 最后甚至把显式思考块压缩成隐式策略。

这样可能得到“不一定把思考写出来,但已经学会在关键位置多算一步”的小模型。(arXiv)


17. 最后给小白的结论

如果你只记住 3 句话:

  1. 这篇论文不是在说“让模型想更多”,而是在说“让模型在写代码时,能在关键位置再想”。(arXiv)

  2. 这个机制确实有效:主实验平均分做到 70.3,超过 base、GRPO 和一批代码后训练方法。(arXiv)

  3. 它最有价值的启发是:推理应该是动态分配的计算,而不是一口气全压在开头。 这点可能不只对代码有用,对数学、Agent、工具使用,甚至更广泛的测试时计算分配都有意义。(arXiv)

博士要是愿意,我下一条可以继续帮你把这份笔记再压缩成 “5 分钟速读版”,或者整理成 更像课程讲义的版本