GRPO · native training workflow

Train a LoRA adapter from scored completions.

Kiln's GRPO path is a generate → score → train loop: produce several completions for each prompt, assign rewards with your own verifier, submit the scored batch to /v1/train/grpo, and let Kiln save a LoRA adapter for evaluation and explicit activation.

Concept

What GRPO does in Kiln

Group Relative Policy Optimization (GRPO) fits tasks where you can score model outputs more reliably than you can write one ideal answer. Each group contains the original chat-format messages and several completions, each with generated text and a finite numeric reward.

Kiln derives relative advantages within each group, applies the selected GRPO-family loss to LoRA parameters, and atomically publishes the completed adapter. The request enters a background queue, but GPU training takes exclusive accelerator ownership; inference resumes only after that ownership is released.

The default profile runs the complete loop

The default stable profile supports generation, training, evaluation, and coordinated adapter-weight transitions in one process. maintenance admits drained mutation but disables generation and evaluation. experimental is reserved for backend development and is not needed for speed or training. See Serving profiles.

Before you run the commands

Start Kiln normally with real Qwen/Qwen3.5-4B weights. The CLI examples assume that the extracted kiln binary is on your PATH; otherwise replace kiln with its local path. Generate and score data from a trusted client because Kiln does not authenticate its HTTP API.

Rollouts

Generate multiple completions per prompt

Use kiln rollout-generate when training with recorded importance correction. It requests one seeded, non-streaming choice at a time, validates the server-issued token and behavior-policy provenance before scoring, and atomically publishes trainer-ready GRPO JSONL only after every rollout passes.

Provenance-bound rollout dataset

kiln rollout-generate \
  --adapter base \
  --thinking false \
  --tasks tasks.jsonl \
  --seeds 8 \
  --seed-start 42 \
  --request-template request.json \
  --scorer ./score_math.py \
  --output math.rollouts.jsonl \
  --summary-output math.rollouts.summary.json

Submit the resulting dataset with config.behavior_policy: "recorded". The trainer revalidates the exact prompt, sampled actions, tokenizer/template identity, behavior model revision, sampling controls, and selected-token log-probabilities before the first forward.

Text-only batch alternative

curl -fsS http://localhost:8420/v1/completions/batch \
  -H "Content-Type: application/json" \
  -d '{"prompts": [[{"role": "user", "content": "What is 47 + 138?"}]],
       "n": 8, "temperature": 0.9, "max_tokens": 64, "seed": 42}' \
  | python3 -m json.tool

This endpoint does not emit per-token behavior probabilities or a trainer-ready scored-group file. Score its completions, reshape them into groups, and train only with behavior_policy: "no_importance_correction". Kiln will not substitute the KL reference for the missing behavior denominator.

Training

Submit scored completions to /v1/train/grpo

The minimal request is {"groups": [...]}; every config field has a server default. Set output_name for a predictable artifact name. Although auto_load defaults to true, the safer train-then-evaluate workflow sets it to false and activates the adapter only after held-out comparison.

First GRPO training request

curl -fsS http://localhost:8420/v1/train/grpo \
  -H "Content-Type: application/json" \
  -d '{
    "groups": [{
      "messages": [{"role": "user", "content": "What is 47 + 138? Reply with just the number."}],
      "completions": [
        {"text": "185", "reward": 1.0},
        {"text": "The answer is 184", "reward": 0.0},
        {"text": "185.", "reward": 1.0},
        {"text": "47 + 138 = 185", "reward": 0.8}
      ]
    }],
    "config": {
      "behavior_policy": "no_importance_correction",
      "kl_coeff": 0.1,
      "clip_epsilon": 0.2,
      "lora_rank": 16,
      "output_name": "math-correctness",
      "auto_load": false
    }
  }' \
  | python3 -m json.tool

Recommended: bind activation to held-out evidence

After registering one versioned held-out suite, submit the server-visible JSONL through the streamed route with a post_eval gate. Replace both example names and the absolute dataset path. Kiln publishes the adapter but defers activation until the exact trained revision passes the fixed paired statistical policy:

curl -fsS http://localhost:8420/v1/train/grpo \
  -H "Content-Type: application/json" \
  -d '{
    "dataset_path": "/absolute/path/scored-groups.jsonl",
    "config": {
      "behavior_policy": "recorded",
      "output_name": "math-correctness",
      "auto_load": true
    },
    "post_eval": {
      "suite": "math-held-out-v1",
      "data_scope": "held-out",
      "include_baseline": true,
      "min_accuracy": 0.85,
      "generation": {
        "temperature": 0.0,
        "max_tokens": 128,
        "seed": 42
      }
    }
  }' \
  | python3 -m json.tool

The gate requires at least 20 independent reduced examples, an exact sign test, and a Wilson confidence floor. Its terminal outcome is promoted, kept, regression, demoted, inconclusive, or error; only promoted activates the adapter. See Post-training auto-eval for the full policy and suite requirements.

Common config fields

  • kl_coeff tunes the independent frozen-reference penalty; learning_rate is resolved per optimizer when omitted (Muon default: 2e-3).
  • is_level selects token PPO (default), sequence GSPO, or CISPO. PPO/GSPO use clip_epsilon plus optional clip_eps_high.
  • CISPO instead uses the absolute upper-only cispo_max_weight cap (default 5.0); it has no PPO lower floor.
  • behavior_policy selects recorded rollout probabilities or an explicit fixed-one ratio; kl_reference_policy independently selects the frozen KL anchor.
  • lora_rank and lora_alpha control adapter capacity.
  • base_adapter continues from an existing adapter.
  • seed selects the run seed. When omitted, Kiln assigns one before queueing the job and returns it as an exact decimal string.
  • output_name names the saved adapter.
  • auto_load defaults to true; set it to false when evaluation should gate activation.
  • checkpoint_interval writes exact state every positive N committed optimizer groups.
  • resume_checkpoint continues from the immutable basename reported by job detail.
  • adapter_smoke_test enables the post-training canary; adapter_smoke_prompts supplies request-local replacement prompts.
  • shared_prefix_reference defaults to true and reuses qualified prompt-side reference state. Disable it only for an explicit comparison.
  • detect_anomaly is an off-by-default, request-local backward diagnostic. It scans each operation's returned gradients and fails at the first NaN or Inf; use kiln train grpo --detect-anomaly to enable it.

Payload guardrails

  • Put options under config, not at the top level.
  • Use groups, messages, completions, text, and reward.
  • GRPO does not use SFT's epochs field.
  • Use at least two completions with different rewards; equal rewards produce zero normalized advantage.

Checkpoint and resume exact GRPO state

kiln train grpo \
  --file scored-groups.jsonl \
  --adapter math-correctness \
  --checkpoint-interval 25

kiln train grpo \
  --file scored-groups.jsonl \
  --adapter math-correctness \
  --checkpoint-interval 25 \
  --resume-checkpoint math-correctness-checkpoint-step-00000025.kiln-checkpoint

A .jsonl file uses the memory-bounded streamed route; JSON containing groups uses the inline route. Resume requires identical source bytes, route, adapter, and effective configuration. Before GPU setup, Kiln validates and restores adapter, optimizer, reference or EMA, cursor and RNG, loss, and diagnostic state. Cancellation settles at the next group boundary. A crash can lose the in-flight group, but not the newest committed checkpoint. Dashboard job details can copy the checkpoint basename or prepare the matching form. See Native training checkpoints for the fail-closed contract.

Submission and status report the immutable effective_seed as a decimal string. Exact resume inherits the checkpoint's original LoRA-initialization seed and rejects a conflicting request. Fresh runs that reuse a seed are comparable only when weights, data, tokenizer, build, backend, runtime, and precision also match.

Monitoring

Watch the background training job

/v1/train/grpo returns a queued job immediately. Poll /v1/train/status to see pending, running, completed, or failed jobs. GET /v1/train/jobs/{job_id} reports the latest exact checkpoint basename, inline/JSONL route, and next group cursor; then confirm the trained adapter through the adapter APIs or the web UI.

If the kiln CLI is on your PATH, run kiln train status for the same status as a readable summary (use --url http://host:8420 for a trusted remote server). The curl command below is the equivalent HTTP probe for CI, scripts, or environments without the CLI.

Training status from the CLI

kiln train status

Training status

curl -fsS http://localhost:8420/v1/train/status | python3 -m json.tool

After a job is completed, confirm that Kiln saved the new adapter. kiln adapters list shows every saved adapter and identifies the active one. With the recommended auto_load: false, math-correctness should be present but inactive. The curl command below calls the same endpoint for CI or scripts.

Confirm the trained adapter from the CLI

kiln adapters list

Confirm the trained adapter

curl -fsS http://localhost:8420/v1/adapters | python3 -m json.tool

Manual review for an ungated artifact

Compare math-correctness with the no-adapter baseline on a held-out suite in the Evals guide. A manual comparison does not create the atomic promotion gate above. If an operator nevertheless approves activation, recheck the adapter detail and receipt immediately before loading it by name, then record the content_revision returned by this request:

curl -fsS http://localhost:8420/v1/adapters/load \
  -H "Content-Type: application/json" \
  -d '{"name": "math-correctness"}' \
  | python3 -m json.tool

This is an operator-controlled action under the default stable profile, not evidence-bound automatic promotion.

If a GRPO request is rejected or stays queued longer than expected, start with the troubleshooting guide and the full endpoint map in API training.

Audit

Verify which policy comparisons trained the adapter

Every non-dry run that reaches the training loop writes grpo.policy_audit into train_receipt.json. The same versioned object is returned by the adapter receipt API and reports behavior-policy importance ratios separately from frozen-reference KL metrics.

Read the policy audit

# This filtered view requires jq.
ADAPTER=math-correctness
curl -fsS http://localhost:8420/v1/adapters/$ADAPTER/receipt \
  | jq '.grpo.policy_audit'
  • importance_sampling reports ratios against recorded behavior probabilities, or exact unit ratios in no-correction mode. CISPO’s below-clip count is always zero; its upper count is measured against cispo_max_weight.
  • kl_reference reports K1/K3 observations against the independently selected reference, including entropy-mask coverage.
  • recorded_provenance binds counts to content-addressed behavior model, adapter revision, tokenizer/template, sampling, and generation-backend identities.

Counts are observations across the actual training loop, so repeated epochs count a completion each time it is trained. See the full guide for sequence-versus-token ratio and masked-KL denominator semantics.

Where to go next