RaifuWars Warrior LoRA v2 β€” Qwen3-VL-4B

Plays a seat in Raifu Wars through the Warrior protocol, trained on RaifuWars-Warrior-SFT-v2 β€” 7 maps, 280 matches, decisions by the game's built-in AI paired with the board and the complete legal action list.

It now takes the action that wins the game. The v1 model would not.

The fix

tier up is the only win condition β€” reach tier 4 and the match ends. The experimental dataset contained 11 examples of it. A model trained on that corpus matched the teacher on rush, reload, attack and cards, then declined tier-up 137 times out of 145 offered: a recognisable game, played by something that would not climb the ladder that ends it.

v2 contains 3,069 tier-up examples, and every one of its 280 matches contains its decisive tier-up.

action n v1 model v2 model
tier up 57 declined 137/145 100%
end turn 333 99.5% 100%
tier choice 54 100% 100%
rush 194 45.5% 96.4%
attack 132 98.9% 96.2%
reload 41 92.3% 92.7%
card 122 93.6% 89.3%
move 559 77.1% 80.9%
select tile (pick) 8 40% 25.0%
overall agreement 1500 85.1% 90.6%
legal action emitted 1500 100% 100%

1,500 held-out decisions, greedy decoding, from matches the model never saw (the split is by match, not by row). Against the dataset's published prompting-only baseline of 36.3%.

select tile is the one regression and the one weak spot: 2 of 8. It is 0.3% of the training data (262 rows in the full corpus, ~79 in the sample used here), and eight decisions cannot separate a real weakness from noise. Treat it as unmeasured rather than as 25%.

Read tier up and rush as the result, not the overall number. Move only improved 77 β†’ 82%; it was never broken. The gain is concentrated in exactly the two actions v1 refused to take, which is the signature of a data fix rather than a training one.

A note on how v1 was mis-measured

The v1 evaluation bucketed actions by name prefix, so tier (tier-up, the win condition) was averaged together with tier_stars and tier_kills (tier choice, which track to climb). It reported 32/32 = 100% while the model was declining the winning move 137 times out of 145.

The metric was not wrong arithmetic β€” it was a bucketing that could not see the failure. v2's data separates the three action ids, and eval_actions.py in this repo reports tier-up on its own line, labelled as the only win condition. If you build your own eval, do the same.

Honest limits

  • Agreement is not win rate. This is imitation of the built-in "classic" AI, which beats a random-legal control 83–17 but is a hand-written heuristic, not a solved policy. 90.6% means "plays like it 90.6% of the time". The dataset card notes a prompting baseline that reached 40.1% agreement and won zero of 40 matches β€” which is exactly why agreement should not be trusted as a proxy. Run it in a real match.
  • Per-action samples vary. tier-up is n=57 and reload n=41, which are meaningful; pick is n=8, which is not.
  • last_action is absent. At inference the sidecar appends a line describing how the previous action resolved mid-turn; traces do not record it, so the model never saw it in training. A known train/serve mismatch, inherited from the data.

Use

llama.cpp, adapter GGUF included:

llama-server -m Qwen3-VL-4B-abliterated-f16.gguf \
             --lora-scaled raifu-v2-lora-f16.gguf:1.0 \
             -ngl 99 -c 32768 --jinja -fa on --port 8090 --host 0.0.0.0

A pre-merged, quantised model is at RaifuWars-Warrior-Qwen3VL-4B-v2 if you would rather not carry the adapter.

Post the Warrior payload unchanged β€” system prompt, board, and the per-turn tools schema whose action_id enum is that turn's legal set:

requests.post(".../v1/chat/completions", json={
    "messages": msgs, "tools": row["tools"], "temperature": 0, "max_tokens": 128})
# -> {"tool_calls": [{"function": {"name": "take_action",
#                                  "arguments": "{\"action_id\": \"tier\"}"}}]}

With peft, load through AutoModelForImageTextToText β€” this is a Qwen3VLForConditionalGeneration, not a plain causal LM.

Training

base huihui-ai/Huihui-Qwen3-VL-4B-Instruct-abliterated (4.44B)
method SFT, LoRA r=32 Ξ±=64 dropout 0.05
data 30,000 rows sampled from 69,502 train (87M tokens)
schedule 1 epoch, 3,750 steps, lr 1e-4 cosine, warmup 0.05
batch 1 Γ— grad-accum 8, max_length 8192
hardware one RTX A6000, 15h53m
trainer grimoire

Train loss 0.96 β†’ 0.025; held-out eval 0.974 β†’ 0.0095, below train loss at every checkpoint β€” no memorisation despite 7 maps.

The 30,000 rows are a uniform random sample of the full train split, preserving the action mix (37.4% move, 3.9% tier-up) and all seven maps proportionally. A full epoch is 238M tokens β‰ˆ 38h; this was the overnight version.

Two build details that matter:

The target text is derived, not written. The conversation is rendered twice through the chat template β€” once without the assistant turn, once with β€” and the difference is the training target. Whatever the template emits for a tool call is what the model learns to emit. (content: null on the assistant turn must become "" first, or the template raises TypeError.)

Over-length rows are dropped, never truncated. The trainer truncates long prompts from the right, which would remove the legal-action list the answer must be copied from β€” training the model to name an action_id it was never shown. 176 of 69,678 rows exceed 8192 tokens and are dropped; a 4096 cap would have dropped 18,285.

Vision is untouched. The seven targeted projections match 252 language modules and zero vision modules, so image understanding is bit-identical to the base.

Downloads last month
24
GGUF
Model size
66.1M params
Architecture
qwen3vl
Hardware compatibility
Log In to add your hardware

16-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for yotisstudios/RaifuWars-Warrior-Qwen3VL-4B-v2-LoRA

Dataset used to train yotisstudios/RaifuWars-Warrior-Qwen3VL-4B-v2-LoRA