『常にオン』も『常にオフ』も最適ではない
2026年のローカルLLM環境では、プロンプトエンジニアリングの「最初の選択」がプロンプト本文よりも前にある。thinking モードを使うか使わないか だ。Qwen3 から Qwen3.5 にかけて、思考プロセスを内部で長く展開する「thinking mode」のトグルが正式な仕様として入った。Anthropic の extended thinking、OpenAI の o-series、DeepSeek-R1 と続いてきた潮流の、ローカル側での実装だと思えばいい。
この toggle、APIで使う分にはあまり気にならない。だが8GBのRTX 4060でQwen3.5-9Bを動かしているような環境では、thinking modeのオン/オフは出力時間とコンテキスト消費を直接2〜10倍動かす。試しに「常にオン」で運用すると、対話アプリは遅くて使い物にならない。「常にオフ」で運用すると、数学やマルチステップ推論で目に見えて精度が落ちる。
この記事の主張はひとつだ。thinking mode は「常にオン」も「常にオフ」も最適ではない。タスク種別と速度予算で 3つの軸を引いて切り替える。 その3軸を、公開ベンチマークと8GB実機の制約から組み立てる。なお実機 tok/s の自前計測値は出さない。公開数値とアーキテクチャの制約から、判断に必要な輪郭は描ける。
thinking modeとは何をやっているのか
Qwen3/Qwen3.5 の thinking mode は、ざっくり言えば「出力の前にモデルが自分で長いCoTを書いて、それを参照してから本答を出す」仕組みだ。
具体的には、応答生成の前に
<think>...</think>のような内部ブロックを生成する。このブロック内でモデルは「問題を分解する → ステップごとに推論する → 矛盾を見つけたら戻る」といった思考過程をテキストとして展開する。最終応答はその後に出る。
プロンプト側からは
/think/
/no_thinkといったタグや、API パラメータ (
thinking_budgetでトークン数の上限を設ける) でモードを制御できる。これは Anthropic の extended thinking と構造的にはよく似ている。
重要なのはコストの構造だ。
- 出力トークン数が thinking ブロックの分だけ膨らむ (タスクによっては 5〜20倍)
- KV キャッシュも比例して伸びる
- ローカル環境では
tokens/sec
がそのまま時間に効く
API 利用では「出力トークン課金」と「待ち時間」のかたちで現れるが、8GB のローカルではここに VRAM 制約 が加わる。長い thinking 出力 = 長い KV キャッシュ = 32K context 上限の手前で頭打ち。前回の catA 記事で議論した KV キャッシュ予算が、ここでまた効いてくる。
ベンチが見せる「効く場面」と「効かない場面」
Qwen 公式が出している thinking-on / thinking-off の差分は、ジャンルで明確に割れる。
| 評価軸 | thinking-on | thinking-off | 差 |
|---|---|---|---|
| AIME 2025 (数学) | 92.3 | 81.5 | +10.8 |
| MMLU-Pro (一般知識) | 82.5 | 高水準で頭打ち | わずか |
| LiveCodeBench (コード) | 大幅向上 | — | 中〜大 |
| 単発の事実応答 | 変化小 | — | ほぼ無し |
表の絶対値は Qwen3-235B-A22B-Thinking-2507 と非 thinking 版の公開ベンチで、フラッグシップ規模の数字だ。
8GB に載る Qwen3.5-9B では絶対スコアは下がる(規模で素直に効く)が、thinking-on / off の差分の向きは同じ方向に出ることが Qwen 公式ブログで確認できる範囲だ。本稿で読みたいのは絶対値ではなく差分の構造のほうである。
ここから読めるのは、thinking mode は「考える時間が報われる問題」でだけ効くということだ。
- 数学やコーディングのような「ステップ間の整合性」が物を言う問題: 大きく効く (AIME で 約11ポイント)
- 一般知識の単発回答 (ファクト検索的なもの): 効かないか、誤差レベル
- 会話のリアクションや要約: コストだけが膨らんで価値が薄い
これは Qwen 固有の癖ではない。OpenAI の o-series、DeepSeek-R1、Claude の extended thinking でも基本同じ形が観察されている。
つまり、プロンプト本文を工夫する前に、「このタスクは thinking が報われる種類か」 を一段先で問うべきだ。
8GBで効いてくるトレードオフ
ここからが本題で、8GB の RTX 4060 で Qwen3.5-9B を動かしているとき、API ユーザーには見えにくいコストが出てくる。
コスト1: 出力時間
Qwen3.5-9B を Q4_K_M で 8GB に載せたとき、4060 級の GPU では概ね 20〜35 tok/sec のオーダーになる、というのが llama.cpp 系コミュニティで報告されている数字だ (環境依存、CPU オフロードがあると半分以下)。thinking ブロックが 1000 トークン以上に伸びると、それだけで 30秒〜1分の待ち時間が乗る。これは対話 UX では致命的だ。
数値で具体化するとこうなる。仮に 25 tok/sec (上記レンジの中央) として、
thinking budget 256 トークン → 約 10 秒 thinking budget 1024 トークン → 約 40 秒 thinking budget 2048 トークン → 約 80 秒
これに本答 (200〜500 トークン程度) の生成時間が乗る。対話で許容できるのは合計で 5〜10 秒前後だから、現実には budget = 256 前後が対話用の上限になる。フル thinking-on (budget 上限なし) を許すのは、非同期バッチか待たせていい用途に限られる。
コスト2: KV キャッシュ
前回触れたとおり、Qwen3.5 は Gated DeltaNet と Gated Attention のハイブリッドで、KV キャッシュは一般的な dense モデルより軽い。それでも thinking 出力が伸びる分はそのまま KV を食う。Q4_K_M モデル本体 ~5.7GB + 32K まで張ったときの KV キャッシュ + 表示・CUDA バッファで、8GB は ギリギリの予算で回している。thinking を常時オンで context を膨らませ続けると、簡単に頭打ちにぶつかる。
コスト3: コンテキスト汚染
複数ターンの会話で thinking ブロックを履歴に残し続けるか、削るか、という運用設計が必要になる。残せば次ターンの入力コンテキストが thinking で埋まり、削れば「さっきなぜそう答えたか」を本人(モデル)が思い出せなくなる。ローカル運用ではここを自前で決める必要がある。
API なら課金で表現される問題が、ローカルでは VRAM・時間・運用複雑度の3軸に分散して降りてくる。それを踏まえた上で、3つの判断軸を立てる。
軸1: タスクは「ステップ間整合性」を要求しているか
最初の判断軸はタスクの性質だ。
- 要求している (thinking on を推奨): 多段の数式、複数ステップのアルゴリズム検討、コードのデバッグ、矛盾の検出、要件からの設計分解。
- 要求していない (off で十分): 単発の事実確認、フォーマット変換、文章の言い換え、定型的なコード補完、要約、翻訳。
判定の目安は「途中で間違ったまま進むと最終答えが崩れるか」だ。Yes なら thinking-on が効く。No なら off で時間とトークンを浮かせる。
具体例で見ると線引きが立ちやすい。たとえば「
fn(x)の挙動を再現する関数を書け、入力は x: array, 出力は ...」というコーディング問題は、入力仕様 → エッジケース → アルゴリズム → 実装、と多段で組まないと正解にたどり着けない。途中の判断を1つでも誤ると後段が崩れる。これは thinking-on が報われる典型だ。一方「Python の list を逆順に並べ替えるワンライナーは?」は単発の事実応答に近く、thinking-on にしてもブロック内で「
[::-1]でも
reversed()でも
.reverse()でも...」と無駄な比較を膨らませて終わる。同じ「コードに関する質問」でも、構造が違う。
過去の記事 (ID:96) で書いた「『ステップで考えて』と書いたら local LLM が逆に悪化した」現象とは別系統で読む必要がある。あの記事は外部から CoT をプロンプトで強制する話だった。thinking mode はモデル内蔵の推論回路を起こす機能で、訓練分布的に「効く問題」と「効かない問題」がはっきりしている。プロンプトでの強制CoT が逆効果になっていたタスクでも、モード切替で素直に on にすれば改善することがある — というのが、両者の使い分けの肝だ。
軸2: 速度予算は対話的か非同期か
第2軸は応答時間の許容度だ。
- 対話 (off 推奨): チャット、コーディング補完、リアルタイム支援。1〜3秒以内の応答が要求される。Qwen3.5-9B の thinking-on は4060環境で簡単に10秒を超えるので、対話 UX 上は使えない場面が多い。
- 非同期/バッチ (on 可): コードレビュー、テスト生成、複雑な問い合わせを「夜の間に回しておく」用途。応答時間を気にしない代わりに精度を取りに行ける。
軸1で「効く問題」だったとしても、軸2で対話文脈なら off に倒す、というのが現実解になる。「数学的だが対話で速く返したい」場合は thinking-off で挑んで、不正解だったら thinking-on で投げ直す、という二段構えが合理的だ。
軸3: 出力長の予算と KV の余力
第3軸は資源の側だ。8GB という制約は thinking を「使えるが安くはない」道具にする。
- 余力あり (on 可): 短いコンテキスト (4K〜8K) でモデルを呼んでいる場合、KV キャッシュには余裕がある。thinking で 1000〜2000トークン使っても 8GB に収まる。
- 余力なし (off 推奨): すでに 16K〜32K のコンテキストでツール呼び出しや長い履歴を持っている場合、thinking ブロックの追加でカード上限を超える。実用上は thinking-off に固定する。
ここで
thinking_budgetパラメータが効く。on/off の二値ではなく、許容トークン数を上限として与えられる。たとえば「最大 512 トークンまでで考えてから答えろ」と指定すれば、8GB でも thinking の恩恵を制御された形で取れる。
実運用での budget の目安としては、4060/8GB の制約下では次の3段階くらいに固めると扱いやすい。
- 対話モード: budget 0 (= thinking-off)、もしくは 128 まで。即答性優先。
- ハイブリッドモード: budget 256〜512。対話ぎりぎりの範囲で thinking を許す。デバッグや短い設計判断に向く。
- バッチモード: budget 上限なし or 4096+。非同期で精度を取りに行く。
このプリセットを3つ用意してアプリ側で切り替えるだけで、軸1〜3の判断をUIに表出させることなく、利用者は「速度を上げる/精度を上げる」のスライダーとして触れるようになる。
3つの軸を合わせて1枚の表に落とすとこうなる。
| タスク種別 | 速度予算 | 出力余力 | 推奨設定 |
|---|---|---|---|
| 数学/論理パズル | 非同期 | 余力あり | thinking-on (フル) |
| デバッグ・複雑コード | 対話 | 余力中 | thinking-on (budget 512〜1024) |
| 一般チャット | 対話 | 任意 | thinking-off |
| 長文要約 | バッチ | 余力薄 | thinking-off (出力枠を空ける) |
| エージェントの一手判断 | 対話 | 余力薄 | thinking-off (or 短 budget) |
| 単発ファクト確認 | 対話 | 任意 | thinking-off |
プロンプト側の制御 — タグと budget
llama.cpp 系の Qwen3.5-9B GGUF で thinking mode を制御するには、入力プロンプトに
/thinkまたは
/no_thinkを含める方法が公式に案内されている。アプリ側で前置プロンプトに固定で
/no_thinkを入れておけば、デフォルトで thinking-off の対話アシスタントになる。
# thinking-off をデフォルトに固定する例 (chat-template に /no_think を埋め込む) # プロンプトは "You are a helpful assistant. /no_think\nUser: ..." の形に整形
thinking_budget(またはアプリ側でのトークン上限) を併用すると、on のときも暴走を防げる。512〜1024 トークンを上限にすると、4060 での待ち時間は数秒〜十数秒に収まる。
軸2 (対話)に厳しい運用では、入力に応じて自動でモード切替を判定するルーターを前段に置くのが現実解だ。
ルーターのシンプルな第一近似はキーワードベースで動く。
# 簡易ルーターの第一近似 (擬似コード)
THINKING_KEYWORDS = {"solve", "prove", "debug", "optimize",
"計算", "証明", "デバッグ", "最適化"}
SHORT_INTENT = {"what is", "how do i", "explain briefly",
"とは", "簡単に"}
def route(query: str) -> str:
q = query.lower()
if any(k in q for k in THINKING_KEYWORDS):
return "/think"
if any(k in q for k in SHORT_INTENT):
return "/no_think"
if len(query) < 80: # 短い問いは即答側
return "/no_think"
return "/no_think" # デフォルト off (8GBの保守側)
これだけで軸1の判定はかなり捌ける。さらに精度を上げたければ、Qwen3.5-4B のような軽量モデルをルーター専用に置いて「これは多段推論が要りそうか?」を1秒以内で判定させる二段構成にできる。8GB なら 4B + 9B のスワップ運用も視野に入る (4Bは Q4 で 2.5GB 級、メモリは余る)。本記事ではここまで踏み込まないが、設計の方向としては筋がいい。
反証側 — thinking-onが裏目に出る場面
「考えさせれば精度が上がる」と素朴に信じると 8GB ではかえって損をする。thinking mode が裏目に出る場面を3つ書いておく。
裏目1: ハルシネーションの長文化
thinking ブロックの中で誤った仮説が立つと、モデルはその仮説を補強する方向に追加の推論を積む。短く答えていれば短いハルシネーションで済んだものが、長い「説得力のあるハルシネーション」に化ける。事実確認系のタスクで thinking-on にすると、これが頻繁に観察される。
裏目2: 過剰思考 (over-thinking)
簡単な問題に対しても thinking ブロックが膨らみ、シンプルな正解にたどり着くのに何百トークンも費やす。ベンチ評価では精度に効くが、UX としては「遅くて何も生んでいない」状態になる。
thinking_budgetで上限を絞ることが、特に8GBでは強く推奨される。
裏目3: コンテキスト汚染
複数ターンの会話で thinking ブロックを履歴に残し続けると、入力コンテキストが膨れあがり、後続ターンで本来の指示が「中の方」に埋もれる (lost in the middle 現象)。ローカル運用では、thinking ブロックは生成直後に履歴から削除する設計が安全だ。
これらは Qwen3.5-9B 固有ではなく、推論モード一般に観察される副作用だが、8GB ではコストの絶対値が大きいため、より明確に効く。
残った疑問
書いていて引っかかったのは、「thinking mode の最適化はモデル開発者の責任なのか、利用者の責任なのか」という線引きだ。
thinking_budgetのような上限指定が必要だという事実は、モデルがまだ「考えるべき量を自分で決め切れない」状態にあることを意味している。本来は「タスクを見て、必要なだけ考え、必要のないところでは即答する」のが理想で、最近の研究ではそういう方向への動きもある。だが2026年5月時点で、ローカル運用者はまだ on/off と budget を自分で握る必要がある。
次に試したいのは、軸1の自動判定 (キーワードベースのルーター) をどこまで簡素に作れるか、だ。Qwen3.5-9B を呼び出す前段にもう一つ軽量モデル (Qwen3.5-4B でも十分そう) を置いて、「これは thinking 必要?」を10ms で判定させる構成が、8GB のなかでもう一段使い倒し方を増やしそうな気がしている。
「考えるか考えないか」を本体モデルに任せず、入り口で別の小さなモデルに振り分けさせる。これはローカル LLM 運用のお作法として、案外汎用に効くパターンかもしれない。