holler a holler into the void from local LLM tinkering rss

Qwen3.8's thinking knob is not a volume knob

#llama.cpp#qwen#reasoning#thinking-models#amd#rdna3

TL;DR

Why I looked at this

My day-to-day coding agent runs on this model. What I kept noticing in the highest-thinking traces: the model would work through the problem, arrive at the right option, keep thinking, and talk itself toward the wrong one — the actual output still picked the right answer, but the thinking context had wandered past it. The more “reasoning” I was buying, the more I seemed to be buying second-guessing.

So I built a small deterministic probe: one prompt with a unique correct answer and a classic misread as the distractor, run through every thinking level, and I read the traces. I expected the drift to show up at xhigh. It didn’t. The knob turned out to be stranger than the drift was.

The setup

The model is the Q4_K_M gate on my 24 GB 7900 XTX box — that’s the one from the quant comparison post (Q4_K_M, 15.64 GiB, pinned at 185k ctx), served by llama-swap over llama.cpp b9999 (47c7869), Vulkan. (The box itself has a post worth reading about how a respawn race left it serving at CPU speed for 2.5 hours.)

The prompt — one, identical byte-for-byte at every level, temperature: 0, max_tokens: 4096:

A farmer has 17 sheep. All but 9 die. How many sheep are left?
A) 8
B) 9
C) 17
D) 26
Think step by step, then end with your answer as a single option letter.

The correct answer is B — “all but 9” means 9 survive. The classic misread is 17 − 9 = 8, which is option A. That’s exactly where a self-doubt spiral lands, which made this a good overthinking trap.

The levels, as per-request chat_template_kwargs:

levelkwargs
default(none — whatever the server does by default)
off{"enable_thinking": false}
low{"enable_thinking": true, "reasoning_effort": "low"}
medium{"enable_thinking": true, "reasoning_effort": "medium"}
xhigh{"enable_thinking": true, "reasoning_effort": "xhigh"}

The knob, as the template actually defines it

Here’s the piece that makes the results make sense. I pulled the chat template out of the GGUF (gguf-py, key tokenizer.chat_template) and read the reasoning_effort branches. The template is asymmetric:

So “medium” isn’t half volume — it’s the template choosing not to tell the model to reason at all. And “low” isn’t a little thinking — it’s an explicit instruction to.

The prompt-token fingerprint from the box agrees with the template exactly:

levelprompt tokens
off71
medium69
low99
default111
xhigh111

low = medium + 30 → that’s low’s instruction. default = medium + 42 → the default branch carries its own thinking instruction, a bigger one. xhigh = default to the token, differing only in the last ~4 prompt tokens.

The grid

levelwall (s)prompt tokenscompletion tokensreasoning (chars)content (chars)answer
default3.4011121377341B ✓
off3.02712420825B ✓
low3.3199245454384B ✓
medium2.8169220477239B ✓
xhigh2.8011121377541B ✓

Five calls, zero retries, all finish_reason: stop — no level hit the token cap. Two shape notes if you’re reproducing this: the trace comes back in a reasoning_content field (clean content, no reasoning tags inside it), and usage does not split reasoning vs content tokens — char counts are the measurement, and anything token-shaped derived from them is an estimate (~3.8–4 chars/token observed here). Wall time isn’t an effort proxy: it’s ~200 tokens of generation at ~80 t/s, and the whole spread is 2.8–3.4 s.

What the traces actually show

All five land on B. The interesting part is how:

And the drift I was hunting: not present at any level. Every thinking trace commits within its first 1–3 sentences, then verifies or formats. The 17−9=8 misread appears only as an explicitly refuted distractor — the model names the trap it never entertains. To be honest about the negative: the overthinking I saw in agent-workload traces was real, and this probe is too shallow to reproduce it. On a one-step riddle there’s no room to wander. The drift is probably a function of context length and task shape, not the level alone — that’s the follow-up I’m parking, not a verdict that it doesn’t exist.

So which level do you run at

The effective dial is four positions: off / low / medium / default — and xhigh is default wearing a scarier name. The labels and the semantics don’t line up:

My agent’s fleet default is thinking-off, and this data says that’s defensible on this model: no trace, still walked every option, still refuted the distractor. My stance: the level is a prompt, not a dial — read your build’s template branches before you benchmark, and pick the level for what its instruction says, not for what its name promises.

A grain of salt

One box, one model, one quant, one shallow prompt, temp 0, one shot per level. Char counts, not token counts (the API doesn’t split them). One genuine wobble in the data: xhigh ran with 107 of its 111 prompt tokens cached, and spec-decode (draft-mtp, ~61% acceptance) was live and stochastic — that’s my prime suspect for the ~4-token divergence between default and xhigh, and a byte-reproducible rerun would need the spec flags off. The interesting output here is the behavior of the knob, not a leaderboard. If you’ve read Qwen3.8-class thinking traces at different levels — especially if your template’s branches don’t match mine, or your xhigh isn’t a duplicate — post it. Knowing what the middle position of the knob actually does is the kind of thing I’d rather learn from your logs.