← writing

Qwen3.8-27B reasoning levels are prompt driven, not enforced

August 20, 2026 · 11 min read

Qwen3.8-27B is one of the best local models you can run on consumer hardware. It comes with four reasoning levels: off, low, medium, and xhigh. However, these levels are not what you might think. They are not enforced limits. They are prompt driven, and that can confuse you, because in some situations a low reasoning effort generates more tokens than xhigh. In this post I want to show you exactly what these levels do, the experiments I ran to measure them, and how I recommend setting them.

Quick background on my setup: I serve this model with vLLM on a two-node DGX Spark cluster, and everything below is measured on that stack. All the runs are single turn, and I count reasoning and answer tokens separately with the server’s own tokenizer.

What the four levels actually do

When thinking is on, the model generates in two phases: a private reasoning trace inside <think> tags, then the visible answer. Your client shows the answer and discards the trace, but you pay for every token of the trace in time and compute. The reasoning levels are supposed to control how long that trace gets.

Here is how they actually work. The chat template, the Jinja file that assembles your messages into the final prompt, handles reasoning_effort in one short block. You can read it yourself in the model’s chat_template.jinja on Hugging Face. The structure is a plain if/elif:

if reasoning_effort == 'xhigh':
    reasoning_instructions = <the xhigh sentence>
elif reasoning_effort == 'low':
    reasoning_instructions = <the low sentence>

For xhigh, the injected sentence is, verbatim:

Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.

For low:

Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.

That string gets prepended to your system message, and that is the entire mechanism. Setting xhigh asks the model to think carefully. Setting low asks it to keep things brief. There is no token budget and no decoding constraint.

Notice there is no medium branch. If you pick medium, the template injects nothing. Medium is the model’s natural behavior with thinking enabled and no instruction at all. So the four levels reduce to: thinking disabled, a sentence asking for less, no instruction, and a sentence asking for more. Only the off setting is structural, because the template closes the think block before the model can write into it.

How other models set their thinking budget

For context, it helps to know what the rest of the industry does, because Qwen’s approach is the weakest of four mechanisms in use.

The first mechanism is a hard numeric budget. Google’s Gemini 2.5 models take a thinkingBudget parameter, an integer between 0 and 24,576 that sets an upper limit on thinking tokens, and 0 disables thinking. Anthropic’s earlier Claude models worked the same way with budget_tokens, an enforced ceiling on the thinking block.

The second mechanism is trained effort levels, and this is where the industry is converging. OpenAI’s reasoning_effort uses the same words as Qwen, low, medium, high, but it is not a prompt sentence. It is an API parameter that conditions how many reasoning tokens the model generates, and that behavior is trained into the model. Google moved the same direction: Gemini 3 replaced the numeric budget with a discrete thinking_level. Anthropic did too: current Claude models combine effort levels with adaptive thinking, where the model decides on its own how much to think, and the old hard budget is deprecated. Both companies retired their enforced numbers, and my data suggests why: the right budget depends heavily on the task, so a fixed number is the wrong abstraction.

The third mechanism is server-injected awareness, and Anthropic’s task budgets are the interesting middle ground. You declare a token budget for a task and the server injects a live countdown that the model sees while it generates, so it can pace itself. It is still advisory, but the model knows how much budget remains. That is exactly the information Qwen’s sentence does not carry.

The fourth mechanism is the prompt sentence you saw above. There is also a fifth option that no API exposes but local serving makes possible: because the trace lives inside think tags, the inference server can force-close the tag when a token counter hits your limit. That is a true cap that needs no cooperation from the model, and nothing stops you from adding it to vLLM.

A sentence can still steer a model. The question is how well, and that is measurable. I ran three experiments to find out.

Experiment 1: the levels against seven coding tasks

First I wanted to see how the levels behave on real work. I wrote seven coding prompts of increasing complexity, from a Pomodoro timer widget up to a full analytics dashboard, and ran each one at all four levels. That is 28 runs, with a 96K token limit so nothing truncates.

Grouped bar chart of thinking tokens for seven coding tasks at the low, medium, and xhigh presets. Bars do not increase with task complexity, and several low bars tower over their xhigh neighbors
Thinking tokens across seven tasks of increasing complexity. The bars do not step up with difficulty, and the low bars are not consistently below the xhigh bars.

The averages move in the right direction: 22K thinking tokens at low, 28K at medium, 43K at xhigh. But look at the individual tasks and the picture falls apart:

  • On the typing benchmark, low generated 49,179 thinking tokens and xhigh generated 38,413. The low run out-thought the xhigh run.
  • On the particle sandbox, medium and xhigh were the same: 65K against 63K.
  • I re-ran one low cell with identical settings and got 24,606 tokens the first time and 5,456 the second. That is a 4.5x difference on the same input.
  • Task difficulty predicted nothing. The correlation between complexity and thinking length was flat to negative at every level, and the hardest prompt drew the smallest xhigh budget of the whole set.

This is what prompt-driven control looks like. The sentence shifts the average, but when the model reads a task that feels hard, the request to be brief loses. A low run that thinks for 49K tokens is not a bug. It is the model weighing a suggestion against its own judgment.

One task in this set behaved differently, and it turned out to matter. The one prompt that was not a web app, a Blender Python script, barely thought at all on low and medium, around 500 tokens, then jumped 80x at xhigh. That pattern is what the second experiment is about.

Experiment 2: task type against task difficulty

The Blender result suggested that the type of task matters more than the difficulty. To test that properly I built a paired benchmark: four complexity tiers, and at each tier one web app task and one Python script task of matched difficulty. Tier 1 is a lava lamp animation and a spirograph poster. Tier 4 is a from-scratch 3D planet renderer and a numpy ray tracer. Eight prompts, four levels each, 32 runs.

Two line charts sharing a y axis. Left panel, web apps: thinking token lines start high and wander with no relationship to tier. Right panel, Python scripts: three green lines rise monotonically from tier one to tier four
The same complexity ladder, two task types. Python thinking rises with difficulty at every level. Web thinking starts high and stays high.

The result is about as clean as experiments get:

  • On Python scripts, thinking scales with difficulty perfectly. The Spearman correlation is 1.00 at low, at medium, and at xhigh, three independent times. At low, the progression reads 2,152 tokens, 6,001, 19,888, 38,027, tier by tier.
  • On web apps, the correlation drops to 0.20. The tier-1 lava lamp drew 39,586 thinking tokens at low. The much harder tier-3 flocking simulation drew 1,033 at the same setting.

My read on why: a Python script is one file with one job, so the model can size the work up front. A web app prompt bundles layout, styling, state, and interaction into one request, and the model treats all of it as open questions. Whatever the cause, the practical rule is simple. Web app prompts are expensive at every level, and Python script prompts cost roughly what they look like they should cost.

Experiment 3: writing the budget yourself

The levels are just sentences, and sentences are text you control. So the third experiment replaces them with explicit numbers. I took the same eight tasks and ran three instructions in the system message: thinking must not exceed 2,000 tokens, thinking must not exceed 8,000 tokens, and thinking must be at least 24,000 tokens. All three ran on top of the low preset, so the only variable is the stated number. That is 24 more runs.

Dot plot with four columns: no budget, a 2,000-token cap, an 8,000-token cap, and a 24,000-token floor. Dashed green lines mark each stated budget. Dots sit far above the 2,000 line, closer to the 8,000 line, and cluster at or above the 24,000 line
Actual thinking against three stated budgets, eight tasks each. The tight cap is ignored, the loose cap attracts, and the floor mostly binds.

The pattern across all 24 runs: a stated number acts like a magnet, not a wall.

  • The 2,000-token cap failed. The median run overshot it 10x, and the worst case was 16.8x.
  • The 8,000-token cap pulled thinking toward 8K from both directions. One task complied at 0.86x the budget. Another task that naturally used 2K tokens got pulled up 4.4x just because the prompt mentioned a bigger number.
  • The 24,000-token floor worked best. Six of eight tasks met it, and one task whose natural appetite was 1,033 tokens rose to 52,906 on request.

The insight here is the asymmetry. You can reliably talk this model into thinking more. You cannot talk it into thinking less when the task pulls the other way. If you need cheap, the only setting that binds is turning thinking off.

There is one more result that held across all 84 runs, and it changes how you should think about cost. Answer length never moved. The mean answer stayed between 8.5K and 11K tokens at every level and under every budget instruction. The dial only changes the size of the trace you throw away. At xhigh, roughly three out of every four tokens you generate are discarded reasoning.

Horizontal stacked bars for the four presets. Green answer segments stay near 9 to 11K tokens on every row while gray thinking segments grow from zero at off to 47K at xhigh
Mean cost per task across 60 preset runs. The answer, in green, is the same size on every row. The level only resizes the gray thinking segment.

Two settings that matter in multi-turn use

Everything above is single turn. If you run this model in an agent loop, one more template setting comes into play: preserve_thinking, which is on by default.

With preserve_thinking on, the template re-embeds the full think block of every previous assistant turn into the next prompt. Each turn’s context grows by that turn’s complete reasoning trace. At xhigh, where a turn generates 30 to 60K thinking tokens, a 262K context window fills in about four to five turns.

The natural fix is to turn it off, but there is a trade. With the flag on, each turn’s prompt is an exact prefix extension of the previous one, so the server’s prefix cache hits and prefill stays cheap. With the flag off, the template re-renders history without the thinking that was originally there, the cached KV diverges at every turn boundary, and every turn pays full re-prefill. You save context or you save prefill, not both. The practical answer is to keep per-turn thinking small in agent loops, off or low, so the default stays affordable on both axes.

How I set it up now

Based on these 84 runs, here is my configuration:

  • Interactive chat runs with thinking off or low. The answer is the same size either way, and the latency difference is the entire user experience.
  • Hard problems get an explicit floor in the system message, “reason for at least N tokens.” That binds better than the xhigh sentence.
  • I never rely on a cap. A cap below the model’s natural appetite gets ignored. When I need cheap, I turn thinking off.
  • Agent loops keep thinking off or low so preserve_thinking can stay on and the prefix cache keeps working.
  • Any time a web app prompt goes in, I expect heavy thinking no matter what the level says.

The general lesson applies beyond this one model. When a provider gives you a knob, check whether it is enforced in code or requested in prose. Code fails loudly at the boundary. Prose fails quietly in the middle, and you only find out by measuring. That is what the reasoning levels on Qwen3.8-27B are: a request, not a rule. Set them accordingly.

Sources