
大模型推理能力是怎么炼成的从 Reasoning Core 看程序化数据与完成监督训练先问一个很现实的问题当我们在评测一个模型的推理能力时它真的在“推理”吗如果你用过大模型做数学题、逻辑题或者代码题你会发现一个现象有些模型能被“问倒”不是因为它不会算而是因为它没有形成稳定的推理路径。同一个模型题目换一个数字、换一个表达方式结果可能就完全不同。这种不稳定性本质上不是模型参数不够多也不是算力不够强而是训练阶段喂给它的数据没能让它学会“一步一步想”这件事。正文开始前先给出我的判断推理能力不是靠模型规模堆出来的而是靠训练数据的结构逼出来的。而 Reasoning Core 这类方法走的正是“用程序化数据构造结构化推理过程再用完成监督来训练”的路线。这篇文章我会从数据生成、训练范式、评测验证、工程落地几个层面拆解这个思路并给出可以直接参考的实践路径。读完这篇文章你能搞清楚三件事为什么推理训练需要专门的程序化数据完成监督和普通指令微调到底差在哪如果自己想复现类似的训练流程应该从哪些环节入手1. 推理训练的核心矛盾模型会背题但不会推理在很多真实项目中我发现一个共性现象用公开 SFT 数据集微调后的模型在训练分布内的题目上表现很好但只要题目稍微变形准确率立刻大幅下滑。这不是过拟合那么简单而是模型根本没有形成“推理”这个能力它只是在做模式匹配。举个例子你让模型算“23 × 47”它可能能答对。但你把题目换成“每箱苹果 23 个47 箱一共多少个”它反而可能算错。这说明什么说明模型在训练时看到的数学题长什么样它就只能处理什么样。它没有真正理解乘法分配律、进位这些底层逻辑而是靠记忆题型来“猜”答案。传统 SFT 数据有几个天然短板数据量有限人工标注高质量推理链路成本极高一个带完整思维链的数学题答案可能比写这道题本身还费时间。推理路径单一人工标注者通常会给出“最标准”的解法但现实问题往往有多种解法模型只能学到一条死路。错误样本缺失真实推理过程伴随着试错、回退、修正但大多数 SFT 数据只包含正确的推理步骤模型没见过自己“走错路该怎么回来”。难度分布不可控人工标注的数据难度天然分散想专门训练某个难度层级的推理能力很难精确控制。这里就引出了这篇文章的核心概念程序化数据Procedural Data。所谓程序化数据就是用代码、规则、模板自动生成训练样本而不是靠人工手写。它最大的优势是无限量、可控制、可验证。你写一个生成器它可以产出几十万道不同难度的题每道题都带结构化的推理过程。这正是 Reasoning Core 这类方法要解决的核心问题。2. Reasoning Core 到底是什么先做个通俗解释。如果说传统的推理训练是在“喂鱼”——人工准备多少数据就吃多少数据那么程序化数据生成就是“造鱼塘”——写一套生成规则让数据自己源源不断地长出来。Reasoning Core 这个名字可以拆成两个部分来理解Reasoning目标很明确就是提升模型的推理能力而不是泛泛的对话能力或知识记忆能力。Core指的是一个“核心数据底座”这个底座是由大量程序化生成的、带完整推理链路的训练数据构成的。从命名和设计方向来看Reasoning Core 的核心思路可以概括为几个关键词第一个关键词是“Broad广度”。它强调的不是只做数学题而是覆盖尽可能多的推理形态。包括但不限于数学推理算术、代数、几何逻辑推理真假命题、条件判断代码推理给定输入输出推导代码行为常识推理基于常识进行多步推断策略推理棋类、博弈、规划这不是把不同类型的数据简单堆在一起而是从数据生成层就设计好分类体系和难度递进关系。第二个关键词是“Procedural程序化”。数据不是人写的而是程序生成的。这样做的好处在于数据数量不受人力限制推理步骤可以进行细粒度控制每道题都有确定的答案天然适合验证性评测可以通过修改生成参数自定义难度和领域分布第三个关键词是“Completion-Supervised完成监督”。这是训练层面的设计选择。和常规 SFT 的区别在于它不只是让模型学会“下一个词是什么”而是让模型学会“完成一整段推理过程”。我自己的理解是普通 SFT 更像是让模型做“完形填空”而完成监督要求模型从头到尾生成完整的多步推理轨迹。一个是记忆局部模式一个是构建全局推理链。3. 程序化数据、完成监督、推理训练之间的关系这三个概念经常被放在一起提但它们的层次是不同的。我用一张关系来说明概念层次作用类比推理训练目标层最终要提升的能力你想锻炼“解题能力”程序化数据数据层提供训练所需的样本你准备的大量“练习题”完成监督训练范式层定义模型如何从数据中学习你采用的“学习方法”也就是说程序化数据是原料完成监督是加工方式推理训练是最终目标。三者缺一不可。有一个容易被忽视的点程序化数据并不只能搭配完成监督使用。你也可以用它做偏好优化、做强化学习、做课程学习。但完成监督有它独特的好处——它足够简单且足够稳定。从工程角度看Completion-Supervised 的训练目标可以理解为输入一个带推理前缀的问题让模型生成完整的推理链和最终答案然后用标准的交叉熵损失反向传播。和强化学习相比完成监督不需要训练奖励模型、不需要做策略采样、不需要担心奖励黑客reward hacking问题。和纯 SFT 相比完成监督更强调推理链的完整性而不是简单的前缀-后缀映射。4. 为什么推理训练需要程序化数据这是文章想重点讨论的部分。不用程序化数据能不能做推理训练也能做但会遇到很多具体问题。4.1 人工数据的“天花板”我自己做过一个实验用 5000 条人工标注的数学思维链数据微调一个 7B 模型效果确实比 base model 好很多。但当你继续增加数据到 20000 条、50000 条时效果提升会越来越小。为什么因为人工标注的数据在“推理模式”上是收敛的。标注者习惯用类似的表达方式、类似的解题套路模型很快就学完了这些模式。数据量增加只是在重复同样的模式并没有带来新的推理形态。程序化数据打破了这个天花板。因为生成器可以随机化数字、随机化题目结构、随机化推理路径选择。每一条数据对于模型来说都像是一个“新题”而且是带详细解答步骤的新题。4.2 可控的难度体系推理训练最讲究的是“循序渐进”。上来就做高难度题模型大概率学不会因为它连基础步骤都没掌握。程序化数据可以做到这一点# 伪代码演示如何通过参数控制题目难度 def generate_arithmetic_problem(difficulty1): if difficulty 1: # 一位数加减法 a random.randint(1, 9) b random.randint(1, 9) op random.choice([, -]) elif difficulty 2: # 两位数加减法可能需要进位 a random.randint(11, 99) b random.randint(11, 99) op random.choice([, -]) elif difficulty 3: # 两位数乘法 a random.randint(11, 99) b random.randint(2, 9) op × # ... 更高级的难度继续扩展 return build_question(a, b, op)这只是最简单的例子。真正生产级的数据生成器会包含数论、代数、几何、逻辑推理等多个模块每个模块有自己独立的难度控制和规则体系。4.3 推理步骤可验证程序化数据的另一个杀手级优势是推理过程的每一步都可以检验。人工标注的思维链里有时候中间步骤是错的但最终答案碰巧对了有时候中间步骤是对的但表达方式有歧义。这些问题在程序化数据中几乎不存在因为生成规则本身就是可验证的。比如生成一道“鸡兔同笼”问题程序可以保证头的总数 鸡的数量 兔的数量脚的总数 2 × 鸡的数量 4 × 兔的数量生成的答案必须有非负整数解这相当于在数据源头就做了约束和验证从机制上避免了“标注错误”导致的模型学歪。5. 完成监督Completion-Supervised与目标函数设计现在聊聊训练层面。5.1 什么是完成监督用一句话概括给定一个包含完整推理过程的问题-解答对模型要学习从问题出发逐步生成完整的推理链条直到最终答案。这和普通的下一个词预测在数学形式上没有区别但在数据设计上有明确差异普通 SFT 数据用户问题 标准答案模型学会“直接给答案”完成监督数据用户问题 分步推理链 最终答案模型学会“先想后答”结构上完成监督的数据长这样{ instruction: 一个长方形的长是12厘米宽是8厘米求它的面积。, completion: 解长方形的面积 长 × 宽 12 × 8 96。所以长方形的面积是96平方厘米。, reasoning_steps: [ 识别公式长方形面积 长 × 宽, 代入数值12 × 8, 计算结果96, 加上单位平方厘米 ] }注意这里的关键区别completion 字段是模型要完整生成的内容reasoning_steps 可以在生成 prompt 时作为推理骨架注入也可以只用来构造 completion。不同实现策略会导致不同的训练效果。5.2 训练目标如果用 PyTorch Transformers 来实现标准完成监督训练核心代码如下import torch from transformers import AutoTokenizer, AutoModelForCausalLM from datasets import load_dataset model AutoModelForCausalLM.from_pretrained(your-base-model) tokenizer AutoTokenizer.from_pretrained(your-base-model) # 假设数据集中每个样本有 instruction 和 completion 两个字段 dataset load_dataset(json, data_filesreasoning_train.jsonl) def format_sample(example): # 将指令和完成序列拼接成一个完整的训练文本 full_text f问题{example[instruction]}\n解答{example[completion]} return {text: full_text} dataset dataset.map(format_sample) def tokenize_function(examples): return tokenizer(examples[text], truncationTrue, max_length2048, paddingFalse) tokenized_dataset dataset.map(tokenize_function) # 关键点这里不对 answer 部分做 mask # 让模型对整个问题推理过程答案序列计算损失 # 这就是 completion-supervised 的含义 def collate_fn(batch): input_ids [item[input_ids] for item in batch] # 动态 padding 到当前 batch 最大长度 padded torch.nn.utils.rnn.pad_sequence( [torch.tensor(ids) for ids in input_ids], batch_firstTrue, padding_valuetokenizer.pad_token_id ) labels padded.clone() # 将 padding 部分的 label 设为 -100不计入 loss labels[labels tokenizer.pad_token_id] -100 return {input_ids: padded, labels: labels} # 需要注意默认 causal LM 会对整个序列做交叉熵 # 所以 padding 位置必须 mask 掉这里有一个非常容易踩的坑我在实际项目中不止一次看到有人犯把 instruction 部分的 label 也 mask 掉了。从直觉上看你可能觉得“instruction 是输入啊不应该计算 loss”。但在因果语言模型的训练中我们是对整个序列做 next-token prediction。如果你把 instruction 的部分 mask 掉模型只对 completion 部分计算损失这种训练方式被称为 instruction-masked training。它和 completion-supervised 的区别很细微但很关键。问题是如果 instruction 是“问题”completion 是“解答”那么模型当然不需要对问题本身做预测。但实际上你在推理时是直接输入问题然后让模型生成。如果训练时完全不计算问题部分的 loss模型对问题文本内部的语义建模就会偏弱。在我测试过的场景中保留 question 部分的 loss 通常能获得更好的推理稳定性。原因可能是当模型需要对整个序列包括问题进行 next-token 预测时它对问题中的关键数字、条件描述会形成更精细的表示。5.3 训练超参设置建议完成监督训练和普通微调的超参不太一样重点在于学习率建议比普通 SFT 稍低推荐 1e-5 到 2e-5因为推理链本身就是结构化信息过大的学习率容易破坏原有能力。Epochs程序化数据量通常比较大不需要反复过拟合1 到 3 个 epoch 即可。如果数据量超百万条1 个 epoch 常常就够。Max length推理链通常较长建议设置 2048 或更长否则长推理链会被截断。Batch size受限显存时可以用梯度累积模拟较大 batch。一个参考的训练参数脚本# 训练脚本示例伪代码 deepspeed --num_gpus8 train.py \ --model_name_or_path Qwen2.5-7B \ --data_path reasoning_train.jsonl \ --max_length 2048 \ --learning_rate 1.5e-5 \ --num_train_epochs 2 \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 8 \ --output_dir ./reasoning_core_model注意这里的参数是基于常见的开源模型训练经验给出的参考值不同模型、不同数据量需要微调不建议直接照搬。6. 程序化数据生成从零构建 Reasoning Core 数据管线这一节是整篇文章最实战的部分。我会从数据生成器的设计角度拆解如何构建一个可扩展的程序化数据管线。6.1 数据生成器整体架构一个典型的推理数据生成器可以分为以下模块data_generator/ ├── base/ │ ├── __init__.py │ ├── problem.py # 题目基类 │ ├── solver.py # 解题器基类 │ └── verifier.py # 验证器基类 ├── generators/ │ ├── arithmetic.py # 算术题生成器 │ ├── algebra.py # 代数题生成器 │ ├── logic.py # 逻辑推理题生成器 │ ├── code_trace.py # 代码追踪题生成器 │ └── mix.py # 混合推理生成器 ├── difficulty/ │ ├── controller.py # 难度控制器 │ └── curriculum.py # 课程学习调度 ├── export/ │ ├── jsonl_writer.py # JSONL 数据导出 │ └── dashboard.py # 数据可视化统计 └── main.py # 入口脚本这种分层设计的好处是新增一种题型只需要新增一个 generator 文件不会影响已有模块。6.2 一个具体的生成器示例以“速度、时间、距离”问题为例展示如何写一个带推理链的生成器import random import json class SpeedTimeDistanceGenerator: 速度、时间、距离类应用题生成器 def __init__(self, difficulty1): self.difficulty difficulty self.templates [ 一辆汽车以{velocity}千米/小时的速度行驶了{time}小时求行驶的距离。, 小明骑自行车以{velocity}米/分钟的速度骑行{time}分钟后到达学校求家到学校的距离。, 一列火车从甲地开往乙地速度为{velocity}千米/小时需要{time}分钟到达求甲乙两地间距离。 ] def generate(self): 生成一道题及其完整推理链 # 根据难度选择数值范围 if self.difficulty 1: velocity random.randint(10, 60) time random.randint(1, 5) elif self.difficulty 2: velocity random.randint(40, 120) time random.randint(2, 8) else: velocity random.randint(60, 200) time random.randint(3, 12) # 更高难度可以加入单位换算 # 例如速度用 m/s时间用小时需要先换算单位 # 选择模板并填充 template random.choice(self.templates) question template.format(velocityvelocity, timetime) # 计算答案这里的计算逻辑就是解题器 distance velocity * time # 构造推理链 reasoning_steps [ f识别已知条件速度为 {velocity} 千米/小时时间为 {time} 小时。, f应用公式距离 速度 × 时间。, f代入数值距离 {velocity} × {time} {distance}。, f得出答案行驶距离为 {distance} 千米。 ] # 生成完整 completion completion \n.join(reasoning_steps) f\n所以答案是{distance}。 return { instruction: question, completion: completion, reasoning_steps: reasoning_steps, answer: distance, metadata: { type: speed_time_distance, difficulty: self.difficulty, velocity: velocity, time: time } } # 批量生成数据 def batch_generate(generator, n1000): data [] for _ in range(n): sample generator.generate() data.append(sample) # 实时校验确保答案无误 assert sample[metadata][velocity] * sample[metadata][time] sample[answer], Answer mismatch! return data if __name__ __main__: gen SpeedTimeDistanceGenerator(difficulty2) samples batch_generate(gen, n100) # 导出为 JSONL with open(speed_time_data.jsonl, w, encodingutf-8) as f: for sample in samples: f.write(json.dumps(sample, ensure_asciiFalse) \n) print(fGenerated {len(samples)} samples.)这个例子的关键点在于题目生成逻辑和解题逻辑是分离的。有问题模板、有数值随机化、有计算逻辑三个部分互不干扰。每次生成后可以校验。代码里显式检查了答案计算是否正确这是程序化数据相对人工数据最核心的优势。metadata 里保留了生成参数。这个设计在后续做难度分析和课程学习时非常重要。推理链是显式构造的不是事后靠 LLM 生成的因此每一句话都可以追溯到生成规则。6.3 为什么推理链本身需要“可追溯”很多时候我们用大模型来自动生成推理链即把题目丢给 GPT-4让它写解题步骤但这样得到的数据存在几个问题模型可能“编造”解题步骤导致步骤和最终答案对不上中间可能跳步导致训练出的模型不会做细粒度的逐步推理推理模式和人类写题很像多样性有限而程序化数据中推理链的每一步都是生成器根据规则“算”出来的可以做到精确到每一步的数值验证。这让数据质量有了确定性的保障。在实际工程中我建议的做法是优先使用程序化生成器构造推理链只在需要“发散性推理”的场景下才借助 LLM 辅助生成且必须用规则校验最终结果。7. 训练流程从数据到模型的完整链路程序化数据有了接下来就是训练。这里给出一个完整的、可运行的训练链路说明。7.1 数据准备格式推荐使用 JSONL 格式每行一个样本。字段建议包含{ instruction: 题目描述, completion: 完整的推理过程和最终答案, answer: 最终答案用于验证, metadata: { type: 题目类型, difficulty: 2, source: reasoning_core_v1 } }做训练的时候指令和完成序列拼成一个完整的文本然后做 tokenize。7.2 加载与预处理from datasets import load_dataset # 加载本地 JSONL 数据集 dataset load_dataset(json, data_filesdata/train.jsonl, splittrain) # 构建统一的训练文本格式 def build_prompt(example): return { text: f题目{example[instruction]}\n请逐步推理并给出答案\n{example[completion]} } dataset dataset.map(build_prompt) # 打乱并划分验证集 dataset dataset.shuffle(seed42) split_datasets dataset.train_test_split(test_size0.01) train_dataset split_datasets[train] eval_dataset split_datasets[test]7.3 训练主循环最终训练代码和标准 CausalLM 训练没有太大区别但有一个关键建议在训练过程中增加一个“推理链长度惩罚”或“步骤完整性检查”的可视化指标。更具体的做法是在训练日志中记录每个 batch 的平均完成序列长度并抽检生成结果的推理步骤数。原因在于推理训练最怕的不是 loss 降不下去而是模型学会了“偷懒”——明明要求分步推理它却直接给答案。如果发现平均生成长度明显变短说明模型在退化需要调整数据分布或检查训练目标。8. 评测方法怎么验证推理能力真的提升了训练完模型不能只看 loss 曲线。推理能力必须用专门的评测集来验证。8.1 评测集设计原则评测集应该满足三个条件分布外测试评测题目的数值范围、模板表达最好和训练集不同这样才能测出真正的推理能力答案可验证推理题的最终答案必须是确定性的能精确判断对错难度分档评测时把题目按难度分开统计这样能看出模型在哪个层级开始崩溃对应地你可以构造这样的评测集# 评测样本示例 eval_samples [ { question: 一个货车以 58 千米/小时的速度行驶了 3.5 小时再以 42 千米/小时的速度行驶了 2 小时总路程是多少, answer: 203 84 287, difficulty: 3 }, { question: 甲、乙两地相距 360 千米一辆车前半程速度为 60 千米/小时后半程速度为 90 千米/小时全程平均速度是多少, answer: 72, difficulty: 4 } ]注意第二个问题是一个典型的“陷阱题”不能简单算 60 和 90 的平均值。这种题能区分“模式匹配型选手”和“真正理解速度公式的选手”。8.2 评测执行脚本import re def evaluate_model(model, tokenizer, eval_samples): 简单评测让模型生成答案与标准答案做比对 correct 0 total len(eval_samples) for sample in eval_samples: # 构造 prompt prompt f题目{sample[question]}\n请逐步推理并给出最终答案 inputs tokenizer(prompt, return_tensorspt).to(model.device) # 生成 outputs model.generate( **inputs, max_new_tokens512, temperature0.0, # 评测时用贪心解码保证确定性 do_sampleFalse ) generated_text tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokensTrue) # 从生成结果中提取最终答案 # 这里可以设计更复杂的提取规则例如找答案是XXX match re.search(r答案[:]\s*([\d.]), generated_text) if match: predicted match.group(1) # 比较数值是否一致 if abs(float(predicted) - float(sample[answer])) 0.01: correct 1 return correct / total代码本身不难但有几个细节需要强调评测时 temperature 一定设为 0否则同一个模型同一道题两次评测结果可能不同没法做横向对比答案提取规则要统一如果训练数据格式是中文冒号评测时的正则也要对应生成的 max_new_tokens 要足够长否则链式推理到一半被截断明明会做的题也会被判错9. 常见问题与排查方法我在实践过程中整理了几个高频问题按出现频率排序问题现象可能原因排查方式解决方案训练 loss 下降缓慢数据难度曲线过陡大量高难度题让模型无法收敛按 difficulty 分桶统计 loss调整课程学习策略加大简单题比例模型生成空答案Max sequence length 不足推理链被截断检查训练数据平均 token 长度增大 max_length 或压缩推理链表达模型只给最终答案不生成推理过程数据中混入了非推理类 SFT 数据抽查训练数据中无推理链样本占比过滤掉无推理链的数据评测分数低于 base model推理数据占比过高破坏模型基础能力对比 base model 在通用任务上的表现降低训练 epoch或混入通用指令数据生成结果中数值正确但步骤错误训练数据中推理步骤与答案不一致检查生成器的校验逻辑加强数据生成端的 verification复杂推理崩溃简单推理正常高难度题数量不足或难度跨度太大按 difficulty 统计评测准确率找到崩溃阈值增加中间难度数据做平滑过渡最典型的坑是“数据难度曲线和课程顺序设计不合理”。很多做数据生成的人容易犯这个错一口气生成大量高难度题结果模型训练后只能做中等难度题高难度题反而因为训练时就已经崩溃导致产生负面影响。10. 最佳实践与工程建议10.1 数据层面推荐“广度优先深度跟进”的策略。先用程序化生成器覆盖尽可能多的推理类型再针对核心场景比如数学题、代码推理单独加深数据量。Reasoning Core 这个名字里的 Broad 就是在强调广度优先这一层。另外程序化生成器要持续维护每生成一批数据建议用一个小模型先做一轮“预训练-快速评测”确认数据确实能提升推理能力再正式纳入大规模训练集。这样能避免因为生成器 bug 导致的数据质量问题浪费大量训练算力。10.2 训练层面关键是保持稳定性。推理训练最容易出现的问题就是“灾难性遗忘”——学推理的同时丢掉了基础语言能力。建议混合训练推理数据与通用指令数据比例控制在 4:1 到 9:1 之间具体取决于模型基础能力低学习率推理链是精细的结构信息过大的学习率会导致模型“记住推理模板但失去推理灵活性”冻结底层 embedding如果基础能力较弱冻结 embedding 层可以防止表示空间被剧烈改变梯度裁剪推理链通常较长长序列会导致梯度范数波动更大clip 到 1.0 左右比较稳妥10.3 评测层面固定一个推理评测集每次训练后跑一遍保留历史记录。不要只看最终正确率要关注正确率随难度的衰减曲线。理想情况下曲线应该是缓慢衰减而不是高难度断崖式下跌。10.4 工程协作层面数据生成器、训练脚本、评测脚本要独立成三个模块由不同的人维护。数据生成器的改动影响面最大建议每次修改都做一次“生成结果 diff”确认新生成的数据不会污染已有数据的分布。11. 一个更前沿的扩展方向和增强学习结合文章开头说了完成监督是训练范式中的一种它简单、稳定、好复现。但它也有局限性模型只能学到数据里有的推理路径很难“发明”新的推理策略。从 Reasoning Core 的方向往下走一个被很多人看好的路径是先用完成监督训练出一个具备基础推理能力的模型再用程序化数据驱动的强化学习做自我提升。结合“Reasoning Core”这种大规模程序化数据池RL 阶段可以从数据池中动态采样题目自动验证模型生成的答案是否正确然后用这个验证结果作为奖励信号让模型在推理链空间里做策略搜索。这比用标准答案的 SFT 多了一个能力维度模型能长出数据中没有的新推理路径。不过这里提醒一下RL 训练的不稳定性比完成监督高很多需要额外的奖励模型校准。如果团队刚接触推理训练还是建议先用完成监督跑通流程再逐步引入强化学习。12. 总结与下一步这篇文章从推理训练的痛点出发讲了程序化数据、完成监督、推理训练三者之间的关系给了完整的数据生成器示例、训练代码示例和评测脚本示例。大概可以这样回顾重点推理能力依赖训练数据的结构程序化数据能提供人工数据无法比拟的规模、可控性和可验证性完成监督是训练推理能力的有效起点实现简单效果稳定适合作为基础训练范式程序化数据生成器要具备“生成-验证-导出”三个环节每一条数据都可以追溯到生成规则评测推理能力时要重点关注分布外表现和难度衰减曲线而不是简单的最终正确率完成监督之后可以继续向强化学习方向延伸用程序化数据池做自提升如果你想动手实践建议从一个小规模的闭环开始写一个特定题型的生成器数学题或逻辑题都行生成 1 万条训练数据微调一个开源模型然后用分布外评测集验证。这里提供一个具体的替代方案如果你不想自己写数据生成器也可以用开源数据集的思路做参考。但要注意使用时确认数据中每条推理链是否带可验证步骤、是否覆盖多种难度、是否做了分布外测试否则很难判断训练效果是来自数据还是模型规模。最后送一个值得持续关注的方向程序化数据设计正在从“搭积木式生成题目”走向“对推理过程逐步建模”。这意味着数据不只是题目和答案还会精确到每一步操作的前置条件、中间状态和后置条件。这种设计会让训练出的模型拥有更接近程序执行的确定性推理能力。如果你正在做推理训练相关的项目欢迎在评论区交流你的数据生成策略特别是解决“模型偷懒直接给答案”的经验。建议收藏这篇文章后续做训练数据管线时可以直接照搬其中的设计思路。