Skip to main content
This guide walks through a small GRPO-style reinforcement-learning training loop that trains a LoRA adapter with Tinker while sampling every rollout from Sail. Tinker owns the optimizer; Sail serves each fresh checkpoint through SailTokenCompleter, so your rollouts run on Sail’s inference fleet. The example fine-tunes Kimi K2.6 to solve grade-school math word problems, rewarding answers that land in \boxed{}.

Prerequisites

  • Python 3.11+
  • A Sail API key and a Tinker API key
  • The packages below

How one step fits together

Each training step is the same five moves:
  1. Snapshot the current LoRA weights as a Tinker sampler checkpoint.
  2. Resolve a signed URL to that checkpoint and wrap it in a SailTokenCompleter.
  3. Roll out a batch of grouped completions through that completer on Sail.
  4. Score the completions, turn them into advantages and Tinker training data.
  5. Train one optimizer step on the Tinker client, then repeat with the updated weights.

1. Score a completion

An environment renders one prompt and scores one sampled completion. tinker-cookbook calls initial_observation to get the prompt tokens (and stop sequences), then step to score the tokens Sail sampled. The reward here is 1 for a correct boxed answer, with a small penalty for missing the \boxed{} format.
A group builder fans one prompt out into group_size environments.

2. Serve the latest checkpoint on Sail

After each Tinker step, save the weights as a sampler checkpoint, resolve a signed archive URL, and build a SailTokenCompleter pointed at it. The adapter_config is the PEFT config matching the LoRA Tinker is training. This ready-made one fits this example (Kimi K2.6 at rank 32); save it as tinker_adapter_config.json. Passing ttl_seconds tells Tinker to expire the checkpoint, so per-step checkpoints clean themselves up instead of piling up.
tinker_adapter_config.json

3. The training loop

Wire it together: load the data, then each step snapshot → roll out on Sail → train.
That’s the whole loop. Tinker holds the optimizer state and applies each update; Sail samples every rollout from the checkpoint you just wrote. To scale the rollout batch, raise group_size and groups_per_step, and Sail runs the completions concurrently. To watch the reward climb, log the metrics returned by compute_advantages/train_step.

4. Sandboxed rollouts

The math environment above only scores text, so it runs safely inside the training process. An environment that executes model-written code should not. tinker-cookbook has a seam for this: recipes that take a sandbox factory run each rollout’s commands in an isolated sandbox. Pass Sail’s factory and those sandboxes are Sailboxes:
When image_ref is preset, the factory uses that registry image. Otherwise, it reads the task.toml next to the environment directory for [environment].docker_image. When the task does not name a registry image, Sail builds the environment’s Dockerfile with the whole environment directory as its build context. The registry image or Dockerfile must produce a Debian- or Ubuntu-based filesystem. The factory creates a Sailbox from the selected image and returns a sandbox the cookbook drives: commands, file reads and writes, and cleanup all happen in the Sailbox, and the cookbook’s timeout terminates it. It is a plain module-level function, so it survives the cookbook’s pickling, and functools.partial keeps preset arguments pickleable too. Each group’s sandboxes are created concurrently. The Tinker reference documents the full sandbox API.

Next steps

  • Tinker: the SailTokenCompleter reference (parameters, LoRA modes, and constraints).
  • LoRAs: upload and serve a PEFT adapter directly, without a Tinker training loop.
  • Completion Windows: control the latency/cost tier your rollouts run on.