Prerequisites
Install the cookbook
Install the Loops cookbook from PyPI into a uv project. The cookbook needs Python 3.12 or later, installs thebaseten-loops SDK, and provides the loops command. loops runs both recipes; each training config names its recipe with recipe = "rl" or recipe = "sft". Create a project first if you don’t have one:
examples/counting/train.toml, from your project’s directory. To change an example, copy its directory into your project at the same path. A config in your project wins over the installed one, and loops check prints the directory of the config it used.
Create a .env file in your project’s directory and set BASETEN_API_KEY to a workspace API key. The commands load .env from the training config’s directory or the directory you run them from. Never commit .env.
A run uses the serverless availability mode when your workspace has serverless access to the model, and the dedicated mode otherwise. To choose a mode, add BASETEN_LOOPS_SCOPE=serverless or BASETEN_LOOPS_SCOPE=deployment to .env.
Build the dataset
examples/sft_toucan/make_data.py downloads part of Toucan and rewrites each conversation into the chat JSONL format described in Format conversations as chat JSONL. It splits by tool set, so the validation conversations use tool combinations the training data never saw. That keeps near-duplicate conversations from landing on both sides of the split.
make_data.py writes the dataset beside itself, and evaluate.py writes its results there too, so copy the example into your project first. To copy it, run the following command:
irrelevant conversations are requests no offered tool can serve, where the right answer calls no tool. They’re 15% of each split, so the model learns when not to call a tool as well as when to call one.
Check what trains
examples/sft_toucan/sft.toml sets the model, data, and schedule:
examples/sft_toucan/sft.toml
Config: names your copy in examples/sft_toucan/, not the installed one. The rest reports what trains:
max_seq_len = 4096. The trained spans of the first row end with <|im_end|>, so each turn learns to stop.
Train
To train for one epoch, run the following command. The run uses the serverless availability mode, billed for the tokens you train, when your workspace has serverless access to Qwen3.6-35B-A3B, and the dedicated mode, billed for GPU time, otherwise.run.json holds the run ID. The last update’s sampler weights are always kept, as step-<N>.
The cookbook’s recorded run of this config took 125 updates, about 12 minutes of training once the trainer was up. val/nll fell from 0.914 before the first update to 0.795, mostly in the first 20 updates, and was flat over the last 20.
Compare the adapter with the base model
Validation loss shows the adapter fits the data. To check that it does the task better, sample both models on the validation conversations.examples/sft_toucan/evaluate.py takes each conversation’s first assistant turn, samples it greedily from the base model and from the adapter, and scores four checks against the data:
decision: calls a tool exactly when the data does.tools: calls the same tools.arguments: calls the same tools with the same arguments.well_formed: stops, and every call parses.
The data’s own replies score 98.5%
well_formed under Qwen3.6’s parser, so the adapter reached that ceiling. The largest change is on requests no tool can serve: the base model reaches for an unrelated tool anyway, and the adapter answers directly.
evaluate.py measures agreement with the data on first turns. It doesn’t run tools or score later turns. Before you ship a model trained this way, build an evaluation that covers your own tools and the rules your task needs. See Evaluate a model.
Shut down the run
loops run leaves the Loops run active. On serverless, deactivation is optional because idle runs incur no token charges. On dedicated capacity, deactivate the run when you finish to stop GPU billing without waiting for automatic shutdown. To end the run and its paired sampler in either mode, run the following command with your run directory:
Next steps
- Deploy a checkpoint: Serve the adapter as an OpenAI-compatible endpoint.
- Prepare data for supervised fine-tuning: Build a dataset from your own traces.
- Train a tool-using agent with RL: Train on outcomes instead of fixed answers.