Skip to main content
The train command group covers LLM fine-tuning and iterative agent training.
Previously invisible commands. On PraisonAI releases before PR #4367, praisonai train exposed only 6 of the 15 subcommands, and the entire remote group was unreachable — you had to install praisonai-train on its own to use them. The integrated CLI imported only the train command module, so the siblings that register as an import side effect never loaded. From #4367 onwards, every subcommand documented on this page (and the new remote group in Remote Training) is reachable through the integrated CLI.

Usage

Every praisonai train <sub> also runs as praisonai-train <sub> when praisonai-train is installed on its own.

Requirements

Pick the install that matches what you want to train.
pip install "praisonai[train]" previously installed nothing (empty extra). It now pulls praisonai-train[llm], so it installs the Unsloth stack.
As of PR #4367, the [llm] extra pulls in the praisonai wrapper transitively — the llm subcommand imports it during parse_args. You no longer need to pip install praisonai separately for praisonai-train[llm] to work; before this, pip install "praisonai-train[llm]" produced an llm command that died at parse_args.
The [llm] extra now needs the modern base stack: torch>=2.6.0, unsloth>=2025.9.1, trl>=0.18.2, transformers>=4.51.3. Pinning torch<2.6 is why install can fail — upgrade torch first. Tested on Python 3.11 with pytorch-cuda 12.4.

praisonai train generate

Synthesize an instruction dataset from a teacher LLM.

Options

YAML-only keys

These have no CLI flag — set them in --config.

Examples

output and num_examples are validated up-front. generate writes to a sibling temp file and os.replaces the destination only when at least one row is produced — a run that fails on credentials or every teacher request leaves any existing file untouched (the swap also preserves the destination’s mode and follows a symlink target). Zero unique rows exits 1. Fixed in PraisonAI PR #4239.
See Dataset Tooling for recipes, dedup, snapshots, and the generate_dataset() Python API.

praisonai train validate

Quality-check and filter an instruction dataset (dedup, boilerplate/refusal, script purity, diversity).

Options

YAML-only keys

Examples

The command prints an in / kept / drops / flags / metrics report to stdout. Malformed JSONL lines are skipped with a warning — the run still succeeds on the remaining rows.
validate writes --out atomically (temp file + os.replace), preserves the destination’s file mode when overwriting, and follows symlink targets — mirroring generate/dedup. Zero-kept exits 1 with error: no rows kept — all rows were dropped by QC ({drops}); the destination is never truncated, so an --out that aliases the input keeps its existing bytes. Fixed in PraisonAI PR #4316.
See Dataset Tooling for the full check list and the score() / filter_rows() Python API.

Export trials with QC (from-trials --qc)

from-trials --qc reuses this QC filter but auto-disables the Tamil script check for English agent trajectories — pass qc_cfg={"script_range": [...]} to opt back in for another language.

praisonai train benchmark

Measure and rank generation speed across LLM deployments.

Options

YAML-only keys

Examples

The command prints each target as it finishes, then a table ranked by rows/min. n < 1 or concurrency < 1 raises ValueError; if every request fails the CLI exits 1. See Speed Benchmark for the benchmark_deployments() Python API and cross-endpoint benchmarking.

praisonai train dedup

Merge many JSONL files into one deduped file with a single shared exact + MinHash/LSH index — catching the cross-file near-duplicates that per-file validate always misses.

Options

Examples

Drive it from YAML for a reproducible config:
The command prints a in / kept / removed stats block on completion and exits non-zero only on IO errors. The write is atomic — an --out that aliases an input is never truncated before its rows are read.

Cross-file dedup (Python API)

The global_dedup(), near_dedup(), and MinHashLSH Python API behind this command.

praisonai train data from-trials

Export the passing attempts from a scored trials report into a trainer-ready SFT dataset (plus a provenance sidecar) — the “keep what verified, then fine-tune on it” step of the trials loop.

Options

Examples

Every run also writes {out}.jsonl.meta.json — a provenance sidecar mapping each emitted row to its source case, attempt, and score.
Exit code 1 when zero rows are exported. The stderr message hints toward --all / --include-saturated, or to check that the report has passing tool-free attempts. Every skip is counted, never silent — the summary block names skipped_failed, skipped_tool_runs, skipped_unscored, skipped_saturated, and skipped_no_text.

from-trials (feature guide + Python API)

The selection discipline, export_trials() Python API, provenance sidecar, and the rejection-sampling caveat.

praisonai train llm

Fine-tune an LLM using Unsloth.
Pass a dataset path, or --config a file that names one — one of the two is required.

Options

The four --remote-* flags send this one run to a remote GPU box instead of training locally — the same as a remote: block in config.yaml or the desktop “Run on” dropdown. Omit --remote-host (and set no remote.host) to train here as before. See Remote in one step.
--config sets the baseline, the tuning flags override it. Run with --dry-run first to see the resolved config the trainer would use — file first, flags on top. Everything not covered by a flag still lives in the config file, reachable via --config. Key config options include method (choose sft, dpo, orpo, or kto), chat_template (pick the model’s chat-template family; omit to use the built-in), and assistant_only_loss (mask prompts out of the loss; auto-picks a masking route for any known template family).

Precedence

The resolved config is built in three layers — each layer overrides the one above. An untouched config key survives when a different key is overridden by a flag.

Preview a run with --dry-run

--dry-run prints the resolved config as YAML you can paste back into a file, then a comment listing which keys came from flags — and exits without training.
When --remote-host (or --remote-python, --remote-workdir, --remote-gpus) is on the command line, the settled remote: block appears in the printed YAML with credentials redacted — the host, interpreter, workdir, and GPU count the run would actually use:
A bad remote: block is caught here too: a malformed block exits 1 with Bad remote settings at --dry-run, not after dispatch. --dry-run does not load the training runner, so it works even without the [llm] extra installed.

Supported models

The trainer uses each model’s own chat template, so chat_template is optional — set it only to override.

Full chat_template reference

Types, defaults, and the fail-fast ValueError when no template is available.
The five families above are curated starting points, not the whole list. Run praisonai train models to see every model the trainer can load (~246 unsloth/… repos), or praisonai train models gemma to filter by family.

Examples

Pass a dataset path or a --config file that names one (via dataset:). With neither, the CLI refuses:
Exit 1 — “I forgot the dataset” never silently trains against the default Alpaca corpus.
method: cpt with modules_to_save: [embed_tokens, lm_head] enables continued pretraining on a raw-text corpus (a dataset with a text column). See Continued Pretraining.
The DATASET positional and --model option are materialised into ./config.yaml in the invocation directory, and that file is the sole input to the trainer — the CLI passes no overrides on top. What --dry-run previews is exactly what trains. See Where config.yaml comes from. Fixed in PR #4367.
praisonai-train llm <dataset> (and the wrapper route praisonai train llm <dataset>) now actually launches the trainer. On PraisonAI releases before PR #4317 the legacy dispatcher had no train case, so the command silently forwarded the literal word "train" to an LLM as a chat prompt, exited 0, and never generated a config.yaml or launched the trainer subprocess. If a previous run appeared to succeed instantly without producing lora_model/ or a praisonai-train show session, upgrade and re-run.
On PraisonAI releases between #4239 and #4367, passing --dataset and --model on the CLI could quietly overwrite tuning parameters (lora_r, epochs, max_seq_length, method) and flip huggingface_save on with the default hf_model_name — which is not yours. The forwarded flags put the legacy dispatcher on its “regenerate config.yaml from defaults” branch:If you ran praisonai train llm <dataset> --model <base> on that range and the run finished with tiny LoRAs, one epoch, or a Hub push you did not expect, this is why. Fixed in #4367 by no longer forwarding those flags.
Vision configs no longer publish by default (PraisonAI #4879). The CLI writes only the keys you supplied to config.yaml, so a vision run (any model whose name contains vision, -vl-, or visionmodel) typically omits huggingface_save. The vision trainer used to default that flag to the string "true" and push to the Hub anyway — or crash on a missing hf_model_name after training. From #4879 an omitted flag skips publishing, matching the LLM path. See Vision Fine-Tuning.

Where config.yaml comes from

The CLI writes ./config.yaml from your flags plus resolved defaults in the invocation directory, then hands that single file to the trainer.
1

Materialise the config

praisonai train llm resolves the config (file first, flags on top) and writes it to ./config.yaml in the directory you ran from — the only path the legacy dispatcher and its trainer subprocess read.
2

Back up any existing config.yaml

If a config.yaml was already there and its contents differ from what’s about to be written, the previous contents are saved to ./config.yaml.bak and the run continues:
3

No-op writes leave no .bak

Re-running the same command produces no .bak file — an identical write is detected and skipped. A first run in an empty directory also writes no .bak.
Running praisonai train llm in your project root materialises a config.yaml next to your other files. Pick a dedicated directory if you don’t want it there.

Config keys for LLM fine-tuning

Drive fine-tuning from config.yaml (all keys optional and backward compatible).

Unsloth surface keys

Model access, PEFT selectors, quant, and vLLM rollouts — all optional and additive. See Unsloth Surface for the full walkthrough.
A --config file must be a YAML mapping of key: value. A top-level list (or scalar) is refused up front with A training config is a YAML mapping of key: value. — exit 1.
Invalid or unknown YAML keys are reported before training starts. Missing required keys (model_name, max_seq_length, dataset) raise ValueError with a minimal example; unknown keys are collected into one WARNING block with did you mean suggestions and training continues. See Train → Config validation.
A model_name not in the unsloth catalog now prints one WARNING: … line — with a “did you mean?” when there’s a close match — and training continues (unsloth loads models it doesn’t map, so a warning is safer than a refusal). Run praisonai train models to see the catalog, or praisonai train models <family> to filter it.
See Train → Config.yaml example for the full file.

Publishing

Publishing defaults off — set the flag and its matching target to push a fine-tuned model. Enabling a flag without its target fails fast on load:

Preflight validation

Before any model or GPU load, the trainer fails fast with a single friendly line for the five most common misconfigurations: stale/missing training deps (checked first, before any heavy import), no CUDA GPU, missing Hugging Face credentials, low disk (< 10 GB free), and an invalid quantization_method. The GPU check is skipped when train: false, so publish-only / export-only runs succeed on CPU. See Train → Preflight validation for the full check table, fix guidance, and the 35 valid quantization values.

Exit codes

The llm (and the praisonai_train.train.llm.trainer module) entrypoint prints a clean one-line error instead of a traceback for expected failures.

Missing-deps behaviour

On a bare pip install praisonai-train, llm prints one of two messages: LLM fine-tuning dependencies not installed with pip install "praisonai-train[llm]" when praisonai-code is absent, or Failed to load LLM fine-tuning runner: <ImportError> when praisonai-code is present but a downstream import (torch/unsloth) failed. See Train for the full flow.

Errors you may see

The most common error after pip install --no-deps or on a hand-built conda env is a stale training stack. The trainer version-checks six packages (torch, transformers, unsloth, trl, peft, bitsandbytes) before any heavy import and prints one actionable block:
Run the printed pip install -U ... command — it upgrades every listed package at once. A dev / rc suffix on a matching release satisfies the floor (2.6.0rc1 counts as 2.6.0). See Train → Runtime version-floor enforcement.

Multi-GPU launch

Fine-tune across every GPU on the machine with torchrun--nproc_per_node is the number of GPUs.
The trainer detects the distributed launch, loads the full model per rank, and lets only rank 0 save and publish. See Multi-GPU for the full walkthrough.

Remote in one step

Send this one run to a remote GPU box with --remote-host — the same as a remote: block in config.yaml or the desktop “Run on” dropdown. Precedence is flag > YAML > default; with no host settled, the command trains locally as before.
The dispatcher ships the config (stripping the remote: block so the far side does not dispatch again), streams the log, and forwards Ctrl-C to the remote run. Credentials in the block (password, token, identity_file, …) are refused up front. See Remote in one step.

Resume

Set resume_from_checkpoint: true in config.yaml, then re-run the same command — the trainer picks the latest checkpoint in output_dir.
See Checkpointing for save_total_limit, best-checkpoint, and early-stopping keys.

Environment variables

torchrun sets the distributed variables; you set only your Hugging Face token.

praisonai train models

List every model praisonai train llm knows how to load — the same catalog model_name is validated against.

Options

Examples

Each row is prefixed with * if it’s part of the small curated starting set, or a space otherwise:
The trailing line explains the marker (* = a good starting point.) and, when the output was truncated, prints {shown} of {total}; -n 0 for all.

How the catalog is built

The command reads unsloth.models.mapper at runtime — both the 4-bit repo keys and their 16-bit mirror values — rather than shipping a vendored copy that would go stale between releases. When unsloth isn’t installed (the CLI is importable without the training extra), the command falls back to a 7-entry curated list of one current base per family, sized for a single 24 GB GPU in 4-bit. See Supported models.

Exit codes


praisonai train export

Publish an already-trained model to Hugging Face, GGUF, or Ollama without re-running training.
Point it at a lora_model/ you trained earlier — no dataset, no re-training. The hf target pushes a merged model to the Hub; gguf writes a local .gguf (and additionally pushes to the Hub when --hf is set); ollama creates and pushes an Ollama model.

Options

Examples

Export skips the training-only validation — TrainModel.for_export() needs no dataset and no dataset: in a config. It still validates --quant. For ollama, when --hf is omitted the Modelfile FROM line points at the local model on disk, so ollama create works without a Hub round-trip.
Ollama export ≤ PR #4239 shipped an indented TEMPLATE. The Modelfile embedded four leading spaces on every continuation line, so the served prompt format tokenised differently from training and degraded fine-tune quality. Re-export any Ollama model built before this fix — the export takes seconds and does not re-train. Fixed in PraisonAI PR #4239.

Exit codes

Export an already-trained model

The TrainModel.for_export() Python API and behaviour notes behind this command.
See praisonai train llm for the full training path that produces the model you export here.

praisonai train agents

Train agents through iterative feedback loops.

Options

Early Stop (LLM mode)

--iterations N is a maximum, not an exact count. In LLM-as-Judge mode, training stops early as soon as any iteration scores ≥ 9.5 — easy factual prompts often finish in one iteration. When training stops early, an INFO line is logged even in --quiet mode:
Reaching 9.5 on the final iteration counts as a full run — Stopped Early stays absent from the summary. Human-in-the-loop mode (--human) does not use the 9.5 threshold — the user decides when to stop.

Examples

Summary Rows

When training completes, the summary table always shows Total Iterations, Average / Min / Max Score, Improvement, and Status. Two extra rows appear only when the run was truncated: The session JSON persists both signals as metadata.target_iterations (int) and metadata.early_stopped (bool), so tools reading persisted sessions can distinguish “user asked for 3, got 1 due to 9.5” from “user asked for 1, got 1”.

Storage Backend Options

Store training data in different backends.

Exit Codes

As of PraisonAI 4.6.148+ (upstream commits ce17828, 1a6eed3, fixes PraisonAI#3040) a display-only encoding error on Windows cp1252 no longer masquerades as a training failure — the session is still saved and the exit code is 0.

praisonai train serve

Serve a fine-tuned GGUF over an OpenAI-compatible endpoint — auto-fetching the MTP drafter for lossless fast inference on Gemma-4.

Options

How MTP is auto-detected

The base model name (from --base-model, --config, or the model directory’s config.json) drives drafter selection. A Gemma-4 target fetches its matching stock drafter from the Hub; any other family falls back to plain serving with a clear message.

Examples

The server binds to 127.0.0.1 — there is no --host flag. Override the llama-server binary with the LLAMA_CPP_BIN env var (points at the binary or its directory). See Serve & MTP Fast-Inference for the concept-level walkthrough and the honest runtime caveats.

praisonai train checkpoints

List the checkpoints a training run saved — answers “what did it save?” without an ls.

Options

Examples

Matches checkpoint-<n> exactly (a checkpoint-final directory is skipped), sorts numerically newest-first, and reports each checkpoint’s size on disk. An empty or missing directory exits 1 with a remediation. JSON entries are {"step": int, "path": str, "bytes": int}. See List Checkpoints for the full workflow and jq patterns.

praisonai train infer

Run a single generation against a model you just trained — answers “did it work?”.
This subcommand is infer, not generategenerate is dataset generation.

Options

Examples

Output streams via TextStreamer(skip_prompt=True), so the prompt is not echoed and the first token arrives early. Without the Unsloth / Transformers stack it exits 1 with pip install "praisonai-train[llm]". See Infer (one-shot inference) for the full walkthrough and the infer vs serve vs benchmark decision guide.

praisonai train list

List all training sessions.

Options

Examples

Without --storage-backend, list scans the default ~/.praison/train JSON directory. Sessions written by praisonai train agents --storage-backend sqlite ... (or Redis, or a custom file dir) are only visible when the same backend/path pair is passed to list.
Each row shows the session ID, iteration count, size, and last-modified time.

praisonai train show

Show details of a training session, including its iterations and best score.

Options

Examples

The summary highlights the best iteration (★) with its score and feedback.

praisonai train apply

Apply learned suggestions from a session to an agent via hooks. Uses the best-scoring iteration by default.

Options

Examples

The backend flags must match those used at training time. Applying with --storage-backend file (default) to a session written with --storage-backend sqlite ... will error with Session not found: <id>.

Standalone script

Install praisonai-train on its own to get a praisonai-train console script that exposes the same subcommands directly.
Every praisonai train <sub> example above runs as-is with the praisonai-train <sub> prefix.

See Also