EnvironmentMultiTurn for tasks that take more than one response: a
conversation, a tool-using agent, anything where the environment reacts to what
the model just did and the model acts again. Subclass it and implement the
episode hooks. GRPO rolls out the whole episode, then scores the finished
trajectory. OPD can use the same plain multi-turn loop and distills the
student’s assistant turns from the managed teacher; native tool-calling
environments remain GRPO-only.
flash env setup --multi-turn scaffolds a runnable starter on this class — a
“guess the secret number” episode with start_episode, step_episode,
max_episode_turns, and score_episode wired end-to-end — that you edit into
your own task.
The episode loop
During training the trainer drives your hooks in a loop, then scores the result:start_episode(example, prompt_text)returns the initial messages (e.g. a system prompt plus the first user message).max_episode_turns(example)returns the maximum number of assistant actions Flash may sample before forcing the episode to end. The value is per example.step_episode(example, messages, assistant_response)is the core.messagesalready includes the assistant action being stepped as its last element. Return anEnvironmentStepResult:done=False, messages=(...)keeps the episode open and injects the environment’s response (a tool result, the next user turn);done=True, final_response_text=...ends it.final_response_textoverrides the last assistant message as the text passed to scoring.
score_episode(example, episode)grades one completed transcript (episode.messages) and returns aRewardResult, exactly likescore_responsebut over the whole trajectory.score_episodes(example, episodes)is the batch wrapper Flash calls for a GRPO group. You normally do not implement it; the SDK default runs your singularscore_episodefor each item, usingmax_score_concurrency.
Full multi-turn contract
This is the worker-facing surface Flash expects:EnvironmentStepResult.messages should contain only new environment-side
messages to append after the assistant action. Do not repeat the assistant
message Flash just sampled. metadata is appended to the completed episode’s
metadata under steps, available to score_episode and logs. Keep durable
episode state in the transcript, not in hidden mutable attributes.
An action protocol the model can emit as text
The trained policy is an open model, not a tool-use API, so define a plain-text protocol it can produce and yourstep_episode can parse — for example, a
tool-call block versus a final reply:
environment.py
SFT targets for multi-turn
SFT does not execute the multi-turn loop. It builds one supervised row as:sft_completion(example) turns example.output into the target
messages — a scalar becomes one assistant message, and a message list (or
{"messages": [...]}) becomes the full transcript (see
Datasets).
Flash uses completion-only loss for SFT. It masks the token prefix rendered from
start_episode(...) and trains on every token in sft_completion(...). In a
multi-turn target transcript, any interleaved assistant, user, and
tool/environment messages in output are part of the supervised completion. If
a multi-turn environment has only scalar outputs, SFT collapses to one assistant
turn per row and does not run step_episode; provide full target transcripts or
override sft_completion when you need multi-turn SFT.
Keep step_episode stateless
The SDK doesn’t hand you a per-episode scratch object, and GRPO runs many
rollouts of the same example concurrently — so don’t stash mutable world-state
on the env keyed by example. If your task has state (an API or service that
changes as tools run), make the transcript the source of truth and rebuild
state by replaying messages inside step_episode / score_episode. Replay is
reproducible as long as your service is deterministic given the same calls (seed
any ids; avoid wall-clock-dependent logic in the reward).
Score behaviours, not a script
Open-ended tasks rarely have one correct transcript, so don’t grade by imitation. Score the behaviours you care about — did it take the resolving action, follow required ordering (e.g. authenticate before a privileged call), stay within limits — and return that blend fromscore_episode. Keeping the reward in a
plain function (importable without the trainer) makes it unit-testable.
Configure for multi-turn
For multi-turn runs, the episode budget comes frommax_episode_turns. What
matters in [train]:
max_completion_tokensis per assistant action (one tool call or one reply), not per episode — keep it modest.max_context_tokensmust hold the system prompt plus the whole running transcript (every turn, every tool result), so set it well above a single-turn task.group_sizeis the number of full episodes rolled out per prompt for the GRPO advantage estimate. OPD usually usesgroup_size = 1.
Managed generation request deadlines
Pure multi-turn GRPO model generations have managed request deadlines between 10 and 60 minutes, based on context size. A timed-out assistant generation is aborted and retried once; timed-out output is never sent to the environment. This covers only model generation in the pure multi-turn GRPO path. It does not cover environment or external-service calls, OPD, or tool-loop paths.Next steps
Training
Submit a run once your environment is ready.
Deploy & chat
Serve the trained adapter.