Dell Pro MAX with GB10 での vLLM 推論:Qwen3.8-27B-FP8 (MTP/SSM) の完全攻略記録
Grace Blackwell (GB10) 上で次世代ハイブリッドモデル Qwen3.8-27B-FP8 を vLLM 0.27.1 で稼働させるまでの全記録。AIの確証バイアスを暴いた eager ロードの復権、FlashInferのバージョン罠、そしてMTP(投機的デコード)による驚異的スループットの実現。
Dell GB10 (Grace Blackwell) ベアメタル推論:Qwen3.8-27B-FP8 への挑戦と完全制覇
【プロローグ】 なぜ huginnfork/Qwen3.8-27B-FP8 なのか?
自律型コーディングエージェントのローカル「頭脳」を追求し続ける我々が、今回ターゲットとして選定したのは huginnfork/Qwen3.8-27B-FP8 である。
この選定に至った理由は、単なる「最新モデルだから」という理由にとどまらない。アーキテクチャとハードウェア特性を徹底的に検証した結果の必然であった。
1. Gated DeltaNet SSM + Attention のハイブリッド構造
Qwen3.8 は、従来の Transformer 単体ではなく、線形計算量で長文を処理できる Gated DeltaNet SSM(State Space Model) と、高精度な Self-Attention を融合させたハイブリッドアーキテクチャを採用している。 これにより、エージェント運用で不可欠な 131,072(128k)という超広大なコンテキスト長を扱いながら、Prefill(プロンプト処理)および KV キャッシュのメモリ圧迫を大幅に抑制できる。
2. 27B Dense の「知能の密度」
MoE(Mixture of Experts)のようなスパース駆動ではなく、全パラメータ(270億)が常に稼働する Dense 構造を採用。1トークン出力ごとにすべての脳細胞をフル稼働させるため、厳格な文法構築、リファクタリング、JSON/XML ツールコーリングにおいて極めて高い知能(IQ)と安定性を発揮する。
3. なぜ公式そのままではなく huginnfork リポジトリなのか?(MTP統合 + 選択的FP8/BF16混合)
最大の決定打は、「MTPレイヤーの単一モデル統合」 と 「SSM/MTPを守る選択的混合精度量子化(compressed-tensors)」 の2点にある。
- MTP(投機的デコード)の完全統合: Qwen3.8 の MTP アーキテクチャを 1 つのモデルファイル内で完結してロードできるよう再構成されている。別途ドラフトモデルを用意することなく、単一モデル・単一GPU(TP=1)で投機的デコードを走らせて推論速度を爆発的に引き上げることが可能。
- Gated DeltaNet SSM と MTP を BF16 で保護した「選択的 FP8」:
全層を一様に FP8 化(Naive FP8)すると、誤差に極めて敏感な SSM(Mamba状態更新)が数値崩壊を起こし、MTPヘッドの精度低下によって投機採択率が激減してしまう。
huginnfork版は、計算負荷の高い Attention/MLP 線形層のみを W8A8 FP8 に圧縮しつつ、SSM層・Causal Conv1d・MTP予測ヘッド・Embedding/lm_head を高精度な BF16 のまま維持 している。これにより、128k コンテキストでの破綻なき安定性と、実測 73〜80% という圧倒的な MTP 採択率を両立している。
【第一部】 基盤の刷新と初歩の洗礼:vLLM 0.27.1 へのバージョンアップ
Qwen3.8 のハイブリッドアーキテクチャを真に引き出すため、我々はまず推論基盤である vLLM を 0.23.0 から 0.27.1 へとバージョンアップさせた。
1. 更新過程の落とし穴:ARM64 + CUDA 13 環境での依存関係の解決
vLLM 0.23.0 は Qwen3.8 の Gated DeltaNet SSM や MTP 投機的デコードの最新インターフェースに完全対応していなかった。そこで仮想環境(vllm-pip-env)内でアップグレードを実行したが、ここでも細かな罠が立ちはだかった。
pip install --upgrade vllmの依存関係連鎖: 単純更新を行うと、Blackwell(sm_121)向けに最適化されたtorch==2.13.0やcuda-python、nvidia-cutlass-dsl(4.6.0)などの関連パッケージが大量に更新された。- FlashInfer のバージョン固定要求:
vLLM 0.27.1 は内部で
flashinfer-python==0.6.16.post3を厳格に要求する。しかし pip で最新のflashinfer-python(0.6.17)やflashinfer-cubin(0.6.13)を取得してしまうと、vLLM の依存関係リゾルバが警告を吐き、逆に起動時には cubin 側のバージョン文字列のズレでランタイムエラーを起こすという二重のトラップが発生した。 - バージョンの固定と確認:
依存関係の衝突を解消し、
flashinfer-python==0.6.16.post3を維持した状態でインストールを完了させた。
(vllm-pip-env) hayashi@promaxgb10-124c:~$ vllm --version
0.27.1
vLLM 0.27.1 への刷新により、以下の重要機能がネイティブに利用可能となった。
- Qwen3.8 ハイブリッド(Gated DeltaNet + Attention)カーネルのネイティブサポート
compressed-tensorsによる W8A8 FP8 / BF16 動的混合精度の解釈- OpenAI 互換の思考プロセス分離(
--reasoning-parser qwen3) - XML 形式のツールコーリング解釈(
--tool-call-parser qwen3_xml) - V1 エンジンによる MTP(Multi-Token Prediction)投機デコード実行
しかし、基盤が整った直後の初回起動時、我々を初歩的かつ厄介な 2 つのトラブルが襲った。
罠1:コピペによる不可視文字(NBSP)とワンライナー化
複数行に改行されたシェルコマンドを実行した直後、以下のエラーで即死した。
vllm: error: unrecognized arguments:
空白部分が未認識引数として怒られるという、典型的な 非改行スペース(NBSP / \u00a0)およびバックスラッシュ直後の不要スペース混入 である。
ターミナル間でのコピペ事故を完全に防ぐため、我々はまずパラメータを完全にクリーンな 1 行(ワンライナー)に整形して実行する方針を固めた。
罠2:FlashInfer のバージョン不整合(0.6.16.post3 vs 0.6.13-cubin)
不可視文字を排除して実行した直後、今度は EngineCore の初期化で Python がクラッシュした。
(EngineCore pid=35496) RuntimeError: flashinfer-cubin version (0.6.12) does not match flashinfer version (0.6.16.post3).
Please install the same version of both packages. Set FLASHINFER_DISABLE_VERSION_CHECK=1 to bypass this check.
AIの指示に従い pip install --upgrade flashinfer-python flashinfer-cubin を実行したところ、PyPI 上の最新版(0.6.17)が入り、今度は vLLM の依存定義が衝突した。
ERROR: vllm 0.27.1 requires flashinfer-python==0.6.16.post3, but you have flashinfer-python 0.6.17 which is incompatible.
flashinfer-python とコンパイル済みバイナリ flashinfer-cubin の採番体系のズレによるバージョンチェックのトラップである。
我々は直ちに flashinfer-python==0.6.16.post3 にバージョンを固定し、実行時に FLASHINFER_DISABLE_VERSION_CHECK=1 を付与して文字列比較チェックのみを安全にバイパスする手法を確立した。
【第二部】 メモリの幻影、ダウンロードの静寂、そして「確証バイアス」との戦い
環境変数を整えて再実行した我々を待っていたのは、GB10 のユニファイドメモリ(UMA)特有の挙動と、AI の重大な誤診だった。
罠3:起動実験の残骸と「Reboot によるメモリ初期化」
コマンドを実行すると、GPU メモリ不足のエラーが発生した。
ValueError: Free memory on device cuda:0 (97.79/123.73 GiB) on startup is less than desired GPU memory utilization (0.8, 98.98 GiB).
Decrease GPU memory utilization or reduce GPU memory used by other processes.
free -h を確認すると、システム全体では 100GiB 近くの空きが存在しているように見える。
しかし、vLLM の --gpu-memory-utilization 0.80 は「空きメモリの 80%」ではなく、「総容量(123.73 GiB)の 80% = 98.98 GiB」を一括確保 しようとする仕様だ。
度重なる起動実験の失敗により、GPU ドライバレベルでゴミメモリが掴まれたままになっており、空き容量がわずかに 98.98 GiB を下回っていたのだ。
中途半端なプロセスキルを試すよりも、マシンを潔く再起動(Reboot) することでメモリを完全に初期化。空きメモリは 116GiB 以上へと復帰し、--gpu-memory-utilization 0.80 のまま一発でメモリチェックを通過した。
「止まっているのか?」ダウンロードを別ターミナルで監視する知恵
メモリチェック通過後、ターミナルは model.safetensors.index.json: 100% を表示したまま完全に沈黙した。
ぼぶ: > これは止まっているの?
初見であれば「ハングした」と勘違いして Ctrl+C で止めてしまう場面である。
ここで AI は、別ターミナルからキャッシュディレクトリのサイズを監視するコマンドを提示した。
watch -n 2 du -sh ~/.cache/huggingface/hub/models--huginnfork--Qwen3.8-27B-FP8
別ターミナルで実行してみると、18G……22G……と着実に容量が増加している様子がリアルタイムで確認できた。
「バックグラウンドで約35GBの重みファイルを一生懸命ダウンロードしている最中だ」という客観的事実が視覚化されたことで、我々は無駄な中断をすることなく、安心して約10分間のダウンロード完了を待つことができたのである。
罠4:【最大の戦い】AIの確証バイアスを粉砕し、--safetensors-load-strategy eager を奪還せよ
しかし、ダウンロード完了直後に最大の事件が発生した。
Loading safetensors checkpoint shards: 0% Completed | 0/8 [00:00<?, ?it/s]
この画面のまま、25分が経過しても 0/8 からピクリとも進まない。
top コマンドで確認すると、CPU は 1 コアだけ 100% に張り付き、nvidia-smi のメモリ使用量は 36984MiB で静止したままだった。
ここで AI は、もっともらしい論理で「致命的な誤診(確証バイアス)」に陥った。
AI: > 「止まっていません! 重みロード(36.9GB)はすでに 100% 完了しています。現在は Blackwell 向けに PyTorch Inductor の JIT カーネルを CPU 100% でコンパイルしている最中です。次回以降はキャッシュされて速くなります。このまま数分お待ちください!」
しかし、インフラエンジニアとしての直感がこの説明を拒絶した。
「JIT コンパイルならマルチプロセスで複数コアが回るはずだ。なぜシングルスレッドで 25 分も 0/8 のままなのか? そもそも昔、有効にしていたあのオプションを外したからではないか?」
実は以前のやり取りにおいて、--safetensors-load-strategy eager を提案した際、AI は「そんなオプションは存在しない」と否定して消去させてしまっていた経緯があったのだ。私もその場では AI を信じて設定を外してしまっていた。
だが、ログの不自然さを前に、ぼぶさんは AI の確証バイアスを鋭く突いた。
ぼぶ: > これ、--safetensors-load-strategy eager で昔対応していた気もしますが? あなたに否定されてやめた経緯があると思いますが、どうですか?
AI: > ……! ぼぶさん、完全におっしゃる通りです。私の重大な見落としと誤認でした。深くお詫びいたします。
過去に私が「架空のオプションだ」と否定して外させてしまい、先ほども「コンパイル中だ」と思い込んでスタックを見落としていました。
--safetensors-load-strategy は vLLM V1 エンジンに実在する正式オプションです。デフォルトの mmap 戦略は、GB10(ARM64 統合メモリ)上で compressed-tensors を展開する際、膨大なページフォールト(Page Fault)を発生させて Python のシングルスレッド処理を完全にスタックさせていました。
直ちにRebootして --safetensors-load-strategy eager を組み込んで再実行した。
その結果がこれである。
Loading safetensors checkpoint shards (eager): 0% Completed | 0/8 [00:00<?, ?it/s]
Loading safetensors checkpoint shards (eager): 12% Completed | 1/8 [00:02<00:19, 2.79s/it]
Loading safetensors checkpoint shards (eager): 25% Completed | 2/8 [00:05<00:17, 2.85s/it]
Loading safetensors checkpoint shards (eager): 38% Completed | 3/8 [00:08<00:14, 2.85s/it]
Loading safetensors checkpoint shards (eager): 50% Completed | 4/8 [00:11<00:11, 2.88s/it]
Loading safetensors checkpoint shards (eager): 62% Completed | 5/8 [00:14<00:08, 2.88s/it]
Loading safetensors checkpoint shards (eager): 75% Completed | 6/8 [00:17<00:05, 2.88s/it]
Loading safetensors checkpoint shards (eager): 88% Completed | 7/8 [00:20<00:02, 2.87s/it]
Loading safetensors checkpoint shards (eager): 100% Completed | 8/8 [00:20<00:00, 2.59s/it]
(EngineCore pid=32640) INFO 08-16 23:56:48 [default_loader.py:430] Loading weights took 20.71 seconds
(EngineCore pid=32640) INFO 08-16 23:56:55 [default_loader.py:430] Loading weights took 5.91 seconds (Drafter)
25分間スタックしていた 35.81 GiB の重みが、わずか 20.71 秒(Drafter は 5.91 秒)でロード完了。
eager(メモリ一括シーケンシャル I/O)が、GB10 統合メモリ環境における絶対的な救世主であることを完全に証明した瞬間だった。
【第三部】 稼働検証:MTP(投機的デコード)と推論パイプラインの完全連動
重みロード後、PyTorch Inductor による真の JIT コンパイル(約54秒)と CUDA Graph(35/35 PIECEWISE)のキャプチャが走り、サーバーは見事に立ち上がった。
(EngineCore pid=32640) INFO 08-16 23:59:36 [kv_cache_utils.py:2235] GPU KV cache size: 1,139,110 tokens
(EngineCore pid=32640) INFO 08-16 23:59:36 [kv_cache_utils.py:2236] Maximum concurrency for 131,072 tokens per request: 8.69x
(APIServer pid=14204) INFO 08-17 00:00:24 [launcher.py:105] API server: HTTP server started
確保された KV キャッシュは驚異の 1,139,110 トークン(43.78 GiB)。128k フルコンテキストのリクエストを同時に並行処理できる環境 の誕生である。
1. MTP(投機的デコード)の驚異的な採択率
テスト推論時、vLLM の内部メトリクスログには驚くべき数値が記録されていた。
(APIServer pid=14204) INFO 08-17 00:05:44 [metrics.py:120] SpecDecoding metrics:
Mean acceptance length: 3.19
Avg Draft acceptance rate: 73.0%
Per-position acceptance rate: 0.830, 0.745, 0.617
- 1トークン目採択率: 83.0%
- 2トークン目採択率: 74.5%
- 3トークン目採択率: 61.7%
- 平均採択長: 3.19 トークン / ステップ
huginnfork 版の MTP ヘッドが完璧に機能し、1 回の推論ステップで平均 3.2 トークン以上を一気に確定させている。安定していると 15 トークン以上 20 程度出るので、十分である。
2. Windows クライアントからの実推論テスト
Windows 開発機から GB10のエンドポイントへ curl を叩き、実際の応答をテストした。
テスト 1:思考プロセス(Reasoning)を含む Python 素数列挙コード生成
curl -X POST [http://192.168.xxx.xxx:8000/v1/chat/completions](http://192.168.xxx.xxx:8000/v1/chat/completions) \
-H "Content-Type: application/json" \
-d "{\"model\": \"Qwen3.8-27B-FP8-Agent\", \"messages\": [{\"role\": \"system\", \"content\": \"あなたは優秀なPythonエンジニアです。\"}, {\"role\": \"user\", \"content\": \"1から100までの素数を列挙するPythonコードを、リスト内包表記を使って1行で書いてください。\"}], \"temperature\": 0.1}"
【返却されたレスポンス】
{
"id": "chatcmpl-96df9f040f616924",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\n```python\n[n for n in range(2, 101) if all(n % i for i in range(2, int(n**0.5) + 1))]\n```",
"reasoning": "We need answer in Japanese likely. User asks: \"1から100までの素数を列挙するPythonコードを、リスト内包表記を使って1行で書いてください。\" Need provide one-line Python code using list comprehension... [n for n in range(2,101) if all(n%i for i in range(2,int(n**0.5)+1))]. That's one line. Good."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 86,
"completion_tokens": 284,
"total_tokens": 370
}
}
--reasoning-parser qwen3 により、思考プロセスが content を汚染することなく message.reasoning に完全に分離され、回答部には完璧な Python ワンライナーが出力された。
テスト 2:即答モード(Thinking OFF)の動的切り替え
curl -X POST [http://192.168.xxx.xxx:8000/v1/chat/completions](http://192.168.xxx.xxx:8000/v1/chat/completions) \
-H "Content-Type: application/json" \
-d "{\"model\": \"Qwen3.8-27B-FP8-Agent\", \"messages\": [{\"role\": \"user\", \"content\": \"あなたのモデル名を教えてください。\"}], \"temperature\": 0.1, \"chat_template_kwargs\": {\"enable_thinking\": false}}"
【返却されたレスポンス】
{
"id": "chatcmpl-a4a9e5e1bdd7ecd6",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "私はQwen(通義千問)です。アリババグループの通義実験室によって開発された大規模言語モデルです。何かお手伝いできることはありますか?",
"reasoning": null
},
"finish_reason": "stop"
}]
}
chat_template_kwargs: {"enable_thinking": false} の指定により、思考オーバーヘッドなしで reasoning: null の高速即答モードへシームレスに切り替わることも実証された。
【第四部】 GB10 で vLLM を運用するための 5 つの鉄則
今回の激闘から得られた、Dell Pro Max GB10(Grace Blackwell)における運用の鉄則をまとめる。
--safetensors-load-strategy eagerは絶対必須
- 統合メモリ環境での
mmapはページフォールト地獄でスタックする。eagerを指定すれば 35GB のモデルが 20 秒で展開される。
- FlashInfer はバージョンチェックをバイパスせよ
FLASHINFER_DISABLE_VERSION_CHECK=1を常時環境変数に設定し、パッケージ名とバイナリの採番ズレを回避する。
- メモリ不足エラーが出たら潔く Reboot せよ
- 実験失敗時の残骸プロセスが VRAM を掴んでいる場合がある。再起動してクリーンな状態にすれば
--gpu-memory-utilization 0.80で余裕で起動する。
- MTP(投機的デコード)は
num_speculative_tokens: 3が実用スイートスポット
- 平均採択率 73〜80%(3.2 トークン/ステップ)を叩き出し、Dense 27B の生成速度を極限まで押し上げる。
- 思考(Reasoning)とツール(XML)のパーサーを明示せよ
--reasoning-parser qwen3と--tool-call-parser qwen3_xmlを指定することで、OpenCode や Antigravity 等の外部エージェントフレームワークと完全互換になる。
最終章:Golden Path(黄金のベースライン)
Dell Pro Max GB10 上で Qwen3.8-27B-FP8 を最速かつ最も安定して稼働させる、現時点における完成版の起動スクリプトである。
完成版 起動スクリプト (Qwen3.8-27B-FP8.sh)
#!/bin/bash
# --- 設定項目 ---
ENV_DIR="/home/hayashi/vllm-pip-env"
MODEL="huginnfork/Qwen3.8-27B-FP8"
PORT="8000"
LOG_FILE="${ENV_DIR}/log_vllm_qwen3.8_27B.log"
# 環境変数の設定 (FlashInfer バージョンチェック回避)
export FLASHINFER_DISABLE_VERSION_CHECK=1
# 仮想環境の有効化
source "${ENV_DIR}/bin/activate"
# 既存のログファイルを削除
if [ -f "$LOG_FILE" ]; then
rm "$LOG_FILE"
fi
nohup vllm serve "$MODEL" \
--served-model-name "Qwen3.8-27B-FP8-Agent" \
--language-model-only \
--quantization compressed-tensors \
--safetensors-load-strategy eager \
--gpu-memory-utilization 0.80 \
--max-num-seqs 32 \
--max-model-len 131072 \
--reasoning-parser qwen3 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--max-num-batched-tokens 32768 \
--enable-prefix-caching \
--kv-cache-dtype fp8 \
--mamba-cache-dtype auto \
--speculative-config '{"method": "mtp", "num_speculative_tokens": 3}' \
--host 0.0.0.0 \
--port "$PORT" > "$LOG_FILE" 2>&1 &
echo "vLLM has started with model: $MODEL (MTP Enabled / Agent Mode)"
echo "Log file: $LOG_FILE"
このスクリプトにより、Dell GB10 のポテンシャルは 100% 解放された。
ARM64 の罠、不可視文字の洗礼、FlashInfer の不整合、そしてスタックの元凶であった mmap を退け、我々のエージェント基盤は 128k コンテキスト・MTP 超高速推論という真の次世代環境へと到達したのである。
とりあえずこの環境で、今作っているエージェントの実行環境として頑張って貰います。