arl., so the imports are part of a saved block.
6.1 The score-function estimator
The objective is the expected return of trajectories sampled from the policy, The return is computed by the environment, here by tests, and has no derivative with respect to . The dependence on sits entirely in the distribution . Differentiate under the sum and use (Appendix A.5): The expectation on the right can be estimated from samples. What remains is the score of a whole trajectory, . Section 2.2 factored the trajectory probability into what the agent controls and what it does not: The initial distribution and the transitions do not depend on , so their gradients are zero, and This is Equation (6.1), the REINFORCE estimator for trajectories (Williams, 1992). Three assumptions went into it, and each one is a place where real systems break it. The return must not depend on directly, which fails if a learned reward model shares weights with the policy. The trajectories must be sampled from itself, which fails as soon as data is reused after an update or the inference engine computes slightly different probabilities (Chapter 8, Chapter 13). And the sum over trajectories must be finite or well behaved, which a turn budget guarantees. The estimator needs only to sample. It never evaluates , and it never differentiates through it. We check that on the repair MDP of Chapter 2, where the exact gradient is available. The sampler below runs many episodes in parallel and records, for each episode, how often each state-action pair was visited and the total return.[S, A], initialized at random so that it is neither uniform nor deterministic. Chapter 2’s policy_evaluation gives exactly, and autograd gives the exact gradient. The Monte Carlo side computes for every episode as visits * log_softmax(theta), multiplies by the return, averages, and differentiates.
returns, visits, and log_pi. Only the last depends on , and appears nowhere in the differentiated expression. A coding environment is in the same position as : we can run it, but not differentiate it.
The surrogate loss
The expression we differentiated, with the returns held fixed, is a surrogate objective. Its gradient at the current parameters equals the estimator of Equation (6.1). Its value is not , and it does not estimate anything useful. The returns enter as constants, so shifting every return by the same amount changes the surrogate’s value arbitrarily, while its expected gradient stays , because .6.2 A base policy to learn from
Equation (6.1) weights each trajectory’s score by its return. If every sampled trajectory has the same return, the estimator is that constant times , a quantity with expectation zero, and with a baseline it is exactly zero. Chapter 5 measured an untrained TinyAgent: 384 malformed calls out of 384 and a reward of 0 in every episode. RL cannot start from there. Large models never start from there either. A pretrained model already writes fluent text, and an instruction-tuned one already emits well-formed tool calls most of the time. RL then improves which calls it makes. We give TinyAgent the same starting point with behavior cloning: supervised training on demonstrations, with the cross-entropy loss restricted to the policy tokens. The demonstrations come from Chapter 4’srandom and blind scripted policies on the training tasks. They are well formed and unskilled: random chooses uniformly among the nine valid calls, and blind guesses a repair op without reading the code. A model that imitates them learns the call format and the rhythm of an episode, but not how to choose the right op. We call the result the base agent. It is the book’s analogue of a pretrained base model, and every later chapter starts RL from it. Chapter 15 studies supervised warm starts properly, including what they do to exploration.
The behavior-cloning loss is a mean over policy tokens, which we need often enough to save as a function.
make_base_agent collects episodes_per_task demonstrations per training task from each scripted policy, encodes them with scripted_rollout (Section 5.6), and runs steps Adam updates on random minibatches. Everything is seeded, so the same call returns the same weights.
rollout_stats reports the mean reward, the pass rate (submitted, and every hidden test passes), the fraction of calls that were well formed and valid, the submit rate, and the mean number of turns and policy tokens per episode. Later chapters log the same numbers.
random (0.10 on the training split in Section 4.6) and blind (0.21), far below the oracle’s 1.00. Its held-out numbers match its training numbers within sampling noise, because nothing it imitated depended on which function it was fixing. Other samples in this chapter put its pass rate between 0.12 and 0.19; with a few hundred episodes, that spread is the sampling error. On the check host, make_base_agent took about five seconds when this chapter was written. Timings are not checked by the build.
A sampled episode shows the behavior that RL has to work with.
clamp, one episode submits the unchanged file, one guesses add_zero_guard and submits, and one wanders through edits and tests until the budget runs out. When the base agent does read, which it does in a minority of episodes, its next op is unrelated to what it read. Successes are lucky guesses. Every success, however, is a trajectory whose return differs from the others, so the policy gradient is no longer zero.
6.3 The estimator on tokens
At the token level of Section 2.8, a trajectory is one token sequence with prefixes . Every token is drawn either by the policy or by the environment and template. Write when token was sampled from the policy, which is exactly the action mask of Section 5.6. Then where includes the prompt, the template, and every tool response. The second sum does not depend on , so This is Equation (6.2). Tool tokens carry no direct gradient: the model is never pushed to predict them. They do carry an indirect gradient, because every later policy token is conditioned on them through attention, so the gradient flows through their hidden representations into the shared weights. The corresponding loss for a batch of trajectories is with treated as a constant. We check both claims on a batch of 64 base-agent episodes. A hook on the token embedding keeps the gradient with respect to each position’s input representation, andretain_grad keeps the gradient with respect to the logits.
6.4 Baselines
The shift experiment of Section 6.1 suggested that subtracting something from the return can reduce the noise of the estimator without biasing it. The general statement is the following. Let be any function of the state at time , or of anything that is fixed before is sampled. Then Taking the outer expectation over (the tower property) gives for every . Therefore for any such . This is Equation (6.3). The proof uses only that is fixed when is drawn. A baseline may depend on the task, the prompt, and earlier tokens. It may not depend on , on anything sampled after , or on the trajectory’s own return. A baseline computed from the batch mean including the trajectory’s own reward violates the last condition slightly, and Chapter 7 quantifies the consequence. The baseline cannot change the mean, so it can only change the variance. For a constant baseline and per-trajectory score , the total variance is a quadratic in , minimized at This is a weighted mean reward, with weights proportional to the squared score norm. In practice the plain mean reward is close to it, and , the expected return from the task’s initial state, is the natural state-dependent choice (Appendix A.3 treats baselines as control variates). We first confirm unbiasedness where the exact gradient is known. We sample 400 independent batches of 64 repair-MDP episodes and average the estimator over the batches for four baselines: none, the exact , the batch’s own mean return, and a deliberately bad constant 1.6.5 Causality and reward-to-go
The baseline argument has a second consequence. Suppose rewards arrive during the episode, . For , the reward is fixed before is sampled, so it can play the role of a baseline for , and . Removing those terms gives Equation (6.4). The inner sum is the reward-to-go . Causality says an action is credited only with rewards that come after it. At the token level, runs over token positions and . For an outcome-only reward placed on the last policy token, at every policy token, and causality changes nothing. It matters when the reward has intermediate terms: a cost per turn, a format penalty on a malformed call, or a per-turn shaping reward (Chapter 11, Chapter 12). We add a cost of 0.1 per call to the probe episodes. The cost is placed on the last token of each call and the hidden-test reward on the last token of the final call.returns_to_go with discounting that skips tool tokens, and replaces the remaining reward-to-go by an estimate of the advantage.
6.6 Loss aggregation
Equation (6.2) is a sum over tokens and an average over trajectories. Implementations rarely compute exactly that. They compute a per-token term on a padded[B, T] tensor and reduce it to a scalar, and the reduction they choose sets a weight on every token: . Let be the number of policy tokens of trajectory . The four reductions in common use are
Only
seq_mean_token_sum is Equation (6.2) as derived. The others rescale it, and two of them rescale it by something that depends on the batch. token_mean divides by the number of policy tokens in the batch, so the effective learning rate shrinks when the batch’s trajectories get longer. seq_mean_token_mean gives every trajectory the same total weight regardless of its length, so a token of a long trajectory counts less than a token of a short one. constant divides by a fixed number , for example the batch size times the maximum number of policy tokens, which keeps the per-token weight fixed across batches. Which public recipes use which mode, and the Dr. GRPO critique of per-sequence normalization (Liu et al., 2025), are the subject of Chapter 9. Here we build the reductions and measure what they do.
reinforce_loss detaches the advantages. They are constants in the derivation, and Section 6.8 shows the bias that appears when they are not. The weights in the table are the gradient of the loss with respect to each per-token term, so autograd can read them off. Three trajectories with 4, 8, and 24 policy tokens:
token_mean, seq_mean_token_sum, and constant, the per-token weight is the same for all three trajectories, so the 24-token trajectory has six times the total weight of the 4-token one. The three modes differ only by an overall scale, and only token_mean’s scale depends on the batch (here ). Under seq_mean_token_mean, each trajectory has total weight , and a token of the short trajectory weighs six times as much as a token of the long one.
Lengths are not random in an agent task. In the probe batch of the base agent, successful episodes are shorter than failures, because a lucky guess ends with an early submit while failures wander. With an advantage of , successes push up and failures push down, and the aggregation decides how hard.
seq_mean_token_mean balances the totals exactly, because each trajectory’s total weight is and the advantages sum to zero. Per token, however, each long failure is pushed down only a third as hard as each short failure. If a policy can make a failing episode longer, for instance by adding calls before giving up, seq_mean_token_mean reduces the per-token penalty on every one of those calls. That is the mechanism behind the length growth that Dr. GRPO attributes to per-sequence normalization (Chapter 9).
seq_mean_token_mean is flat, so the shortest and the longest trajectories in the batch count the same. On the right, the length of the rest of the batch: only token_mean changes a trajectory’s per-token weight when other trajectories get longer, falling to a quarter when their mean length grows from 10 to 40. As episodes lengthen during training, token_mean therefore lowers the effective learning rate of every token, and the other three modes do not. The book uses token_mean as the default, because it is what most public recipes use and because the batch-dependent scale is easy to monitor. Appendix B, Problem 1 argues the choice at length.
6.7 Training TinyAgent with REINFORCE
Everything is now in place for the first RL run. One update samples a batch of episodes from the current policy, computes advantages, and takes one gradient step on the surrogate.policy_gradient_step performs the update on a collated batch. It clips the global gradient norm, a standard safeguard whose value we also log, and it returns the loss and the gradient norm before clipping. Chapters 8 and 9 replace the loss but keep this shape.
no change) or changes it harmlessly, so a sequence such as fix_comparison -> add_zero_guard -> fix_comparison repairs both the compare and the zero families. The most frequent held-out episode applies swap_minmax five times, which leaves min and max swapped by parity. Stacking is a legitimate repair under this reward, but it is a strategy no engineer would accept, and it transfers to held-out tasks only because the edit menu is small. Reading, the behavior that would transfer cleanly, never appears. The base agent reads in only a minority of episodes and then picks an op unrelated to what it read, so reading is not correlated with reward in its own samples, and REINFORCE can only reinforce what it samples.
Evidence. Measured here, on one seed of a 158k-parameter model with 20 training tasks. The run demonstrates the mechanics of the estimator. It is not a claim about which strategies large models learn. The qualitative lesson is a general property of the estimator rather than of this model: RL amplifies behavior that is already rewarded in the policy’s own samples, and memorizing training tasks or exploiting a small action menu are cheap ways to be rewarded. Chapter 4 set a held-out bar of 0.2 for “learned more than the format”, and this run shows that clearing it does not by itself prove the agent learned to read. Chapters 7, 9, and 12 return to this run’s two failure modes, credit for useless steps and the absence of information gathering.
6.8 Design problems in practice
Problem: high variance on sparse binary rewards. Symptom: the per-step reward curve is noisy, different seeds learn at very different speeds, and progress on hard tasks stalls while easy tasks saturate. Cause: with binary rewards and a low pass rate, most episodes contribute a score with a small negative weight and a few contribute large positive ones. The estimator’s variance is large relative to its mean (Figure 6.1), and a task whose sampled episodes all fail contributes nothing. Options: (a) more episodes per step, which costs rollout compute linearly for a reduction in noise. (b) Better baselines, especially per-task ones from groups of samples (Chapter 7). (c) Denser rewards, which risk being gamed (Chapter 11). (d) A curriculum that keeps tasks near a 50% pass rate (Chapter 14). Default: measure the gradient noise directly before tuning anything else. The block below splits the 240 probe episodes into disjoint batches and computes the cosine similarity between gradient estimates from independent batches of the same size.
Problem: length bias introduced by aggregation. Symptom: mean episode length grows over training without a matching rise in reward, often concentrated in failing episodes. Cause:seq_mean_token_meandivides each trajectory’s push by its own length (Section 6.6), so a longer failure is penalized less per token.token_meancouples the step size to the batch’s total length. Options: (a)seq_mean_token_sumorconstant, which keep per-token weights fixed and need a learning rate retuned for the larger gradient. (b)token_mean, which is widely used, with the batch’s policy-token count logged. (c) Explicit length costs, which change the task. Default:token_mean, while logging the mean length of successes and failures separately. If failures lengthen, switch toconstant.
Problem: detaching the wrong tensor. Symptom: training runs and the loss decreases, but learning is slower or drifts in odd directions, and the gradient differs from a reference implementation. Cause: a quantity that should be a constant in the surrogate, such as a baseline or advantage computed from the model’s own outputs, was left attached to the graph. Its gradient is then added to the estimator, and nothing in the derivation justifies that term. Options: (a) detach advantages inside the loss function, asreinforce_lossdoes. (b) Compute advantages undertorch.no_grad(). Default: both, plus a unit test that compares the gradient to a reference on a fixed batch. The block below reproduces the bias on a five-call bandit with a baseline read off the policy’s own probabilities.
detach() (Chapter 7).
Problem: the zero-advantage batch. Symptom: the policy keeps changing, and sometimes degrades, during steps in which every episode had the same reward and the logged loss and gradient were exactly zero. Cause: with all rewards equal and a mean baseline, every advantage is zero, so the gradient is zero. Adam still moves the parameters, by its first-moment estimate from earlier steps. Weight decay moves them too. A zero-advantage step is not a no-op. With token_mean, zero-advantage trajectories also dilute every other trajectory’s weight by adding tokens to the normalizer. Options: (a) skip the optimizer step when the batch has no signal. (b) Filter zero-advantage groups before forming the batch and resample, which is dynamic sampling (Chapter 9). (c) Accept the drift. Default: (b) when groups are used, otherwise (a). The block below takes one ordinary step and then one step on an all-zero advantage.
Summary
- The REINFORCE estimator needs the environment only to sample. Its dynamics drop out of the score, and at the token level so do all prompt, template, and tool tokens, which is the action mask. Tool tokens still affect the gradient through attention.
- The surrogate loss has the estimator as its gradient and no meaningful value. Log rewards and pass rates, not the loss.
- Any baseline fixed before the action is sampled leaves the estimator unbiased. On the base agent, the mean reward is close to the optimal constant, a baseline of 1 inflates the variance more than sevenfold, and a per-task baseline estimated from 12 episodes is no better than the global mean. Reward-to-go removes past rewards from each token’s weight, which cut the variance by 43% under a per-call cost.
- The loss aggregation sets every token’s weight. Three common modes weight trajectories in proportion to their length,
seq_mean_token_meanweights them equally, andtoken_meancouples the step size to the batch’s length. - The base agent, TinyAgent behavior-cloned on
randomandblindplay, writes valid calls 98% of the time and passes 12% to 19% of episodes. Eighty REINFORCE steps raise its pass rate from 0.14 to 0.78 on training tasks and from 0.13 to 0.46 on held-out tasks, by partly memorizing function names and by stacking edits, never by reading.
Exercises
- Repeat the unbiasedness check of Section 6.4 with a state-dependent baseline from
policy_evaluation, applied at every step. How much does the per-batch variance fall compared with the exact constant ? Then use and show that the mean is now wrong. Which line of the proof fails? - Prove that the optimal constant baseline is , and explain why it differs from the mean reward. For which kinds of episodes is large in TinyAgent?
- Change an assumption: train with REINFORCE using
seq_mean_token_meanand thenseq_mean_token_sum(retune the learning rate for the second, and say why it needs retuning). Log the mean number of policy tokens in failing episodes. Does the length of failures behave as Section 6.6 predicts? - Build the base agent from
blindplay only, and fromrandomplay only. Measure the pass rate, the read rate, and then the result of 80 REINFORCE steps. Which starting distribution leads to more held-out improvement, and why? - Make reading useful to REINFORCE: give the base agent demonstrations from an oracle that is wrong 50% of the time (it reads, then picks the right op with probability 0.5). Does REINFORCE now learn to read? Measure the held-out pass rate.
- The token-level estimator treats each token as an action. Derive the turn-level estimator in which an action is a whole call. Show that its score is the sum of the token scores of the call, so the two estimators are identical. Where do the two castings stop being equivalent?
- Design review: a colleague proposes to stabilize training by normalizing the surrogate loss by its running mean absolute value, “so that the loss stays around 1”. What does this do to the gradient? Under what condition is it equivalent to a learning-rate schedule, and when does it change the direction of the update?