Parry Tactician 1.5B — LoRA adapter
This is the LoRA adapter (r=32 α=64); the ready-to-use merged checkpoint with all eval reports is at parry-tactician-1.5b-merged.
The opponent brain of Parry
(HF × Gradio Build Small Hackathon): a real-time 1v1 footsies duel where a 1.5B
model runs in the player's browser, emitting one grammar-constrained intent
letter (S F P L R W) every ~160ms, conditioned on a natural-language
plan-string written by its own Analyst pass — the plan it shows you on screen
is the plan that steers it, and you can edit that plan live in the judge panel
and watch its behavior flip.
This checkpoint runs inside the submitted app: converted to
GGUF Q4_K_M, it
executes in-browser through llama.cpp's WASM runtime (wllama) as the Analyst —
reading your behavior log and writing the plans. The judge-override demo
additionally serves it through a classifier-free-guidance gateway (vLLM, two
parallel calls, uncond + γ·(cond − uncond) over the action letters).
Training
Behavior cloning (LoRA r=32 α=64, completion-only loss) on plan-conditioned duel logs from a deterministic integer engine: on-trajectory expert play + counterfactual same-state×6-plans expansion (the conditioning signal) + Analyst-role rows. This final generation adds 20% plan-dropout so the model learns an unconditional branch — enabling CFG at inference. Trained on Modal (A100); no added tokens, so weights-only conversion reaches MLC/WebLLM and GGUF.
The six-generation arc (honest numbers)
Pre-registered gate before any training: plan-intervention ΔP ≥ 0.30 (mean pairwise total-variation across held-out states × held-out plan paraphrases), ≥2× base, direction-correctness ≥0.70.
| gen | examples | held-out agreement | ΔP (γ=1) | what we learned |
|---|---|---|---|---|
| pilot | 20k | 0.795 | — | wiring works |
| bc_v1 | 98k | 0.771 | 0.071 | plan-DEAF (base: 0.129) — great state player, ignores the plan |
| bc_v2 | 153k | 0.695 | 0.159 | counterfactual data helps; no-plan ablation 0.643 proves the plan now carries signal |
| bc_v3 | 188k | 0.735 | 0.224 | disagreement-upweighting — best pure-BC steering |
| bc_v4 | 129k | 0.739 | 0.078 | engine-v4 retrain silently lost the steering — regressions are real |
| bc_v5 | 173k | 0.724 | 0.123 | plan-flavored expert + tiered contrast weights |
| bc_v6 | 173k | 0.727 | 0.112 → 0.292 @ γ=4 | plan-dropout + classifier-free guidance |
The pre-registered ΔP≥0.30 gate never passed at γ=1 — and the eval explains why: the expert itself is plan-invariant in 40% of states, putting the identifiability ceiling at ΔP 0.330. No objective can recover steering the teacher doesn't exhibit.
Classifier-free guidance closes the gap
Two parallel decodes (with-plan / plan-stripped), letter-logprobs combined as
uncond + γ·(cond − uncond):
| γ | ΔP | argmax-flip (disagreeing states) | rush→turtle P(STRIKE) |
|---|---|---|---|
| 1.0 | 0.112 | 0.179 | 0.30→0.07 |
| 1.5 | 0.150 | 0.215 | 0.29→0.05 |
| 2.0 | 0.187 | 0.260 | 0.29→0.03 |
| 3.0 | 0.249 | 0.326 | 0.28→0.03 |
| 4.0 | 0.292 | 0.385 | 0.28→0.03 |
γ=4 reaches 88% of the expert ceiling (0.292 / 0.330). In the app, guidance stays at γ=1 during normal play (higher γ trades fight-IQ for obedience) and jumps to γ=3 when a judge edits the plan — obedience is exactly what that demo measures.
Raw reports for every generation — metrics, intervention tests, the CFG sweep —
are in evals/ in the merged repo (18 JSON files, nothing curated away).
Reproduce
npx tsx training/datagen/run.ts --duels 600 --heldout 120
python3 -m modal volume put parry-data training/out duels_v5
python3 -m modal run modal/pipeline.py --run-name bc_v6 --plan-dropout 0.2
python3 -m modal run modal/cfg_sweep.py --run-name bc_v6
Engine/prompt/grammar versions: see evals/*/metrics.json. Full build story:
Field Notes.
🧭 All Parry artifacts
Play: the Space · Models: merged / LoRA / GGUF · llama.cpp · Data: agent traces / field notes