
昨天把架構攤開:誰用 KDA 換掉 KV、誰用 sliding window 把快取壓成常數。那些是天生體質。今天動的是另一半——同一副體質,「回答前要不要先想」是後天練出來的行為。架構沒換,行為卻可以換檔;有些甚至只差一個 boolean。
但這一刀不是「thinking 都該關」,是簡單題別白想。值不值得,我用 60 題量給你看。
Day 01 那 45 秒,用示範機的帳本拆開。那台是單張 RTX 4090 24GB,既不是 T1 也不是 T2,TPOT 取它的設定值(溢位 25 ms、健康 5 ms),全部是【推算】:
檢索 0.50 s + prefill 7.50 s + decode 37.00 s = 45.00 s
decode 37.00 s = 1,480 tokens × 25 ms
└ thinking 1,100 + 真正的回答 380
1,480 個 output tokens 裡,有 1,100 個是你永遠看不到的自言自語。關掉:decode 37.00 → 9.50,一刀省 27.50 秒,E2E 45.00 → 17.50。Day 01 說「隔天 5 秒」的另一半,是第二刀換量化檔修 CPU 溢位(再省 12.60 秒 → 4.90 秒);相對 Day 01 最後 45 → 2 秒的總改善,前兩刀就拿走九成以上,一毛硬體錢都沒花。

圖 1:關掉省下的不是「一部分」,是整條紅色——那 74% 的 output token 使用者一個字都看不到【推算,4090 案例】。
背後只有一條乘法:thinking 多出來的 decode 時間 ≈ thinking_tokens × 平均 TPOT。兩個因子都能改,所以順序會偷走功勞:先修溢位讓 TPOT 掉回 5 ms,decode 只剩 7.40 秒,這時再關 thinking 就只省 5.50 秒。別爭哪刀大,兩刀都做。
而 thinking 長度很不受控。我在 T1 上量到中位數 463 tokens、p95 1,164、最長一發 5,995——那一發跟同一題另外兩次的 306、408 用的是同一組參數、三個相鄰 seed,長度卻差 20 倍【實測】。拿最長那發乘:健康的 5 ms 要 30 秒,溢位的 25 ms 要兩分半。
Day 13 我量過這個現象——三顆模型開關 thinking,端到端差 3.4 到 14.3 倍,decode 速度幾乎沒變。但那一輪只量了快多少,沒量答案有沒有變差。少生的那些 token 到底有沒有在做事?
我跑了 60 題:兩個題組各 30 題、每題三次取中位數。抽取/事實題用標準答案字串比對,多步推理題只看最終答案對錯、不給部分分(程式判,不用 LLM-as-judge——它自己也會被 thinking 影響)。三欄取自同一批 run。
| 題組 | 輸出 tokens(含 thinking) | wall-clock(p50 / p95) | 正確率(30 題 × 3 runs) |
|---|---|---|---|
| 抽取/事實題 × ON | 386 | 8.09s / 23.62s | 100.0% |
| 抽取/事實題 × OFF | 4 | 0.17s / 0.50s | 100.0% |
| 多步推理題 × ON | 513 | 10.01s / 23.24s | 100.0% |
| 多步推理題 × OFF | 3 | 0.10s / 0.32s | 53.3% |
【實測,T1 M4 Max 128GB・qwen3.6:latest=Qwen3.6-35B-A3B Q4_K_M・Ollama 0.32.15・GGUF/llama.cpp engine・投機解碼確認為關(server log draft: 0.000 MiB)・num_ctx 8192・ON 與 OFF 的 sampling 與 seed 完全相同。正確率是 90 個 run 的平均。兩組 prompt 都帶格式指令:推理題「只回答最後的數字」、抽取題「只回答答案本身」】

圖 2:同樣開 thinking,抽取題多生幾百個 token 換到 0 pp,推理題卻從 53.3% 拉回 100%。關鍵不是「想多久」,是這題需不需要中間步驟。
倍數是逐題配對後取中位數:抽取題 wall 58×、tokens 98×;推理題 wall 108×、tokens 176×。
前兩欄一定好看,第三欄才是這張表存在的理由:**關 thinking 不是免費的。**抽取題那一列量到的是 0——逐題配對後中位數多生 376 個 token,正確率一個百分點都沒動。推理題那一列是 46.7 個百分點。
錯法也有形狀:30 題裡 15 題三次全對、13 題三次全錯,只有 2 題會隨 run 翻轉。它不是整體變得比較不準,是遇到需要中間步驟的題直接崩掉。
兩個誠實的但書。那 46.7 pp 帶著一個會放大差距的實驗條件:我要求「只回答最後的數字」,等於堵死了關掉之後把算式寫在答案裡的退路。90 次 OFF 有 89 次只吐 2 到 6 個 token,唯一一次違規把算式寫出來的就答對了。允許模型算給你看,這個缺口會變成多少,我沒量。
另一個是 wall-clock 那欄帶著跑序效應:連跑 40 分鐘 decode 掉 36.8%,而抽取題 ON 跑在最快那段,所以倍數只會被低估。load_duration 全程中位 4.9 ms——這些秒數裡沒有冷啟動,98% 是真的在生 token。
坊間那句「關 thinking = 2–5x 加速」本系列不用:它沒綁任何條件。你省下的 decode 時間 ≈ thinking_tokens × 平均 TPOT;真正的加速倍率是 E2E_ON ÷ E2E_OFF,那要另外量。

圖 3:判準只問一句話。左邊兩格各有一個實測數字撐著;右邊那格是流量還沒分類時的預設動作。
所以判準只有一條:這個任務的答案品質,會因為「多想一輪」而變好嗎?
而且要收窄一格:RAG 本身不是「關 thinking」的理由。我量到 0 pp 的那 30 題是單跳、答案就是段落裡一個字串的 grounded extraction。同樣走 RAG,若變成「比對三份規格、排出操作順序、處理版本衝突」,那已經是多跳推理,只是證據來自檢索——該路由的是題目的形狀,不是資料的來源。
<think> 不是架構元件,是訓練資料與 prompt 格式共同定義的行為介面。這類切換通常落在 prompt/chat-template 層,不必換架構也不必換 checkpoint;但切完還能不能答好,是 post-training 與權重決定的。
最直白的證據是 GLM-4.7-Flash 的 chat_template.jinja:enable_thinking=false 時,它直接在生成起點插一個 </think>【官方】——生成位置被放到思考區之後,模型接著續寫的就是 final-answer 那一段。它以為自己已經想完了。
| 模型 | thinking 預設 | 關/調法【官方,2026-08 查證】 |
|---|---|---|
| Qwen3.8-27B | 開 | 同下;⚠️ 走 Ollama 的 MLX runner 時投機解碼是自動開的(T1 實測 acceptance=0.65),量 decode 前先確認 |
| Qwen3.6(27B/35B-A3B) | 開 | chat_template_kwargs: {"enable_thinking": false};不吃 soft switch |
| GLM-4.7-Flash | 開 | 同上(template 插 </think> 封死) |
| gpt-oss-20b/120b | 恆推理 | 關不掉,system prompt Reasoning: low/medium/high |
| Gemma 4 | 關 | 要開:system prompt 放 `< |
| DeepSeek-V4-Flash | 開(另有 Non-Think 檔) | reasoning_effort: low/high/max(T3 級參照) |
**Qwen 三個世代,同一個「關思考」動作是三種答案。**Qwen3 有 soft switch;2507 世代拆成 Instruct 與 Thinking 兩顆;Qwen3.6 回到 hybrid,但 model card 白紙黑字【官方】:
Qwen3.6 does not officially support the soft switch of Qwen3, i.e.,
/thinkand/nothink.
所以教你打 soft switch 關 Qwen3.6 的文章,是照抄舊教學。那句話打進去只是普通文字:思考照跑,而且不會有任何報錯。改用 enable_thinking=false。
不 pin 版本的下場不是實驗失敗,是實驗靜默變質。ollama pull 拉到新 tag,你以為在量同一顆模型的 on/off,實際在量兩顆不同 checkpoint。
# ── Ollama 線(前面那張三欄表就是用這條跑出來的)──
ollama --version # ollama version is 0.32.15
ollama list | grep qwen3.6 # 這行的 ID 是 config digest 前 12 碼
# 完整 digest 在 ~/.ollama/models/manifests/.../qwen3.6/latest,連權重層一起記
# ON(預設)與 OFF 只差一個欄位:
curl -s localhost:11434/api/chat -d '{"model":"qwen3.6","stream":false,
"think":false,"messages":[{"role":"user","content":"<同一題>"}]}'
# ── vLLM 線(T3 級參照;T2 的 12GB 放不下 35B-A3B 的 22.13 GB,見 Day 09/14)──
vllm serve Qwen/Qwen3.6-35B-A3B --revision <commit> --reasoning-parser qwen3
# extra_body={"chat_template_kwargs": {"enable_thinking": False}}
sampling 也要釘,而且有個岔路:兩邊各用官方建議值,量到的是「兩種官方姿勢」的差距;兩邊用同一組,量到的才是那個 boolean。前面那張表選了後者。選哪一種都行,但一定要在文中明寫。
全關在我的題組掉了 46.7 pp。那中間檔呢?我沒量。
第三方的 ThinkingCap 給了一個不同方法的參照:它讓 Qwen3.6-27B 的平均 thinking 從 10,777 降到 3,351 tokens,GPQA-Diamond 只掉 1.6 個百分點【引用,廠商自家評測】。⚠️ 但那是微調出來的一顆模型,不是把 budget 設成 3,351,模型與題組也都跟我不同——不能當成「限量」的等價實驗。它能說明的只有一件事:「少想」和「完全不想」之間,可能有值得利用的區間。
地端三個 stack:Ollama 沒有 budget 參數。llama.cpp server 的 --reasoning-budget 真的能限量——-1 不限、0 直接關、N>0 就是 token 預算【官方,--help build 10360 實跑】。vLLM 的 thinking_token_budget 有,但雷不少:V2 model runner 直接拒收(not yet supported by the V2 model runner),要你退回 V1,而對 hybrid/mamba 架構那個退路等於沒有【官方 issue #50473,仍開著】。
所以順序是:先 on/off 加按任務路由;要限量,先核對你的 runtime 與版本。別相信參數送進去就算成功——上線前直接數 completion_tokens。
一台跑得動 Ollama 的機器就能跟。上面那張表跑在 T1(M4 Max 128GB)、22.29 GiB 的 Qwen3.6-35B-A3B;機器小就換一顆小的 hybrid thinking 模型,協定一樣。
開跑前記三件事:engine 版本、model digest、sampling。還有一件容易漏的——確認投機解碼是關的。我這條線的 server log 每次都印 draft: 0.000 MiB;換成 MLX runner 就不一定了。
今天砍的是 decode 端「多生的 token」——但那本 45 秒的帳裡,decode 佔 82%,prefill 只有 17%。明天 Day 17 要問的是:**這個形狀,換一種 workload 還成立嗎?**同一台機器、同一顆模型,把輸入從 RAG 的幾 K 換成整份文件的幾十 K,瓶頸會自己換邊。
咱們明天見。