ToRL: Scaling Tool-Integrated RL
Paper: arXiv:2503.23383 Code: GAIR-NLP/ToRL Code reference:
main@1db091d9(2025-05-24)
1. Motivation (研究动机)
现有 LLM 推理增强工作虽然已经证明 RL 能催生 backtracking、self-correction、reflection 等能力,但在需要精确计算、方程求解、枚举验证的题目上,纯 Chain-of-Thought 仍然容易在算术和符号操作上失真。另一条路线 Tool-Integrated Reasoning (TIR) 通过写代码并调用解释器缓解这一问题,但主流做法多数依赖更强模型蒸馏轨迹,再做 SFT;模型学到的是“人类或教师模型已经写好的用工具模式”,而不是自己在 RL 中探索“何时调用工具、调用几次、代码该写到什么粒度”。
论文 introduction 把这个缺口说得更具体:一类方法已经证明“语言内推理”可以借助 RL 扩展,但它们的优化对象仍主要是自然语言 token 序列;一旦题目真正需要精确求根、穷举、化简或者代数检查,模型即便 reasoning 结构正确,也可能在中间运算处出错。另一类 program-aided / code-based 数学模型虽然能把计算外包给解释器,却通常先假定“该怎么调工具”已经由监督样本规定好,因此没有回答一个更基础的问题:如果不给定固定模板,RL 本身能不能把 tool use 学出来。这也是作者为什么把 prior work 分成两条不完全相交的技术线。
一条是 o1 / R1 / Kimi 这一类“强化推理行为”的 RL scaling 路线,重点在长链推理、自我纠错和搜索式解题;另一条是 ToRA、MathCoder、Qwen2.5-Math-Instruct-TIR 这一类“把代码解释器嵌进解题流程”的 TIR 路线,重点在让模型会写并执行程序。ToRL 认为这两条线此前并没有被真正打通,因为现有 TIR 成果大多建立在 instruction tuning 或蒸馏,而不是从 base model 直接做 tool-aware RL。
这篇论文要解决的具体问题是:能否直接从 base model 出发,把“工具调用”本身纳入 RL rollout,让模型在奖励驱动下自主发现有效的 tool-use 策略,而不是建立在已有 TIR 指令微调模式之上。作者选择数学推理作为验证场景,因为这类任务既要求自然语言分解能力,也强依赖精确可执行计算,非常适合检验“推理 + 工具”闭环是否真正带来策略层面的收益。作者把目标定义得很窄但很关键:不是泛泛地做一个会调用 Python 的 agent,而是检验在自动可验证、答案空间明确、工具收益可直接测量的数学环境里,RL 是否会推动模型形成稳定的工具使用策略。这里“稳定”至少包含三层含义。第一,模型要学会把工具调用放在真正有收益的题目和步骤上,而不是逢题必调。
第二,模型写出的代码需要逐步变得可执行,否则 environment feedback 不能形成正向循环。第三,模型必须能根据 observation 改写后续 reasoning,而不是把工具仅当作答案后的装饰性核验。从实验设定也能看出这一问题的边界意识。作者没有一上来就讨论 browser、search、API orchestration 等复杂 agent 场景,而是刻意选了 code interpreter 这个反馈最干净的工具:输入是模型生成的 Python 代码,输出是确定性的 stdout / error message,奖励则可以直接回到最终答案正确性上。
这样做的价值在于把研究问题压缩成“RL 能否学会与外部执行器形成闭环”,而不是混入太多检索噪声、环境随机性或人工规则。
这个问题值得研究,因为它不仅关系到数学 benchmark 分数,更关系到 agent 类模型的策略学习范式。如果工具使用可以通过 RL 自发涌现,而不是靠监督指定模板,后续更复杂的 browser、API、simulator、robot-tool 调用也可能沿同样路线扩展;换句话说,ToRL 试图把“tool use”从 prompt engineering / SFT pattern,推进到 RL policy learning。作者实际上还在挑战一个默认前提:过去很多工作把“会用工具”视为先验能力,认为 RL 只能在这个能力之上做局部打磨;ToRL 则把这个前提拆掉,直接问 base model 能否从 reward 中学出工具偏好。
这一点很重要,因为如果答案是肯定的,那么 demonstration 在 agent post-training 中的角色就会从“定义行为上限”降为“提供 warm start 的可选项”。从 benchmark 选择也能看出论文的研究姿态并不是只追求 headline number。AIME、AMC、MATH500、Minerva Math、OlympiadBench 这些集合共同覆盖了竞赛数学、代数推导、数值计算与多步验证,恰好能暴露“语言推理正确但中间算错”以及“需要外部计算帮助”的问题形态。ToRL 在这些数据集上验证,实质上是在测一个更底层的问题:tool-augmented policy 是否比纯文本 policy 更能稳定跨越精确计算瓶颈。
论文摘要和引言都在强调一个更长远的含义:一旦工具调用能像 backtracking、self-correction 一样成为 RL 自发涌现的行为,那么“调用工具”就不再只是 instruction following 的格式能力,而是策略优化下的决策能力。这会改变我们理解 agent post-training 的方式。过去常见做法是先把工具协议固定,再让模型学会服从协议;ToRL 则暗示另一条路径,即把协议约束到最低限度,只保留必要的 stop token、执行环境和 reward,然后让策略自己学出何时探索、何时停手、何时复核。
对数学推理之外的意义也很直接。代码解释器只是最简洁的外部世界接口,如果这一设定下已经能观察到 strategic tool invocation、self-regulation of ineffective code generation、dynamic adaptation between computational and analytical reasoning 这些行为,那么它至少说明 RL 可以把“会不会用工具”从静态模板问题提升为可优化的行为分布问题。这正是论文把自己定位成 “Scaling Tool-Integrated RL” 而非单纯 “tool-augmented math reasoning” 的原因。 还可以把论文动机理解为对 post-training 目标函数的一次扩展。传统答案级 reward 只告诉模型“最后对没对”,却不区分这一路上是盲猜、硬算还是有效借助工具;ToRL 试图让环境交互过程本身也变成可以被优化的对象。对 agent 研究来说,这比单纯多拿几分 benchmark 更重要,因为它意味着 RL 未来可以直接塑造 action pattern,而不必先完全依赖 demonstration。
2. Idea (核心思想)
核心 insight 是:不要先把工具使用模式蒸馏进模型,再让 RL 只在既定模板上微调;而是直接在带 code interpreter 的环境里做 GRPO,让模型在 rollout 中自己试错,学会“什么时候该写代码、什么时候直接推理、什么时候复查工具输出”。工具调用不再是静态格式要求,而是策略空间的一部分。换句话说,ToRL 的新意不在“把 RL 和 Python 解释器接起来”这个表面工程动作,而在于重新定义 rollout 的状态转移。普通 RL reasoning 任务里,环境几乎只在序列末端给出一个 correctness reward;
ToRL 则允许 trajectory 在中途因为 tool execution 发生可观察状态更新,模型能看到自己写出的程序究竟跑成什么样,再决定下一步。这让 tool interaction 从单向生成变成了闭环决策。这一定义也解释了为什么论文反复强调 unrestricted exploration。若先用 SFT 把“标准 TIR 轨迹”灌给模型,再做 RL,模型实际探索的是一个已经被 teacher strongly regularize 的局部策略空间;而 ToRL 从 base math model 起步,允许模型自己发现某些题目根本不必写代码、某些题目只要一次短程序、某些题目则需要在 observation 后二次修正。
作者认为正是这种自由探索,才使后文分析里看到的若干 emergent behaviors 成为可能。作者的关键创新可以概括为两点。第一,把 TIR 环境直接接入 veRL rollout,令模型生成到output 前暂停,执行代码,再把 observation 续接回上下文继续生成。第二,在最终答案正确性奖励之外,额外引入基于代码可执行性的 penalty,并在训练中 mask 掉 sandbox observation token,避免模型去拟合冗长执行输出。
如果把这两点拆开看,它们分别对应“让工具反馈进环境”和“让学习信号不过拟合环境输出”。前者解决的是 credit assignment 能否跨越语言推理和外部执行;后者解决的是一旦环境能返回大量 stdout / stderr,模型会不会把学习能力浪费在模仿 observation 文本上。ToRL 选择只让 observation 影响后续决策,不让它成为 token-level 监督目标,这一点非常像 agent RL 中常见的 state/action 分离思想。论文还有一个隐含但重要的设计判断:工具调用成败本身应当具有独立价值。
作者观察到 code pass rate 和答案正确率存在相关性,所以 reward 不只看 final answer,也在代码不可执行时施加惩罚。即便公开脚本里这项 penalty 是否开启存在 paper-vs-code 差异,这个设计思路本身仍揭示了 ToRL 对工具使用的理解:一段无效代码不是中性的,它意味着策略在环境交互上走错了路。它与已有 TIR 方法的根本差异在于:ToRA、MathCoder、Qwen2.5-Math-Instruct-TIR 更偏向“给定或蒸馏好的工具使用模式”;ToRL 则是从 Qwen/Qwen2.5-Math-1.5B 这样的 base math model 起步,在 RL 过程中探索策略。
与“RL but no tool”基线相比,ToRL 不是只优化最终答案,而是把工具执行反馈显式纳入 trajectory。与现有方法相比,ToRL 最本质的区别其实是优化对象的层级不同。ToRA / MathCoder 一类方法主要优化“给定工具轨迹时,模型能否把这种轨迹学会并复现”;无工具 RL 方法优化“不给工具时,模型能否靠纯 reasoning 提高最终正确率”;ToRL 优化的则是“模型能否在交互过程中自己决定是否进入 tool-using mode,并把工具反馈转化为后续策略改进”。
这让它比单纯的 TIR instruction tuning 更接近 agent training,也比纯 reasoning RL 更贴近真实 external-action setting。
因此,这篇论文的核心思想可以压缩成一句话:ToRL 不是把工具当作 prompt 里的外挂能力,而是把工具当作 RL 环境中的可学习动作接口。只要这个动作接口足够稳定、反馈足够可验证,策略就可能从 base model 出发涌现出比手工模板更优的调用习惯。再进一步说,ToRL 实际上把“是否调用工具”与“调用后如何继续推理”统一成同一个 policy optimization 问题。模型并不是先在外部 planner 决定要不要调工具,再由另一个模块写代码;同一个语言模型同时负责生成 reasoning、触发代码块、吸收 observation 并收束答案。这种单策略闭环,使 emergent behavior 更容易直接归因到 RL,而不是归因到额外编排器或规则系统。
论文因此把 tool use 从“格式化能力”重新表述成“时机选择 + 交互修正”的联合决策问题。只有当模型既能判断某一步是否值得调用解释器,又能在拿到 execution result 后更新后续策略时,工具才真正进入了 policy class,而不再只是 prompt 中的固定插槽。这也是 ToRL 与许多 agent pipeline 的一个根本分野。很多系统通过外部 controller 决定是否调工具,再让语言模型完成局部子任务;ToRL 则把 controller 职责尽量内生到同一个策略里。对研究者来说,这样的好处是 credit assignment 更直接:性能提升可以更可信地归因于 RL 学出的行为,而不是复杂 orchestration 规则。
3. Method (方法)
Overall framework
论文没有给一个传统意义上的“模块框图”,最接近整体框架的是 Figure 1:上半部分展示 ToRL 与两类基线的训练动态差异,下半部分展示训练后出现的“先推理、再调用工具、再校验/反思”的行为闭环。结合 release code,可以把 ToRL 视为 prompt -> vLLM rollout -> detect tool call -> sandbox execution -> append observation -> continue generation -> terminal reward 的 RL 环境。
Figure 1 解读:上半部分不是单一 benchmark 柱状图,而是 1.5B 和 7B 两个规模上 AIME24 随训练步数变化的曲线;蓝线 ToRL 持续压过无工具 RL 基线与 Qwen2.5-Math-Instruct-TIR。下半部分给出一个典型行为链:模型先给出 reasoning,再通过 Python 工具求值;当工具输出与前述推理不一致时,模型会进一步反思并再次验证。这说明 ToRL 学到的不只是“会写代码”,而是“会利用代码反馈修正策略”。
从论文表述看,ToRL 的训练数据来自 NuminaMATH、MATH、DeepScaleR 题库,先过滤 proof-based 和难以自动验证的问题,得到 75,149 道可验证题,再用 LIMR 做 RL data distillation,最终保留 28,740 条训练样本。release 数据也与此对齐:data/torl_data/train.parquet 有 28,740 行,test.parquet 有 374 行,字段为 data_source / prompt / ability / reward_model / extra_info。
这里有一个方法层面的重点:作者并没有把所有数学题都平等地送进 tool-integrated RL,而是先要求题目能自动验证,再要求它适合用当前 reward pipeline 评估。这个过滤步骤决定了 ToRL 的学习问题是“在可判分环境中学策略”,而不是“在开放式文本任务中猜测偏好”。也正因如此,数学场景成为一个很理想的 tool-RL 试验台:最终 reward 清晰,工具反馈确定,中间代码是否可执行也能被稳定记录。LIMR distillation 在这里也不是普通的数据瘦身步骤,而是把原始可验证题集进一步压缩到更适合 RL 训练的样本子集。
虽然论文没有把筛选器内部机制展开到实现级细节,但从结果上看,它承担的是控制训练分布难度与可学习性的角色:既保留需要推理和计算协同的问题,又避免让大量噪声样本稀释 tool-use 策略学习信号。从方法论角度看,这一步也在帮助 RL 避开一个常见陷阱:如果训练样本里多数题目要么完全不需要工具、要么即使用工具也难以自动判分,那么策略很难学出清晰的调用边界。ToRL 先把问题空间收缩到“工具可能真的有帮助且 reward 可靠”的区域,再观察是否会出现稳定 tool-use behavior,这使论文结论更容易解释。
因此,ToRL 的 overall framework 不只是“模型 + 解释器 + reward”的三件套,而是一个经过数据筛选、模板约束、sandbox 执行和 mask 设计共同塑形的交互学习环境。论文的贡献恰恰在于把这些环节拼成了一个足以让工具策略涌现的最小闭环。
Key components
3.1 TIR trajectory and prompt template
论文在方法里把 TIR 描述为一个交替生成自然语言 reasoning、代码和 observation 的过程。注释掉的形式化写法是:
其中 是问题, 是第 轮 reasoning, 是该轮生成的 code, 是解释器执行返回的 observation。虽然这组三式在 main.tex 中被注释掉,但 release code 的实现与它高度一致:verl/workers/rollout/vllm_rollout/vllm_rollout_spmd.py 在 _tir_generate 中先 decode prompt,设定 sampling_params.stop=["```output"],一旦采样结果触发 tool pattern,就抽取代码,送入 code_interpreter_batch_call(),再把 observation 以 OBS_START ... OBS_END 的格式拼回 responses_w_res,随后继续下一轮生成,直到 tool budget 用尽或模型自然结束。 直觉上,这个设计的关键不是“多了一次函数调用”,而是把工具输出变成了策略反馈的一部分。模型不再只能在 hidden state 里“想象”计算结果,而是可以显式地看到环境返回,再决定是否继续推理、复写代码、还是直接收束到最终答案。 对应 prompt 侧,release code 通过 scripts/torl_1.5b.sh 将 data.template_type 设为 tir_base_0309,随后 verl/trainer/ppo/ray_trainer.py 在 RLHFDataset(...) 中把 template_type=self.config.data.template_type 注入数据管道。也就是说,prompt format 不是 README 里松散描述,而是由实际训练脚本与 dataset loader 联动决定的。 这一步与普通 ReAct 或 PAL prompt 的区别在于,ToRL 的模板不是单纯告诉模型“你可以写代码”,而是把 reasoning/code/observation 三种片段组织成一个可多轮展开的环境协议。模型一旦输出到约定的 tool-call 边界,rollout 就会暂时从 token 生成切换到 sandbox execution;environment 再把结果回填进同一条 trajectory。也就是说,模板在这里承担的不是 instruction aesthetics,而是 state machine interface。 因此,TIR trajectory 的关键价值在于把“中间计算”外显化。普通 CoT 即使也能写出类似“let us compute”之类的文字,它看到的仍只是自己先前生成的 token;ToRL 则能把真实执行结果写回状态,这使后续 token 真正条件化在外部世界反馈上。对 RL 来说,这比单纯增加 prompt 花样更接近环境交互。
3.2 Sandbox execution and observation masking
ToRL 的环境执行发生在 sandbox,而不是本地 PythonExecutor。虽然文件里同时 import 了 PythonExecutor 和 CodeInterpreter,真正的 released path 是 vllm_rollout_spmd.py 里的 send_request() 与 code_interpreter_batch_call():代码被包装成 {'code': ..., 'language': 'python'},并发发往 sandbox_url;成功时保留 stdout,失败时只保留错误末行。论文也明确强调这一点:为了缩短上下文并避免无关 traceback,训练中只保留最后一行错误信息,例如 NameError: name 'a' is not defined。 更重要的是,作者在 loss 计算时 mask 掉 sandbox observation token。release code 会在 _tokenize_and_find_mask_token_indices() 里定位 observation 字符范围,并生成 tool_output_masks;随后 verl/workers/actor/dp_actor.py 把 response_mask = response_mask & tool_output_masks。这意味着模型利用 observation 做后续决策,但 observation 自身不直接参与 token-level 学习信号,避免模型把冗长执行结果当成语言建模目标。 这一步的必要性很强。如果不 mask observation,模型会被大量执行输出“污染”:它既要学解题,又要学复述 stdout / stderr,训练信号会被拉散;而 mask 后,模型只为自己生成的 reasoning / code 负责,把 sandbox 返回当作外部环境状态。 从 agent 学习视角看,这相当于明确区分了“agent 产生的 action / thought token”和“environment 返回的 observation token”。两者都进入上下文,但只有前者参与 policy update。这个区分很关键,因为它避免了一个常见伪捷径:模型也许能通过记忆某类 observation 的表面模式来提高语言建模似然,却没有真正学会如何根据 observation 改变动作。ToRL 用 mask 把这种捷径切断了。 这也是为什么论文在 qualitative case 里强调“模型根据工具结果修正自己”,而不是强调“模型复述了工具结果”。如果 observation 不被 mask,训练目标会暗中鼓励后一种更容易的行为;mask 之后,唯一有利可图的路径是把 observation 当作决策依据,用它改善后续 reasoning 或 final answer。
3.3 Reward design
论文给出的 reward 由两部分组成: 其中 作者的理由很直接:他们在分析里观察到 code pass rate 与最终答对概率正相关,因此把“代码能否跑通”作为额外执行信号压入 reward。
这里必须写一个 paper-vs-code 差异。论文方法部分把 -0.5 execution-based penalty 当作 ToRL 的正式奖励设计;release code 也在 verl/workers/reward_manager/naive.py 第 72-73 行写了:
if self.config.data.execution_error_penalty and data_item.batch['execution_passes'].item() == 0:
score -= 0.5但默认配置 verl/trainer/config/ppo_trainer.yaml 将 data.execution_error_penalty: False,而公开的 scripts/torl_1.5b.sh 没有显式把它改成 True。因此,论文公式与 released code 实现差异:论文把执行错误惩罚作为默认训练设计写入方法;released code 虽然提供了该逻辑,但公开 launch script 没有显式开启,读者无法仅凭公开脚本确认论文主结果是否使用了这项 penalty。
3.4 Tool budget and exploration
tool use 的另一个关键控制量是最大调用次数 。release script 中 numcall=1,并映射到 actor_rollout_ref.rollout.num_llm_calls_available=$numcall。在 _tir_generate() 中,代码先复制 num_llm_calls_available,然后在 while num_llm_calls_available >= 0: 循环里逐轮生成;当 budget 降到 0 时,停止条件由 sampling_params.stop=["```output"] 变成 None,模型不会再触发新的工具执行,只能在已有上下文上收束回答。 这个设计使 tool call budget 既是 rollout horizon 的一部分,也是推理成本控制器。论文分析部分也指出:增大最大 tool calls 可以提升效果,但会带来显著计算开销,形成效率与性能的 trade-off。 这点体现了 ToRL 并不把“更多工具调用”默认视为更好策略。若无限放开 budget,模型完全可能把每一步都外包给代码,换来更高 token 成本和更慢 rollout;而把最大调用数显式纳入配置后,策略必须学会在有限交互次数内决定最值得执行的程序。换言之,ToRL 学的不是抽象的“会用工具”,而是在成本受限条件下的工具使用分配。 从策略优化角度看,tool budget 还提供了一个隐式稀疏化效应。因为每次调用都会消耗有限机会,模型会被迫比较“立即写代码”和“先继续语言推理”哪个更值。这种预算约束让 strategic invocation 更容易显现出来,也使后文分析里 code ratio、pass ratio、effective code ratio 的演化具有解释力。
把这几部分合在一起看,ToRL 的方法并不是在 PPO/GRPO 外层简单包一层 code executor,而是同时改写了 rollout 状态、可观测反馈、token mask、reward 组成和交互预算。也正因为五个环节是联动的,论文里的收益不能简单归因于“多了 Python”。真正被训练出来的是一种受约束的交互策略:在需要时调用工具、尽量写出可执行代码、读取反馈后继续推进,并在预算耗尽前完成答案收束。
Figure 4a-4d 解读:这组图总结了训练过程中 code 行为如何演化。(a) Code Ratio 在前 100 steps 内大约从 40% 升到 80%,说明模型越来越倾向于在适合的题上主动写代码;(b) Pass Ratio 持续上升,说明代码质量也在变好,而不是只增加工具调用次数;(c) 正确答案对应的代码通过率高于错误答案,支持“executability 与正确性相关”的奖励设计动机;(d) 有效代码比例上升,说明模型逐渐减少无意义或仅用于事后验证的代码片段。
3.5 Main benchmarks and ablations as part of the method story
方法是否成立,核心要看它是否真的比“无工具 RL”与“已有 TIR SFT 模式”更强。官方结果表明,在 1.5B 和 7B 两个模型规模上,ToRL 都优于这两条基线路径:
| Model | SFT/RL | Tool | AIME24 | AIME25 | MATH500 | Olympiad | AMC23 | Avg |
|---|---|---|---|---|---|---|---|---|
| Qwen2.5-Math-1.5B-Instruct | RL | No | 10.0 | 10.0 | 66.0 | 31.0 | 62.5 | 35.9 |
| Qwen2.5-Math-1.5B-Instruct-TIR | RL | Yes | 13.3 | 13.3 | 73.8 | 41.3 | 55.0 | 41.3 |
| ToRL-1.5B | RL | Yes | 26.7 | 26.7 | 77.8 | 44.0 | 67.5 | 48.5 |
| Qwen2.5-Math-7B-Instruct | RL | No | 10.0 | 16.7 | 74.8 | 32.4 | 65.0 | 39.8 |
| Qwen2.5-Math-7B-Instruct-TIR | RL | Yes | 26.7 | 16.7 | 78.8 | 45.0 | 70.0 | 47.4 |
| SimpleRL-Zero | RL | No | 33.3 | 6.7 | 77.2 | 37.6 | 62.5 | 43.5 |
| rStar-Math-7B | SFT | No | 26.7 | - | 78.4 | 47.1 | 47.5 | - |
| Eurus-2-7B-PRIME | RL | No | 26.7 | 13.3 | 79.2 | 42.1 | 57.4 | 43.1 |
| ToRL-7B | RL | Yes | 43.3 | 30.0 | 82.2 | 49.9 | 75.0 | 62.1 |
这些数字说明 ToRL 的收益并不只是单点 benchmark 运气,而是跨五个数学基准的系统性提升。尤其 7B 上 AIME24 的 43.3、AIME25 的 30.0、OlympiadBench 的 49.9,是论文在分析段反复强调的三个难点指标。
Pseudocode
下面的 pseudocode 不是从论文摘要复写,而是基于 release code 的真实控制流,主要锚定 verl/workers/rollout/vllm_rollout/vllm_rollout_spmd.py、verl/workers/reward_manager/naive.py、scripts/torl_1.5b.sh。
def tir_rollout(prompt_token_ids, sampling_params, tokenizer, sandbox_client, max_calls):
prompts = [tokenizer.decode(x, skip_special_tokens=False) for x in prompt_token_ids]
samples = [{"sequence": p, "response": "", "execution_pass": 0, "mask_info": []} for p in prompts]
sampling_params = copy.deepcopy(sampling_params)
sampling_params.n = 1
sampling_params.detokenize = True
sampling_params.stop = ["```output"]
remaining_calls = max_calls
while remaining_calls >= 0:
if remaining_calls == 0:
sampling_params.stop = None
remaining_calls -= 1
responses = llm_generate([x["sequence"] for x in samples], sampling_params)
tool_indices, tool_inputs = extract_tool_calls(responses)
if not tool_inputs:
append_plain_responses(samples, responses)
break
observations = sandbox_client.batch_exec([json5.loads(x)["code"] for x in tool_inputs])
for local_idx, sample_idx in enumerate(tool_indices):
obs_text, execution_pass = postprocess_observation(observations[local_idx])
samples[sample_idx]["response"] += responses[sample_idx] + "\n" + obs_text
samples[sample_idx]["sequence"] += responses[sample_idx] + "\n" + obs_text
samples[sample_idx]["execution_pass"] = execution_pass
samples[sample_idx]["mask_info"].append(find_observation_span(obs_text))
return build_tokenized_outputs(samples)def compute_torl_reward(response_str, ground_truth, execution_pass, reward_type, execution_error_penalty):
score = compute_score(
data_source="math",
solution_str=response_str,
ground_truth=ground_truth,
extra_info=None,
reward_type=reward_type,
)
if execution_error_penalty and execution_pass == 0:
score -= 0.5
return scoredef launch_torl_1p5b():
cfg = {
"algorithm.adv_estimator": "grpo",
"data.train_files": "../data/torl_data/train.parquet",
"data.val_files": "../data/torl_data/test.parquet",
"data.train_batch_size": 128,
"data.val_batch_size": 2048,
"data.max_prompt_length": 400,
"data.max_response_length": 3072,
"data.template_type": "tir_base_0309",
"actor_rollout_ref.model.path": "Qwen/Qwen2.5-Math-1.5B",
"actor_rollout_ref.actor.optim.lr": 1e-6,
"actor_rollout_ref.actor.ppo_mini_batch_size": 1024,
"actor_rollout_ref.rollout.n": 16,
"actor_rollout_ref.rollout.num_llm_calls_available": 1,
"trainer.n_gpus_per_node": 8,
"trainer.nnodes": 1,
"trainer.total_epochs": 300,
}
run_ppo(cfg)Code reference:
main@1db091d9(2025-05-24) — pseudocode and mapping based on this commit
| Paper Concept | Source File | Key Class/Function |
|---|---|---|
| TIR rollout 主循环 | verl/workers/rollout/vllm_rollout/vllm_rollout_spmd.py | _tir_generate, code_interpreter_batch_call |
| Sandbox 执行与 observation 回填 | verl/workers/rollout/vllm_rollout/vllm_rollout_spmd.py | send_request, postproc_observation |
| Prompt 模板注入 | verl/trainer/ppo/ray_trainer.py, verl/utils/dataset/rl_dataset.py | RLHFDataset(... template_type=...), tir_base_0309 |
| 训练入口与超参数覆盖 | scripts/torl_1.5b.sh | shell launch config for verl.trainer.main_ppo |
| 正确性 reward | verl/utils/reward_score/math.py | compute_score, is_equiv |
| 执行错误 penalty | verl/workers/reward_manager/naive.py | NaiveRewardManager.__call__ |
| observation masking | verl/workers/rollout/vllm_rollout/vllm_rollout_spmd.py, verl/workers/actor/dp_actor.py | _tokenize_and_find_mask_token_indices, response mask merge |
4. Experimental Setup (实验设置)
Datasets used and their scale
- 训练数据来自 NuminaMATH、MATH、DeepScaleR。论文先过滤 proof-based 与无法可靠自动验证的问题,得到 75,149 条可验证题;再用 LIMR distillation 压缩到 28,740 条最终训练数据。
- release data 与论文一致:
train.parquet28,740 条,test.parquet374 条,test_0524.parquet1,275 条。 - 评测基准是 AIME24、AIME25、MATH500、OlympiadBench、AMC23,覆盖高难竞赛数学与标准验证数学集。
Baselines compared
- 无工具 RL 基线:
Qwen2.5-Math-1.5B-Instruct与Qwen2.5-Math-7B-Instruct的 RL 版本。 - TIR 基线:
Qwen2.5-Math-1.5B-Instruct-TIR、Qwen2.5-Math-7B-Instruct-TIR。论文说明为公平比较,最大 tool calls 设为 1。 - 其他公开方法:
SimpleRL-Zero、rStar-Math-7B、Eurus-2-7B-PRIME。
Evaluation metrics
- 主指标是各 benchmark 的 accuracy。
- 训练动态分析还使用
Code Ratio、Pass Ratio、Correct/Incorrect Pass Ratio、Effective Code Ratio。 - 这些分析指标分别度量:是否生成代码、代码能否执行、代码执行成功与最终正确性的相关性、以及真正参与求解而非事后验证的代码比例。
Training config (model, hardware, hyperparameters)
- 训练框架: veRL + Sandbox Fusion,算法是 GRPO。
- 实际 launch script 来源:
scripts/torl_1.5b.sh,不是默认ppo_trainer.yaml。 - 模型:
Qwen/Qwen2.5-Math-1.5B。 - Batch / rollout:
data.train_batch_size=128,actor_rollout_ref.rollout.n=16,actor_rollout_ref.actor.ppo_mini_batch_size=1024。 - 长度设置:
data.max_prompt_length=400,data.max_response_length=3072。 - 优化器:
lr=1e-6,kl_loss_coef=0.0,algorithm.kl_ctrl.kl_coef=0.0,entropy_coeff=0。 - Tool setting:
data.template_type=tir_base_0309,actor_rollout_ref.rollout.num_llm_calls_available=1,temperature=1.0,top_p=1.0。 - 并行 / 资源:
trainer.n_gpus_per_node=8,trainer.nnodes=1,gpu_memory_utilization=0.85。 - 训练轮数:
trainer.total_epochs=300,save_freq=20,test_freq=4。 - 未明确说明: 论文与公开脚本都没有给出 GPU 型号;7B 模型的独立 launch script 也未在当前 release 中显式提供,因此 7B 资源细节只能部分推断,不能伪造。
为避免把 README 或默认配置误写成训练实参,公开脚本里的关键 override 可以直接按原名列出:
- 脚本原文包含
data.train_batch_size=128。 - 脚本原文包含
data.max_response_length=3072。 data.train_batch_size=128data.val_batch_size=2048data.max_prompt_length=400data.max_response_length=3072actor_rollout_ref.actor.optim.lr=1e-6actor_rollout_ref.actor.ppo_mini_batch_size=1024actor_rollout_ref.rollout.n=16actor_rollout_ref.rollout.num_llm_calls_available=1trainer.n_gpus_per_node=8trainer.total_epochs=300
5. Experimental Results (实验结果)
Performance numbers on main benchmarks
上表已经给出全部主结果。最重要的结论有三条:
ToRL-1.5B在五个 benchmark 上平均 48.5,相比Qwen2.5-Math-1.5B-Instruct-TIR的 41.3 提升 7.2 分;其中 AIME24 / AIME25 都从 13.3 提到 26.7。ToRL-7B平均 62.1,明显高于Qwen2.5-Math-7B-Instruct-TIR的 47.4,也高于无工具 RL 基线 39.8。- 论文特别强调 7B 在困难集上的表现:AIME24 43.3、AIME25 30.0、OlympiadBench 49.9。
Figure 3 解读:这张图展示 7B 模型在五个 benchmark 上随训练步数变化的动态,而不是静态终点柱状图。蓝线 ToRL 在所有 benchmark 上都高于红线无工具 RL 基线与灰色虚线 TIR baseline,说明收益并非只来自单个数据集偏好;尤其在 AIME24、AIME25、OlympiadBench 这些高难题集上,蓝线优势更明显,支持“工具作为 RL policy 一部分”而不仅是格式微调。
Key findings from ablation studies
Figure 5 解读:这组 ablation 聚焦两个变量。第一,最大 tool call 数 增大通常能继续提高最终性能,但训练与推理成本也同步增加,说明 tool budget 是一个真实的效率-效果 trade-off,而不是“越多越好”的免费增益。第二,reward 设计里引入 execution penalty 后,作者观察到更稳定的 tool-use 行为;但这一点在 release code 中受 execution_error_penalty 配置开关控制,公开脚本没有显式打开,因此读者应区分“论文主张的奖励设计”和“当前仓库默认配置”。
论文分析部分还给出几条更细的现象级发现:
- 训练前 100 steps 内 code ratio 从约 40% 升到 80%,说明模型快速学会在需要时主动用工具。
- pass ratio 持续提升,说明模型不只是“更常写代码”,而是“写得更能跑通”。
- 正确回答对应更高的 pass ratio,支撑作者把 executability 作为 reward 辅助信号的直觉。
Limitations of the method
- 论文和代码都表明 tool calls 带来显著计算开销;更高的 会增加 RL 成本。
- 公开 release 只明确给出 1.5B 训练脚本,7B 的完整训练脚本和硬件细节没有随论文同步给出。
- 论文声称使用 execution-based penalty,但 release 默认配置
execution_error_penalty=False,公开脚本也未覆盖,导致复现实验时需要额外确认论文主结果究竟使用了哪种配置。 - 任务范围目前集中在可自动验证的数学问题;方法能否平移到更开放的 agent tool-use 任务,论文没有给出直接实验。
Overall conclusions
ToRL 的核心贡献不是“把 code interpreter 接进 LLM”这件事本身,而是证明:工具使用可以作为 RL 中的可探索策略维度,而非必须依赖 SFT/蒸馏模板。实验显示这种探索式 tool-integrated RL 在 1.5B 和 7B 两个规模上都能显著提升数学推理,且伴随出现自发的策略行为,如交叉验证、自我纠错和按题型切换 reasoning / computation。若后续工作把同样范式扩展到更复杂工具,ToRL 可以被看成 agentic tool-use RL 的一个早期、干净且可验证的原型。