Notes on how the 2025–26 open-weight model reports describe turning base models into assistants and agents: supervised fine-tuning, preference learning, RL algorithms and rewards, agent environments, RL systems, distillation, thinking control, quantization-aware training, safety and the cost of it all. Each chapter explains the method, works a real example, and gives the reasons the labs themselves stated.
Disclaimer: This blog is compiled and edited with help from LLMs. Every number links to the public document it came from, so please check the source before citing it.
This is Part 3 of Reading Open-Weight Recipes, a series where I review what open-weight model releases have written down about their training recipes between December 2024 and October 2026. The atlas indexes the disclosures lab by lab; these notes go through how base models are turned into assistants and agents, one method at a time, with the mechanism, a worked example using numbers from a released model, and the reasons the labs themselves gave.
The same caveats as the rest of the series apply. Everything here comes from the public reports of open-weight releases (plus Microsoft AI's reports, whose models are mostly closed-weight), so it describes what those labs chose to disclose. It is not a description of how frontier closed models are trained, and where a report is silent I don't fill the gap. These are personal notes, not written for, reviewed by, or endorsed by any lab covered here.
A pretrained model can continue any text. What turns it into an assistant that follows instructions, reasons step by step and uses tools, and how did the recipe for that change in 2025 and 2026?
In the open-weight reports of 2024, posttraining was a small final step: some supervised fine-tuning, then preference tuning with a reward model. Over 2025 and 2026 it became a second major training stage. DeepSeek's posttraining budget for V3.2 exceeded a tenth of its pretraining cost, Inkling's announcement reports more than 30 million RL rollouts (sampled responses or agent episodes), and the labs reviewed here built hundreds of thousands of software environments for agents to practice in. Most of the new ideas in these notes come from that change: reinforcement learning on checkable tasks, environments for agents, systems that keep huge RL runs stable, and ways to merge many specialist models into one.
These notes follow the same format as the pretraining notes. Each chapter opens with the question it answers, explains the method with a diagram, equations and, where a method has one, short pseudocode, works a real example, quotes the labs' own reasons, and ends with trade-offs, three takeaways and two check-yourself questions. Underlined numbers link to their source; hover them for the exact quote.
The notes assume you know how a transformer is trained with next-token prediction and have seen basic probability (expectations, logarithms, KL divergence). Reinforcement learning is introduced from scratch in chapter 03. Some chapters also use systems ideas from the pretraining notes:
The models and labs are the same as in the pretraining notes; its roster lists each model's size and release date. The notes were first written from seven labs (DeepSeek, Kimi, GLM, MiMo, Gemma, Microsoft AI and Thinking Machines). The six that the atlas added later (Qwen, MiniMax, StepFun, Ling from Ant, Nemotron from NVIDIA, and Mistral) appear where they change the picture, and none of their self-reported benchmark numbers are quoted.
| Symbol | Meaning | Symbol | Meaning |
|---|---|---|---|
| x, y | prompt, response (tokens y1 … y|y|) | r(x, y) | reward for a response |
| πθ | the policy being trained (the model) | A | advantage: how much better than expected |
| πold | the policy that generated the samples | G | samples per prompt (group size) |
| πref | reference policy for the KL penalty | ρt | importance ratio πθ / πold for token t |
| πT, πS | teacher and student in distillation | ε, β | clip range, KL coefficient |
| V, λ | value function, GAE parameter | e, τ | reasoning-effort level, sampling temperature |
| 𝒱, |𝒱| | vocabulary and its size | t, i | index of a token, index of a response in a group |
Other symbols are local to the chapter that defines them. Several tables cite "the coded grid": the release-by-feature table, checked against the labs' sources, behind the posttraining section of the atlas.
The chapters fall into five groups:
For which lab adopted what and when, see the posttraining section of the atlas.
What can a pretrained base model not do on its own, which three tools do labs use to fix that, and how did the order and cost of those tools change between DeepSeek-V3 in December 2024 and Inkling in July 2026?
A base model is the network that comes out of pretraining (and often a mid-training phase that reweights the data, for example toward math and code). It has learned one task, predicting the next token of text like its training data, and it will continue any text it is given. Posttraining is everything a lab does afterwards to turn that predictor into an assistant or agent that answers in a chat format, follows instructions, reasons before answering, calls tools and declines harmful requests.
Continuing text and helping a user are different goals. The InstructGPT paper (OpenAI, 2022) notes that base models often make up facts, produce biased or toxic text, or ignore the instruction. The skill is often present but unused, and two results show the size of the gap. Human raters preferred outputs of a 1.3B-parameter posttrained model to those of the 175B GPT-3. And DeepSeek-R1-Zero, trained with RL directly from the DeepSeek-V3 base, raised its single-attempt accuracy (pass@1) on the AIME 2024 math competition from 15.6% to 77.9%.
Most pipelines in these reports are assembled from four stages. The list is a composite, not one lab's recipe, and some labs skip a stage; Figure 0.1 below draws four real ones.
Each stage uses one of three tools. They differ in who writes the response y that the model learns from, and in what tells the model whether y is good:
| Tool | Who writes y | Signal | Optimizes |
|---|---|---|---|
| SFT | a fixed dataset (people or another model) | every token of the demonstration | likelihood of the demonstration |
| RL | the model being trained | one reward per response | expected reward, KL-penalized |
| Off-policy distillation | the teacher | every token of the teacher's sample | likelihood of teacher samples |
| On-policy distillation | the student | teacher's distribution at every token | KL to the teacher on the student's text |
Thinking Machines labels the three as off-policy and dense (SFT), on-policy and sparse (RL), and on-policy and dense (OPD): on-policy means the trained model wrote y, dense means a signal at every token. Each tool is a loss on that model, the policy πθ, a probability distribution over responses y to a prompt x.
Supervised fine-tuning (SFT) trains on fixed prompt-response pairs (demonstrations) from a dataset D, with the pretraining loss on the response tokens (chapter 01):
SFT imitates: it puts probability on whatever the demonstrations contain, mistakes included. Formally, write pD for the distribution of demonstrations. Since EpD[−log πθ] = KL(pD ‖ πθ) + H(pD), and the entropy H(pD) does not depend on θ, minimizing the SFT loss minimizes the Kullback-Leibler (KL) divergence from the data to the model.
Reinforcement learning (RL) lets the model write its own responses and scores each with a reward r(x, y) from a rule, a test suite or a learned judge. It maximizes expected reward minus a penalty, weighted by β, that keeps πθ near a reference policy πref (chapters 03 to 06):
InstructGPT used this form with a per-token KL penalty toward the SFT model. The update behind it: sample a response and push its log-probability up or down by how much its reward beat the average (the baseline). If the model answers a prompt correctly 30% of the time, with reward 1 or 0, the baseline is 0.3, so a correct sample gets weight 1 − 0.3 = 0.7 and a wrong one 0 − 0.3 = −0.3. RL is on-policy (it trains on the model's own samples), so it can find answers no demonstrator wrote, but it gets one number per response.
The reward term's gradient follows from the score-function (log-derivative) identity ∇πθ = πθ ∇log πθ, which holds because ∇log πθ = ∇πθ / πθ:
The last form can be estimated from the model's own samples, and it raises the probability of high-reward responses; chapters 03 and 07 reuse this identity. A baseline that does not depend on y leaves it unchanged, since E[∇log πθ] = ∇ ∑y πθ = 0; it only reduces noise (chapter 03).
Distillation trains a student πS to match a teacher πT, an idea Hinton et al. (2015) introduced as using a large model's probabilities as "soft targets". The student is the policy being trained, so πS = πθ. Off-policy distillation runs SFT on teacher samples. On-policy distillation (OPD) samples from the student and penalizes, at every token, the gap between the student's and teacher's next-token distributions. DeepSeek-V4 merges several teachers this way, with a teacher weight wi for teacher i (written here in the full-vocabulary form DeepSeek-V4 uses; most labs use only the sampled token's log-ratio, chapter 07):
The order inside the KL flips. SFT minimizes the forward KL, KL(data ‖ model); OPD minimizes the reverse KL, KL(student ‖ teacher), averaged over the student's own text. Chapter 07 shows why the direction matters.
# One schematic update per tool. logp(m, x, y) = sum over t of log m(y_t | x, y_<t)
def sft_loss(policy, x, y_demo): # y written by people or another model
return -logp(policy, x, y_demo) # imitate the demonstration
def rl_loss(policy, x, reward, baseline):
y = sample(policy, x) # y written by the model itself
r = reward(x, y) # one number for the whole response
return -(r - baseline) * logp(policy, x, y) # policy-gradient surrogate, r constant
def opd_loss(student, teacher, x):
y = sample(student, x) # on-policy: the student writes y
loss = 0.0
for t in range(len(y)): # the teacher grades every token
p_s = next_token_probs(student, x, y[:t]) # shape [|𝒱|]
p_t = next_token_probs(teacher, x, y[:t]) # shape [|𝒱|], teacher frozen
loss += (p_s * (log(p_s) - log(p_t))).sum() # reverse KL over the vocabulary 𝒱
# full-vocabulary form (DeepSeek-V4); most labs use only the sampled token (ch. 07)
return loss
2022: SFT, a reward model, then RL. InstructGPT set the baseline: SFT on demonstrations for about 13k prompts, a reward model trained on labelers' rankings, then RL against it with PPO (Proximal Policy Optimization, chapter 03).
January 2025: RL at the center. DeepSeek-R1's sibling R1-Zero ran RL on the V3 base with rule-based rewards and learned long chains of thought (CoT, written reasoning before the answer) that were hard to read and mixed languages. R1 alternates tools instead (Figure 0.1, top row):
2024-26: specialists merged into one model. DeepSeek-V3 (December 2024) already generated reasoning SFT data with per-domain expert models. V3.2 (December 2025) made this the pipeline: RL-trained specialists for six domains, SFT of one model on their outputs, then one mixed RL stage, chosen because it avoids "catastrophic forgetting issues commonly associated with multi-stage training paradigms" (later stages erasing earlier skills). V4 (April 2026) kept the specialists and replaced the mixed RL stage with on-policy distillation from more than ten teachers.
Multi-teacher OPD appears in 12 of the 47 pipelines in the coded grid for the first seven labs, first in Xiaomi's MiMo-V2-Flash (December 2025), and, among the six labs added later, in NVIDIA's Nemotron 3 Ultra and Nemotron 3.5 Super; the coded grid is the release-by-feature table, checked against the labs' sources, behind the posttraining section of the atlas.
2026: RL first, SFT to merge. Microsoft AI's MAI-Thinking-1 (June 2026) runs no SFT before RL. Three specialist RL "climbs" start from the mid-trained base. When a climb collapses (its scores drop suddenly, for example after small numerical mismatches between inference and training build up), a mid-trained checkpoint is fine-tuned on the climb's own successful traces and RL restarts from it (self-distillation). A consolidation SFT merges the three teachers, and a light final RL stage tunes safety and style. Thinking Machines' Inkling (July 2026) is shortest: a bootstrap SFT on data from other labs' open-weights models, including Kimi K2.5, then asynchronous RL (rollout workers keep generating while the trainer updates, chapter 06) over math, code, tools, audio, image, chat and safety, scaled to over 30M rollouts (sampled responses).
Across these pipelines SFT's role narrowed to a short start or a step that merges RL-trained skills into one model, while RL and teacher-matching do most of the training.
Stated posttraining compute rose fast, though the reports measure it unevenly:
Other costs sit outside these numbers: labelers (InstructGPT hired 40 contractors), teacher models, RL rewards and environments (chapters 04-05), and research runs, which DeepSeek's figures exclude.
Table 1 of the V3 report lists H800 GPU-hours by stage: pretraining 2,664K, context extension 119K, posttraining 5K, total 2,788K.
Within a year, DeepSeek's posttraining went from about 0.2% to 5% to over 10% of pretraining compute. V3's table has no line for the expert models that generated its reasoning data, so 5K may understate what it relied on.
"Pre- and mid-training give MAI-Base-1 broad predictive competence and knowledge, but they do not specify how the model should behave, solve long-horizon tasks, or allocate inference-time computation."
"Second, regarding resource allocation, open-source models suffer from insufficient computational investment during the post-training phase, limiting their performance on hard tasks."
"The bootstrap accounts for a small fraction of compute, with the majority being employed for large-scale RL on synthetic and human-created environments."
SFT minimizes KL(pD ‖ πθ), so it raises the probability of everything in the data, wrong answers included. RL weights each sampled response's gradient by its reward, so wrong answers get no increase, and a decrease once a baseline is subtracted.
DeepSeek says one mixed stage avoids catastrophic forgetting, where later stages erase earlier skills. V4 replaced it with on-policy distillation from more than ten teachers: the student writes responses and learns to match each teacher's token distribution on them.
How does training on example responses teach a base model to answer in a chat format, where did labs get those examples between 2022 and 2026, how many did they use, and what can imitation not teach?
Supervised fine-tuning (SFT) reuses the pretraining objective on a much smaller, curated dataset. Each example is a prompt x and a response y, joined into one token sequence by a chat template (next section). At every position the model predicts the next token, and the loss is cross-entropy: the negative log-probability it gave the correct token. The one change from pretraining is that only response tokens count. The model reads the prompt but is never graded on it.
With easy numbers: take an example of 10 tokens whose last 4 are the response. The model reads all 10, but the loss averages only 4 terms, one per response token, each predicted from everything to its left. If the model gives those 4 tokens probabilities 0.5, 0.5, 0.25 and 0.5, the loss is (0.69 + 0.69 + 1.39 + 0.69) / 4 = 0.87 nats (natural-log units). Over a batch 𝓑 of B examples:
The divisor is a choice:
Shapes: tokens [B, L] (L the padded or packed length), logits [B, L, |𝒱|] over a vocabulary 𝒱 of |𝒱| tokens, and a 0/1 loss mask [B, L] marking response tokens.
Three kinds of mask appear in the reports. The first two act on the loss, the third on attention:
A chat template turns a list of messages (system, user, assistant, tool) into one token string. It marks roles and boundaries with special tokens, vocabulary entries reserved for the format. DeepSeek-R1's template, stored in its tokenizer_config.json, writes the system prompt after <|begin▁of▁sentence|> (the bars are full-width characters), puts <|User|> before each user message and <|Assistant|> before each reply, and closes each reply with <|end▁of▁sentence|>. To request an answer it appends <|Assistant|><think> and a newline, so the model starts by reasoning, and it strips the reasoning from earlier assistant turns.
OpenAI's gpt-oss uses a richer format called harmony. Roles are ranked System > Developer > User > Assistant > Tool to resolve conflicting instructions, and "channels" separate analysis (the chain of thought), commentary (tool calls) and final (the answer shown to users). Its tokenizer has 201,088 tokens, extending the o200k tokenizer with format tokens such as <|start|>, <|channel|>, <|message|> and <|end|>.
SFT is where a model learns what these tokens mean, including the end-of-response token that tells it to stop. Training and serving must use the same template: OpenAI says gpt-oss "should only be used with the harmony format as it will not work correctly otherwise". Figure 1.1 puts the template and both kinds of mask on one example.
# Build one example: token ids plus a 0/1 loss mask (simplified R1-style template).
TAGS = {"system": "", "user": "<|User|>", "assistant": "<|Assistant|>"}
def build_example(tok, messages):
ids, mask = [tok.bos_id], [0]
for m in messages:
head = tok.encode(TAGS[m.role]) # role tag: never trained
body = tok.encode(m.content)
trained = m.role == "assistant"
if trained:
body = body + [tok.eos_id] # the model must learn to stop
ids += head + body
mask += [0] * len(head) + [int(trained)] * len(body)
return ids, mask
def sft_loss(model, ids, mask): # ids, mask: [B, L]
logp = log_softmax(model(ids[:, :-1]), dim=-1) # [B, L-1, |𝒱|]
target = ids[:, 1:] # the next token at each position
tok_logp = logp.gather(-1, target[..., None])[..., 0] # [B, L-1]
m = mask[:, 1:].float() # count only response targets
return -(tok_logp * m).sum() / m.sum() # mean over response tokens
SFT teaches format, style and instruction following from little data: InstructGPT's first stage used labeler-written demonstrations for about 13k prompts. Its limits come from the loss itself, and there are two:
RL and on-policy distillation train on the model's own outputs and remove this mismatch (chapters 03 and 07). GLM-5's masked error segments are a partial fix inside SFT: the model reads its mistakes in context, but the loss skips them.
The reports name five sources for the responses:
| Who writes the responses | Examples | What the source says |
|---|---|---|
| People | InstructGPT (2022), Mistral Small 3 (Jan 2025), Llama 4 (Apr 2025) | labeler demonstrations; Llama 4's safety fine-tuning data combines "human-generated data from our vendors with synthetic data"; Mistral Small 3 was "neither trained with RL nor synthetic data" |
| The lab's earlier models | DeepSeek-V3 (Dec 2024), Kimi K2 (Jul 2025) | DeepSeek-V2.5 wrote non-reasoning answers, humans verified them; K1.5 and in-house experts generated candidates, LLM or human judges filtered them |
| The lab's own specialists | GLM-4.5 (Jul 2025), DeepSeek-V3.2 (Dec 2025) | "millions of samples" from the Reasoning, Agent and General-chat experts; data from six RL-trained specialists |
| Another lab's open models | NVIDIA's Nemotron line (from Mar 2025), Inkling (Jul 2026) | Nemotron 3 Super distills from open models such as Qwen3-Coder-480B and MiniMax M2.5; bootstrap SFT on data from open-weights models including Kimi K2.5 |
| No one: RL starts from the base | R1-Zero (Jan 2025), MiMo-7B-RL-Zero (Apr 2025), MAI-Thinking-1 (Jun 2026) | MAI: "learning to reason with no prior exposure to reasoning traces" |
The trend runs from answers people write to answers models write and people or programs check. Data also flows between labs the other way: DeepSeek fine-tuned six Qwen2.5 and Llama models on R1's 800K samples, and Xiaomi fine-tuned Qwen3.5-9B on MiMo-V2.6 outputs. NVIDIA builds much of its SFT data from other labs' models and names the teachers: Llama-Nemotron (March 2025) distilled its reasoning from DeepSeek-R1 by SFT, and for Nemotron 3.5 Super (October 2026) NVIDIA switched its main reasoning teacher from DeepSeek V4 Pro to Kimi K3.
Two recipes turn a model's own samples into SFT data: the cold start, which builds a small, readable seed set for RL, and rejection sampling, which keeps only the samples that pass a check.
A cold start is a small SFT stage whose job is to give RL a good starting format. R1-Zero's chains of thought were hard to read and mixed languages, so for R1 DeepSeek built thousands of examples from R1-Zero samples at temperature 1.0:
The cold start bought instruction following at some cost in reasoning, and the RL after it won most of the reasoning back. DeepSeek's first intermediate checkpoint, Dev1 (the cold-start model), followed instructions better than R1-Zero (on IF-Eval, an instruction-following benchmark, 46.6 to 71.7) but reasoned worse (AIME 2024 77.9% to 59.0%), which DeepSeek attributes to "the limited size of the cold-start dataset". The RL stage after it (giving checkpoint Dev2) brought AIME back to 74.0%.
Rejection sampling builds SFT data from a model's own attempts:
STaR (Zelikman et al., 2022) ran this as a loop: generate rationales with a few worked examples in the prompt, keep those reaching the correct answer, fine-tune, and repeat, restarting each round from the original model "to avoid overfitting".
Why does training on filtered samples help? Because it is a form of RL. Give a reward of 1 for a correct answer and 0 otherwise. RL raises each sampled response's log-probability in proportion to its reward, so correct samples are pushed up and wrong ones, with reward 0, are ignored. Fine-tuning on the correct samples does the same. The STaR paper makes this exact: the expected 0/1 reward is the probability of answering correctly, and the score-function identity (chapter 00) gives its gradient:
The indicator 1(·) zeroes every wrong sample, so this gradient is the SFT gradient on the correct samples. Fine-tuning on filtered samples therefore climbs the probability of answering correctly: it is RL with a 0/1 reward, approximated, because its samples come from a fixed earlier model and are reused for several epochs.
In one careful test, variety mattered more than volume. Yuan et al. (2023), who called the method rejection-sampling fine-tuning (RFT), worked on the GSM8K math word problems. From each SFT model they drew 100 samples per question at temperature 0.7, kept the correct paths, removed duplicates (paths with the same list of equations) and fine-tuned on the original SFT data plus the distinct correct paths:
The gain tracks distinct correct solutions; near-duplicates add little.
Labs run the same step at scale. R1's second SFT took about 600K reasoning samples by rejection sampling from the first RL checkpoint. MAI-Thinking-1's self-distillation fine-tunes on successful RL traces; Microsoft AI found that also including traces with wrong answers "performs similarly", and kept only successful ones because RL produced more than enough.
def rejection_sample(model, prompts, check, k, temperature, distinct):
data = []
for x in prompts:
ys = [sample(model, x, temperature) for _ in range(k)]
keep = [y for y in ys if check(x, y)] # correct answer, passes filters
if distinct:
keep = dedupe(keep) # RFT: one path per equation list
data += [(x, y) for y in keep]
return data
def rft(base, sft_data, prompts, check, k=100):
sft_model = sft(base, sft_data)
data = rejection_sample(sft_model, prompts, check, k, temperature=0.7, distinct=True)
return sft(base, sft_data + data) # RFT keeps the original SFT data too
def star(base, prompts, check, rounds):
model = base
for _ in range(rounds):
data = rejection_sample(model, prompts, check, k=1, temperature=0.0, distinct=False)
model = sft(base, data) # STaR restarts from the base model
return model
Stated SFT sizes use different units (prompts, samples, tokens), so they compare only roughly:
| Release | SFT set as stated | Unit |
|---|---|---|
| InstructGPT (2022) | about 13k | prompts |
| DeepSeek-V3 (Dec 2024) | 1.5M, two epochs | instances |
| DeepSeek-R1, second SFT (Jan 2025) | 804,745, 2-3 epochs | samples |
| Llama-Nemotron (Mar 2025) | 33,011,757 synthetic, 66.8% math | samples |
| MiMo-7B (Apr 2025, May 2025 update) | about 500K, later about 6M | samples |
| GLM-4.5 (Jul 2025), MiMo-V2-Flash (Dec 2025) | "millions" | samples |
| MAI-Thinking-1 (Jun 2026) | O(1M) per self-distillation round | traces |
| MiMo-V2.6-Distill-Qwen-9B (Sep 2026) | 77.4B, of which 27.2B trained | tokens |
Two findings bear on how much to use:
The worked example below sizes one of these sets in tokens and compute.
Table 5 of the R1 report gives samples and average tokens per sample for each domain: math 395,285 × 6,094.2; code 211,129 × 7,435.7; STEM 10,124 × 4,928.8; logic 10,395 × 2,739.0; general 177,812 × 1,419.8; total 804,745 × 5,355.3.
Table 5 does not say whether its token counts include prompts; if they do, the loss tokens are fewer than these totals.
"The motivation is primarily product-driven, with a strong emphasis on enhancing user experience."
"This method ensures that the final training data retains the strengths of DeepSeek-R1 while producing responses that are concise and effective."
"In the expert training stage, the primary role of SFT is to provide a cold start, empowering the model with basic chat, reasoning, and tool-use capabilities, which can then be further enhanced in subsequent expert RL training to achieve improved performance."
700 + 800 = 1,500 of the 2,000 tokens (75%) carry loss; the 500 prompt tokens are masked. Sample masking stops example 2's tokens from attending to example 1's, so example 2 is learned as if it were alone instead of conditioned on an unrelated conversation.
The 33B model's correct samples had only 2.78 distinct solutions per question against 5.25 for the 7B model. Rejection-sampling fine-tuning gains from new distinct correct paths; near-duplicates of answers the model already gives teach it little.
When no program can check whether an answer is good, how do labs turn judgments like "answer A is better than answer B" into a training signal, and where does that signal still appear in 2025-26 pipelines?
A math answer can be checked against the known result, and code can be run against tests. "Be helpful" or "write clearly" has no such check, but people can say which of two answers is better. Christiano et al. found consistent comparisons "much easier" for people than consistent absolute scores, and their simulated robot learned a backflip from 900 comparisons collected in under an hour. Comparisons are still noisy: the trained labelers for InstructGPT, OpenAI's 2022 instruction-following GPT-3, agreed with each other only 72.6% of the time.
A reward model (RM) turns comparisons into a score: it reads a prompt x and a response y and outputs one number r(x, y), higher for responses people prefer. It is usually the supervised fine-tuned (SFT) model of chapter 01 with the unembedding replaced by a linear head that maps one hidden state (width d) to one number. With causal attention only the final token's position has seen the whole prompt and response, so the head typically reads the last layer's hidden state there.
The Bradley–Terry model links scores to comparisons: it assumes that the probability people prefer the winner yw over the loser yl depends only on the reward gap.
A gap of 0 is a coin flip, a gap of 1.4 gives σ(1.4) = 0.80, and a large gap gives nearly 1. Fitting the RM by maximum likelihood on labeled pairs is logistic regression on reward differences:
Two properties follow:
InstructGPT's 6B RMs matched held-out labelers 69.6% of the time, against 72.4% for the labelers they were trained on.
Reinforcement learning from human feedback (RLHF) then trains the policy πθ to maximize the RM's score. The RM is accurate only on responses like those it was trained on, so a KL penalty keeps πθ near a reference policy πref, usually the SFT model. This is the KL-regularized objective J(θ) of chapter 00:
InstructGPT optimized this with PPO, an RL algorithm explained in chapter 03, charging the penalty at every token with β = 0.02. Without it, the policy finds answers the RM overrates: Gao et al., with a fixed "gold" RM standing in for people, found the gold score rises and then falls as the policy drifts, while the proxy RM's score keeps climbing. DeepSeek saw this in its reasoning model R1: the helpful RM's reward rose while performance on CodeForces (competitive-programming problems) fell, so R1 used preference rewards only in the last 400 of 1,700 steps of its final RL stage.
RLHF needs a reward model and a sampling loop. Direct Preference Optimization (DPO) removes both, starting from one fact: for any reward, the best policy for J(θ) is known in closed form. That policy tilts πref toward high reward, multiplying each response's probability by exp(r/β) and renormalizing:
A toy: one prompt has two answers, A and D, which πref gives 0.5 each, with rewards r(A) = 1.0 and r(D) = −0.4; take β = 1. The tilted weights are 0.5 · e1.0 = 1.359 and 0.5 · e−0.4 = 0.335, so Z = 1.694, π*(A) = 0.8022 and π*(D) = 0.1978. Going backwards, log(0.8022 / 0.5) = 0.473 and log(0.1978 / 0.5) = −0.927: their difference, 1.400, is the reward gap, and each is its reward minus the same constant, log Z = 0.527. In general, taking logs of π* gives
so β times a policy's log-ratio to the reference is the reward it is optimal for, up to a constant per prompt. DPO calls β log(πθ/πref) the implicit reward and puts it into the Bradley–Terry loss in place of r. The constant β log Z(x) appears for both responses of a pair and cancels, so maximum likelihood on the pairs needs only policy log-probabilities:
Instead of fitting a reward model and then solving for its optimal policy, DPO fits the implied reward to the preference data, which fits πθ directly.
For one prompt, Ey∼π[ r − β log(π/πref) ] = −β · Ey∼π[ log( π / (πref er/β) ) ]. Dividing πref er/β by its sum over all responses, Z(x), gives a distribution π*, and the objective becomes −β · KL(π ‖ π*) + β log Z(x). Z(x) does not depend on π, and the KL term is zero only when π = π*, so π* is the optimum.
Read backwards, the identity says every policy πθ is the optimum for the reward β log(πθ/πref) + β log Z(x). Substituting it into Bradley–Terry gives LDPO.
A labeler ranks K = 4 answers A ≻ B ≻ C ≻ D, the smallest of InstructGPT's 4 to 9. A ranking of K answers gives K(K − 1)/2 pairs: here 4 · 3 / 2 = 6 pairs; K = 9 would give 36.
Reward model. With illustrative scores r = 1.0, 0.6, −0.2, −0.4 for A to D: P(A ≻ D) = σ(1.4) = 0.802, loss −ln 0.802 = 0.220; P(C ≻ D) = σ(0.2) = 0.550, loss 0.598. The six pair losses (0.513, 0.263, 0.220, 0.371, 0.313, 0.598) average 0.380, and the close pairs A–B and C–D contribute almost half of it (1.11 of 2.28).
DPO on A ≻ D, with the DPO paper's default β = 0.1. At the start πθ = πref, both log-ratios are 0 and the loss is ln 2 = 0.693. Suppose training raises log πθ(A|x) by 3.0 nats (natural-log units) relative to πref and lowers log πθ(D|x) by 2.0 (illustrative). The implicit rewards are 0.30 and −0.20, the margin is 0.50, and the loss is −ln σ(0.50) = −ln 0.622 = 0.474. The pair's gradient weight σ(−0.50) = 0.378 is down from 0.5, so DPO now pushes harder on pairs it still gets wrong.
As in the RM loss, each pair's gradient is weighted by how badly the implicit rewards misorder it. Training is one supervised stage with no reward model and no sampling (Figure 2.1), but DPO learns only from fixed pairs and never tries new responses, as online RL (learning from the policy's own fresh samples) does.
import torch, torch.nn.functional as F
def seq_logprob(model, x, y):
# log π(y|x) summed over response tokens; logits at response positions: [B, L, |𝒱|]
logits = model(x, y)
tok = logits.log_softmax(-1).gather(-1, y.unsqueeze(-1)).squeeze(-1) # [B, L]
return tok.sum(-1) # [B]
def reward_model_loss(rm, x, y_w, y_l):
r_w, r_l = rm(x, y_w), rm(x, y_l) # one scalar per response: [B]
return -F.logsigmoid(r_w - r_l).mean() # Bradley-Terry
def dpo_loss(policy, ref, x, y_w, y_l, beta=0.1):
with torch.no_grad(): # π_ref stays frozen
ref_w, ref_l = seq_logprob(ref, x, y_w), seq_logprob(ref, x, y_l)
pi_w, pi_l = seq_logprob(policy, x, y_w), seq_logprob(policy, x, y_l)
margin = beta * ((pi_w - ref_w) - (pi_l - ref_l)) # implicit reward gap
return -F.logsigmoid(margin).mean()
Google's Gemma 3 bases its RL phase on "improved versions" of three Google DeepMind methods; the report does not say what was improved.
WARM fine-tunes M reward models from one initialization with different hyperparameters and averages their weights, ψWARM = (1/M) ∑m ψm, where ψm are the weights of RM m. Models fine-tuned from a shared checkpoint tend to be linearly mode connected (the average is at least as accurate as the mean of their accuracies), so WARM gets ensemble-like robustness at one RM's inference cost. A summarization policy trained against WARM beat a single-RM policy 79.4% of the time.
WARP applies the same idea to policies: it averages the weights of several RL-trained policies (weight merging, chapter 07).
BOND distills best-of-N sampling into a single sample. Best-of-N draws N answers from πref and keeps the RM's favorite, which costs N generations per answer; BOND trains the policy to produce that distribution in one draw. Let p≤(y) be the fraction of reference samples that score no better than y. Answer y wins if it is drawn in one of the N slots and the other N − 1 draws score no better, so
The paper's exact form adds a small correction for drawing y more than once. With N = 4, an answer at the 90th percentile of reference scores (p≤ = 0.9) becomes 4 × 0.93 = 2.9 times as likely as under πref, and a median one (p≤ = 0.5) half as likely. In logs, log(πBoN/πref) = (N − 1) log p≤(y) + log N. Matching this to the RLHF optimum πref exp(r/β)/Z gives reward r = log p≤(y) and β = 1/(N − 1), with log N playing the role of −log Z (that is, Z = 1/N). Only ranks matter, so rescaling the RM monotonically changes nothing.
Since R1, reasoning RL in the reports covered here mostly uses checkable rewards (chapter 04). Preference signals remain in narrower roles:
| Lab, model | Preference signal | Role |
|---|---|---|
| Meta, Llama 4 | DPO | "lightweight" stage after online RL for response-quality "corner cases" |
| Z.ai, GLM-4-32B-0414 (32B dense) | "human preference alignment for dialogue scenarios"; method not stated | beside rejection sampling (keeping only good samples for SFT) and RL |
| Z.ai, GLM-Z1-0414 | RL "based on pairwise ranking feedback" | general RL beside reasoning RL |
| DeepSeek, R1 | helpful RM on 66,000 pairs judged by DeepSeek-V3; safety RM | last 400 RL steps |
| Google, Gemma 3 | WARM reward models trained on human feedback | one RL phase with code-execution and math rewards |
| Microsoft AI, MAI-Thinking-1 | generative RM (an LLM that outputs its judgment as text tokens; chapter 04) fine-tuned to write human 1–5 scores; reward = P(score 5) | helpfulness and safety RL |
| Meta, Muse Glimmer (~29.6B on-device agent) | "reward models" for RLHF; type not described | multi-objective RL, last stage |
| DeepSeek, V3.2 and V4 | generative RM guided by rubrics (per-prompt lists of criteria); in V4 the policy itself judges, no scalar RM | hard-to-verify tasks |
| Mistral, Voxtral and Ministral 3 | online DPO: two fresh samples per prompt from the current policy, ranked by a pairwise reward model | final stage, after GRPO in Ministral 3's reasoning models |
Preferences now arrive late, in small doses, or through an LLM judge reading a rubric. Chapter 04 covers those judges.
"A key learning was that SFT and DPO can over-constrain the model, restricting exploration during the online RL stage and leading to suboptimal accuracy, particularly in reasoning, coding, and math domains."
"We find that more training steps with the model based preference reward signal may lead to reward hacking"
No. The loss depends only on r(x, yw) − r(x, yl), so the constant cancels. That is why InstructGPT could shift its RM so human demonstrations averaged 0.
For large β, exp(r/β) ≈ 1, so π* ≈ πref and the reward is ignored. As β → 0, nearly all probability goes to the highest-reward responses, where the reward model's mistakes are exploited most.
A model writes eight answers to one math problem and three are graded correct: how do those eight grades become a gradient step, and why did most labs drop PPO's learned critic for GRPO's group average while StepFun kept it and GLM-5.2 brought it back?
Reinforcement learning (RL) for a language model is a loop: the model writes, a grader scores, and training makes high-scoring responses more likely. The model is the policy πθ. For a prompt x it samples a response y = (y1…y|y|), and a grader returns a scalar reward r(x, y), such as 1 if unit tests pass. Training raises the reward-only objective Jr(θ) = Ex, y∼πθ[r(x, y)] (the J(θ) of Chapter 00 also subtracts a KL penalty, which returns below). The reward comes from code or a judge and cannot be backpropagated through.
The simplest method treats each sample as a demonstration weighted by its reward: take the gradient of its log-likelihood, as in supervised fine-tuning, and multiply it by r(x, y). The score-function (log-derivative) identity ∇π = π ∇log π, derived in Chapter 00, shows that this is the gradient of Jr:
This is the REINFORCE algorithm of Williams (1992). Every token of a response shares its reward. For G responses padded to length |y| the log-probabilities form a G×|y| tensor, and the gradient flows only through them.
The estimate is noisy. With rewards in {0, 1}, every correct answer is pushed up equally whether the problem was easy or hard, and wrong answers are never pushed down. One number judges thousands of tokens, and the variance of this estimator "scales unfavorably with the time horizon", which for a language model is the response length.
Subtract a baseline b(x), ideally the prompt's expected reward, and weight each response by its advantage A = r(x, y) − b(x): positive when a sample beat the policy's average, negative otherwise, so wrong answers are now pushed down. A baseline that does not depend on the sampled response keeps the gradient unbiased, because Ey[b(x) ∇log πθ(y|x)] = b(x) ∇∑yπθ(y|x) = b(x) ∇1 = 0. Weighting by the true advantage (the expected reward after the sampled choice minus the expected reward before it) "yields almost the lowest possible variance".
Every method in this chapter builds the same update. Token t of response i, one of the responses sampled for a prompt, contributes
REINFORCE with a baseline sets w = 1, N = 1 and no KL term. The methods below differ in five choices about how to weight and limit each token's update, the columns of the comparison table later on:
One prompt, G = 2, with Ri = r(x, yi) for response i's reward. Response 1 is correct (R1 = 1, 3 tokens); response 2 is wrong (R2 = 0, 7 tokens).
Advantage baseline. The mean reward is 0.5 and the standard deviation 0.5, so GRPO gives A = (1 − 0.5)/0.5 = +1 and −1. Dr. GRPO, without the division, gives +0.5 and −0.5.
Ratio and clip. On the first gradient step πθ = πold, every ratio is 1 and every rule gives w = 1, as in REINFORCE. The rules part once the policy moves. Say response 1's tokens (A = +1) now have ratios 1.10, 1.25 and 1.40. A kept token's weight is its ratio (the gradient of ρ is ρ ∇log π); a clipped one gets 0. CISPO is given DAPO's upper bound for comparison:
| Token | Ratio | PPO, GRPO: clip [0.8, 1.2] | DAPO: clip [0.8, 1.28] | CISPO: cap at 1.28 |
|---|---|---|---|---|
| 1 | 1.10 | 1.10 | 1.10 | 1.10 |
| 2 | 1.25 | 0 (clipped) | 1.25 | 1.25 |
| 3 | 1.40 | 0 (clipped) | 0 (clipped) | 1.28 |
GSPO uses one ratio for all three, their geometric mean (1.10 × 1.25 × 1.40)1/3 ≈ 1.24, and keeps or clips them together.
Length normalization. GRPO divides by each response's length and by G: response 1's tokens are scaled by 1/(2 × 3) = 1/6, response 2's by 1/(2 × 7) = 1/14. DAPO scales all ten by 1/10.
Proximal Policy Optimization (PPO) answers the five choices with a learned critic, a per-token ratio, a symmetric clip, a token average and, in RLHF-style training, a KL penalty in the reward. The ratio comes from reuse. Sampling is expensive, so one wants several gradient steps per batch, but after the first the samples come from an older policy πold. Importance sampling corrects for this by weighting each sample with a probability ratio: Ey∼πold[(πθ(y)/πold(y)) f(y)] = Ey∼πθ[f(y)]. PPO applies it per token with the importance ratio ρt = πθ(yt | x, y<t) / πold(yt | x, y<t) and maximizes the surrogate E[ρtAt]. The per-token ratio is a cheap stand-in for the whole-response ratio, which GSPO revisits below.
∇ρt = ρt ∇log πθ(yt | x, y<t). At θ = θold, ρt = 1, so ∇ρt = ∇log πθ(yt | x, y<t) and the surrogate E[ρtAt] has the policy gradient E[At ∇log πθ] as its gradient there.
Unchecked, ρt drifts far from 1; several steps on the plain policy-gradient loss "often leads to destructively large policy updates". PPO therefore clips the ratio to a trust region, a band around the old policy:
The min makes the clip one-sided, and the sign of the advantage picks the side:
In the other two cases (a good token became less likely, or a bad one more likely) the unclipped term is the smaller, so its gradient flows and the policy can undo a harmful move. The result is a pessimistic lower bound on the unclipped objective. Figure 3.1 draws all four cases.
For the baseline, PPO trains a value function V, also called the critic: a second network that predicts the expected future reward from each prefix st = (x, y<t), so every token gets its own advantage. Generalized advantage estimation (GAE) builds that advantage from the critic's one-step errors δt = rt + γV(st+1) − V(st), with rt the reward paid at token t and γ a discount factor, summed with a decay λ:
λ trades bias against variance:
In RLHF-style PPO the KL term lives in the reward. The reward model usually scores only the final token, and every token pays −β log(πθ/πref), which holds the policy near a reference policy πref (usually the SFT model) with KL coefficient β.
The critic is what costs. It is "typically another model of comparable size as the policy model", so PPO trains two large networks. DeepSeek's R1 report adds that with only a final reward the critic must predict success from a partial chain of thought that may later be revised, and that PPO is sensitive to λ. On MATH with DeepSeek-Coder-V2-Lite, a 16B-total MoE (mixture-of-experts: a router sends each token to a few of many expert MLPs; pretraining notes, Chapter 02), PPO at the common default λ = 0.95 did considerably worse than GRPO and came close only at λ = 1.0.
Group Relative Policy Optimization (GRPO), introduced with DeepSeekMath, changes the first choice: the other samples for the same prompt replace the critic as the baseline. For each prompt it samples a group of G responses from πold (64 in DeepSeekMath, 16 in DeepSeek-R1), grades them, and gives every token of response i one advantage, its reward Ri measured against the group:
Otherwise GRPO keeps PPO's per-token ratio and clip, averages each response's token terms over its length |yi| before the group, and moves the KL term into the loss, estimated per token as πref/πθ − log(πref/πθ) − 1.
Write Ri − mean = ((G−1)/G)·(Ri − R̄−i), where R̄−i is the mean of the other G−1 rewards. Those are independent of yi, so up to the factor (G−1)/G this is an unbiased baseline. Dividing by the standard deviation gives every prompt's advantages unit spread; the argument does not cover this step, since the std depends on Ri, and Dr. GRPO (below) removes it.
On samples from πθ the mean of the KL estimate is the KL divergence, as for the log-ratio estimate of Chapter 02, but no sample is negative; on samples from πold it is biased, which DeepSeek-V3.2 corrects with the ratio πθ/πold.
Figure 3.2 shows the group baseline at work on pass/fail rewards.
Take G = 8 with rewards (1, 1, 1, 0, 0, 0, 0, 0), an illustrative group. The mean is 3/8 = 0.375. The population variance is 0.375 × 0.625 = 0.2344, so std = √0.2344 = 0.484. Some implementations use the sample standard deviation instead, dividing the variance by G − 1 = 7; that gives std 0.518 and advantages +1.21 and −0.72.
Labs drop such groups. MAI-Thinking-1, Microsoft AI's reasoning model, samples G = 128 rollouts per problem and trains only on problems whose pass rate lies in [0.1, 0.8]. Applied to groups of eight, that filter would keep 1/8 = 0.125 and 3/8 = 0.375 but drop 7/8 = 0.875 and 8/8. DAPO and MiMo-V2.6 drop only groups that are all correct or all wrong.
# One GRPO update. policy and ref return per-token log-probs, shape [|y|].
def grpo_update(policy, ref, prompts, G, eps, beta, n_minibatches, opt):
batch = []
for x in prompts: # weights now = pi_old
ys = [policy.sample(x) for _ in range(G)]
R = torch.tensor([float(reward(x, y)) for y in ys]) # [G]
A = (R - R.mean()) / (R.std(unbiased=False) + 1e-6)
for y, a in zip(ys, A):
with torch.no_grad():
batch.append((x, y, a, policy.logprobs(x, y), ref.logprobs(x, y)))
for mb in split(batch, n_minibatches): # pi_theta drifts from pi_old
loss = 0.0
for x, y, a, logp_old, logp_ref in mb:
logp = policy.logprobs(x, y) # [|y|], with grad
rho = torch.exp(logp - logp_old)
surr = torch.min(rho * a, torch.clamp(rho, 1 - eps, 1 + eps) * a)
d = logp_ref - logp # log(pi_ref / pi_theta)
kl = torch.exp(d) - d - 1 # per-token estimate, >= 0
loss = loss - (surr - beta * kl).mean() / len(mb) # mean over tokens, then responses
opt.zero_grad(); loss.backward(); opt.step()On long chains of thought and large MoE models, GRPO's settings were changed one or two at a time. The table gives each method's five choices; the paragraphs say which ones it changed and why.
| Method | Advantage baseline | Importance ratio | Clip, cap or mask | Length normalization | KL term |
|---|---|---|---|---|---|
| PPO (RLHF-style) | learned critic, GAE | per token | clip [1−ε, 1+ε] | mean over tokens (Et) | per-token penalty in the reward, toward πref |
| GRPO (DeepSeekMath) | group mean, ÷ std | per token | clip [1−ε, 1+ε] | each response's tokens, then the group | in the loss, toward πref |
| DAPO | group mean, ÷ std | per token | clip [1−εlow, 1+εhigh] with εlow = 0.2, εhigh = 0.28 | all tokens of the group | none |
| Dr. GRPO | group mean, no ÷ std | per token | clip [1−ε, 1+ε] | a constant, no length division | none (β = 0) |
| CISPO (MiniMax-M1) | group mean, ÷ std | per token | no token is cut off: its ratio, capped at 1+εhigh, becomes a constant weight on its gradient | all tokens of the group | none |
| GSPO | group mean, ÷ std | one per response, si | si clipped to [1 − 3×10−4, 1 + 4×10−4] | responses | left out of the paper |
| Kimi K2.5, K3 | group mean, no ÷ std | per token | zero gradient outside a log-ratio band | all tokens of the batch | toward πold, through a squared log-ratio penalty |
| MIS-PO (Step 3.5 Flash) | learned critic, γ = λ = 1 | none: kept tokens get weight 1 | binary masks on the trainer-to-engine ratio: per token [0.5, 2], per trajectory (geometric mean) [0.996, 1.001] | not stated | unbiased estimate, 0.001, in the final stage |
| MAI-Thinking-1 | group mean, ÷ std | per token, capped at ρmax = 50 | clip [0.4, 2.5 + k], k set by an entropy controller | all tokens of the global batch | none in the written objective |
| MiMo-V2.6 | group-relative (GRPO) | per token, to the rollout policy | mask: the ratio must lie in bounds that start at [0.2, 5.0], separate for A ≥ 0 and A < 0 | all tokens of one prompt's G responses | none in the written objective |
DAPO (ByteDance Seed, 2025), built while training Qwen2.5-32B base on math, changed the clip and the length normalization, and added two fixes to what the model trains on:
Together they raised AIME 2024 accuracy from 30 to 50.
Dr. GRPO (Sea AI Lab, 2025) changed the baseline and the length normalization after tracing two biases to GRPO's normalizers. Dividing by |yi| gives short correct answers larger per-token updates and penalizes long wrong answers less per token, so wrong answers drift longer (the 1/6 against 1/14 of the toy example). Dividing by std weights prompts that are almost always or almost never solved more heavily, as in the 1-of-8 case above. Dr. GRPO deletes both divisions.
CISPO (MiniMax, 2025) turned the clip into a cap, to keep the tokens that clipping silences. Training MiniMax-M1, the lab found that reflective tokens such as "However" and "Wait" were rare under its base model, so their ratios jumped after the first update and the clip dropped them from the later updates on the same batch (M1 runs 16 per batch). CISPO clips the importance weight rather than the token update: each token's A·∇log πθ is multiplied by min(ρt, 1+εhigh), held constant, so a token past the cap still contributes at the capped weight, like token 3 of the toy example. There is no lower cap and no KL term. MiniMax kept CISPO for the M2 series, and Mistral trained Leanstral 1.5 with it.
GSPO (Qwen, 2025) changed the importance ratio from one per token to one per response. It argues that ρt, an importance weight from a single sample of each next-token distribution, adds noise that accumulates over long responses and, the authors report, can cause irreversible collapse. It clips one length-normalized ratio per response, si = (πθ(yi|x)/πold(yi|x))1/|yi|, a geometric mean of token ratios that stays close to 1, which is why its band is tiny; GSPO's and GRPO's ranges "typically differ in order of magnitude". Chapter 06 returns to its claim that this removes the need for routing replay. Qwen names GSPO for Qwen3-Next and Qwen3-Omni, and its use in the July 2025 Qwen3 updates is inferred from Qwen's GSPO blog. Ant uses GSPO for Ling-2.6-1T's agentic specialists; its own LPO, for Ling-1T, sits between token and sequence ratios with one ratio per sentence.
Kimi's objective changes the KL term: a squared penalty on each token's log-ratio pulls toward πold, not πref. It comes from the k1.5 paper (derivation below) and K2 built on it; K2.5 changed it to a per-token form: for each token, the ratio ρt times (r − r̄), with r̄ the group's mean reward, minus ω(log ρt)², summed over all tokens and divided by the batch's total token count. Tokens whose log-ratio leaves a fixed interval get zero gradient, whatever the advantage's sign. K3's optimization "follows the algorithm in Kimi K2.5".
The k1.5 objective maximizes reward minus ω times the KL divergence to the previous policy πold; Kimi writes this coefficient as τ, which these notes keep for the sampling temperature. As for DPO in Chapter 02, the optimum π* satisfies r(x, y) − ω log Z(x) = ω log(π*(y|x)/πold(y|x)). Kimi fits it by least squares, minimizing (r(x, yi) − r̄(x) − ω log(πθ(yi|x)/πold(yi|x)))², with the group's mean reward r̄ in place of ω log Z, which "approaches the expected reward" as ω grows.
MIS-PO (StepFun, 2026) changed the importance ratio and the limit: it keeps a critic and drops the importance weight altogether. In Step 3.5 Flash each token's ratio between the trainer's and the inference engine's probability must lie in [0.5, 2], and each trajectory's geometric-mean ratio in [0.996, 1.001]; tokens and trajectories outside are masked, and MIS-PO "treats retained trajectories as effectively on-policy", so they enter the loss with weight 1. Like IcePop (Chapter 06) it filters on the gap between the two engines, but the kept tokens carry no ratio at all.
MAI-Thinking-1, Microsoft AI's reasoning model, changed the clip in two ways. It widens its upper clip bound when entropy falls below a target of 0.3 and narrows it above, in place of an entropy bonus. It also caps ρt at ρmax on every branch, including the two cases that PPO leaves unclipped (a good token that became less likely, a bad one that became more likely), because those branches caused gradient-norm spikes (quote below).
MiMo-V2.6, Xiaomi's agentic MoE, changed the length normalization and the mask. It averages over all tokens of one prompt's G responses (the loss of Chapter 05), which the report says "prevents response length from growing too quickly". Its mask bounds, set separately for each sign of the advantage, are retuned from entropy during training.
The latest change reverses the first choice. Z.ai's GLM models used group-wise GRPO variants through GLM-5, whose reasoning RL used DAPO-style bounds εlow = 0.2, εhigh = 0.28 (the β = 2 beside them in GLM's report is the IcePop band of Chapter 06, not a KL weight). GLM-5.2's long agent runs use compaction, rewriting the history mid-episode so the work continues in a shorter context (Chapter 05); in GLM's words, a super-long trajectory "is split by compaction into multiple sub-traces". Rollouts of one prompt then yield different numbers of sub-traces of very different lengths, leaving a group average nothing consistent to compare. GLM-5.2 therefore moved to PPO with a critic that estimates token-level advantages for each rollout alone, trained every sub-trace with a token-level loss, and later named the method SAO. GLM does not report the critic's size, its λ, or its cost.
StepFun never dropped the critic. Its Open-Reasoner-Zero (February 2025), which ran RL straight from Qwen2.5 base models, picked PPO over GRPO for the critic's value estimates, with GAE at λ = γ = 1, the value at which DeepSeek found PPO close to GRPO (above). Step-3 trained with a value network supplying the advantages, Step 3.5 Flash runs MIS-PO with a critic at the same λ = γ = 1, and Step 5 Preview pretrains its critic to reduce the variance of the actor loss in long agentic RL.
The table follows the release-by-feature grid behind the cross-lab posttraining section of the atlas.
| Lab | Policy-optimization objective, by release |
|---|---|
| DeepSeek | GRPO from V3 to V4.1-Flash; V3.2 adds the corrected KL estimate and drops negative-advantage responses whose sampling policy drifted too far from the current one |
| Kimi | k1.5 policy gradient in Kimi-VL, K2, Kimi-Linear, K2.5 and K3 |
| GLM | GRPO (4.5), GRPO variants (4.1V to 4.6V, 5), critic-based PPO named SAO (5.2, 5.3) |
| MiMo | DAPO-style GRPO (MiMo-7B), GRPO with four ratio bounds (V2.6) |
| MAI | GRPO variant with entropy-controlled upper clip and outer cap (MAI-Thinking-1) |
| Gemma | Gemma 3: RL built on BOND, WARM and WARP; Gemma 4 not disclosed |
| Qwen | GRPO (Qwen3); GSPO (Qwen3-Next, Qwen3-Omni; inferred for the July 2025 Qwen3 updates); SAPO (Qwen3-VL); not disclosed for Qwen3.5 and 3.8 |
| MiniMax | modified GRPO (Text-01); CISPO (M1, M2 to M2.7); M3 not disclosed |
| StepFun | PPO with a critic (Open-Reasoner-Zero); actor-critic RL (Step-3); MIS-PO with a critic (3.5 Flash); actor-critic with a pretrained critic (Step 5 Preview) |
| Ling | DPO (Ling-plus); C3PO, GRPO under a per-step token budget (Ring-lite); IcePop (Ring-flash-2.0, Ring-1T); LPO (Ling-1T); LPO and GSPO specialists (Ling-2.6-1T); KPop (Ring-2.6-1T) |
| Nemotron | GRPO, RLOO and RPO (Llama-Nemotron); GRPO with masked importance sampling (Nemotron 3 Nano); asynchronous GRPO (3 Super, 3 Ultra, 3.5) |
| Mistral | none (Small 3); GRPO without KL, with clip-higher (Magistral); CISPO (Leanstral 1.5); not disclosed (Large 3, Medium 3.5) |
| Inkling, gpt-oss, Llama 4, Muse | Not disclosed |
"Since the value model is usually of similar size as the policy model, it introduces a significant memory and computational overhead."
"We therefore move from group-wise optimization to a critic-based PPO formulation that learns from individual rollouts, relying on a critic to estimate token-level advantages rather than group-relative comparisons."
"In practice, however, we found that these unclipped branches sometimes led to catastrophic gradient-norm spikes."
"We select PPO over GRPO due to its superior value estimation enabled by a learned critic."
For A > 0 the objective is min(ρ, 1.2)·A. At ρ = 1.3 it is the constant 1.2A, with zero derivative. At ρ = 0.7 the min picks the unclipped ρA, whose gradient flows and raises a good token that has become less likely.
Mean 0.875, std = √(0.875 × 0.125) = 0.331. Correct answers get (1 − 0.875)/0.331 = +0.38 and the wrong one (0 − 0.875)/0.331 = −2.65, the mirror image of the 1-of-8 case. The pass rate 0.875 is above MAI's upper bound 0.8, so the group is dropped.
How does a group of rewards become per-sample advantages, and when does PPO-style clipping stop a token's gradient?
Two small experiments that go with chapter 03. In the first, mark which of eight sampled answers to one prompt were correct and see the advantage each sample gets. In the second, drag the probability ratio of one token and see the clipped objective that PPO and GRPO optimize.
What produces the number that RL maximizes for a math answer, a code patch or an essay, and what happens when the model finds a way to raise that number without doing the task?
A reward turns a finished response, or the final state of an agent's sandbox, into one number. That number comes from a program that checks the output or from a model that judges it. This chapter takes the two sources in turn, then how labs combine several rewards into one, then what goes wrong when the policy raises the number without doing the task.
When a task has a known answer or a test, a program can compute the reward. The reports describe three kinds of check, the three left columns of Figure 4.1:
Open-ended tasks have no answer key, so a model reads the response and judges it. A generative reward model (GRM) is an LLM that reads the prompt, the response and often a rubric (criteria written for that prompt) and writes its judgment as text. The labs differ in who judges and what the judge checks:
One response often earns several rewards, and RL needs one advantage. For helpfulness and safety, MAI-Thinking-1 ranks its rewards by priority and combines them in two ways:
Most recipes add one more term, a price on length, which has its own section below.
Figure 4.1 places all of these reward sources on one scale, from fully checkable to fully judged. Its lower two rows preview the rest of the chapter: how each source gets hacked, and what labs do about it.
Outcome rewards say nothing about length, and RL tends to grow it. R1-Zero's thinking lengthened under outcome rewards alone (chapter 08), Kimi k1.5 saw "an overthinking phenomenon" during RL, and learned judges drift toward verbosity (Kimi K3, next section). A long response is paid for twice: every RL step has to generate it, and every user waits for it. A length penalty adds a term that depends on the response's length ℓ in tokens, often counting only the reasoning tokens:
The designs differ in what ℓ is measured against (Figure 4.2):
Penalties have failure modes of their own. A plain penalty can be hacked by collapsing into overly terse answers, so MAI penalizes only lengths outside a per-context range. Applied from the first step, a penalty can slow training early on, so Kimi k1.5 starts without it and adds a constant one later. And length can fall with no penalty at all: Inkling's chain of thought grew terser during RL, dropping grammatical overhead, and Thinking Machines says "efficiency alone drove the compression".
A length penalty changes the reward. Length normalization in the loss (chapter 03) is a different lever: it sets how much each token of a long response counts in the update. It can also hold length back, as MiMo-V2.6's group-level token average is meant to, but it never scores a response lower for being long.
Reward hacking is earning reward in a way the designer did not intend, with solutions "valid in some literal sense but don't meet the designer's intent" (Amodei et al.). The route depends on the kind of reward:
| Model | Reported hacking behavior |
|---|---|
| GLM-5.2 (Z.ai) | downloaded the solution with curl https://raw.githubusercontent.com/…; ran find /workspace -name "*hidden*" and read .eval/secret_cases.json |
| MAI-Thinking-1 | searched the internet for the original pull request; searched git history for the fix; tampered with tests |
| MiMo-V2.6 (Xiaomi) | pip-installed a newer pytest as an answer key; fetched an Astropy file from GitHub; cloned the latest Matplotlib |
| Kimi K3 | on GPU-kernel speed tasks: replaying recorded GPU work (CUDA graph replay), caching inputs, computing in lower precision than required |
| DeepSeek-V4.1-Flash | exploited recently disclosed bugs in the sandbox's file system and security software, and took answers leaked by package mirrors (servers that host software packages) |
Hacking tends to grow with capability: Moonshot says stronger agents "explore more aggressively and may even attempt reward hacking".
The reported defenses act at five points, from mid-training to auditing the run:
The sketch joins two of these defenses: GLM-5.2's guard screens each tool call during the episode, and MAI-style grading restores the test files and runs hidden tests at the end.
def coding_rollout(policy, env, task, rule_filter, judge, max_turns):
"""Sketch: GLM-5.2-style online hack guard, then MAI-style grading."""
history = [task.statement]
for _ in range(max_turns):
call = policy.next_tool_call(history) # e.g. a shell command
if call.is_submit:
break
if rule_filter(call) and judge.is_hack(call, history): # recall, then precision
result = "[blocked]" # dummy output, rollout goes on
else:
result = env.run(call)
history += [call, result]
env.restore(task.test_files) # undo any edits to tests
env.apply(task.hidden_tests) # tests the agent never saw
passed = env.run_tests(task.fail_to_pass + task.pass_to_pass)
return 1.0 if all(passed) else 0.0 # binary rewardA plain correctness reward teaches guessing, since any answer with a chance of being right beats "I don't know". Thinking Machines trained Inkling for calibration "with RL against proper scoring rules on a large corpus of resolved real-world questions." A scoring rule grades a reported probability q once the outcome o ∈ {0, 1} is known (o = 1 if the event happened). It is proper if reporting one's true belief p gives the best expected score, and strictly proper if that optimum is unique (Gneiting and Raftery). Three common rules:
Rubric rewards can push the other way. Kimi K2's report says its rubrics penalize hedging such as "I might be wrong", which may favor confident answers on ambiguous questions. Rewards for saying "I don't know", including Inkling's and MAI-Thinking-1's, are covered in chapter 10.
A model believes an event has probability p = 0.7 and reports q. The outcome is o = 1 with probability p, so its expected Brier loss is p(1 − q)² + (1 − p)q²:
The derivative −2p(1 − q) + 2(1 − p)q = 2(q − p) is zero only at q = p, so the honest report minimizes the loss. The log score, a reward, punishes overconfidence harder: its expectation p ln q + (1 − p) ln(1 − q) is 0.7 ln 0.7 + 0.3 ln 0.3 = −0.611 at q = 0.7 but 0.7 ln 0.99 + 0.3 ln 0.01 = −1.389 at q = 0.99. The linear score's expectation, 0.7q + 0.3(1 − q) = 0.3 + 0.4q, is highest at q = 1, so it teaches exaggeration.
For scale, Inkling's reported Brier score on the Prophet Arena forecasting benchmark is 0.1617, measured on a checkpoint other than the released one. Prophet Arena averages the squared error over each event's outcomes, which for a yes/no event is exactly (q − o)². On such an event, always saying 0.5 scores 0.25 and a perfect forecaster scores 0.
"We find that GLM-5.2 shows more potential hacking behavior than GLM-5.1. This makes the verification signal easy to optimize, but fails to actually improve the fundamental capabilities of the model."
"Rubrics can penalize errors in principle, but in practice they emphasize recall and can be hacked by models spraying plausibly relevant facts hoping to match rubric items."
Z.ai says abruptly stopping rollouts can cause training instability and collapse. Blocking only the hacked call denies the shortcut and keeps the rest of the trajectory for training.
Brier: q = 0.6, where the derivative is zero (expected loss 0.24, against 0.25 at q = 0.5 or 0.7). Linear: the expectation 0.6q + 0.4(1 − q) = 0.4 + 0.2q peaks at q = 1, an overconfident report.
What does an environment for training an agent contain, and how did labs scale from grading one written answer to grading multi-turn agents in hundreds of thousands of sandboxed tasks?
Chapter 03's RL loop needs a prompt x, a sampled response y and a reward r(x, y). DeepSeek-R1, DeepSeek's January 2025 reasoning model, trained on 26K math questions, 17K algorithm problems and 8K bug-fixing problems. A bug-fixing prompt holds a GitHub issue, the buggy source and failing unit tests; the reward checks whether the corrected code passes. The report describes no tool use, so the checker is the whole environment.
An agent also needs a world to act on. DeepSeek-V3.2 calls this an "<environment, tools, task, verifier>" tuple. In the terms an episode uses, an environment has four parts:
ReAct (Yao et al., 2022) formalized the loop: at turn k the agent sees the context ck = (o1, a1, …, ak−1, ok) and picks action ak (k counts turns). A harness or scaffold, the program around the model, runs each tool call in a sandbox (an isolated container or virtual machine) and appends the output. MAI-Thinking-1, Microsoft AI's reasoning model, ends the loop, and with it the rollout (one sampled episode), "when the model emits no tool calls, or when the rollout exceeds the step, context-length, or time budget". Figure 5.1 follows one episode of a software task, and shows what the trainer receives from it.
For training, the episode is one token sequence; in agentic RL the "response" yi is this whole trajectory. A mask mi,t ∈ {0, 1} marks the tokens the policy generated. MiMo-V2.6, Xiaomi's September 2026 release, writes its loss in this form, chapter 03's update with the mask added and the per-token importance ratio omitted here:
Here yi and mi have |yi| entries and Ai is one scalar per trajectory. Tool outputs sit in yi,<t and condition later tokens, but mi,t = 0 keeps them out of the gradient: a policy gradient reweights the policy's own choices, and the policy did not choose the tool output. One reward covers every policy token; MAI applies credit "uniformly across all tokens from all policy steps".
As written here, with yi the whole trajectory, the normalizer ∑i |yi| also counts the tool tokens; MiMo defines this length as the generated-token count, so its own normalizer appears to be ∑i,t mi,t, an average over policy tokens only. The two differ by one factor per prompt: counting tool tokens gives a prompt whose trajectories are mostly tool output a smaller step per policy token.
def run_episode(policy, sandbox, max_turns, max_tokens):
x = sandbox.task_prompt() # task x
y, mask = [], [] # trajectory y and its loss mask m
for k in range(max_turns):
out = policy.generate(x + y) # one turn: reasoning + optional tool call
y += out; mask += [1] * len(out)
call = parse_tool_call(out)
if call is None: # no tool call: the agent has finished
break
obs = sandbox.execute(call) # e.g. run a bash command
y += obs; mask += [0] * len(obs)
if len(x) + len(y) > max_tokens: # context budget exhausted
break
return x, y, mask, sandbox.verify() # e.g. run hidden F2P and P2P tests
def agentic_loss(policy, episodes): # G episodes of the same task
r = [reward for _, _, _, reward in episodes]
A = [(ri - mean(r)) / (std(r) + 1e-6) for ri in r] # group advantage, chapter 03
total = sum(len(y) for _, y, _, _ in episodes) # sum of |y_i|, tool tokens included
loss = 0.0
for i, (x, y, mask, _) in enumerate(episodes):
logp = policy.logprobs(x, y) # log π(y_t | x, y_<t) for each t
loss -= A[i] * sum(m * lp for m, lp in zip(mask, logp))
return loss / totalThe pipelines in these reports share one shape:
Software engineering is the most developed case, and SWE-bench (Jimenez et al., 2023) set its template. From about 90,000 pull requests (PRs) in 12 Python repositories it kept merged PRs that resolve an issue and add tests, and required a fail-to-pass (F2P) test, one that fails before the fix and passes after; 2,294 tasks remained. MAI also checks pass-to-pass (P2P) tests, which must keep passing.
Labs scaled this up by having an LLM agent build each repository's container image (a snapshot of the code with its dependencies installed). DeepSeek-V3.2 accepted an environment only if the reference patch gave a nonzero F2P count and zero pass-to-fail tests, and trained on 24,667 code-agent tasks. MiMo-V2-Flash, Xiaomi's 309B-total mixture-of-experts model (MoE: each token passes through only a few of many expert sub-networks; pretraining chapter 02), reports 70% build success on over 10,000 concurrent containers (Kubernetes pods) and RL across approximately 120K environments; GLM-5 (Z.ai) built over 10K environments in 9 languages. Qwen3-Coder-Next (February 2026) reports 807,693 task instances from real pull requests in 52,960 repositories, plus about 800K with injected bugs, each in an executable Docker environment.
MAI starts from 102 million public GitHub PRs and keeps those that were merged, touch fewer than 15 files, change both code and tests, and link to an issue.
| Stage | Count | Kept from previous | Kept from 4.87M |
|---|---|---|---|
| Public GitHub PRs | 102,000,000 | ||
| Filtered, with linked issue | 4,870,000 | 4.87 / 102 = 4.8% | 100% |
| Agent-built image runs the tests | 2,080,000 | 2.08 / 4.87 = 42.7% | 42.7% |
| At least one F2P test | 745,452 | 745,452 / 2,080,000 = 35.8% | 15.3% |
| Empty patch fails, gold patch passes, deterministic | 265,617 | 265,617 / 745,452 = 35.6% | 5.5% |
The 42.7% here differs from the report's 42.8% because 2.08M is rounded. End to end, 265,617 / 102,000,000 = 0.26%, one PR in 384, from 94,044 repositories (≈ 2.8 each) and 265,617 / 2,294 ≈ 116 times SWE-bench's task count. Of the 2.08M − 265,617 ≈ 1.81M images that built but failed later checks, many still run, so MAI generates synthetic bug tasks in them: "in most cases, the failures were due to low-quality problem statements or insufficient test coverage rather than invalid execution environments."
Terminal. Terminal tasks set a goal reached through a shell and checked by a test script. GLM-5 turned LLM-drafted tasks into Harbor-format tasks (a description, a Docker environment and tests), giving thousands of environments with over 90% of Docker builds succeeding. MiMo-V2-Flash built about 30,000 from Stack Overflow and Stack Exchange material.
Search. Search questions must be hard to answer and easy to check. DeepSeek-V3.2 had an agent build questions about long-tail entities with web search, and kept one only when a verification agent confirmed its answer and rejected every other agent's candidate; with filtered prompts from its existing helpful-RL data added, the set had 50,275 search tasks.
General tool use (booking, inventory, support) runs in synthesized closed worlds: a seeded database, tool functions, tasks and a checker. DeepSeek-V3.2's synthesis agent wrote tools, a task, a solution and a verifier, then raised the difficulty, keeping tasks with nonzero pass@100 (solved at least once in 100 samples): 1,827 environments and 4,417 tasks. MAI reports more than 150 environments and 130,000 tasks, often with over 50 tools each. GLM-5.3's research agents turn real work into long-horizon environments, some worth days of an engineer's time, and each synthesized verifier is tested before its binary reward is used for training.
Stated task or environment counts (blank: not reported):
| Model | Software engineering | Terminal | Search | General tool use |
|---|---|---|---|---|
| DeepSeek-R1 | 8K bug-fix prompts | |||
| DeepSeek-V3.2 | 24,667 tasks | 50,275 tasks | 4,417 tasks, 1,827 envs | |
| MiMo-V2-Flash | 90K tasks | ~30K | 150K | 50K |
| GLM-5 | >10K envs | thousands | ||
| Qwen3-Coder-Next | 807,693 real-PR tasks, ~800K with injected bugs | |||
| MAI-Thinking-1 | 265,617 envs | none targeted | >150 envs |
MiMo-V2-Flash's task table has no terminal row; its 30K "code agent" tasks with synthesized prompts are most likely the ~30K terminal tasks built from Stack Overflow and Stack Exchange, so they are counted once, under terminal.
The same task can reach the model through different harnesses. The harness sets the system prompt, which tools exist and how their calls are written, what tool responses look like, and how the context is shortened when it fills. A model that trains inside only one harness can learn the harness along with the task: Xiaomi warns that training in a single harness may couple task-solving strategies to that harness, and users run models in harnesses the lab never trained in.
A toy case: harness A edits files with edit_file(path, old, new) and summarizes the history at 80% of the window; harness B accepts only unified diffs through apply_patch and never summarizes. A policy trained only in B can learn to answer every edit with a diff, which A cannot apply, although the bug and its fix are the same in both.
Labs vary the harness in three ways:
Mixing harnesses also changes the RL system. In MiMo's large batches each group of samples is bound to one harness, so a Harness Pool keeps many harness instances running at once, each configured independently (chapter 06 covers the rest of the RL system).
Besides reward hacking through the environment (chapter 04), long and numerous episodes raise two problems; which harness to train in is the previous section.
The context overflows. DeepSeek-V3.2, with a 128K window, tested three rules at evaluation, triggered at 80% of the window: summarize and restart, discard the oldest 75% of tool history, or discard all of it. Compaction is such a rewrite done by the harness inside an episode: when the context fills, it shortens the history (for example into a summary) and the episode continues in a fresh context, so one long trajectory becomes several sub-traces.
Sub-traces are awkward for group-relative advantages, which compare G whole responses: GLM-5.2 found that "different rollouts under the same prompt yield different numbers of trainable traces with highly variable lengths". It trained every sub-trace with a token-level loss and returned to a critic for advantages; chapter 03 explains that switch.
The sandboxes must scale and contain the agent. Kimi K3 created 51,219,741 sandboxes from 1,505,678 images in training and evaluation, added Firecracker microVMs (small, fast-starting virtual machines) after agents crashed the operating-system kernel in container sandboxes, and pauses a sandbox while it waits for the model, a wait that can fill 98% of its lifetime. DeepSeek-V4.1's training needed "millions of concurrent sandbox instances" and packs over 2,500 live containers per node, up from about 1,000.
"large-scale RL on synthetic data yields substantial improvements over DeepSeek-V3.2-SFT on Tau2Bench, MCP-Mark, and MCP-Universe benchmarks. In contrast, restricting RL to code and search scenarios does not improve performance on these benchmarks, further highlighting the potential of synthetic data."
"Training with a single fixed agent harness can cause a model to overfit to a particular tool schema, system prompt, context management mechanism, or interaction protocol."
"As agent capability improves, much of the difficulty in scaling post-training moves from the model to the environment."
The environment produced them. A policy gradient changes the probability of the policy's own choices in proportion to the advantage, so it has nothing to say about tool output. The tokens stay in the context yi,<t, but mi,t = 0 removes them from the sum.
The task is a question about a long-tail entity whose reference answer a verification agent confirmed. The actions are web-search tool calls, and the observations are the search results returned to the model. The verifier checks the final answer against the confirmed reference (V3.2 also scores search responses with a rubric-guided generative reward model), which is why the questions are built to be hard to answer but easy to check.
When an agent rollout can run for hundreds of tool calls on an inference engine separate from the trainer, how do labs keep each sample close enough to the weights being trained that the importance ratios of Chapter 03 still mean something?
Each RL step in Chapter 03 runs four jobs, usually split between two systems (Figure 6.1):
Rollout is a large share of the bill: MiMo-V2.6-Pro's RL cost split 43.8% rollout, 43.5% training and 12.7% grading, and in MAI's largest job 4,096 of 4,864 GB300 chips ran inference.
Two separate things can go wrong between steps 1 and 3:
The importance ratio separates them into two factors. Let πθ be the trainer's probability under the weights being updated and πtrain its probability under the weights that sampled the token (Chapter 03's πold, computed in the trainer):
Toy numbers: the engine logged 0.30 for a token; the trainer computes 0.31 with the same weights and 0.36 with its current ones. The ratio 0.36 / 0.30 = 1.20 is 0.36 / 0.31 ≈ 1.16 from the policy moving times 0.31 / 0.30 ≈ 1.03 from the systems disagreeing.
In synchronous RL the four steps run in order: the engine samples a whole batch with the current weights, the trainer updates, and the next batch waits for the new weights (Figure 6.2, top). Kimi runs this way with both engines colocated on the same GPUs, each offloading while the other works, and K2 copies a full parameter update to the engines in less than 30 seconds.
The cost is the long tail. A batch is done only when its longest rollout is, and Kimi describes K3's agent environments as running "often over hundreds or thousands of tool calls". GLM-5 gives the idle GPUs this causes as its reason for making agentic RL asynchronous (quote below). Even a synchronous step is slightly off-policy once its batch is split into several mini-batch updates (worked example below).
Asynchronous RL (Figure 6.2, bottom) lets the steps overlap: rollout workers keep generating with whatever weights they hold, and the trainer updates whenever enough finished samples have accumulated. Staleness is the number of policy updates between the weights that generated a sample and the weights being trained. AReaL, an open asynchronous RL system from Tsinghua and Ant Group, caps the staleness allowed in each batch (a cap of 0 is synchronous RL) and reports up to 2.77× faster training than synchronous systems on the same GPUs.
Labs limit staleness in three ways, often combined:
Earlier, MiMo-7B had avoided asynchronous training because it "introduces staleness in long-sequence responses".
MAI's oldest kept sample is thus more than twice as stale as R1's last mini-batch (40 against 15 gradient steps); R1 bounds staleness through the number of mini-batches, MAI through an explicit cap.
A long rollout may still be running when new weights arrive. Labs handle it in four ways:
A trajectory from several versions breaks the assumption that one πold produced every token. The common fix takes the ratio's denominator from the rollout engine itself: πinfer, logged at sampling time by whichever version produced the token. This ratio contains both factors of the split above, and labs keep it in check in three ways:
# Trainer side of asynchronous RL (sketch). Rollout workers run in parallel and
# put finished samples on a queue. Each sample s carries s.version (the weights it
# started with), s.logp_infer (per-token log-probs the engine logged, [|y|]) and
# s.A (its group-relative advantage, Chapter 03).
def trainer_loop(policy, queue, engines, B, max_stale, eps_low, eps_high, opt):
version = 0
while True:
batch = []
while len(batch) < B:
s = queue.get() # waits for a finished rollout
if version - s.version <= max_stale: # drop samples that are too stale
batch.append(s)
loss = 0.0
for s in batch:
logp = policy.logprobs(s.x, s.y) # [|y|], with grad
rho = torch.exp(logp - s.logp_infer) # pi_theta / pi_infer, per token
keep = (rho > 1 - eps_low) & (rho < 1 + eps_high) # mask, not clip
loss = loss - (keep * rho * s.A).mean() / B
opt.zero_grad(); loss.backward(); opt.step()
version += 1
for e in engines: # in-flight rollouts continue
e.load_weights(policy.state_dict(), version) # on the new weights
Logging πinfer exposes the second factor. With identical weights, the trainer's πtrain(yt) and the engine's πinfer(yt) still differ, because the two use different kernels, reduction orders, batch shapes and precision. In a mixture-of-experts (MoE) model, where a router sends each token to a few experts (pretraining notes, Chapter 02), a rounding error can change which experts are picked. Illustratively, scores of 0.3001 and 0.2999 for a token's second and third expert swap under a 0.0003 error. Ma et al. measured Qwen3-30B-A3B rolled out in SGLang and replayed in Megatron: about 10% of router decisions differed, and 94% of tokens used a different expert in at least one layer. The ratio ρt then mixes real policy change with engine noise, and the Ring-1T paper argues that the gap can grow from one training step to the next.
The fixes work at three levels: replay the engine's discrete choices in the trainer, make both systems compute the same numbers, or drop tokens where they still disagree.
MiMo-V2.6 also froze the router during RL after a trainable router collapsed expert load within 20 steps (at decoder layer 9: coefficient of variation 0.78 to 2.0, cold experts 0.5% to 22%). The stated reason is load balance, not engine mismatch.
Ring-1T keeps a token when its mismatch ratio κt lies in [0.5, 5]. GLM-5 uses the band [1/b, b] with b = 2, so [0.5, 2]. Four illustrative tokens:
| Token | πinfer | πtrain | κt | Ring-1T [0.5, 5] | GLM-5 [0.5, 2] |
|---|---|---|---|---|---|
| a | 0.600 | 0.570 | 0.95 | kept, weight 0.95 | kept, weight 0.95 |
| b | 0.020 | 0.008 | 0.40 | masked | masked |
| c | 0.010 | 0.030 | 3.0 | kept, weight 3.0 | masked |
| d | 0.002 | 0.014 | 7.0 | masked | masked |
A masked token contributes zero gradient. Mismatches large enough to mask are common without alignment: in one GRPO run without R3, Ma et al. found that after 60 steps more than 10% of tokens had probabilities differing by more than a factor of 2, so GLM-5's band would mask over a tenth of them. With R3 that fraction stayed below 10−4 most of the time.
The table follows the release-by-feature grid behind the cross-lab posttraining section of the atlas.
| Lab | Rollout and training timing | Training–inference alignment |
|---|---|---|
| DeepSeek | Synchronous through V4 (inferred); asynchronous in V4.1-Flash | Keep Routing since V3-0324; sampling mask (V3.2); deterministic kernels, FP4 rollout (V4); concatenated routing replay (V4.1-Flash) |
| Kimi | Synchronous, colocated, with partial rollouts (K2, K2.5, K3) | Token log-ratio mask (K2.5, K3); MXFP4/MXFP8 in both engines (K3) |
| GLM | Synchronous reasoning RL, asynchronous agentic RL (4.5, 5); asynchronous (5.2, 5.3) | IcePop, deterministic top-k (5); R3-style replay, numerical alignment (5.3) |
| MiMo | Synchronous (7B); partial rollouts (V2-Flash); asynchronous, staleness 4 (V2.6) | R3, FP8 in both, token discard (V2-Flash); R3, candidate-set replay, MXFP4 re-quantization, frozen router (V2.6) |
| MAI | Asynchronous: 5 steps per weight update, drop beyond 8 updates | BF16 in both, routing replay, top-p mask replay |
| Qwen | Asynchronous, disaggregated, bounded staleness (3.5) | Rollout router replay, FP8 end to end (3.5) |
| MiniMax | 16 off-policy updates per batch (M1); asynchronous scheduling in its Forge framework (M2.5) | FP32 LM head (M1) |
| StepFun | Strictly on-policy PPO (Open-Reasoner-Zero); stale rollouts with replay (Step 5 Preview) | MIS-PO masks (3.5 Flash); identical kernels at staleness 0, routed-expert and top-p replay for stale rollouts (Step 5 Preview) |
| Ling | Partial rollouts under a token budget (Ring-1T); asynchronous, bounded staleness (Ring-2.6-1T) | IcePop (Ring-flash-2.0, Ring-1T); FP32 state, LM head and norms (Ring-linear-2.0); KPop (Ring-2.6-1T) |
| Nemotron | Synchronous (3 Nano); asynchronous, at most one update behind, in-flight weight updates (3 Super); asynchronous (3 Ultra, 3.5) | Masked importance ratio (3 Nano, 3 Super); IcePop in distillation (3 Ultra); router replay, FP32 LM head (3.5 Super) |
| Mistral | Asynchronous, in-flight weight updates (Magistral); asynchronous, low staleness (Large 4) | Not disclosed |
| Inkling, Llama 4 Behemoth | Asynchronous (stated without details) | Not disclosed |
| Gemma, gpt-oss, Muse | Not disclosed | Not disclosed |
"Even small per-token logprob discrepancies compound across long rollouts and can destabilize the importance-sampling correction in off-policy RL."
"This Keep Routing operation was found crucial for RL training stability of MoE models, and has been adopted in our RL training pipeline since DeepSeek-V3-0324."
"Due to the long-tail nature of the rollout process, naive synchronous RL training introduces substantial bubbles during the rollout stage because of the severely imbalanced generation of agentic tasks, which can cause large GPU idle time."
κt = 0.09 / 0.04 = 2.25. That is above GLM-5's upper limit 2, so GLM-5 masks it. It is inside Ring-1T's [0.5, 5], so Ring-1T keeps it with weight 2.25.
Replay removes the discrete part of the gap, the expert choices that differ between engines. The trainer and engine still use different kernels, reduction orders and precision, so probabilities differ slightly even on the same experts; R3 brought Qwen3-30B-A3B's KL down to about the level of a dense model, not to zero. That residual is what masking, deterministic kernels and a shared precision address.
A lab has RL-trained separate specialists for math, code and agents: how does it turn them into one model, and why did several labs move from fine-tuning on the specialists' outputs to grading the student's own samples?
Reinforcement learning (RL) on one domain at a time is easier to run: each team owns its prompts and rewards, and runs proceed in parallel. Training all skills together risks what Xiaomi calls the "see-saw" effect, where improving one skill regresses another. So labs train specialists, copies of one base model each RL-trained on one domain (MAI-Thinking-1, Microsoft AI's reasoning model, used three "to enable parallel development"; DeepSeek-V3.2 used six). Shipping one model then needs a consolidation step, and there are three ways to do it:
In both distillations the specialist is the teacher πT and the model being trained is the student πS, the policy πθ with parameters θ. They differ only in who writes the tokens.
Knowledge distillation trains a student to imitate a teacher; Hinton et al. trained small classifiers on a large model's class probabilities, its "soft targets". For language models the simplest form samples responses from the teacher and runs supervised fine-tuning (SFT) of the student on them. Say the math teacher answers "17 × 23?" with "17 × 20 = 340, 17 × 3 = 51, so 391". SFT raises the student's probability of each of those tokens, each time given the teacher's tokens before it. The data come from another model, so this is off-policy distillation. Over prompts x and teacher responses y:
The teacher's entropy H(πT) does not depend on θ, so SFT on teacher samples minimizes the forward KL, teacher first.
KL(πT ‖ πS) = Ey∼πT[log πT(y|x) − log πS(y|x)]: the first term is −H(πT) and the second is the SFT loss.
This was the 2025 default. Each DeepSeek-V3 expert "serves as a data generator for the final model"; GLM-4.5, Z.ai's mixture-of-experts flagship (pretraining notes, ch 02), distills three experts with SFT; MAI-Thinking-1 runs 4 epochs of consolidation SFT, then light RL, and StepFun's Step 3.5 Flash (February 2026) distills its domain experts into one student through their rejection-sampled outputs before its final RL. Gemma 3 distills from "a large IT teacher" without saying whether on- or off-policy.
In SFT every prefix y<t was written by the teacher; at test time the student continues its own. If it writes "17 × 20 = 34" where the teacher wrote 340, the next token follows a prefix no training example covered. This is exposure bias (ch01), and it gets worse with length: illustratively, if the student strays from what the teacher would write with probability 0.001 per token, a 10,000-token chain of thought stays on teacher-like prefixes with probability 0.99910,000 ≈ e−10 ≈ 0.00005.
On-policy distillation (OPD) changes who writes the tokens. The student answers the prompt itself; the teacher reads that answer in one forward pass and, at every position, gives the probability it would have written the same token. Say the student writes "17 × 20 = 340, 17 × 3 = 41". At "340" both give 0.9 and the signal is ln(0.9/0.9) = 0; at "41" the teacher gives 0.001 against the student's 0.4, and the signal is ln(0.001/0.4) ≈ −6.0, a strong push away from the error where it happened. Figure 7.1 sets the two side by side.
Formally, the student samples y ∼ πS(·|x) and minimizes the reverse KL, student first, at each of its own prefixes (MiniLLM, Gu et al.; GKD, Agarwal et al., samples the same way but treats the divergence as a choice: forward KL, reverse KL or a mix of the two). Here πS(·|x, y<t) is the student's next-token distribution, a vector in ℝ|𝒱| over the vocabulary 𝒱:
Estimating each position's KL from the one sampled token gives the example's per-token signal, used as a fixed weight on the gradient:
sg is stop-gradient; At acts as a per-token advantage (ch03), so OPD runs in ordinary RL code. The per-token form ignores a token's effect on later positions; Thinking Machines calls this a "discount factor of zero".
The gradient line follows from the score-function (log-derivative) identity of ch00 applied at each position; the extra term it produces, Ev∼πS[∇log πS(v)] = ∑v∇πS(v) = ∇1, is zero, so only the log-ratio weight remains.
Three properties follow:
A teacher's next proof step is A ("by induction") or C ("by contradiction") with 0.49 each and the muddled B with 0.02: πT = (0.49, 0.02, 0.49). Two unimodal students: Smid = (0.25, 0.50, 0.25) and SA = (0.96, 0.02, 0.02).
Forward KL ∑ πT ln(πT/πS): Smid = 2 × 0.49 ln(0.49/0.25) + 0.02 ln(0.02/0.50) = 0.659 − 0.064 = 0.595; SA = 0.49 ln(0.49/0.96) + 0 + 0.49 ln(0.49/0.02) = −0.330 + 1.567 = 1.238.
Reverse KL ∑ πS ln(πS/πT): Smid = 2 × 0.25 ln(0.25/0.49) + 0.50 ln(0.50/0.02) = −0.336 + 1.609 = 1.273; SA = 0.96 ln(0.96/0.49) + 0 + 0.02 ln(0.02/0.49) = 0.646 − 0.064 = 0.582.
SFT's forward KL prefers Smid, which says B half the time; OPD's reverse KL prefers SA, which commits to a good answer. The sampled signal is noisy: under Smid, drawing A gives At = ln(0.49/0.25) = +0.67 and drawing B gives ln(0.02/0.50) = −3.22. The KL estimate −At averages to the exact 1.27 (2 × 0.25 × (−0.67) + 0.50 × 3.22) but is negative whenever A or C is drawn.
Real numbers, MiMo-V2-Flash (Table 7). Multi-teacher OPD lifted the student on AIME 2025 from 89.3 to 94.1, past its RL teacher's 93.9, but on BrowseComp only from 42.5 to 45.4 against a teacher at 51.7, closing (45.4 − 42.5)/(51.7 − 42.5) = 2.9/9.2 ≈ 32% of the gap.
def opd_step(student, teachers, prompts, eta, R_max=None):
"""One on-policy distillation step with a per-token reverse-KL reward."""
losses = []
for x in prompts:
teacher = teachers[domain(x)] # multi-teacher: route by the prompt's domain
y, logp_old = student.sample(x) # y ~ pi_S(.|x); logp_old[t] = log pi_S(y_t|x,y_<t), no grad
logp_T = teacher.logprob(x, y) # one teacher forward pass over the student's tokens
A = logp_T - logp_old # A_t = minus the sampled reverse KL; a constant (sg)
if R_max is not None:
A = clip(A, -R_max, R_max) # Kimi K3 clips the per-token reward at R_max
logp = student.logprob(x, y) # same tokens, now with gradient
rho = exp(logp - logp_old) # importance ratio rho_t; equals 1 when fully on-policy
losses.append(-(rho * A).mean()) # grad = -mean_t A_t * grad log pi_S(y_t|x,y_<t)
loss = mean(losses)
loss.backward()
student.update(eta)
# Full-vocabulary variant (GKD's per-position form; DeepSeek-V4 with reverse KL): exact KL per position
def full_vocab_kl(logits_S, logits_T): # both [|y|, |𝒱|]
logq, logp = log_softmax(logits_S), log_softmax(logits_T)
return (exp(logq) * (logq - logp)).sum(-1).mean()
Multi-teacher OPD (MOPD, Xiaomi's name) routes each prompt to its domain's teacher. MiMo-V2-Flash, Xiaomi's 309B-total MoE, introduced it; a teacher may be an RL model, an SFT model "or even the student model itself", and its advantage is added to an outcome-reward advantage. GLM-5's final cross-stage OPD uses its own earlier-stage checkpoints as teachers to recover skills lost in sequential RL, and GLM-5.2 ran parallel OPD over more than ten experts. Kimi K3, Moonshot's 2.8T-total MoE, picks the teacher by domain and requested effort and clips the per-token reward.
DeepSeek-V4 "entirely replaced" its mixed RL stage with OPD, minimizing ∑i wi KL(πS ‖ πT,i) over teachers i with teacher weights wi, each KL computed exactly over the full vocabulary ("full-vocabulary logit distillation"). Instead of storing teachers' logits it caches their last-layer hidden states and recomputes logits during training: a hidden state is d numbers per token (7,168 for V4-Pro), a logit vector |𝒱| numbers (129,280), 18 times more. V4.1-Flash's teachers may come from different development stages and architectures. Teacher counts from the coded grid (the release-by-feature table behind the cross-lab posttraining page, the atlas's posttraining section):
| Release | How specialists become one model | Teachers |
|---|---|---|
| DeepSeek-V3.2 | SFT on specialist outputs, then mixed RL | 6 |
| DeepSeek-V4 | full-vocabulary multi-teacher OPD | >10 |
| DeepSeek-V4.1-Flash | full-vocabulary multi-teacher OPD | >40 |
| GLM-4.5 | SFT on expert outputs | 3 |
| GLM-5 | cross-stage OPD | 3–4 (inferred) |
| GLM-5.2 | parallel multi-teacher OPD | >10 |
| Kimi K3 | MOPD, 3 domains × 3 efforts | 9 |
| MiMo-V2-Flash | MOPD plus outcome rewards | 6 (inferred) |
| MAI-Thinking-1 | consolidation SFT, then light RL | 3 |
| Step 3.5 Flash | SFT on specialists' rejection-sampled outputs, then RL | 7 |
| Nemotron 3 Ultra | multi-teacher OPD after one unified RL stage | >10 |
| Gemma 3 (Google's open models) | distillation, on/off-policy not stated | 1 |
| Qwen3 small models (0.6B to 14B, 30B-A3B) | off-policy, then single-teacher OPD on the logits | 1 |
| Inkling-Small (preview) | single-teacher OPD | 1 |
| Muse Glimmer (Meta's 30B on-device model) | single-teacher OPD between SFT and RL | 1 |
Weight merging averages the parameters θi of models fine-tuned from one base, θ = ∑i ci θi with mixing weights ci summing to 1. Take one weight that is 0.50 in the base, 0.62 in a math specialist and 0.44 in a chat specialist: an even merge gives 0.53, so each specialist's change (+0.12 and −0.06) survives at half strength, and changes in opposite directions partly cancel. Kimi k1.5 merged a long-CoT and a short-CoT model "by simply averaging their weights".
For consolidation it fell out of favor: Xiaomi says merging and sequential training "often trade off capabilities", and DeepSeek-V4 consolidates without it, saying OPD sidesteps "the performance degradation often encountered in traditional weight-merging". Merging survives for other jobs:
The same two distillations make small models from large ones:
"By learning from its own distribution, the student avoids the exposure bias and distribution mismatch common in off-policy methods trained on static datasets."
"Although this approach is resource-efficient, it leads to high variance in gradient estimation and often causes training instability. Therefore, we adopt full-vocabulary logit distillation in our OPD."
The gradient of the per-position reverse KL is E[(log πS − log πT) ∇log πS]: by the score-function identity the only other term is E[∇log πS], which is zero. A fixed weight At = log πT − log πS on ∇log πS(yt) reproduces exactly that, which is a policy-gradient step with advantage At, so OPD reuses ordinary RL code.
Forward KL weights each token by the teacher's probability. SA gives C 0.02 where the teacher gives 0.49, costing 0.49 ln(0.49/0.02) = 1.57; Smid's heavy use of B is weighted by πT(B) = 0.02 and costs little. Reverse KL weights by the student's probability, so B costs Smid 0.50 ln(0.50/0.02) = 1.61.
How does one model learn to think for a few hundred tokens on an easy request and tens of thousands on a hard one, and how do labs let users turn that dial?
A chain of thought is "a series of intermediate reasoning steps" written before the answer. A reasoning model is trained, usually by reinforcement learning (RL), to write a long one between <think> tags and is rewarded on the answer that follows. DeepSeek-R1-Zero showed that the length need not be taught: RL from DeepSeek-V3-Base with only a think-then-answer template and rule-based rewards for correctness and format made its responses grow steadily, while AIME 2024 pass@1 (the share of problems solved by a single sample) rose from 15.6% to 77.9%. In DeepSeek's words the model "naturally learns to solve reasoning tasks with more thinking time".
Thinking costs output tokens and latency: DeepSeek-R1 averaged 8,793 thinking tokens per 2024 competition-math problem, under 7,000 on easy ones and over 18,000 on the hardest. Labs expose three separate controls over it:
The first reasoning models shipped as separate checkpoints beside a chat model: DeepSeek-R1 beside V3, GLM-Z1-0414 beside GLM-4-0414, Kimi K2-Thinking beside K2-Instruct. From March 2025 labs began to merge them into one hybrid model with a switch in the chat template, the code that turns a conversation into tokens. The switch is a marker. The model learns it from training examples in both formats, each preceded by its marker; GLM-4.5, for instance, balanced its supervised fine-tuning (SFT) data "containing full reasoning with data lacking explicit thought processes". Schematically, with the marker at the start of the answer as in DeepSeek-V3.1:
User: 17 × 23?
thinking: Assistant: <think> 17×20=340, 17×3=51 </think> 391
direct: Assistant: </think> 391
Labs place the marker in different spots:
<|think|> system-prompt token./think and /no_think flags, which Qwen says "eliminates the need to switch between different models".<think> to think or with a closed </think> to answer directly.thinking=False template flag.The merge did not always last: Qwen split its line again in July 2025, when Qwen3-2507 came as a non-thinking Instruct model and a separate Thinking model, and returned to one model that thinks by default with Qwen3.5 in February 2026.
A switch cannot say how much to think. Effort levels write the requested level into the prompt and make the price of length, the length penalty of chapter 04, depend on it, so the model learns a length per level.
DeepSeek-V4.1-Flash made effort a number: the system prompt carries "Reasoning Effort: {effort}" with an integer from 1 to 100. The idea is a price per reasoning token that falls as the requested effort rises, so long answers pay off only at high effort. Each prompt gets several samples at each of a few training levels, and rewards are mean-centered within each (prompt, effort) subgroup, so "responses from different effort levels are not directly compared" (group advantages: ch03). Effort enters the reward only through the length penalty:
ℓ counts the reasoning tokens of response y, Lnorm is a reference length, Cmax caps the deduction, k0 applies at the lowest training level emin, and w is the decay width (DeepSeek writes b for e and τ for w). Figure 8.1 draws the penalty at two levels with illustrative constants. The API maps low, high and max to e = 50, 75 and 100.
Take DeepSeek-V4.1's form with made-up constants: k0 = 0.4 at emin = 25, w = 25, Lnorm = 10,000 tokens, Cmax = 0.5, and rtask = 1 for a correct answer, 0 otherwise. Then k(25) = 0.4 and k(100) = 0.4 · exp(−3) = 0.020.
Two correct answers, short (ℓ = 2,000) and long (ℓ = 12,000):
e = 25: short 1 − 0.4 × 0.2 = 0.92; long 1 − min(0.5, 0.4 × 1.2 = 0.48) = 0.52 (gap 0.40).
e = 100: short 1 − 0.020 × 0.2 = 0.996; long 1 − 0.020 × 1.2 = 0.976 (gap 0.02).
A hard prompt where the short strategy is right half the time and the long one 80% of the time; the penalty applies whether or not the answer is right:
e = 25: short 0.5 − 0.08 = 0.42; long 0.8 − 0.48 = 0.32, so the short strategy wins.
e = 100: short 0.5 − 0.004 = 0.496; long 0.8 − 0.024 = 0.776, so the long strategy wins.
The same policy learns to stop early when the prompt says 25 and to keep checking when it says 100. Real numbers: DeepSeek-V4.1-Flash's average Pass@1 on eight reasoning benchmarks rises from 67.1% at effort 25 to 76.3% at 100 for roughly 2.5× the output tokens; gpt-oss-120b on AIME 2025 (no tools) goes from 50.4 at low to 92.5 at high.
DeepSeek's appendix motivates the exponential. If px(ℓ) is the chance of solving prompt x after ℓ tokens, the expected reward of length ℓ is px(ℓ) − k(e)ℓ/Lnorm (below the cap); setting its derivative to zero gives px′(ℓ*) = k(e)/Lnorm. If the marginal gain decays as px′(ℓ) ≈ ax exp(−ℓ/sx) with problem-specific constants ax, sx, taking logs gives ℓ* ≈ sx ln(axLnorm/k0) + (sx/w)(e − emin): preferred length grows linearly with e.
def effort_rl_step(policy, prompts, efforts=(25, 50, 75, 100), m=4,
k0=0.4, w=25.0, L_norm=10_000, C_max=0.5):
"""Effort-conditioned rewards in the style of DeepSeek-V4.1 (illustrative constants)."""
batch = []
for x in prompts:
for e in efforts: # one subgroup per (prompt, effort)
k = k0 * exp(-(e - min(efforts)) / w) # penalty coefficient k(e)
sub = []
for _ in range(m):
y = policy.sample(system=f"Reasoning Effort: {e}", prompt=x)
ell = count_reasoning_tokens(y)
r = task_reward(x, y) - min(C_max, k * ell / L_norm)
sub.append((y, r))
baseline = sum(r for _, r in sub) / m # mean of this subgroup only
batch += [(x, e, y, r - baseline) for y, r in sub] # advantage A
policy.update(batch) # clipped policy-gradient step (ch03)
A hard budget needs no effort-conditioned reward: thinking is cut off at a token count. Qwen3 (April 2025) halts thinking at a user-set length by inserting a stop-thinking instruction, an ability Qwen says was not explicitly trained, and NVIDIA's Nemotron Nano 2 (August 2025) put about 5% deliberately truncated reasoning traces into SFT so the model can answer after its thinking is cut off at the budget.
Some 2026 models dropped the non-thinking mode. Kimi K3 "always has thinking enabled" with low, high and max effort, and GLM-5.3 says "Disabling thinking is no longer supported", telling users of the old off switch to request low effort instead. Neither lab states why. Qwen3.8 (August 2026) followed, offering xhigh, medium and low effort with no way to turn thinking off in its open 2.4T checkpoint. The cheapest setting is now low effort. Figure 8.2 places each style on a timeline.
The third control is about memory rather than length. Before each response the chat template rebuilds the history and decides whether earlier thinking goes back in. If an agent thinks, calls a tool and thinks again, dropping the first block leaves the second with the call and its result but not the reasoning behind the call. For an agent that makes dozens of tool calls, dropping thinking at every tool result forces it to rebuild its plan each time; keeping it costs context, about 30,000 extra tokens for 30 calls with 1,000 thinking tokens each (illustrative). Releases follow three policies:
preserve_thinking flag, Qwen3.6 (April 2026) was "additionally trained to preserve and leverage thinking traces from historical messages", and Kimi K3 was "trained in the preserved thinking history mode".The coded grid (the release-by-feature table behind the cross-lab posttraining page, the atlas's posttraining section) records each release:
| Release | How thinking is controlled | Earlier thinking kept in context? |
|---|---|---|
| DeepSeek-R1 | separate reasoning model | no: dropped when the next round arrives |
| DeepSeek-V3.1 | hybrid toggle | no: earlier thinking dropped |
| DeepSeek-V3.2 | hybrid toggle | yes: across tool calls, dropped at a new user message |
| DeepSeek-V4 | effort: Non-think, High, Max | yes: across user turns in tool-calling contexts |
| DeepSeek-V4.1-Flash | effort 1–100 | not disclosed |
| GLM-4.5 | hybrid toggle | no (inferred): Preserved Thinking arrived in GLM-4.7 |
| GLM-4.7 | hybrid, set per turn | yes: across turns in coding agents |
| GLM-5.3 | always on: low, high, max | yes: clear_thinking defaults to false |
| Kimi K2.6 | hybrid toggle | yes: opt-in preserve_thinking |
| Kimi K3 | always on: low, high, max | yes: trained in preserved mode |
| MiMo-V2-Flash | hybrid toggle | yes: reasoning_content kept across tool calls |
| Gemma 4 | hybrid toggle | yes within tool calls; stripped at the next user turn |
| MiniMax M2 | interleaved thinking between tool calls | yes: earlier thinking must stay in the history |
| Step 3.5 Flash | single reasoning model | yes: within the tool calls after the latest user message |
| Qwen3.8 | always on: xhigh, medium, low | yes: preserve_thinking on by default |
| gpt-oss | effort: low, medium, high | within a turn only (tool calls inside the chain of thought) |
| Inkling | effort: 7 presets | not disclosed |
"Alongside advances in model architecture and hardware, the number of output tokens is another key determinant of serving cost and, consequently, of the cost–quality trade-off in real-world applications."
"Preserved Thinking: In coding agent scenarios, GLM-4.7 automatically retains all thinking blocks across multi-turn conversations, reusing the existing reasoning instead of re-deriving from scratch."
k(25) = 0.4 and k(100) = 0.020, so the deduction k · ℓ/Lnorm is 0.48 versus 0.024. At low effort short correct answers earn higher advantages within their subgroup; at high effort extra checking that raises accuracy pays, so the policy learns to set its length by the effort line.
reasoning_content (the API field that carries the thinking) from Kimi K3's history to save context. What goes wrong?K3 was trained in the preserved thinking history mode, so it always saw its earlier reasoning in context. Without it the input looks unlike training, and Moonshot warns that generation quality may become highly unstable.
How do labs ship models whose expert weights are stored in 4 bits while keeping the accuracy those models had in 16 bits?
When a model generates text, each new token reads every active weight from GPU memory (pretraining chapter 00). The bits stored per weight therefore set both how large a model fits on a GPU and how many bytes each decode step moves. The weights alone take
A toy model with 10B parameters takes 10 × 109 × 16 / 8 = 20 GB in BF16 and 5 GB at b = 4, a quarter of the memory and of the weight bytes each decode step reads.
OpenAI's gpt-oss-120b, a mixture-of-experts (MoE) model with 116.8B parameters (5.1B active per token), keeps 114.71B of them in MoE weights stored at 4.25 bits, 4 bits per weight plus a shared scale (explained below): 114.71 × 109 × 4.25 / 8 = 60.9 GB. The other 2.12B parameters in BF16 add 4.2 GB, for 65.2 GB (60.7 GiB), close to the 60.8 GiB checkpoint OpenAI lists, which fits on a single 80GB GPU. In BF16 the model would take 116.83 × 109 × 2 bytes = 234 GB.
The 4 bits go where the bytes are: MoE weights are 90+% of gpt-oss's parameters, and Kimi says expert weights "dominate the model's parameter memory" in K3. Attention, routers and embeddings stay in higher precision (gpt-oss config); K3 also keeps shared experts and latent projections there.
Four bits give only 16 codes, too few to serve both a block of weights near 0.01 and one near 1. So every 4-bit format stores a 4-bit code per weight plus a scale shared by a small block, and a stored weight is code × scale. This is block scaling: divide the block by its scale so that its largest value lands near the top of the code range, round, and multiply the scale back. Formats differ in which codes they allow and how they store the scale.
Integer codes are evenly spaced. Floating-point codes have a sign bit, exponent bits that set the range and mantissa bits that set the precision, so they crowd near zero and thin out away from it (details in pretraining chapter 11); BF16 is the usual 16-bit training format and FP32 the 32-bit one. Four formats appear in this chapter:
Figure 9.1 draws the two FP4 layouts to scale, above the E2M1 grid they share.
Choosing the scale. Unlike INT4's min-max scale, an MXFP4 scale must be a power of two. For a block of weights w1 … w32, the MX paper's example conversion (Algorithm 1, which follows the OCP specification) puts the block maximum in E2M1's top power-of-two range and clamps anything beyond 6:
A power-of-two scale cannot put the block maximum exactly at 6, so values between 6 and 8 times the scale clamp. Doubling the scale would avoid that, but then no block would use the top grid values 4 and 6 and every weight would sit on a grid twice as coarse. The MX paper's rule aims at "full utilization of the element data format's exponent range" and accepts occasional clamping. NVFP4's fractional scale can put the block maximum right at 6, and its smaller blocks follow local ranges more closely, at a quarter bit more per value. The worked example below shows both effects.
Eight illustrative expert weights (a real block has 16 or 32): w = [0.92, −0.41, 0.07, 0.33, −0.18, 0.55, −0.76, 0.12]. Each format divides by its scale s, rounds to the nearest code and multiplies back:
Each cell reads w/s → code → ŵ, with the relative error below:
| w | INT4, s = 0.131 | MXFP4, s = 0.125 | NVFP4-style, s = 0.156 |
|---|---|---|---|
| 0.92 | 7.00 → 7 → 0.920 (0%) | 7.36 → 6 → 0.750 (18.5%, clamped) | 5.89 → 6 → 0.938 (1.9%) |
| −0.41 | −3.12 → −3 → −0.394 (3.8%) | −3.28 → −3 → −0.375 (8.5%) | −2.62 → −3 → −0.469 (14.3%) |
| 0.07 | 0.53 → 1 → 0.131 (87.8%) | 0.56 → 0.5 → 0.063 (10.7%) | 0.45 → 0.5 → 0.078 (11.6%) |
| 0.33 | 2.51 → 3 → 0.394 (19.5%) | 2.64 → 3 → 0.375 (13.6%) | 2.11 → 2 → 0.313 (5.3%) |
| −0.18 | −1.37 → −1 → −0.131 (27.0%) | −1.44 → −1.5 → −0.188 (4.2%) | −1.15 → −1 → −0.156 (13.2%) |
| 0.55 | 4.18 → 4 → 0.526 (4.4%) | 4.40 → 4 → 0.500 (9.1%) | 3.52 → 4 → 0.625 (13.6%) |
| −0.76 | −5.78 → −6 → −0.789 (3.8%) | −6.08 → −6 → −0.750 (1.3%) | −4.86 → −4 → −0.625 (17.8%) |
| 0.12 | 0.91 → 1 → 0.131 (9.5%) | 0.96 → 1 → 0.125 (4.2%) | 0.77 → 1 → 0.156 (30.2%) |
| relative RMS error | 7.7% | 13.0% | 12.1% |
Relative RMS error is √(∑(ŵ − w)² / ∑w²). The block shows three effects:
The baseline is post-training quantization (PTQ): train in 16 bits, then round the finished weights once, as in the worked example. Jacob et al. found that PTQ works for large models but loses accuracy on small ones, for example when outlier weights force a coarse grid on all the others, and the LLM-QAT authors found that PTQ methods for LLMs "perform well down to 8-bits" but break down below. PTQ can work at 4 bits (NVIDIA reports 1% or less accuracy loss for DeepSeek-R1-0528 in NVFP4), but Kimi says low-bit quantization often causes substantial drops for thinking models because their outputs are so long.
Quantization-aware training (QAT) puts the rounding inside training, so the network can adapt to it. The optimizer keeps FP32 master weights w, and every forward pass uses a fake-quantized copy ŵ: each block is rounded to its 4-bit grid and converted straight back to a float. The loss is then the loss of the model that will be served. For each weight of a block:
Q is a staircase. It is flat between rounding points, so dQ/dw = 0 almost everywhere and the exact gradient would never change w. The straight-through estimator (STE), named by Bengio et al. after a proposal by Hinton, back-propagates through the rounding as if it were the identity:
Bengio et al. call it "clearly a biased estimator". It works because the master weights stay in floating point "so that they can be easily nudged by small amounts" (Jacob et al.): small updates add up in FP32 until a weight crosses a rounding boundary. In the worked example's MXFP4 block every master value from 0.3125 to 0.4375 is served as 0.375, so a weight of 0.33 nudged by −0.001 per step (scale fixed) is served as 0.375 for 17 steps and as 0.25 from the 18th.
E2M1 = tensor([0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0]) # FP4 magnitudes
def mxfp4_fake_quant(w): # w: [n_blocks, 32] FP32, blocks along the input dim
amax = w.abs().amax(-1, keepdim=True).clamp(min=1e-30) # [n_blocks, 1]
s_blk = 2.0 ** (floor(log2(amax)) - 2) # power of two (E8M0); 6 = 1.5 * 2**2
r = (w.abs() / s_blk).clamp(max=6.0) # values above the grid clamp to 6
q = E2M1[(r[..., None] - E2M1).abs().argmin(-1)] # nearest FP4 magnitude
return sign(w) * q * s_blk # dequantized w_hat: [n_blocks, 32]
def ste_quant(w): # forward: w_hat; backward: identity
return w + (mxfp4_fake_quant(w) - w).detach()
def qat_step(model, w_master, batch, opt): # w_master: FP32 expert weights
w_hat = ste_quant(w_master.reshape(-1, 32)).reshape(w_master.shape)
loss = model.loss(batch, expert_weights=w_hat) # forward sees 4-bit values
loss.backward() # dL/dw_master = dL/dw_hat (STE)
opt.step(); opt.zero_grad() # FP32 update; small steps accumulate
Kimi K2-Thinking, Moonshot's MoE with 1T total and 32B active parameters, applies QAT during post-training with INT4 weight-only quantization of the MoE components. "Weight-only" means activations and arithmetic stay in 16 bits, so the gain comes from moving fewer bytes: Kimi reports a roughly 2× generation speedup, with every reported benchmark run at INT4. The checkpoint config stores symmetric 4-bit integers in groups of 32 weights and leaves attention, shared experts, the dense MLP and the output head unquantized. K2.5, K2.6 and K2.7-Code reuse the method.
Other labs place INT4 QAT elsewhere. Z.ai's GLM-5 applies it in the SFT stage, using one kernel for training and for the final weight conversion so the two match bit for bit. Google's Gemma 3 uses a short QAT run after training: about 5,000 QAT steps per model, with the unquantized model's probabilities as targets.
gpt-oss was post-trained with its MoE weights quantized to MXFP4, ships them in MXFP4, and ran all its evaluations in MXFP4.
DeepSeek-V4 (V4-Pro: 1.6T total, 49B active) added MXFP4 QAT in post-training in two places: the MoE expert weights, and the query-key path of a small scoring module inside its sparse attention (the indexer, which picks the cached tokens each query reads). Teacher and reference models get QAT too.
For the expert weights, DeepSeek turns the FP4 values back into FP8 for computation and calls this lossless. An FP4 value times its power-of-two scale has one mantissa bit, which E4M3 (three mantissa bits, two more exponent bits) stores exactly: FP4 value 1.5 with scale 2−3 is 0.1875 = 1.5 × 2−3, an exact E4M3 value. The one condition is range: each FP8 block of 128 × 128 weights has a single scale, so the FP4 scales of the 32-value blocks inside it must not spread too far apart, which DeepSeek checked empirically for its weights. QAT therefore runs in DeepSeek's existing FP8 framework, with gradients of the FP8 weights passed straight to the FP32 masters (the STE).
Moonshot's Kimi K3 (2.8T total, 104B active) moved from the K2 line's INT4 to MXFP4 expert weights with MXFP8 activations (the 8-bit MX format) "for broad hardware compatibility", and runs QAT through all of post-training, SFT and RL. MiMo-V2.6, Xiaomi's MoE family (the Pro model has 1.02T total, 42B active), starts MXFP4 QAT earlier, in mid-training, and serves its experts in MXFP4 during RL rollouts.
Two of the labs added to the atlas later ship NVFP4 weights. MiniMax's M3.1-Flash quantizes its routed experts and attention paths during training and stores the experts in NVFP4. NVIDIA ships Nemotron 3 Super and Ultra in NVFP4 after post-training quantization alone (Ultra keeps attention in BF16); for Nemotron Nano 2 VL (October 2025) and the Nemotron 3.5 models it adds a short repair step it calls quantization-aware distillation (QAD), training the PTQ model to match the BF16 model's outputs, much as Gemma 3's QAT used the unquantized model's probabilities as targets. Bolt-74B extends QAD to 2-bit experts (2.0625 bits per weight).
DeepSeek-V4.1-Flash, a multimodal MoE with 552B backbone parameters, applies the NVFP4 layout to its main KV cache (the keys and values kept for past tokens, pretraining chapter 03). QAT in post-training lets it store the cache as E2M1 with one E4M3 scale per 16 channels and no global scale, which it can drop because cache values stay far inside the range: at most about 22.6, against the format's 448 × 6 = 2688. The main cache ends up nearly half the size of V4's FP8 cache.
In RL, two engines evaluate the same weights. The inference engine samples a response and logs πinfer(yt | x, y<t), the probability it gave each sampled token; the trainer recomputes the same probability, πtrain. If the engine serves FP4 experts while the trainer evaluates BF16 weights, the mismatch ratio κt = πtrain / πinfer differs from 1 before any update, and the clipped objective treats numerical noise as policy change. Illustratively, trainer and sampler probabilities of 0.30 and 0.25 give κ = 1.2, already at the edge of a clip range of ε = 0.2. This is the training-inference mismatch of chapter 06; its quantization side is covered here. QAT removes most of it, since the trainer's forward pass already uses the quantized values, and the labs give the sampler the same quantized weights:
Quantized rollouts also mean RL optimizes the model that users will run.
"However, thinking models use excessive decoding lengths, and thus quantization often results in substantial performance drops."
"To achieve inference acceleration and reducing memory traffic at deployment, we introduce Quantization-Aware Training (QAT) (Jacob et al., 2018) during the post-training stage"
"During RL, rollout and training share the same quantization scheme — eliminating the train–inference mismatch."
⌊log2 3.5⌋ = 1, so sblk = 21−2 = 0.5. Then 3.5 / 0.5 = 7 lies beyond the largest E2M1 value, so it clamps to 6 and returns as 3.0, an error of 14.3%.
The RL update uses πtrain, computed by the trainer, and πinfer, logged by the sampler. If one uses MXFP4 experts and the other 16-bit weights, the two probabilities differ even for identical parameters, a training-inference mismatch. Sharing one quantization scheme removes that gap, and RL then optimizes the 4-bit model that is deployed.
How do labs train a model to refuse what it should, answer what it can and admit what it does not know, and how do they decide that its weights are safe to publish?
The chapter takes the question in three parts: training a model to refuse the right requests, deciding whether its weights are safe to publish, and training it to say "I don't know".
Safety training steers between two errors:
Benchmarks measure both sides. FORTRESS, in Thinking Machines' table for its Inkling model, gives each model two scores: the share of harmful requests it refuses and the share of benign look-alikes it still answers.
A model can usually raise one number by giving up some of the other, so labs train and report both.
The common recipe has two parts: SFT data that shows refusals for harmful prompts and full answers for borderline ones, and a safety reward in RL. Labs fill in the parts differently:
Microsoft AI's reasoning model MAI-Thinking-1 builds both failure modes into its rewards. Each safety prompt is labeled harmful (target: refuse or partially refuse) or borderline (target: a bounded, helpful answer "rather than hedging or refusing"). Judges score policy compliance, whether the response matches its target, and style. A weighted average of such scores can stay positive for a response that breaks policy: with illustrative equal weights on compliance and style, a fluent answer that breaks policy still earns 0.5 × 0 + 0.5 × 1 = 0.5. So MAI gates the reward: a non-compliant rollout gets the minimum reward whatever its other scores.
OpenAI argues that models trained on labeled examples must infer the safety rules indirectly, which generalizes poorly. Deliberative alignment teaches the written rules themselves, in two stages:
OpenAI reports that its o1 models, trained this way, advanced the Pareto frontier of refusing jailbreaks (StrongREJECT) against not over-refusing benign prompts (XSTest), compared with GPT-4o and other models: o1 led on StrongREJECT while staying close to the best XSTest score. gpt-oss uses deliberative alignment to refuse disallowed content, resist jailbreaks and follow the instruction hierarchy (system messages outrank developer messages, which outrank user messages), and OpenAI put no direct optimization pressure on its chain of thought.
Open weights change the question. Anyone can fine-tune a released model and train its refusals away, so labs test what the model can do with safeguards removed. OpenAI built adversarially fine-tuned versions of gpt-oss-120b: helpful-only (RL-trained to comply with unsafe requests instead of refusing them), with extra biology and cyber training. Its Safety Advisory Group concluded that even these did not reach High capability in either area under its Preparedness Framework, OpenAI's process for tracking capabilities that could cause severe harm. OpenAI also ran its biology evaluations on other open models and found another open model scoring at or near gpt-oss-120b on most of them. Thinking Machines (TML) tested Inkling with internal evaluations, four external testing organizations and helpful-only fine-tunes, and concluded that releasing it was unlikely to add material risk beyond existing open-weight models. Besides OpenAI's fixed thresholds, both decisions ask a relative question: does this release move the open-weight frontier in a dangerous domain?
Cyber capability drives the newest decisions. Z.ai added vulnerability-discovery data and environments to GLM-5.3's post-training and saw cyber capability grow "faster than we expected": ExploitBench rose from 24.4% to 54.4% over GLM-5.2. It launched the model to API and subscription users and held the weights for two weeks of safety evaluation and hardening. Moonshot launched Kimi K3 on 16 July 2026 with weights promised by 27 July; its report adds a cyber evaluation and a joint assessment by the UK AI Security Institute and NIST's CAISI, without saying whether they came before the release decision.
For Kimi K3.1 Pro, a further post-trained K3, Moonshot traced rising vulnerability-research scores to cyber content in the mid-training corpus and cyber tasks in the RL mix, and removed both for the open-weight release. A checkpoint trained without the removal solves 90.5% of Moonshot's in-house xdev-bench-hard exploit tasks at pass@3 (solved in at least one of three attempts); the released model solves 81.0%, which Moonshot says remains behind frontier proprietary models. Moonshot promises the weights by 4 November.
TML describes staging as a general tool: inference access for a limited group, monitored general access and hosted fine-tuning can each come before open weights, and each stage should widen access "only when the evidence supports it". Inkling-Small followed a short version: TML showed preview results with Inkling on 15 July and released the weights on 30 July, after testing.
Figure 10.1 lays these checks out in the order a release meets them, from data filtering to the license, with the loop through which evidence of risk sent a model back into training or delayed its weights.
The license is the last release choice: it sets what others may do with the weights. Labs have moved in both directions:
Among the labs added later, MiniMax moved furthest, one release at a time: Apache 2.0 for M1, a modified MIT for M2, a non-commercial license for M2.7 and a community license for M3.
Hallucination is partly an incentive problem. Kalai et al. show that under binary grading (1 for a right answer, 0 for a wrong one or for "I don't know") abstaining is never optimal, so a model trained or selected on such scores learns to guess. On SimpleQA, a set of short fact-seeking questions, gpt-oss-120b answers 16.8% correctly and 78.2% incorrectly.
The fix is to make wrong answers cost something. Score a correct answer 1, an abstention 0 and a wrong answer −φ, where φ ≥ 0 is the penalty. A model whose belief that its answer is right is p expects, if it answers,
Abstaining earns 0, so answering beats abstaining when p − φ(1 − p) > 0, that is when p > φ / (1 + φ). With φ = 1 a model should answer only above 50% confidence, and with φ = 3 only above 75% (the worked example below tabulates more). Labs put the penalty into rewards or into data.
Inkling's largest targeted dataset for this is short-form factual QA with abstention-aware rewards, built so that the best policy answers when confident and otherwise says "I don't know" or hedges. Some prompts encourage hedging and others forbid it, so the model also learns to follow the user's preference. TML does not publish the reward values. Gemma takes a data route: its Gemma 3 and Gemma 4 reports say that adding examples that encourage hedging and refusals "to minimize hallucinations" improved factuality scores.
Microsoft AI's MAI-Thinking-1 grades factual answers on two axes, correctness and confidence, into five classes: confident-correct gets the highest reward, a confident hallucination the steepest penalty and an abstention a neutral reward, while unconfident-but-correct answers get a reduced reward "to discourage over-hedging". MAI does not publish the weights. The box below sketches the shape of such rewards, with illustrative values.
# illustrative reward values; labels come from a judge
def factual_reward(label, phi):
return {"correct": 1.0, "partial": 0.0,
"abstain": 0.0, "incorrect": -phi}[label]
def should_answer(p, phi): # p: belief answer is right
return p - phi * (1 - p) > 0 # i.e. p > phi / (1 + phi)
def gated_safety_reward(scores, mix, r_min): # MAI-style gate
if not scores["policy_compliant"]:
return r_min # violation: minimum reward
return sum(c * scores[k] for k, c in mix.items())
Forecasts, where the model states a probability q for an event, are graded with proper scoring rules such as the Brier loss (q − o)² for outcome o ∈ {0, 1}. A forecaster who believes 0.7 and reports 0.7 expects a loss of 0.7 × 0.3² + 0.3 × 0.7² = 0.21; reporting 1.0 instead expects 0.3 × 1² = 0.30, so overstating confidence costs; reporting q = p is optimal (chapter 04). TML trained Inkling and Inkling-Small with RL against proper scoring rules on resolved real-world forecasting questions, without naming the rule.
Evaluations now use the same accounting. AA-Omniscience, Artificial Analysis's benchmark of 6,000 knowledge questions, reports an Omniscience Index OI = 100 · (c − i) / N, with c correct and i incorrect answers out of N questions. Partial answers and abstentions count as neither correct nor incorrect, so they score 0, and the penalty is φ = 1.
Illustrative confidences p and three penalties φ. Each cell is the expected reward of answering, p − φ(1 − p); abstaining always earns 0.
| confidence p | φ = 0 (plain accuracy) | φ = 1 (AA-Omniscience) | φ = 3 |
|---|---|---|---|
| 0.3 | +0.30 answer | 0.3 − 0.7 = −0.40 abstain | 0.3 − 2.1 = −1.80 abstain |
| 0.5 | +0.50 answer | 0.5 − 0.5 = 0.00 either | 0.5 − 1.5 = −1.00 abstain |
| 0.7 | +0.70 answer | 0.7 − 0.3 = +0.40 answer | 0.7 − 0.9 = −0.20 abstain |
| 0.9 | +0.90 answer | 0.9 − 0.1 = +0.80 answer | 0.9 − 0.3 = +0.60 answer |
| break-even p | 0 | 1 / 2 = 0.50 | 3 / 4 = 0.75 |
With φ = 0 answering always wins, which is Kalai et al.'s point. Raising φ raises the confidence a model needs before it should answer.
Real numbers. On AA-Omniscience, Z.ai's GLM-4.7 (reasoning) answered 29.3% of questions correctly. Of the other 70.7%, only 7% were abstentions or partial answers, so 0.93 × 70.7% = 65.7% were wrong and OI = 100 × (0.293 − 0.657) = −36.4. GLM-5 (reasoning) answered fewer correctly, 26.3%, but 64.7% of its other 73.7% were abstentions or partial answers, so 0.353 × 73.7% = 26.0% were wrong and OI = 100 × (0.263 − 0.260) = +0.3. Artificial Analysis lists −36.43 and 0.27; the small gaps are rounding. Z.ai credits part of GLM-5's jump on Artificial Analysis's overall index to "knowledge/hallucination"; its report lists knowledge hallucinations among the errors its general RL penalizes but describes no abstention reward.
"Because open weights can be customized to strip a model of its safety training, refusal behavior cannot be treated as a durable safeguard for an open-weight release."
"First, we aim not to release open weights that would extend the absolute frontier of cyber capability."
"The largest is short-form factual QA with abstention-aware rewards: answering only pays off when the model is likely to be right, so the optimal policy is to answer when confident and otherwise say “I don’t know” or give a hedged best guess."
Answering pays when p − 4(1 − p) > 0, that is p > 4 / 5 = 0.8. At exactly 0.8 the expected reward is 0.8 − 4 × 0.2 = 0, the same as abstaining.
Anyone with open weights can fine-tune the refusals away, so refusal rates say little about risk after release. The helpful-only variants estimate what the model can do with safeguards removed; OpenAI checked them against its High-capability thresholds, and TML compared them with existing open-weight models.
How much compute do labs now spend on RL after pretraining, in what units do they report it, and why does generating rollouts take most of the hardware?
An RL run repeats one loop: sample a batch of rollouts, score them, update the weights. Labs report how much of that loop they ran in five different units, and each hides something:
Many reports give no number at all: Kimi K3.1 Pro, Moonshot's October 2026 model, has "substantially larger post-training compute" than K3, and GLM-5.3 spent "more compute" than GLM-5.2. When numbers are given, the units convert along one chain, and each link needs one extra number that a report may leave out:
R1-Zero ran 10,400 steps of 512 samples and cost $202K, priced at the $2 per H800 GPU-hour that DeepSeek states, enough to walk most of the chain:
Per sample that is $202K / 5.32M ≈ $0.04, or 101K × 3,600 s / 5.32M ≈ 68 GPU-seconds. Going on to FLOPs would take the missing link, tokens per sample.
Share of pretraining is the unit that shows the trend. DeepSeek's reports give a rare series from one lab, worked through in chapter 00 and plotted in Figure 11.1A: posttraining took 0.19% of DeepSeek-V3's pretraining GPU-hours; R1, trained from the same base, used 147K GPU-hours, 5.5% of that pretraining; and V3.2's posttraining budget was more than 10% of the pre-training cost. That is a more than fiftyfold rise in under a year (10 / 0.19 ≈ 53), though the last point is a cost share and a lower bound.
What does more RL buy? Thinking Machines and Microsoft AI report the same shape: the score rises by a constant amount each time the number of rollouts doubles. That is a log-linear curve,
Suppose b = 0.03. A run that gains 0.03 between 1M and 2M rollouts needs 4M for the next 0.03 and 8M for the one after: doubling from M to 2M costs M new rollouts, so each gain of b costs as many rollouts as all training before it, and a gain Δ from M0 needs M0 · 2Δ/b rollouts in total. The curve is a fit over the range a lab reports, not a law that says how far it continues.
Thinking Machines reports Inkling's RL in rollouts: over 30M across two long continuous runs, which used the majority of post-training compute. Its score on a held-out set of reasoning evals rose from 0.264 at the SFT start to 0.356 at the released checkpoint, about 26.5 million rollouts, and "improved log-linearly throughout"; the blog does not say what the rollouts past the released checkpoint were. MAI-Thinking-1, Microsoft AI's reasoning model, likewise reports "sustained log-linear climbs over many thousands of steps", and MiMo-V2.6 plots its climb against dollars: DeepSWE average@3 (the mean score over 3 attempts) for Pro rose from 58.4 to 72.6 over the run.
A token costs about 2·Na FLOPs to generate, one forward pass through the Na active parameters (the weights one token actually uses in a mixture-of-experts model), and about 6·Na to train on, forward plus backward (derived in the pretraining notes' chapter 00). By arithmetic alone the learner needs three times the hardware of generation. MAI reports the opposite split: its largest RL job put 4,096 of 4,864 GB300 chips on inference and 768 on the learner, 84% against 16%.
Decode explains the gap. Generation has two phases (pretraining notes chapter 00):
Training processes all tokens of a trajectory at once, like prefill, and keeps the arithmetic units busy. If both of MAI's pools were fully busy on the same tokens, each inference chip would sustain (2 / 4,096) ÷ (6 / 768) = 1/16 of a learner chip's FLOP rate. This is illustrative only: it ignores prefill, extra log-probability passes and idle time.
def rl_chip_split(tokens_per_s, n_active, peak_flops, mfu_decode, mfu_train):
"""Chips needed to generate tokens and to train on them at the same rate.
mfu: model FLOPs utilization, the achieved share of peak arithmetic."""
gen_flops = 2 * n_active * tokens_per_s # forward pass only
train_flops = 6 * n_active * tokens_per_s # forward + backward
gen_chips = gen_flops / (peak_flops * mfu_decode) # decode: memory-bound, low mfu
train_chips = train_flops / (peak_flops * mfu_train) # large matmuls, high mfu
return gen_chips, train_chips
# Illustrative: if decode reaches 1/16 of the learner's utilization,
# gen_chips / train_chips = (2 / 6) * 16 ≈ 5.3, the ratio of MAI's 4,096 : 768.
Agent rollouts add to the bill: 100K-token contexts make each KV cache large, tool calls stall sequences, and the slowest trajectories hold a batch open. Partial rollouts, which pause unfinished trajectories and resume them after the update (chapter 06), keep the GPUs busy at a price: MiMo-V2.6 pays for "re-prefill", rebuilding each resumed sequence's KV cache under the new weights, and uses a large batch to amortize it.
MiMo-V2.6, Xiaomi's September 2026 release, reports a split of cost rather than chips, and it is more even (Figure 11.1B): rollout 43.8%, training 43.5%, grader 12.7% for Pro. The report credits its large batch for efficient rollout: "With HBM mostly occupied by the running batch, we can keep a high arithmetic intensity to fully use the GPU in rollout." HBM is the GPU's main memory, and arithmetic intensity is FLOPs per byte read from it. A decode step over many sequences reads each weight once for all of them, so it does more arithmetic per byte; that is one reason the split can sit closer than MAI's chip split to the 1 : 3 FLOP ratio of generation to training, though the report makes no such comparison and does not split the training share further. The grader, an LLM agent that judges trajectories within each group, takes 12.7% by itself.
Figure 11.1 plots the DeepSeek share series from above (A) next to MiMo's cost split (B).
MiMo-V2.6-Pro (42B active parameters; Flash 15B) trains with 1,568 prompts and G = 16 per step, and each step yields 2.7B–3.7B training tokens.
The report itself states "roughly 110K–150K tokens per sequence". Its timelines cover 30 training steps. The report does not say these 30 steps are the whole run. Assuming they are, that every step used the full batch, and that Flash used the same batch and token counts (the report states one batch size without separating the models):
Pro cost $2.6M and Flash $0.9M, so:
| Pro | Flash | |
|---|---|---|
| Per step | $2.6M / 30 ≈ $86.7K | $0.9M / 30 = $30K |
| Per trajectory | $2.6M / 752,640 ≈ $3.45 | $0.9M / 752,640 ≈ $1.20 |
| Per million training tokens (81,000 to 111,000 million) | $2.6M / (81,000 to 111,000) ≈ $23 to $32 | $0.9M / (81,000 to 111,000) ≈ $8 to $11 |
| Wall clock per step, failures included | 123.1 h / 30 ≈ 4.1 h | 81.8 h / 30 ≈ 2.7 h |
Pro's cost splits into 0.438 × $2.6M ≈ $1.14M of rollout, 0.435 × $2.6M ≈ $1.13M of training and 0.127 × $2.6M ≈ $0.33M of grading. The Pro-to-Flash cost ratio, 2.6 / 0.9 ≈ 2.9, is close to the ratio of active parameters, 42 / 15 = 2.8; the report does not make this comparison. For scale, one MiMo-V2.6-Pro agent trajectory cost roughly 90 times one R1-Zero sample ($0.04, from the worked conversion above). DeepSeek states its GPU price; MiMo does not, so this comparison stays in dollars.
Few, huge steps make late problems expensive. After release, MiMo-V2.6 was found to repeat tool calls and to issue too many of them; its RL penalty for such "flooding" zeroed a rollout only above 32 tool calls in one turn, too loose a threshold. Lowering it to 8 worked in a small experiment but took about 20 steps to act: "Applying this fix at full scale would therefore require restarting 20 steps of MixRL, at an estimated cost of $2.31 million." (MixRL is the mixed RL run above.) Xiaomi instead trained a repetition-specialist RL teacher for 12 steps on about 7,000 examples, rolled its final multi-teacher on-policy distillation run (MOPD: the model learns from several teachers' token probabilities on its own samples; chapter 07) back five steps, and continued with the new teacher. The fix cost about $90,000 against the estimated $2.31M, "only 4%" in the blog's words (both are Xiaomi's figures).
"Over recent months, we have observed consistent performance improvements correlating with extended RL training budget, which already exceeds 10% of the pre-training cost."
"Because most of a job’s GPU usage comes from inference, optimizing inference throughput is critical for efficient RL."
"To enable large-scale joint RL across hundreds of downstream domains, we optimized every layer of the stack — the RL framework, long-context inference, training efficiency, sandbox infrastructure, rollout scheduling, and a shared platform for environment quality checks — so that each increment of RL compute delivers stable, predictable returns."
Each doubling adds the same 0.01, so 0.02 needs two more doublings: 2M → 4M → 8M. About 8M rollouts in total, 6M more than now.
Decode emits one token per sequence per pass and must reread the weights and KV cache each time, so it is memory-bound and uses a small fraction of the chip's arithmetic. Training processes a whole trajectory in parallel at near full speed. Fewer FLOPs at a much lower rate take more chips.
When a lab rebuilds its posttraining from one release to the next, which parts change, and why?
Earlier chapters took one posttraining method at a time; real pipelines combine them, and each release changes several at once. This chapter follows two labs through 2025 and 2026: DeepSeek, which trains specialists and merges them, and Xiaomi's MiMo, which moved to one mixed RL run. For each, the latest pipeline comes first, end to end, then what changed across releases; a fix Xiaomi made after a release closes the chapter.
DeepSeek-V4, end to end. After pretraining, V4 passes through three stages:
Alongside the stages, the expert weights and the indexer train with MXFP4 QAT and RL samples rollouts with FP4 weights (chapter 09); the finished model offers three effort modes (chapter 08). The table reads across four releases.
| Choice | R1 · Jan 2025 | V3.2 · Dec 2025 | V4 · Apr 2026 | V4.1-Flash · Sep 2026 |
|---|---|---|---|---|
| Pipeline | cold-start SFT → reasoning RL → rejection-sampling SFT → RL for all scenarios | RL specialists → SFT on their outputs → one mixed RL stage | SFT → specialist RL → multi-teacher on-policy distillation | same algorithms as V4; more synthesized tasks and environments |
| RL algorithm | GRPO | GRPO with stabilizers | GRPO for the specialists | GRPO-style, asynchronous |
| Rewards | rules for reasoning; a reward model for general tasks | rules plus a rubric-guided generative reward model | the model itself as rubric judge, trained jointly | verifiable tasks built as (problem, environment, verifier) triples |
| Merging specialists | none | SFT distillation from 6 specialist domains | on-policy distillation from more than 10 teachers | on-policy distillation from more than 40 teachers |
| Thinking control | reasoning-only model | thinking on/off; reasoning kept across tool calls | three effort modes | continuous effort from 1 to 100 |
| Low precision | none | none | FP4 experts and indexer (QAT) | adds an FP4 KV cache (QAT) |
| Scale | 512 GPUs for R1-Zero's RL | more than 10% of pretraining cost | not stated | not stated; asynchronous rollouts |
Figure 0.1 draws the R1, V3.2 and V4 pipelines stage by stage, and chapter 07 explains V4's switch to on-policy distillation. Three threads run across the table's rows.
Rewards. R1 scored reasoning with rules and gave a specific reason for avoiding learned reward models there:
"This decision is predicated on our observation that neural reward models are susceptible to reward hacking during large-scale reinforcement learning."
For open-ended tasks DeepSeek went the other way, to V4's self-judging policy (stage 2 above).
Where the effort goes. V3.2 spent more than a tenth of its pretraining compute on posttraining (chapter 00) and merged six specialists; V4 and V4.1-Flash merged more than ten and then more than forty teachers. V4.1-Flash is notable for what it did not change. DeepSeek kept V4's algorithms and put its effort into building tasks and environments, scaling rollouts, and running RL asynchronously so that a few very long agent trajectories no longer hold up a whole batch:
"In this release, we refrain from introducing novel post-training algorithms."
Control. Each step gave users finer control over how much the model reasons, because output tokens are a large part of serving cost. V4.1-Flash's effort level from 1 to 100 is trained by sampling several effort levels per prompt in RL, with a token penalty that depends on the effort.
MiMo-V2.6, end to end. MXFP4 QAT starts before posttraining, in mid-training (chapter 09). Posttraining then has three stages:
The table sets V2.6 beside V2-Flash, its predecessor.
| Choice | V2-Flash · Dec 2025 | V2.6-RL · Sep 22, 2026 | V2.6-MOPD · Sep 27, 2026 |
|---|---|---|---|
| Main stage | SFT → domain RL teachers → multi-teacher on-policy distillation (MOPD) | SFT → one mixed asynchronous GRPO run across all tasks and harnesses | a second multi-teacher distillation run (Xiaomi calls it MOPD2) on top of the RL checkpoint: several domain teachers, including ones for hard-to-verify domains, plus a repetition-specialist teacher |
| Environments | over 100K code tasks from real GitHub issues, plus search, web-development and general tool-use agents | coding 68%, general tool use 12%, design 13%, context following 3%, cybersecurity 4% of tasks | adds hard-to-verify domains such as game development and scientific research through their teachers |
| Rewards | unit tests, outcome reward models, and a visual verifier for web pages | tests plus groupwise grading that ranks passing rollouts | no reward: the student matches each teacher's token probabilities |
| Stability | routing replay, FP8 in both training and rollout, capped importance weights for partial rollouts (chapter 06) | router frozen during RL, routing replay | as V2.6-RL |
| Cost | not stated | $2.6M (Pro) and $0.9M (Flash) of RL | about $90,000 for the repetition fix |
MiMo moved in the opposite direction from DeepSeek. V2-Flash merged separate domain teachers with multi-teacher on-policy distillation so that gains in one skill would not cause regressions in others (chapter 07); V2.6 ran one large RL job over every task type at once:
"Tasks and multiple harnesses are mixed in the same batch so capabilities reinforce each other and strategies transfer to harnesses never seen in training."
Both labs end with on-policy distillation: DeepSeek to merge many specialists, MiMo to add or fix a behavior cheaply after one mixed RL run, as the exercise below shows. MiMo also froze the expert router during RL "to maintain stable expert loads": in a test with a trainable router, the share of almost unused experts in one layer rose from 0.5% to 22% in 20 steps.
Each row of the two tables answers one of five pressures. Figure 12.1 sorts the choices from chapters 03 to 12 by the pressure each one answers.
After release, MiMo-V2.6 sometimes kept issuing the same or similar tool calls without making progress. Instead of rerunning mixed RL, Xiaomi trained the repetition teacher of the walkthrough with a 12-step RL run and merged it into the released models by on-policy distillation (costs in chapter 11). To see what the teacher learned, Xiaomi took one trajectory and measured, at each tool call i, the probability pi that the model ends its turn instead of calling another tool, and from it the probability of having stopped by the k-th call:
Before the fix, F59 was still only about 0.5. After training, the teacher had F8 = 0.9987. Assuming pi is the same at every call, estimate it before and after.
With a constant p, Fk = 1 − (1 − p)k. Before: (1 − p)59 = 0.5, so 1 − p = eln 0.5 / 59 = e−0.0117 ≈ 0.988 and p ≈ 1.2% per call. After: (1 − p)8 = 0.0013, so 1 − p = eln 0.0013 / 8 = e−0.831 ≈ 0.436 and p ≈ 56% per call. Xiaomi's single-position measurement points the same way: at the 12th tool call, the model before the fix put 94.56% on another tool call, and the teacher after training put 92.17% on ending the turn. The constant-p assumption is only a summary; the blog says the repaired model "showed a strong tendency to stop at every position from five through 20". The lesson: the bug was a small probability repeated over many calls. A 12-step RL run moved that one probability a lot, and on-policy distillation copied the change into the main models without rerunning the mixed RL.
Not entirely. Both labs still use RL to build capabilities and on-policy distillation to combine or adjust them. DeepSeek trains specialists with RL and merges them by distillation; MiMo trains one model on all tasks with RL and uses distillation afterwards to add or repair specific behaviors, such as the repetition fix. They differ in where the merging happens, not in which tools they use.
DeepSeek observed that neural reward models are susceptible to reward hacking during large-scale RL: the policy learns to exploit the reward model's mistakes instead of solving the task. Reasoning tasks with checkable answers could be scored by rules instead. A rule has no learned blind spots for the policy to find, though the policy can still exploit gaps in the check itself, such as weak tests (chapter 04). Rewards for open-ended tasks still need a learned or generative judge.
Short definitions of the terms used in these notes, with the chapter that explains each one.
To give credit if you find this work useful, please cite:
@misc{ma2026openrecipesposttrain,
title={Reading Open-Weight Recipes, Part 3: What 2025--26 Model Reports Disclose About Posttraining},
author={Ma, Ziqiao},
howpublished={\url{https://mars-tin.github.io/blogs/posts/open_recipes_posttraining}},
note={Blog post},
year={2026}
}