DGX ローカルLLM thinking-token 過消費の診断 — Gemma vs Qwen reasoning-budget 実測記


🖥️ DGX Spark | llama.cpp b9743 | terry-llm v0.4.5 | 2026-06-29

thinking-token reasoning-budget llama.cpp Gemma-4 Qwen3

🔍 発端 — 翻訳結果はなぜこうなったのか

Atelierブログの翻訳結果が継続して不良だった。文章がぎこちなく、途中で切れる現象が繰り返された。最初はモデル自体の品質問題と判断したが、追跡した結果、原因は別の場所にあった。

llama.cppはmax_tokens一つでthinkingトークンと回答(content)トークンを統合管理する。thinkingが有効な状態でモデルが些細な作業に過度にthinkingトークンを消費すると、実際の回答(content)生成に使う予算が残らない。つまり、翻訳が切れたのはモデルの品質問題ではなく、予算不足の問題だった。

thinking-token 予算共有問題

max_tokens予算をthinkingトークンとcontentトークンが分け合う。モデルが些細な作業に過度に思考(thinking)すると、content生成前に予算が枯渇し、finish_reason=lengthが返される。

📊 測定 — 3スロットマトリクス

DGXの3スロット全てを対象に測定を実施した。各スロットに些細な作業(1行翻訳)と推論作業(数学問題)を投げて、thinkingトークン消費量を測定した。条件はmax_tokens=2048finish_reason=stop

スロット モデル 些細な作業 (1行翻訳) 推論作業 (数学)
model1 Gemma-4-26B-A4B think 1,601c / 5.4s think 742c / 4.0s
model2 Gemma-4-E4B think 943c / 2.3s think 253c / 1.4s
model3 Qwen3.6-27B think 4,498c / 63.5s think 906c / 23.4s

Qwen3.6(model3)は1行翻訳のために63.5秒を消費した。数学問題でもない単純な翻訳に1分以上かかるのだ。これはQwen3系の典型的な失敗モード「過思考(over-thinking)」現象だ。

🔬 コミュニティクロスチェック

測定データがモデルのスペックと一致するか検証した。直接測定した数値がモデルの設計意図と合っているか確認する段階だ。

Gemma 4 — ネイティブthinking、enable_thinkingフラグ

Gemma 4はネイティブthinkingモードをサポートし、enable_thinkingフラグで制御する。公式ドキュメントには別途のbudgetガイドが明記されていないが、コミュニティの慣例に従って運用される。マルチターン会話時に以前のthinkingブロックを除去することが公式要件だ。

Gemma E4B 例外動作公式モデルカード基準、E2B/E4Bを除くモデルはenable_thinking=false設定でも空のthoughtブロックを出力する。E4Bモデルのみこのブロックが完全に省略される。

Qwen3 — ハイブリッドthinking、過思考が深刻

Qwen3系はハイブリッドthinking構造を持つ。enable_thinking=falseまたは/no_thinkシステムプロンプトで無効化できるが、完全に無効化するとtool-calling性能が低下する。Agenticパスでは完全無効化よりbudgetキャップを設定することが推奨されるというのが、公式thinking_budgetドキュメントの結論だ。

llama.cpp — –reasoning-budget スペック

公式README基準、--reasoning-budget値の範囲は以下の通り:-1(無制限) / 0(即時終了) / N > 0(トークンキャップ)。デフォルト値は-1だ。

重要な優先順位ルールがある。GH disc 21445によると、サーバー設定が--reasoning-budget=-1の場合のみ、per-requestのthinking_budget_tokensフィールドが適用される。サーバーフラグが0や正の値であれば、per-request設定は完全に無視される。

Transition message欠如時の品質急落r/LocalLLaMAベンチマーク基準、thinkingを強制終了した場合、HumanEvalスコアが94%から78%に低下する。</think>を挿入して応答を続けるtransition messageを追加すると89%まで回復する。

🛠️ 実証 — per-requestコントロール検証 (build b9743)

理論を確認後、実際にper-requestコントロールが機能するか検証した。対象はmodel3(Qwen3.6-27B)とmodel2(Gemma-4-E4B)で、再起動なしで設定を適用した。

条件 content reasoning 所要時間 短縮率
model3 baseline 106c 7,262c 102.7s
model3 thinking_budget_tokens=256 106c 827c 16.1s 6.4×
model3 enable_thinking=false 109c 0c 2.4s 43×
model2 enable_thinking=false 110c 0c 0.7s

per-requestコントロールが正常に機能することを確認した。model3 baseline 102.7秒がbudgetキャップ適用後16.1秒に短縮された。thinkingを完全に無効化すると2.4秒まで下がる。contentトークン数は3条件全て106〜109cで同一だった。これはthinkingトークン消費が問題だったのであり、モデル自体に問題はなかったことを証明する。

⚙️ 実装 — terry-llm v0.4.5 (コミット 220b85a1)

実証結果をもとにterry-llmゲートウェイに反映した。主な変更点は以下の通り。

1. providers/router.py — per-request budget パッシング

クライアントが送信したthinking_budget_tokensextra_body経由でllama.cppサーバーに渡す。

# providers/router.py
if request.thinking_budget_tokens is not None:
    extra_body["thinking_budget_tokens"] = request.thinking_budget_tokens

2. server.py — agenticパス budget キャップ

Agenticループでthinkingトークンが無制限に消費されることを防ぐため、設定値からキャップを読み込んで適用する。デフォルト値は4096トークンだ。

# server.py _do_agent()
budget = cfg.defaults.agent_thinking_budget_tokens
# config.yaml:
#   agent_thinking_budget_tokens: 4096

3. agent/loop.py — 空contentの正直な処理

finish_reason=length(切断)とfinish_reason=stop(正常終了)を区別する。contentが空の場合はtruncatedフラグを付けて返す。以前は空文字列を正常な応答として返す問題があった。

4. run_http.py — セッション独立HTTP サービス (:8090)

既存のMCPセッションに依存せず:8090ポートで独立して動作するstandalone HTTPサービスだ。セッションが終了してもHTTPエンドポイントは安定して維持される。

✅ 3スロット最終状態

スロット モデル 状態 処理方式
model1 Gemma-4-26B-A4B ✅ 最適化完了 ゲートウェイbudgetキャップ(4096) + 単発パスenable_thinking=false適用
model2 Gemma-4-E4B ✅ 最適化済み terryragsysがenable_thinking=falseおよびGBNF制約デコーディングを適用中
model3 Qwen3.6-27B ✅ 意図的に保持 コーディング専用モデルとして、thinking機能を維持

実質的な変更はmodel1のみに適用した。model2はすでに最適化済みで、model3はコーディング作業のためにthinking機能が必須なため保持した。

⚠️ 注意点

  • DGXサーバーの--reasoning-budget=-1維持必須:この設定が-1でなければ、per-requestのthinking_budget_tokensは無視される。サーバー設定を変更すると全てのper-requestコントロールが一括で無効化される。
  • 正のbudgetキャップ使用時はtransition message推奨:thinkingを強制終了する際に</think>を挿入しないと性能が急激に落ちる。Qwen3基準でHumanEvalスコアが約16%p低下するため、ライブラリが対応していない場合は自前実装が必要だ。
  • Gemmaマルチターン — 以前のthinkingブロック除去必須:公式要件だ。マルチターンパイプライン構成時には以前の会話のthinkingブロックを必ず処理しなければならない。
  • E4B以外のGemma — 空thought block出力enable_thinking=false設定でも<|channel>thoughtのような空ブロックが出力される場合がある。パースロジックでこれへの対処が必要だ。

📚 参考資料

関連するDGXインフラ運用記録はインフラアーカイブで確認できる。以前のモデル混在事例とレイテンシ改善結果もまとめられている。

✅ まとめ

問題は単純だった。thinkingオン状態でmax_tokens予算をthinkingとcontentが共有するのだが、モデルが些細な作業に過度に思考するとcontentが切れる。Qwen3基準で1行翻訳に63.5秒を消費し、baseline reasoningトークンは7,262cに達した。

解決策も明確だった。DGXサーバーの--reasoning-budget-1に維持し、ゲートウェイからper-requestのthinking_budget_tokensを渡し、agenticパスに4096トークンキャップを適用した。再起動なしでmodel1パスのみに適用して問題を解決した。


Discover more from AI-Girls Lab

Subscribe to get our latest posts delivered to your inbox.


AI-Girls Labをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む