Skip to main content
Train a LoRA adapter for Qwen3.6-35B-A3B with reinforcement learning (RL) on serverless Loops, sample from its published weights, save resumable training state, and deploy the tuned model for inference. Baseten manages the infrastructure. Training bills for token usage, and deployment bills for GPU usage while the deployment is live. Use the Python SDK to write the training loop.

Try the cookbook workflow

Run the same task with a config file instead of a Python training loop.

Define the task and reward

When you fine-tune a model with RL, you provide a task and a reward function that scores the model’s replies. The rewards tell the training loop which behaviors to reinforce. For example, if you want a model to classify support tickets, reward it for assigning the correct category. This quickstart uses a task with one correct answer so you can compare rewards across training steps. The script sends this prompt:
A reply of Quake scores 1.0. Any other reply scores 0.0. Thinking is off, and a reply can have at most 16 tokens. Each training step samples 16 replies from the current policy. A reply’s advantage is its reward minus the mean reward of the 16, so the step makes correct replies more likely and wrong replies less likely. When all 16 replies score the same, every advantage is zero and the step samples again. The script runs five training steps with LoRA rank 8 and a learning rate of 2e-5, and prints the mean reward for each step.
To have your coding agent run this quickstart, see Run a quickstart with your agent.

Prerequisites

Optionally, install the Baseten CLI to list and deploy checkpoints and deactivate the run from your terminal.

Install the SDK

Create a uv project and install baseten-loops:
Export your workspace API key as BASETEN_API_KEY. Replace YOUR_API_KEY with your key:
Save the following code as train_loops.py, then verify the installation by running uv run python train_loops.py:
train_loops.py
Output

Create a training run

Create a ServiceClient, then call create_lora_training_client() with the ID of the model you want to train. This example uses Qwen/Qwen3.6-35B-A3B. Set scope="serverless" so Baseten runs the training on hardware it selects and configures for you. The method waits until the trainer accepts operations and returns a TrainingClient. Replace the contents of train_loops.py with:
train_loops.py

Score a reply

Write the question in Qwen’s chat format. The empty thinking block turns thinking off. The reward decodes a reply and compares it with the answer. Append to train_loops.py:
train_loops.py

Train on sampled replies

Each completed training step samples a usable group of 16 replies, updates the model from that group, and publishes new sampling weights for the next step. For RL, each sampled reply becomes one datum. It carries the reply’s tokens as targets, the sampler’s logprobs for those tokens, and the reply’s advantage. Prompt positions carry -100 as the target, so they don’t train. Append to train_loops.py:
train_loops.py
The forward_backward() call uses the cispo loss and the sampler’s logprobs to correct for any difference between the policy that sampled a reply and the policy that trains on it. For more information, see Loss functions. save_weights_and_get_sampling_client() publishes a sampler checkpoint under sampler_weights/ that you can deploy to inference, and returns a SamplingClient for it. This checkpoint omits optimizer state, so you can’t resume training from it; use save_state() when you need a resumable checkpoint.

Sample from the tuned weights

The last save_weights_and_get_sampling_client() call returned a client for the weights from step 5. Generate one reply from it. Append to train_loops.py:
train_loops.py
You’ve now trained a LoRA adapter and generated a reply from its published weights. This confirms that sampling works; it doesn’t evaluate fine-tuning quality. To sample from newer weights, create a new sampling client. Baseten pins each serverless sampling client to the checkpoint used to create it.

Save resumable training state

Call save_state() to save the model and optimizer state. Append to train_loops.py:
train_loops.py
Use the returned weights/ path to resume training.

Inspect the results

Call list_checkpoints() to list the run’s checkpoints, then close the local clients. Append to train_loops.py:
train_loops.py
Run the script from your project directory:
The output has the following shape. Values and checkpoint rows vary:
Output
The script prints the session and run IDs before the first sampling request. That request can take several minutes while serverless capacity starts.
Check that the script finishes without an error. Compare the mean rewards across steps. Sampled rewards can vary between runs. Use the returned checkpoint paths, names, and IDs directly. Serverless checkpoint names can include a unique suffix. To list the run’s checkpoints after the script exits: Replace <run_id> with the printed run ID:

Deactivate the run

Closing the local clients doesn’t deactivate the run or its paired sampler. The run incurs no token charges while idle, so deactivation is optional. To deactivate the run and its paired sampler: Replace <run_id> with the ID printed by your script:
Saved checkpoints remain available after deactivation.

Deploy the checkpoint

Now that you’ve fine-tuned your model, deploy its latest sampler checkpoint to run evaluations or inference. Baseten serves the checkpoint and your trained adapter as a dedicated inference deployment. The deployment needs an hf_access_token in workspace secrets and bills for its GPU while it’s live.
To deploy a checkpoint:Replace <id> with a sampler checkpoint’s id, the first column of the listing above, then run:
Follow the prompts for the model name, GPU type and count, and Hugging Face secret name. The output includes the IDs needed to call the model:
Output
Use the returned model and deployment IDs to run inference on your endpoint. Baseten handles model serving and infrastructure management. When you’re done, delete the deployment to stop GPU billing. Deactivating the Loops run doesn’t delete the deployment.

Next steps