RTX3090単体でHermes Agentを回す最良の構成|Qwen3.8-27B × llama.cpp × 262K context

RTX3090 24GB 1枚でHermes Agentを回す構成。Qwen3.8-27B・llama.cpp・UD-IQ4_XS・262K context AI活用
  1. 先に結論だけ
  2. そもそも何を組み合わせているのか
    1. RTX3090 24GB 1枚、という出発点
    2. Qwen3.8-27B ── なぜこのモデルなのか
    3. llama.cpp ── GGUFを動かす推論エンジン
    4. 262K context ── エージェントで効いてくるのはここ
    5. 投機デコード(MTP)── つまみが集中している場所
  3. この設定は誰向けなのか
    1. 前に「並列ならEXL3が2.2倍」と書いたのと矛盾しないのか
  4. 最終構成
  5. 同じ環境を作る手順
    1. 必要なもの
    2. 24GBのGPUを持っていない場合、いま何を買うか
    3. 1. モデルは手でダウンロードしない
      1. MTPヘッドは本体に同梱されている(別ファイルは要らない)
    4. 2. 起動する
    5. 3. 起動したか確認する
    6. 4. speedを測る
    7. つまずきやすいところ
  6. ここから先は、実際に動かした記録
  7. 効いたもの:投機デコードの下書き数
    1. 公式に推奨されていた値が、いちばん遅かった
    2. ただし、長い文脈では差が消える
  8. 効かなかったもの①:別の投機方式
    1. ここで一度、判断を間違えかけた
  9. 効かなかったもの②:文脈長を削る
  10. 効かなかったもの③:量子化をもっと落とす
    1. 名前で比べてはいけない
  11. 効かなかったもの④:prefill
    1. 世の中にある手法は、まだ使えない
    2. そもそも prefill は律速ではなかった
  12. 事前確保は「安全策」として効いている
  13. 測り方の罠が7個あった
    1. ① 同じプロンプトを2回送ると嘘の数字が出る
    2. ② 文脈の長さを書いていない数値は、比べられない
    3. ③ --help に載っていても動くとは限らない
    4. ④ 公式推奨値も他人のベンチも、自分の機体では反転する
    5. ⑤ 自分の診断リクエストが、本番のスロットを塞ぐ
    6. ⑥ /completion は chat template を通らない
    7. ⑦ pgrep -f が自分のSSHセッションにマッチする
  14. 作ったものを社外の人に見せる置き場として、VPSを1台借りている
  15. まとめ
  16. 付録:再現に使ったコマンド
  17. Qwen3.8-27B × Hermes Agent シリーズ
  18. 24時間回すなら、あると詰まらないもの

先に結論だけ

RTX3090 24GB を1枚だけ持っている状態で、エージェントに長い作業を任せるための構成。2日かけて詰めた結果がこれ。

前提はこれだけ。VRAM 24GBのGPU(この構成で22.9GB使う)/Docker と NVIDIA Container Toolkit/ディスクの空き20GB。モデルの取得は無料で、HuggingFaceのアカウントも要らない。初回だけ14.3GBのダウンロード待ちがある。

用語は次の節でまとめて説明する。ここは結果だけ並べる。

  • モデルは Qwen3.8-27B、量子化は UD-IQ4_XS。24GBに載る範囲で一番大きいクラス。GGUF(llama.cpp用のモデル形式)なので、登録なしでコマンド1本から取得できる
  • 推論エンジンは llama.cpp、文脈は262,144トークンを丸ごと1本に渡す。--parallel 1。同時に何人も捌く構成とは前提が違う
  • 投機デコードはMTP。下書き用の別モデルは要らない。本体のGGUFに同梱されている

そのうえで、速度のつまみを7種類ぜんぶ掃引した。

  • 効いたのは1つだけだった。投機デコードの下書き数 --spec-draft-n-max を既定の3から2に下げる。それだけで短いやり取りが +7.2% 速くなる
  • 残り6つは触っても無駄だと確定した。文脈長・量子化サイズ・prefill・別方式の投機・イメージ更新。どれも試したうえで戻した。探索をやめていい範囲が決まったことのほうが、7%より価値がある
  • 公式推奨値と他人のベンチは、自分の機体では反転する。推奨されていた n-max 7 が最速どころか最遅(MTPの半分)、推奨 p-min 0.60〜0.75 も逆効果だった。数字は借りずに測る
  • 測り方に罠が7個あった。同じプロンプトを2回投げるとキャッシュが効いて嘘の数字が出る、--help に載っていても実装が無い、自分の診断リクエストが本番のスロットを塞ぐ。全部、黙って間違った数字が出るタイプ

つまみの話は後半にまとめてある。先に、何をどう組んでいるのかから書く。

この構成で実際に何が作れるのかは、東京ゲームショウ2026を3Dで歩けるようにした記事に書いた。調査から実装まで、この1枚のGPUで通っている。

そもそも何を組み合わせているのか

設定の細部に入る前に、この構成が何でできているかを短く。すでに llama.cpp を動かしている人は、次の見出しまで飛ばしていい。

RTX3090 24GB 1枚、という出発点

VRAM(GPUが持っている作業用メモリ)が24GB。ここにモデルの重みと、会話の途中経過を覚えておくKVキャッシュの両方を収める必要がある。だから話は「24GBに入る中で、どれを選ぶか」から始まる。

Qwen3.8-27B ── なぜこのモデルなのか

27B(270億パラメータ)は、そのままの精度では24GBに入らない。量子化(重みの数値を粗くしてサイズを縮める処理)で4bit相当まで落とすと収まる。使っているのは unsloth が公開している GGUF の UD-IQ4_XS。アカウント登録もトークンも要らず、起動コマンドの中で指定するだけで落ちてくる。

ただ、24GBに入るモデルは他にもある。これを選んだのは、公式が出している数字がPC操作とエージェント用途に振れているから。Hugging Faceのモデルカードに載っている値を、Claude Opus4.6 Max と並べるとこうなる。

ベンチマーク 測っているもの Qwen3.8-27B Claude Opus4.6 Max
OSWorld-Verified デスクトップ画面を見て操作を完了できるか 84.3 72.7
AndroidWorld スマホ画面の操作 81.9 62.0
SWE-bench Pro 実在リポジトリのissueを直せるか 61.7 53.4
CoWorkBench 業務タスクの遂行 70.7 68.2
Terminal Bench 2.1 ターミナル上での作業 73.0 78.2

画面を見て操作する系で差が大きい。OSWorld-Verified は 84.3 対 72.7 で11.6ポイント上、AndroidWorld は 81.9 対 62.0。手元の3090に載るモデルが、この項目ではフロンティアモデルを上回っている。Hermes Agent にPC操作をやらせる用途と、測っている中身がそのまま重なる。ここが選んだ理由。

ただし全部で勝っているわけではない。ターミナル作業(Terminal Bench 2.1)は 73.0 対 78.2 で負けている。用途が「画面を見て操作する」側に寄っているほどこの選択が効く、という読み方が正しい。

もう1つ、262,144という文脈長はこのモデルのネイティブ値。無理に引き伸ばした設定ではなく、モデルカードに 262,144 natively と書いてある数字をそのまま使っている(1,000,000まで拡張できるとも書いてあるが、今回はネイティブのまま)。

※上の数値はモデル提供元が公開しているもので、自分で取り直したものではない。この記事で自分が測っているのは速度のほう。

量子化はサイズと引き換えに賢さが落ちる。もっと落とせばもっと速くなるのでは、というのも試した。結果は後半に書く。

llama.cpp ── GGUFを動かす推論エンジン

推論エンジン(モデルを実際に動かして文章を生成する土台)は複数ある。8月にllama.cpp・EXL3・vLLMを同じ3090で比べた記事を書いていて、そのときの結論は「並列で走らせるなら llama.cpp は不利」だった。KVキャッシュを並列数で先に固定分割してしまうため。

今回それでも llama.cpp を使っているのは、並列をやめたから。1本しか走らせないなら、固定分割そのものが起きない。この話は次の節で詳しく書く。

262K context ── エージェントで効いてくるのはここ

文脈長(モデルが一度に覚えていられる長さ)を262,144トークンに設定している。エージェントは調べて、失敗して、やり直して、また調べる。その履歴が全部そこに積まれていく。

投機デコード(MTP)── つまみが集中している場所

投機デコードは、小さくて速い予測器に先を何トークンか下書きさせて、本体がまとめて正誤を判定する仕組み。当たれば1回の処理で複数トークン進むので速くなる。外れた分は捨てる。

Qwen3.8-27B の場合、下書き役を別に用意しなくていい。本体のGGUFにMTPヘッドが同梱されているので、--spec-type draft-mtp だけで立ち上がる。この記事で唯一効いたつまみは、ここの「何トークン先まで下書きさせるか」だった。

この設定は誰向けなのか

先に線を引いておく。ここに書く設定は、1人(1本のエージェント)が使い切る前提のものだ。

--parallel 1 で動かしている。つまりスロット(同時に受け付けられるリクエストの枠)が1つしかない。複数人にAPIとして配ったり、Webサービスの裏側に置いたりする用途には向かない。そういう使い方をしたいなら、この記事の数字は参考にならない。

じゃあ何のためかというと、ゴールを渡して最後まで粘らせるタイプのエージェントのためだ。自分の場合は Hermes Agent(PC操作やWeb調査を、完了するまで自分で判断しながら進めるエージェント)を回している。調べて、失敗して、やり直して、また調べる。1本の長い作業を最後まで走らせる形。

この形だと、同時実行の数より1本にどれだけ文脈を積めるかのほうが効く。だからスロットを1つにして、262,144トークンを丸ごとそこに渡している。

前に「並列ならEXL3が2.2倍」と書いたのと矛盾しないのか

8月に書いた推論エンジンの比較記事で、llama.cpp はKVキャッシュ(AIが会話の途中経過を覚えておくメモリ)を並列数で固定分割してしまうので、メインとサブを同時に走らせると EXL3 に2.2倍負けると書いた。今回その llama.cpp を使っている。

矛盾はしていない。あの2.2倍は「2本同時に走らせたとき」の話だった。llama.cpp は --parallel 2 なら262,144を131,072ずつ先に割り当ててしまうので、片方が遊んでいる時間帯の分が丸ごと無駄になる。EXL3 は共有プールから必要なぶんだけ取るので、そこで差がついた。

並列をやめれば、固定分割そのものが起きない。--parallel 1 なら262,144が全部その1本のものになる。分ける相手がいないので、共有プールの利点も消える。

つまり選択はこうなる。

  • メインとサブを同時に回すなら EXL3。共有プールが効く
  • 1本を長く粘らせるなら llama.cpp を --parallel 1 で。固定分割は問題にならないし、後で書くとおり事前確保のおかげで途中で落ちない

エージェントを1本ずつ順番に回す運用に落ち着いたので、今回は後者を詰めた。

最終構成

モデル    Qwen3.8-27B-UD-IQ4_XS.gguf (14GB)
エンジン  llama.cpp (docker ghcr.io/ggml-org/llama.cpp:server-cuda / 2026-09-07 ビルド)
文脈      262,144(1スロット)
投機      --spec-type draft-mtp --spec-draft-n-max 2
KV        -ctk q4_0 -ctv q4_0
その他    -fa on / -ngl 99 / --cache-ram 12288 / -ub 512(既定)

実測      decode 59.6 tok/s (4.6K文脈) / 44.3 tok/s (57K文脈) / prefill 976 tok/s
VRAM      22,916 / 24,576 MiB

ここから、何を試して何が効かなかったかを順に書く。

同じ環境を作る手順

ここまでが「何が効いて何が効かなかったか」の記録。結論は出ているので、同じものを動かしたいだけの人は、ここだけ読めば足りる。

必要なもの

  • VRAM 24GBのGPU(自分は RTX3090)。この構成は22.9GB使うので、24GB未満だと文脈長を削る必要がある
  • DockerNVIDIA Container Toolkit(コンテナからGPUを使うための追加パッケージ)
  • ディスクの空き 20GBくらい。モデルが14.3GB、これに投機用のファイルが加わる
  • HuggingFace のアカウントは不要。登録なしで取得できるモデル

24GBのGPUを持っていない場合、いま何を買うか

ここから商品リンクを含みます(PR)。

この構成でいちばん効くのはVRAMの量で、それ以外はほとんど効かない。27Bを4bitに落として文脈を262K積むと22.9GB。16GBのGPUではこの記事の構成はそのまま載らないので、モデルを小さくするか文脈を削ることになる。

VRAM 代表的なカード この構成との関係
16GB RTX 5060 Ti / 5070 Ti / 5080 この記事の量子化(IQ4_XS)では載らない。量子化を落とせば27Bも載る(下記)
24GB RTX 3090(中古) この記事の構成がそのまま動く。ただし新品では買えない
32GB RTX 5090 27Bをもっと余裕のある量子化で置ける。文脈もさらに積める

16GBでも27Bを動かす手はある。量子化をもっと落とせばいい。Quesmaが公開している比較では UD-Q2_K_XL の重みは10.7GBまで縮むので、16GBのカードにも入る。

ただしタダではない。同じ比較で、エージェント系のベンチ(Terminal-Bench)が 77 → 72 で5点落ちる。知識系(GPQA 95→92)と指示追従(IFBench 85→85)はほとんど落ちないので、削られるのがちょうどエージェント用途の部分。加えて重みで10.7GB取ると残りがKVキャッシュの取り分になるので、262Kの文脈はそのままでは積めない。

つまり「安いカードで27Bを動かす」は成立する。ただしこの記事が狙っている用途から一番遠いところが削れる。数字は後半の量子化の節に置いた。

3090は新品の流通が終わっている。中古で18〜20万円あたり。VRAM24GBを最も安く手に入れる方法ではあるけれど、保証と使用履歴が読めないのと、350W級で電気を食う。この記事の数字は、その中古の3090で出したもの。

新品で24GBを超えようとすると RTX 5090(32GB)になるが、ここに2026年特有の事情がある。

GPU単体が高騰していて、BTO一式との差がほとんどない。2026年8月時点で、RTX 5090の単体最安が737,800円、同じ5090を積んだBTO一式が749,800円。差は12,000円。NVIDIAがGPUキットの供給価格を引き上げた影響がカード単体に集中していて、他のパーツをまとめて仕入れられるBTO側が相対的に安くなっている。

つまりいま5090を積むなら、カードを買って自分で組むより、載った状態で買うほうが安く上がる場合がある。5090はTGP 575W級なので、電源とケースの選定もついて回る。そこを込みで見積もると差はさらに縮まる。

BTOで探すなら、性格の違う2つを挙げておく。

  • 価格で選ぶならFRONTIER(BTOゲーミングPC)。セール中心の売り方をしていて、同じGPUでも時期で値段が動く
  • 構成を自分で指定したいならサイコム(@Sycom)。電源やケースを個別に選べるので、575W級を積むときに後から困りにくい

※価格は2026年9月10日時点で確認した数字。GPUの相場は月単位で動くので、買う前に現在の表示を見てほしい。VRAMの数字だけは変わらないので、そこを軸に選ぶのが確実。

1. モデルは手でダウンロードしない

llama.cpp の -hf フラグを使うと、起動時にHuggingFaceから自動で取ってくる。ファイルを探して手で配置する必要はない。

-hf unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS

取得先は unsloth/Qwen3.8-27B-GGUF。Unsloth Dynamic 量子化(層ごとにビット数を変えて、削っていい場所だけ削る方式)の版で、同じサイズの素朴な量子化より品質が落ちにくい。

量子化の選択肢はこう並んでいる。UD-IQ4_XS(14.3GB)が24GBでの最適点だったのは記事の前半で書いたとおり。

タグ サイズ 24GBでの立ち位置
UD-Q3_K_XL 13.1GB 速度はほぼ増えないのに品質が落ちる帯
UD-IQ4_XS 14.3GB ここを使う
UD-Q4_K_M 16.5GB 7%遅くなる。品質差は検出できなかった
UD-Q4_K_XL 17.6GB 文脈を削らないと載らない

ダウンロード先は /root/.cache/huggingface/hub/(コンテナ内)。ここをボリュームとして外に出しておかないと、コンテナを作り直すたびに14GBを取り直すことになる。

MTPヘッドは本体に同梱されている(別ファイルは要らない)

リポジトリを開くと mtp-Qwen3.8-27B-Q4_0.gguf という1.37GBの別ファイルが並んでいる。これも取ってこないと投機デコードが効かないのでは、と迷うところ。要らない。

UD-IQ4_XS の中身を確認すると blk.64.nextn.* が4個入っている。これがMTPヘッドで、別ファイルのほうは同じ4個を抜き出しただけのものだった。

ドラフトモデル指定(-md)を外して起動すると、ログにこう出る。

creating MTP draft context against the target model '...UD-IQ4_XS.gguf'

本体から立ち上がっている。速度も -md なしで 62.6 tok/s、あり で 60.6 tok/s と、実用上は同じだった。

つまり --spec-type draft-mtp --spec-draft-n-max 2 を渡すだけでいい。ドラフトモデルを別に取る必要も、-hfd を書く必要もない。ダウンロードが1.37GB減って、フラグも1つ減る。

ちなみに自分の起動スクリプトは -md を渡したままになっている。Qwen3.6の頃の名残で、3.8では本体に入ったので冗長になっていた。害はないが、書き起こすなら外したほうが素直。

2. 起動する

docker compose で書くとこうなる。docker-compose.yml と同じ場所に置いて docker compose up -d

name: qwen38-llama

services:
  llama-server:
    image: ghcr.io/ggml-org/llama.cpp:server-cuda
    container_name: qwen38-llama
    restart: unless-stopped
    runtime: nvidia
    ipc: host
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    environment:
      - LLAMA_ARG_HOST=0.0.0.0
      - LLAMA_ARG_PORT=8080
    volumes:
      - llama-cache:/root/.cache/huggingface
    command: >
      -hf unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS
      -c 262144
      -np 1
      -ngl 99
      --cache-type-k q4_0
      --cache-type-v q4_0
      --flash-attn on
      --spec-type draft-mtp
      --spec-draft-n-max 2
      --cache-ram 12288
      --jinja
      --metrics
    ports:
      - "8001:8080"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 10
      start_period: 1800s

volumes:
  llama-cache:
    driver: local

フラグの意味を1つずつ。

フラグ 意味
-c 262144 文脈長。速度には効かない。VRAMを決める
-np 1 スロット数。1本のエージェントに全部渡すならここは1
-ngl 99 全部の層をGPUに載せる
--cache-type-k/v q4_0 KVキャッシュを4bitに量子化。24GBに262Kを載せるにはここを削る必要がある
--flash-attn on attention の高速化。KV量子化とセットで使う
--spec-type draft-mtp モデル内蔵のMTPヘッドで投機デコード。別に下書きモデルを用意しなくていい
--spec-draft-n-max 2 この記事で唯一効いたつまみ。既定は3
--cache-ram 12288 使い終わった文脈をシステムRAMに退避する量(MiB)。会話を切り替えたときの再計算が減る
--jinja モデル同梱のチャットテンプレートを使う。ツール呼び出しに要る

初回は14GBのダウンロードがあるので、起動完了まで時間がかかる。healthcheck の start_period を短くしていると、落ちてもいないのに異常判定されて再起動を繰り返す。上の例で30分取っているのはそのため。

3. 起動したか確認する

推論を走らせずに状態だけ見る。スロットが1つしかないので、確認のために推論を投げると本番のリクエストを待たせることになる(後述する罠の⑤)。

curl -s localhost:8001/health                  # ok が返れば起動済み
curl -s localhost:8001/props  | jq .           # build_info / model_path / n_ctx
curl -s localhost:8001/slots  | jq .           # is_processing で使用中か分かる

VRAMの実際の使用量はこれで見る。

nvidia-smi --query-gpu=memory.used,memory.total --format=csv

22,916 / 24,576 MiB あたりで落ち着いていれば、この記事と同じ状態になっている。

4. speedを測る

OpenAI互換のエンドポイントを叩いて、返ってくる timings を読む。

curl -s localhost:8001/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen","messages":[{"role":"user","content":"日本の交通系ICカードの歴史を200字で"}],"max_tokens":256,"temperature":0}' \
  | jq '.timings | {prefill: .prompt_per_second, decode: .predicted_per_second}'

predicted_per_second が decode、prompt_per_second が prefill。/completion(v1が付かないほう)は使わない。チャットテンプレートを通らないので、生のプロンプトだと1トークンで止まる。

つまずきやすいところ

  • イメージは新しいものを使う。投機デコードまわりは更新が速く、--help に選択肢が出ていても実装が入っていないビルドがある。自分はこれで半日溶かした
  • ボリュームを外に出す。出していないと、コンテナを作り直すたびに14GBを取り直す
  • 他のものと同時に起動しない。24GBは1本で使い切る。別のモデルを動かしているなら先に止める

ここから先は、実際に動かした記録

ここまでが構成と手順。以降は2日ぶんの実験ログで、数字と失敗がそのまま並ぶ。上の構成をそのまま動かしたいだけなら、ここで読むのをやめても足りる。つまみを自分で回す気があるなら、この先が本体。

効いたもの:投機デコードの下書き数

投機デコード(先に小さいモデルで何トークンか下書きして、本体にまとめて答え合わせさせる高速化)は既に有効にしていた。Qwen3.8 には MTP というヘッドが焼き込まれていて、llama.cpp では --spec-type draft-mtp で使える。別のモデルを用意しなくていい。

取りこぼしていたのは、そこではなく --spec-draft-n-max(一度に何トークン下書きするか・既定3)のほうだった。

n-max decode (4.6K文脈)
1 58.6
2 ← 最速 60.6
3(既定) 56.6
4 50.6

+7.2%。2日のチューニングで得られたのは、実質これだけ。

ひとつ断っておく。同じ構成でも、測るセッションが変わると 59.6〜60.6 くらいのばらつきが出る(約1.7%)。なので上の表は、同じ日に続けて掃引した中の数字だけを並べている。日をまたいだ数字と比べると、ばらつきを効果と読み違える。

公式に推奨されていた値が、いちばん遅かった

もう一つのつまみ --spec-draft-p-min(下書きを採用する最低確率)。コミュニティの記録には「帯域律速のカードでは 0.60〜0.75 が効く」とあった。3090 はまさに帯域律速(メモリの読み出し速度で頭打ちになる状態)のはずなので、素直に上げてみた。

p-min decode
0.00(既定) 60.6
0.60 52.9
0.75 49.4

逆だった。推奨値は環境で反転する。これはこの記事でずっと繰り返されるテーマになる。

ただし、長い文脈では差が消える

ここがエージェント用途では重要なところ。

4.6K 文脈 57K 文脈
n-max 2 60.6 44.3
n-max 3(既定) 56.6 44.0

57Kまで積むと、n-max を変えた差はほぼ消える。

エージェントの長い作業は60K前後で動くので、メインの会話そのものは速くならない。ではどこに効くかというと、短いやり取りのほうだ。

  • サブエージェントに投げる小さな判断
  • ツールを叩いた結果を受けて次の一手を決める短い応答
  • 単発のチャット

エージェントを回していると、この短い往復の回数がとにかく多い。1回あたりは小さいが、それが何十回も積み上がる。そこに7%が乗るという理解でいる。

効かなかったもの①:別の投機方式

DFlash2 と DSpark という新しい投機デコードが llama.cpp に入っている。MTP より速いという触れ込みだったので試した。

方式 n-max decode
draft-mtp(モデル内蔵) 2 59.6
draft-dflash(DFlash2) 2 47.7
draft-dflash 4 41.8
draft-dflash 7 ← 公式推奨 31.2
draft-dspark 起動しない

公式に推奨されていた n-max 7 が、内蔵MTPの半分の速度だった。

DSpark はそもそも起動しない。理由が構造的で面白い。

failed to allocate CUDA0 buffer of size 5368709120  (5GB)
→ failed to allocate buffer for kv cache

DSpark は下書き専用のKVを別に要求する。MTP は本体のKVを共有する。24GBだとこの差がそのまま勝敗になる。llama.cpp には下書き側の文脈長だけを絞るフラグが無いので、逃げ道も無い。

ここで一度、判断を間違えかけた

llama-server --help--spec-type には draft-dflashdraft-dspark も列挙されていた。なのに両方とも起動に失敗する。原因はイメージが古かったことだった。

手元のイメージ    2026-08-15 ビルド
DFlash2 対応PR    2026-08-27 マージ   ← 12日足りない

「–help に載っている」は「動く」ではない。選択肢の一覧だけ先に入って、実装が後から来ることがある。イメージを09-07版に更新したら DFlash2 は起動した。そして MTP に負けた。

効かなかったもの②:文脈長を削る

「文脈を絞れば速くなるのでは」。直感的だが、間違いだった。

構成 decode VRAM
IQ4_XS / 262K 59.6 22,916
IQ4_XS / 131K 59.7 ← 差なし 19,332(−3,584)

-cKVを前もって確保しておく量にすぎない。decode のときに実際に読むのはいま埋まっているトークンの分だけだ。4.6Kしか入っていなければ、枠が262Kでも131Kでも読む量は同じ。だから速度が変わらない。

-c は速度のレバーではなく、VRAMのレバーだった。

そしてここが大事で、浮かせただけの空きVRAMは何の役にも立たない。3.5GB空いても、そこに置くもの(埋め込みモデルなど)が決まっていなければ、文脈の上限を半分にして失うものだけが確定する。使い道が決まってから削るのが順序として正しい。

効かなかったもの③:量子化をもっと落とす

「もっと小さくすれば速くなるのでは」。これも測ると否定される。

まずサイズと速度の関係を2点から出す。

IQ4_XS  14.0GB → 59.7 tok/s
Q4_K_M  16.5GB → 55.5 tok/s

サイズ +17.6% で 速度 −7.1%   ⇒ 傾き ≒ −0.40

重みのバイト数は decode の時間を支配していない。KVを読む時間、attention の計算、下書きの生成、サンプリング。これらの固定費が大きいので、重みを削っても比例しては速くならない。この傾きで外挿するとこうなる。

候補 サイズ 予測 decode
Q3_K_XL 12.8GB +3.4%
EXL3 2.87bpw 11.5GB +7.1%

取れてもせいぜい +7%。では品質のほうはどうか。ここは自分の実測ではなく、公開されているベンチを参照する。

Quesma が公開している量子化別の品質比較(同一モデルの BF16 55GB を基準)

Q4_K_M      17GB    GPQA 94 / IFBench 85 / Terminal-Bench 77   ← BF16(95/85/77)とほぼ同じ
UD-Q2_K_XL  10.7GB  GPQA 92 / IFBench 85 / Terminal-Bench 72   ← エージェント系で −5点
UD-IQ1_S     6.2GB  ランダム同然

11.5GB は「−5点だった10.7GB」と「12.8GB」の間、つまり劣化が始まる帯に入る。+7%の速度と引き換えにエージェント性能を5点落とすのは、割に合わない。エージェントは失敗すると全部やり直しになるので、なおさら。

名前で比べてはいけない

量子化の名前は、実際のビット数を素直には表していない。EXL3 の作者本人が、EXL3 の 3.00bpw はほぼ正確に3ビットなのに対し、UD_Q3_K_XL は平均で約3.9bpw だと書いている。

GGUF の名前は、実 bpw より小さく見える。比べるならファイルサイズで比べる。

結論として IQ4_XS(14GB)が最適点だった。上に行っても遅くなるだけで品質の差は検出できず、下に行っても速度がほとんど増えないのに品質が落ちる。

効かなかったもの④:prefill

エージェント用途では prefill(送った文章を読み込む工程)こそ効きそうに思える。llama.cpp で触れるのは -ub(一度に処理するトークンのかたまりの大きさ)だけ。

-ub prefill VRAM
512(既定) 976 tok/s 22,916(空き 1,660)
1024 1,018(+4.2%) 23,578(空き 1,000)
2048 起動しない VRAM不足

+4.2%のために安全余裕を660MiB削ることになるので見送った。理由は後述する。

世の中にある手法は、まだ使えない

  • UBBoost(llama.cpp の discussion #23262)— prompt を処理するときだけ ubatch を上げる仕組み。実測で +38%〜+125% と、まさに今回VRAMで詰まった問題の解になっている。ただし未マージの試作で、作者自身が概念実証だと書いている
  • SpecPrefill / PFlash(issue #24126・ICML 2025)— 小さいモデルでトークンの重要度を採点して、本体は上位だけを prefill する。128Kで最大10倍を主張。llama.cpp には未実装で、issue は「対応予定なし」で閉じられている。そして重要度の低いトークンを捨てる=元に戻せないので、RAGや書類読み取りのように「どこに答えがあるか事前に分からない」用途では取りこぼす危険がある

そもそも prefill は律速ではなかった

いちばん効いたのは、測ってみたらそこがボトルネックじゃなかったという事実のほうだった。

同じプロンプトを再送したときに実際に評価したトークン数   20,007 → 4
サーバ申告の cache_read_ratio                          98.9%

prefix cache(前と同じ前置きの計算結果を使い回す仕組み)が効いている限り、エージェントは毎ターンの prefill をほとんど払っていない。

prefill が効くのは、①セッションの初回 ②長い文書をまるごと読み込むとき ③写真やPDFを次々に処理するとき(毎回 cold になる)。この3つだけだった。

事前確保は「安全策」として効いている

llama.cpp は起動時にKVを全部確保する。一見もったいないが、エージェント用途ではこれが効いている。

アイドル               22,916 MiB(空き 1,660)
小さい画像 640x200     +82 MiB
大きい写真 4032x3024   +346 MiB(ピーク 23,344)

動的に増えるのは vision タワー(画像を読む部分)の数百MiBだけ。長いリクエストが途中でメモリ不足になることが、原理的に起きない。

対比として、動的に確保する方式(EXL3 の共有KVプール)は、15万トークンに大きい写真3枚を同時に投げたところ、メモリ不足でプロセスごと落ちた。

エージェントは途中で落ちると、そこまでの作業が全部消える。1時間粘らせた末に落ちるのと、起動時に「無理だ」と弾かれるのとでは、後者のほうが圧倒的にましだ。今回 DSpark が起動しなかったのも、会話の途中ではなく起動時に5GBを要求して拒否されたもの。事前確保は、失敗を起動時に前倒しする仕組みだと考えると腹落ちする。

先ほど -ub を上げなかったのも同じ理由で、+4.2%と引き換えに落ちる確率を上げたくなかった。

測り方の罠が7個あった

ここが記事の本命。2日で7個踏んだ。全部、エラーも出さずに間違った数字が出るタイプだった。

① 同じプロンプトを2回送ると嘘の数字が出る

prefix cache が効いて、評価するトークン数が 20,007 から 4 に落ちる。この状態で prefill を測ると、実際にはありえない数字が出る。

prefill を測るときは毎回ちがうプロンプトを作る。逆に decode を測るなら同じプロンプトでいい(むしろばらつきが減る)。測る対象によって使い分けるのが正解だった。

② 文脈の長さを書いていない数値は、比べられない

同じ構成でも 4.6K で 60.6、57K で 44.3 と26%違う。

過去の記録の「41.3 tok/s」と今回の「56.6 tok/s」を並べて、速くなったと喜びかけた。条件が違っただけだった。tok/s を残すときは、文脈の長さもセットで書く。

③ --help に載っていても動くとは限らない

前述のとおり。選択肢の一覧に名前があるのに、そのビルドには実装が入っていなかった。起動しないときは、まず自分の設定を疑う前にビルド日とPRのマージ日を突き合わせる。

④ 公式推奨値も他人のベンチも、自分の機体では反転する

推奨 n-max 7 が最遅、推奨 p-min 0.60〜0.75 が逆効果。

さらに、コミュニティの表にあった「79.6 tok/s」に届かないので設定が悪いのかと疑ったが、その数字はオーバークロックした機体のものだった。条件を揃えて測り直したら、自分の機体はむしろ平均より上だった。借りてきた数字を目標にすると、追いかけなくていいものを追いかけることになる。

⑤ 自分の診断リクエストが、本番のスロットを塞ぐ

--parallel 1 で運用しているので、スロットは1つしかない。状態を調べようとして投げたリクエストの後ろで、本番のリクエストが待たされていた。「急に遅くなった」の犯人が自分だった。

状態確認は推論を走らせずに済ませる。

curl -s host:8001/props   # build_info / model_path / n_ctx
curl -s host:8001/slots   # is_processing / n_prompt_tokens_processed

⑥ /completion は chat template を通らない

生のプロンプトを投げると1トークンで止まる。チャット用に調整されたモデルを測るときは /v1/chat/completions のほうを使う。

⑦ pgrep -f が自分のSSHセッションにマッチする

検索したパターン文字列が、ssh のコマンド行そのものに含まれてしまうため。llama-serve[r] のように文字クラスで書いて逃げる。

作ったものを社外の人に見せる置き場として、VPSを1台借りている

エージェントに作らせたものは、当然だけど自宅のマシンの中にある。動いているところを社外の人に見てもらう段になると、自宅サーバーをそのまま外に開けるのは筋が悪い。その置き場としてVPSを1台借りている。月数百円で、要らなくなったら消せるのがちょうどいい。

使っているのはConoHaとKAGOYA。デモ用に立てて、見てもらって、終わったら消す。自分だけが見るなら外に出す必要はなくて、その場合はTailscaleで足りる。

9月17日までPR

自分が使っている ConoHa VPS が、初回料金最大68%OFF

68%は36ヶ月契約のときの数字。2GBプランで月632円(更新時は903円)。新規契約のみで、いま動いているサーバーは対象外。

キャンペーンを見る

2026年9月9日時点の内容。割引率は月ごとに変わるので、金額と終了日は公式ページで確認してください。

まとめ

いじった対象 結果
投機の下書き数 --spec-draft-n-max 2が最適。短いやり取りで +7.2% ← 唯一の当たり
投機の --spec-draft-p-min 既定の0.00が最良。上げると悪化
別方式の投機(DFlash2 / DSpark) MTPに負ける/VRAMで詰む
文脈長 -c 速度は変わらない。VRAMのレバー
量子化サイズ 上も下も損。IQ4_XS が最適点
prefill -ub +4.2%と引き換えに安全余裕を失う
イメージ更新 速度の回帰は無し。ただし新機能は使えなかった

2日かけて、当たりは1つ。

ただ、残りの6つが「触っても無駄」だと確定したことのほうが、実は大きいと思っている。次に速度が気になったとき、もうこの6つは見に行かなくていい。チューニングの成果は速くなった数字より、探索空間を閉じたことのほうだった。

そして、エージェントを回す人にとっての本当の結論はここになる。速度で稼げるのは数%だが、途中で落ちれば1時間の作業がゼロになる。だから -ub も量子化も、余裕を削る方向には振らなかった。

付録:再現に使ったコマンド

# 設定を環境変数で差し替えられるようにしてある
SPEC_NMAX=2 bash swap-qwen38.sh xs 262144 12288 1 on
UBATCH=1024 SPEC_NMAX=2 bash swap-qwen38.sh xs 262144 12288 1 on
SPEC_TYPE=draft-dflash SPEC_DRAFT=zlab-dflash2-Qwen3.8-27B-Q4_K_M.gguf SPEC_NMAX=7 \
  bash swap-qwen38.sh xs 262144 12288 1 on

掃引の前に、5分ごとに動くウォッチドッグを止めておく。既定のままだと勝手に起動し直して掃引が壊れる。自分はこれで一度やり直した。

計測は /v1/chat/completionstimings を読む。predicted_per_second が decode、prompt_per_second が prefill。

Qwen3.8-27B × Hermes Agent シリーズ

RTX3090を1枚だけ持っている状態で、手元のAIをどこまで実用にできるかを順に試した記録。

  1. 機材を選ぶ ── ローカルLLMはMacとGPUどっちで動かす?
  2. エンジンを選ぶ ── 推論エンジンはどれを選ぶ? llama.cpp・EXL3・vLLMを実測
  3. 構成を詰める ── RTX3090単体でHermes Agentを回す最良の構成 ← いまここ
  4. 性格を知る ── Qwen3.8-27Bにサブエージェントを任せたらどうなるか
  5. 調べさせる ── Qwen3.8-27B × Hermes Agent で調査エージェントを作った
  6. 丸ごと任せる ── 「港区のうまい店を調べて特集ページ作って」と頼んだら
  7. サービスを作らせる ── 東京ゲームショウ2026を3Dで歩けるようにした

24時間回すなら、あると詰まらないもの

ここから商品リンクを含みます(PR)。

エージェントを回すというのは、GPUを数時間〜十数時間、連続で高負荷にし続けるということ。ゲームと違って休みが無い。そこで効いてくるのは速度のつまみではなく、熱とホコリと電気代のほうだった。

上から2つで足りる。全部そろえる必要はない。

  1. ワットモニター — 連続稼働にすると電気代が読めなくなる。コンセントに挟むだけで実消費が数字で出る。「思ったほど食っていない」のか「想像以上だった」のかが分からないまま回し続けるのが一番よくない
    サンワサプライ ワットモニター
  2. 電動エアダスター — 24時間ファンが回るとホコリの溜まり方がまるで違う。スプレー缶と違って充電式は残量を気にせず使えるので、結果的に掃除の頻度が上がる
    エレコム 電動エアダスター 充電式
  3. ケースファン — 熱でクロックが落ちると、この記事で稼いだ7%どころではない量を失う。排気を足すのが一番安い対策
    SCYTHE Wonder Snail 2400 PWM 12cm
  4. GPUサポートステー — 3090は重い。挿しっぱなしで何ヶ月も動かさない使い方だと、スロット側にずっと荷重がかかる
    upHere GPUサポートステー
  5. スイッチングハブ — 推論サーバを別筐体にすると、メインPCとの間を有線で繋ぐことになる。エージェントからのリクエスト自体は軽いけど、GGUFを取ってくる段階では20GB超が流れるので、ここが細いと待ち時間にそのまま出る
    TP-Link スイッチングハブ 5ポート TL-SG605(Amazon限定)
  6. WiFiルーター — 家の外からエージェントに作業を投げるなら、経路の一番細いところが自宅のルーターになる。IPv6とEasyMeshに対応していれば、あとで推論サーバを別の部屋に移したときに繋ぎ直しで悩まない
    TP-Link WiFiルーター AX3000 Archer AX3000V(Amazon限定)

1〜4は自分の環境で使っているわけではないものも含む。効く理由だけ書いて、使用感は書いていない。手元にあるのはワットモニターとエアダスターの2つ。5と6のネットワーク機材は、自分が選んで挙げているもの。

この検証もGPUを2日占有しているので、それなりに電気を食っている。何か買うものがあるときに、この記事のリンクから入ってもらえるとコーヒー代くらいにはなる。次の検証の燃料になるので、気が向いたら。

当サイトは、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。

実測はすべて RTX3090 24GB 単体・Qwen3.8-27B・n=1・2026年9月7〜8日。decode の数値は文脈4.6Kのもので、文脈長によって大きく変わる。品質ベンチのみ Quesma の公開データを参照した。

タイトルとURLをコピーしました