Reasoning models
A reasoning student writes out its reasoning in a thinking block, then gives the answer. For
Qwen3 models the block is <think>…</think> at the start of the reply. One config key controls
it: base.enable_thinking (default false). With it on, synthetic data generation writes the
reasoning, and the student is trained, evaluated and served with thinking on. It works for every
task type.
When to use it
Section titled “When to use it”Use a reasoning student when getting to the answer takes several steps (a calculation, a rule applied to the facts, a choice between close options) and a normal student comes in short of the teacher. It adds output tokens to every request, so replies are slower, and only the answer is scored. Train the same student with and without thinking on the same data and compare.
A -thinking teacher doesn’t make the student a reasoning student; the two are separate
settings.
Supported students
Section titled “Supported students”Qwen3-0.6B, Qwen3-1.7B, Qwen3-8B, Qwen3.5-0.8B, Qwen3.5-2B, Qwen3.5-4B, Qwen3.5-9B,
Qwen3.8-27B and Qwen3.6-35B-A3B. Any other student fails when the config is loaded, with:
base.enable_thinking=true is not available for student model '<name>'
For a 4B reasoning student use Qwen3.5-4B, not Qwen3-4B-Instruct-2507.
Where to set it
Section titled “Where to set it”base.enable_thinking: true goes in the config of the synthetic data generation run into the
train split (Building the train set), as an override on the dataset it
runs on. The dataset it writes carries the setting, so training inherits it and needs no
override. Download the dataset’s config into reasoning:
distil dataset download-metadata -d reasoning <dataset-id>
Set base.enable_thinking: true in reasoning/config.yaml, and submit the generation job with
it:
distil dataset generate-synthetic-data-train --config reasoning/config.yaml <dataset-id>
- Relabeling into the train split accepts it too and writes reasoning on the relabeled rows (Where the reasoning comes from). Test rows need no reasoning, so relabel the test set with it off.
- Turning it on only at training, on a dataset generated without it, trains an empty thinking block without an error. Generate again with it on instead.
synthgen.train_generation_target: 0skips generation and still writes reasoning onto the existing train rows, which adds reasoning to a labeled set without generating new rows.- A run with it on splits multi-turn rows per turn, so a later top-up can’t use that dataset’s train split as examples (Topping up).
Where the reasoning comes from
Section titled “Where the reasoning comes from”- Relabeling. Every relabeled assistant turn carries reasoning. Reasoning found in the
trace is kept when it is correct, grounded and leads to the answer; missing or bad reasoning
is written by the teacher. With
relabel: falsethe rows are written as traced, and the backfill fills the gaps. - Generation. The teacher writes
reasoning_contentnext to every assistant message it generates. - Backfill. After generation, a teacher pass writes reasoning on the final assistant message
of every training row that has none, uploaded and relabelled rows included. It changes only the reasoning, costs
one teacher call per row, and drops rows still without reasoning after a retry. The log says
Reasoning backfill: kept X of Y.
The teacher writes in the first person, works the answer out rather than explaining it, rules out
the nearest alternative, and invents no facts. Instructions on the style and length of the
reasoning go in synthetic_data_generation_instructions
(Job description), for example “Keep the reasoning under 250
tokens: one short line per check.” In a smoke run, read the reasoning_content of a handful of
rows: it should work the answer out, not restate it.
Reasoning in your data
Section titled “Reasoning in your data”-
reasoning_contentgoes on the assistant message; for a tool call, on the assistant message that carriestool_calls.{"messages": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "5", "reasoning_content": "..."}]} -
In train and test rows, a
reasoningkey, or any other key at the row’s top level, is dropped without an error. -
Reasoning is optional per row; the backfill fills the gaps.
test.jsonlneeds none. -
In traces, an assistant message’s reasoning is read from
reasoning_contentorreasoning(the key OpenRouter and newer vLLM versions return). Withenable_thinking: falseit is removed from the traces. -
Conversations are split into one row per assistant turn, and only the final turn keeps its reasoning.
-
With
enable_thinking: false, reasoning in the data is removed on read, so the same data serves both kinds of student.
Length limits
Section titled “Length limits”tuning.max_completion_length(default 2048 tokens) caps what the student generates in evaluation, reasoning included. A cut-off completion scores near zero. Training logstuning.max_completion_length (X) is below the longest training completion (Y)when it is too low; raise it then.synthgen.validation_max_total_lengthis also checked after the backfill, and rows that grew past it are dropped. Backfill and length drops aren’t replaced, so compare the train row count with the target after generation.
Evaluation
Section titled “Evaluation”Metrics read only content and tool_calls; there is no reasoning metric. The student is
evaluated with thinking on, at the vendor’s thinking sampling settings (Qwen3: temperature 0.6,
top_p 0.95, top_k 20; Qwen3.5, Qwen3.6 and Qwen3.8: temperature 1.0, top_p 0.95, top_k 20). To
judge the reasoning, read it in the prediction rows.
Deployment
Section titled “Deployment”- The generated
model_client.pysendschat_template_kwargs: {"enable_thinking": true}with the thinking sampling settings above. Use it, as for any model. - Hosted inference needs no extra setup.
- When you serve the model yourself, start vLLM with the reasoning parser for the student’s family
(Local deployment), or the thinking block stays
at the start of
content. With the parser, the reasoning comes back inreasoning_contentandcontentholds only the answer. - Reasoning text in the answer is expected from a reasoning student. It’s a problem only for a
model trained with
enable_thinking: false.
Every other parameter: Config file. The stage that writes the reasoning: Relabeling and synthetic data.