はじめに
これは技術詳細の記事(Sonar (Aphrodite Engine) で EXL3 の未報告バグを2つ見つけて直した話)の前段として、そこに至るまでの数週間の遠回りをまとめたものです。
環境は一貫して WSL2 Ubuntu + RTX 5070 ×2(12GB×2、合計24GB)。目標はずっと同じでした:「1人あたり最低80Kトークン以上のコンテキストを、複数人が同時に使えるようにする」。
結論から言うと、この目標に何度も「達成した」と思っては別の問題にぶつかり、最終的に3つのサービングエンジン(vLLM系、sglang系、Aphrodite系)と4種類以上の量子化方式を渡り歩くことになりました。うまくいかなかった話も含めて記録しておきます。
出発点: TabbyAPI(exllamav3)単独運用の限界
最初はTabbyAPI(exllamav3バックエンド)でQwen3.6系のEXL3量子化モデルを動かしていました。個人で使う分には十分でしたが、OpenWebUI経由で複数の用途(通常チャット、エージェント用のHermes等)から同時にアクセスすると、リクエストが直列処理になり、1人が長文を生成している間は他のリクエストが完全に待たされる状態でした。
exllamav3自体はdynamic batchingを持っていますが、本格的な多人数向けcontinuous batching(vLLM/sglangのような)を想定した設計ではありません。ここから「Continuous Batchingを持つエンジンへの移行」という長い旅が始まりました。
第一の壁: vLLM+KVarN計画は「机上」で終わった
最初に立てた計画は、AWQ INT4量子化 + KVarN(KVキャッシュ量子化、k4v2形式)+ PagedAttention + Continuous Batching + MTP投機的デコードという「5つの技術の掛け算」でした。狙いは以下の設計思想です:
vLLMの設計はユーザー数に上限がない。制約は「ユーザー数 × 1人あたりコンテキスト = 総KV容量」であり、この総量をKV圧縮でどれだけ伸ばせるかが勝負になる。
計算上は AWQ INT4(モデル軽量化)× KVarN k4v2(KV圧縮5倍)で、24GBのVRAMから562Kトークン相当のKVプールが取れ、「1人80Kなら6人同時」という試算でした。
しかし実際にはこの構成に一度も本番投入しませんでした。理由は、この後すぐ**独自の2bit量子化(GSQ/humming)**という、当時さらに強力に見えた別の道に切り替えたためです。
GSQ(独自2bit量子化): 131Kコンテキストは達成したが、品質で頓挫
GSQは自作の2bit量子化パイプライン(MoEの専門家部分のみ2bit化する"humming"方式)で、Qwen3.6-35B-A3B(MoEモデル、アクティブパラメータ3B)を量子化したものです。dense 27Bと違い、MoEなら「アクティブでない専門家」をCPU RAMに逃がせるため、VRAM予算に対してずっと有利という発想でした。
達成できたこと: Qwen3.6-35B-A3B-2Bit-GSQで131,072トークンのコンテキストを実現。当時の目標(80K以上)は数字の上ではクリアしていました。LoRAとの組み合わせ(3アダプタ同時ロード+170Kコンテキスト)も動作確認済みで、tool-calling(qwen3_xmlパーサー)も動いていました。
しかし品質面で複数の問題が残りました:
- 駄洒落生成が100%失敗(ループするか捏造する)
- 日本語生成時に中国語が混入することがある
- 実在トピックの詳細を聞くと捏造しやすい
- 独自LoRA訓練による品質改善も試したが、訓練データ由来の長文英語混入(58〜71%)が悪化し、最終的に全アダプタ不採用、ベースモデル(LoRA無し)が最良という結論に
さらにFP8 KVキャッシュ+humming量子化の組み合わせはCUDAグラフメモリが10倍に膨張するバグ(vLLM本家Issue #47281で既知)を踏み、bf16 KV運用に後退。2GPU化(データ並列)も9回以上の試行の末、NCCL固定オーバーヘッドがVRAM予算を食い尽くして断念しました。
最終的に2026-07-06、「GSQは使わない、LoRA訓練の予定もない」という判断で、プロジェクト自体を打ち切りました(関連ファイル計95GB削除)。コンテキスト長は目標を達成していたが、品質が実用に耐えなかったというのがGSQの結末です。
寄り道: mini-sglang + EXL3でKVarNのGDNハイブリッド対応を実証
GSQと並行して、mini-sglang(自作/カスタマイズしたsglang簡易実装)+ exllamav3バックエンドでQwopus3.6-27B系のEXL3量子化モデルを動かす検証もしていました。ここでの重要な発見は、KVarNのGDNハイブリッド対応(full_attention層のみKV量子化、Mamba/GDN層はrecurrent stateのまま扱う)が既に実機で動くことを確認できた点です(k4v2形式で19.7→57.8 tok/sの実測改善)。
これは後で「GDNハイブリッドアーキテクチャはKVキャッシュ量子化に対応不可能」と誤って結論しかけた際に、それを訂正する根拠になりました。正しくは「vLLM(+humming)側にまだこの対応が移植されていないだけ」で、アーキテクチャ的な限界ではなかったということです。
またEXL3のCUDAグラフ対応・MTP(投機的デコード)の実装も試しましたが、bs=1でのメモリ帯域ボトルネックにより2-tokenプレフィル方式のMTPでは速度向上が得られない(kernel launch overheadが増えるだけで正味ゼロ)ことも分かりました。
第二の壁: OSCAR(sglangフォーク)への本格移行
GSQ打ち切りと同時期、OSCARというsglangフォークを使ってQwen3.6-27B-AWQ-INT4(denseモデル)をINT2 KVキャッシュで動かす検証に切り替えました。ここが最も長く格闘した区間です。
実バグ1: 長文でOOMクラッシュ
INT2 KV量子化のフォールバック実装が、head_dim=256という大きめの次元のせいでPyTorchの融合SDPAカーネル(flash/mem-efficient/cuDNN)の対応上限(128)を超えており、非効率なmath backend(O(q_len×k_len)メモリ)に落ちていました。標準のflash-attnパッケージ(FA2)に差し替えることで、ピークメモリを約72分の1に削減し解決しました。
実バグ2: 「思考中に応答が空になる」現象
長文+思考モードで、モデルが</think>を出す前にEOSトークンを出して即座に応答が空になる現象がありました。INT2量子化・KVスロット会計・回転行列較正・repetition_penalty・履歴汚染・プロキシのバグ、と6つの仮説を潰した末、真因はsglang本家の既知バグ(Issue #24839、本家では修正済み)がOSCARフォークに一度も反映されていなかったことでした。該当箇所に直接パッチを当てて解決しました。
このデバッグの過程で、当初「モデル固有の弱点だろう」と決めつけかけたことがありましたが、経路(サービングスタック)がおかしいと思い、それが的中しました。モデルの弱点と決めつける前にサービングスタック自体を疑うべき、という教訓が残っています。
到達点(2026-07-07時点)
--context-length 81920を本番仕様として確定。自動選択で5並列、実効KVプール102,432トークン(CUDAグラフ有効時)。並列数を絞ればさらに伸び、1並列なら175,824〜177,168トークンまで確保できました。tool-calling(qwen3_coderパーサー)も実機確認済みで、一時期はこれが本番として安定稼働していました。
新たな壁: radix cache無効によるTTFT線形増加
安定稼働後、「プロンプト入力後6-7秒待たされる」という報告がありました。当初CUDA断片化を疑いWSL再起動まで試しましたが解決せず、実測の結果、radix cache(prefixキャッシュ)を無効にしていたため、会話が続くたびに履歴全体をゼロから再prefillしていたことが真因と判明しました(TTFTがプロンプト長にほぼ線形比例: 数十トークンで0.59秒、5,000トークンで2.84秒、12,000トークンで13.66秒)。
これはINT2 KV量子化パスとの相性上の設計上のトレードオフで、単純な「バグ」ではありませんでした。radix cache有効化・並列prefill高速化(mamba-scheduler-strategy extra_buffer等)を4パターン試しましたが、いずれもOOMまたは新種のクラッシュで失敗。sglang本家にはMambaRadixCacheという正しい解決設計が既に存在するものの、OSCARフォークが本家から約6週間遅れており未導入、という結論に至りました。
エンジン乗り換えも検討したが、全滅
「別のエンジンに変えれば速くなるのでは」という発想で、sglang本家・vLLM・llama.cppを横断調査しました。結果は以下の通りです。
| エンジン | Mamba/GDNハイブリッドのprefix caching |
|---|---|
| sglang(OSCARフォーク) | 未反映(本家にMambaRadixCacheはあるがフォークが古い) |
| vLLM | 実験的実装(mamba_cache_mode=align)だが528トークン単位でしか機能せず、既知バグでサイレントに0%に落ちることも |
| llama.cpp | Qwen3.6系で「プロンプトキャッシュが機能せず毎ターン全文再処理」の未解決バグが複数(#22746等)、head-of-line blocking問題もメンテナーが「対応しない」と明言 |
3エンジンとも2026年半ば時点でこの領域が未成熟という結論になり、「エンジンを変えれば解決する」という発想自体を諦めました。
インフラ側のトラブルも並行して発生
この時期、WSL2のdxgkrnl(GPU仮想化層)のバグでOSCARがcudaErrorUnknownで突然落ちる問題が発生し、systemdサービス化(Restart=on-failure)で自動復旧するようにしました。さらにディスプレイの黒画面(WSL全体フリーズ)対策として、Windows側のNVIDIAドライバを安定版にロールバックする対応も行いました。このロールバックの安定性を切り分けるため、OSCARは一時的に意図的に無効化されたままになっています(黒画面自体はロールバック後は再発していない、との報告あり)。
さらなる寄り道: 量子化方式そのものを探す旅、ことごとく失敗
OSCARの並列prefill遅延を回避する別の切り口として、Qwopus3.6-27B系の他の量子化版を探す試行もしました。結果はすべて不採用でした。
- DBF 1.5bit量子化(自作): PPL 177.3(fp16比24倍劣化)、出力は文字化けレベルで使用不可
- HLWQ/PolarQuant: 撤回論文が元の技術という点で警戒しつつ検証したが、対象リポジトリ自体がembed/lm_head/vision重みを含まない未完成アップロードと判明。GPTQ版に切り替えて再挑戦したが、「12GB×2枚(TP=2)」は「24GB単体」とは別物(TP固有の固定オーバーヘッドがある)という構造的な壁にぶつかり、あらゆるパラメータ調整をしても0.1〜0.2GB差でKVキャッシュがゼロになる状態から抜け出せず断念
- GGUF+vLLM: 「GGUF=遅い」という思い込みを疑い検証したが、vLLMのGGUFローダー自体がQwen3.6/Qwen3-Next系のhybrid Mamba/GDN構造を一切サポートしていない(未解決issue多数)とコードレベルで確認、断念
この一連の探索で得た教訓は、VRAM試算は慢性的に楽観しすぎていたということと、技術記事のREADME通りに動かないときは、技術自体がダメなのか、単にそのアップロードや自分の環境固有の問題なのかを切り分けてから結論を出すべき、ということでした。
たどり着いた解: Sonar(Aphrodite Engine)+ EXL3
上記の紆余曲折を経て、最終的に選んだのがContinuous BatchingをネイティブにサポートするSonar(Aphrodite Engine、vLLMのフォーク)でした。EXL3量子化を直接サポートしている点が決め手でした。
ここでもQwen3.6系のハイブリッドMamba/GDNアーキテクチャ + EXL3 + LoRAの組み合わせで、upstreamに未報告の実バグを2つ踏み(shard_id不一致、LoRAのデバイス判定漏れ)、原因を特定してPRとして提出しました。詳細は前回の記事にまとめています。
そしてこの環境でOrnith-1.0-9B(DeepReinforce AI製、同じQwen3_5/GDNハイブリッド構造)という新モデルを見つけ、27Bモデルと対決させたところ、VRAM効率・速度・コンテキスト長のすべてで上回るという結果になりました。
まとめ: 数字で振り返る「複数人80K」への到達過程
| 段階 | エンジン/量子化 | 到達コンテキスト | 課題 |
|---|---|---|---|
| 出発点 | TabbyAPI(exllamav3) | (単独利用が前提) | 複数人アクセスで直列処理化 |
| 計画のみ | vLLM+AWQ+KVarN | 試算562K(6人×80K) | 実装前に方針転換、未検証 |
| GSQ(2bit MoE) | 独自humming量子化 | 131,072(達成) | 品質問題(駄洒落・言語混入・捏造)で打ち切り |
| OSCAR(sglang) | AWQ INT4 dense+INT2 KV | 81,920、5並列、KVプール102,432 | radix cache無効でTTFTが長文で線形増加、ハード安定性問題で現在停止中 |
| Sonar+Ornith-9B(現行) | EXL3 6bpw dense | 262,144(1人フル)、64並列で1096 tok/s、300並列までエラーなし | (現時点で大きな課題は未発見) |
こうして並べると、「131Kも81Kも一度は到達していたのに、なぜまた探し続けていたのか」と思われるかもしれません。答えは単純で、コンテキスト長という1つの数字だけでは「使える」と言えなかったということです。GSQは長さを達成しても品質が伴わず、OSCARは長さと並列数を達成しても会話が長くなるほど待ち時間が伸びる構造的な欠陥がありました。Sonar+Ornith-9Bで初めて、コンテキスト長・並列性・応答品質・安定性のすべてが揃った、というのが現時点の実感です。
もっとも、Sonar+Ornith-9Bもまだ本番投入したばかりで、OSCARのように「安定運用してから新しい問題が出てくる」可能性は十分にあります。次に何か問題が出たら、また記事にすると思います。