- ローカルなコーディングエージェント構成は、インターネット障害時でも macOS 上で OpenAI 互換 API によりモデルを実行し、Pi でテキストと画像入力を処理できるようにする設定
- Apple M1 Max 64GB、macOS 15.7.7 で llama.cpp Metal と Gemma 4 26B-A4B GGUF モデルを使用し、基本の生成速度は 58.2 tok/s だった
- MTP draft model を追加し、
--spec-draft-n-max 3 に調整した後、生成速度は 72.2 tok/s まで向上し、約 24% 改善した
mmproj-BF16.gguf を --mmproj で読み込み、Pi のモデル入力を ["text", "image"] に設定することで、スクリーンショットのような 画像入力 が渡される
- 最終構成では llama.cpp サーバーを
127.0.0.1:8080/v1 で実行し、Pi がそれをローカルプロバイダーとして使用する。Qwen3.6 35B-A3B はより優れたコーディングエージェントのベンチマークを示したが、このテストでは 55 tok/s とより遅かった
ローカルなコーディングエージェント構成の目標
- インターネット障害が何度か発生し、コーディングエージェントを使えなかったことが、ローカル実行構成を試すきっかけになった
- 望んでいた構成は、Mac で実用的な速度で動作し、OpenAI 互換 API を通じて他のツールからも利用できることだった
- 必要なときにスクリーンショットや画像を処理し、エージェントが作成した結果を再び入力として渡せる構成を目指した
- 最終構成は
llama.cpp、Gemma 4 26B-A4B GGUF、Q8 MTP draft model、Gemma 4 multimodal projector、Pi ターミナルコーディングエージェントで構成される
- テスト環境は Apple M1 Max、64GB 統合メモリ、macOS 15.7.7 だった
モデル
- メインモデルは
gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf で、Hugging Face の unsloth-gemma-4-26B-A4B-it-GGUF リポジトリにある
- このファイルサイズは約 16GB で、MTP draft head と multimodal projector を合わせて置くと、モデルフォルダーは約 17GB になる
- ベンチマーク用プロンプトは
Write a compact Python function that parses a unified diff and returns the changed file paths. Then explain two edge cases. だった
- 各ベンチマークでは約 128 トークンを生成する
基本実行: llama.cpp + Metal
- メインモデルは
llama.cpp と Metal アクセラレーション で直接実行した
- 実行コマンドは
llama-cli にモデルパス、-ngl 999、-fa on、-c 4096、-n 128 を指定する形だった
- 基本構成でのプロンプト処理速度は 298.0 tok/s、生成速度は 58.2 tok/s だった
- 58 tok/s は高速とまでは言えないが実用可能な水準であり、コーディングエージェントの作業ではツール呼び出しが多いため、できるだけ高速である必要がある
MTP draft model の追加
- Gemma 4 には
MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf 形式の MTP draft model が提供されている
llama.cpp ではこれを --model-draft、--spec-type draft-mtp、--spec-draft-n-max で speculative decoding に読み込む
- MTP の最初の実行では、draft token 4 個で 69.2 tok/s を記録した
- Unsloth のドキュメントでは
--spec-draft-n-max 2 を開始点として推奨しているが、1 から 6 までをハードウェアごとに試し、最も速い値を使うよう勧めている
--spec-draft-n-max を調整した結果、draft token 3 個で 72.2 tok/s が最速だった
- メインモデル単体は 58.2 tok/s で、Q8 MTP draft model を追加した構成は 72.2 tok/s だった
- プロンプト処理速度はほぼ維持され、生成速度は約 24% 改善した
MTP チューニング結果
--spec-draft-n-max の値を 1 から 6 までテストした
- 値 1 はプロンプト 295.5 tok/s、生成 68.4 tok/s だった
- 値 2 はプロンプト 299.1 tok/s、生成 72.0 tok/s だった
- 値 3 はプロンプト 295.6 tok/s、生成 72.2 tok/s で最速だった
- 値 4 は生成 70.7 tok/s、値 5 は 63.7 tok/s、値 6 は 61.2 tok/s と遅くなった
- M1 Max 環境では
3 が最速で、2 も十分近い結果だった
MLX との比較
- Mac でモデルを動かすより高速な方法があるかを確認するため、
mlx-lm ベースの MLX モデルもテストした
llama.cpp Metal + MTP は Unsloth GGUF Q4 と Q8 MTP の組み合わせで 72.2 tok/s を記録した
llama.cpp Metal 単体は Unsloth GGUF Q4 で 58.2 tok/s を記録した
MLX-LM は Unsloth UD MLX 4-bit で 45.8 tok/s を記録した
MLX-LM は mlx-community 4-bit で 43.9 tok/s、mlx-community OptiQ 4-bit で 38.1 tok/s を記録した
- この特定の構成では llama.cpp が MLX より高速で、MTP を適用した llama.cpp が最良の選択だった
gemma-4-swift-mlx で Gemma 4 MTP も試したが、テストした 26B 4-bit MLX チェックポイントがローダーの想定する weight key と一致せず、新しいモデルを再ダウンロードして調整することなく中断した
画像サポートの追加
- Pi でスクリーンショットを添付するには、モデル入力がテキスト専用ではいけない
- 元のローカルモデル項目は
"input": ["text"] に設定されており、この場合 Pi は画像ツールの出力をモデルに正しく送れなかった
llama.cpp サーバーでも、マルチモーダル機能のために Gemma 4 の multimodal projector である mmproj-BF16.gguf が必要になる
--mmproj で projector を読み込むと、llama.cpp がマルチモーダル対応を通知し、Pi が画像を送れるようになる
- projector なしで
llama.cpp Metal + MTP を実行したテストでは、プロンプト 120.3 tok/s、生成 71.4 tok/s だった
mmproj-BF16.gguf を読み込んだ最終実行では、プロンプト 297.4 tok/s、生成 72.2 tok/s だった
- projector を読み込んだ最終実行では、テキスト生成速度の低下は見られなかった
llama.cpp のインストール
- 依存関係は Homebrew で
cmake、git、tmux、python@3.11 をインストールする
~/Developer/ML-Models/Gemma4/repos パスを作成し、ggml-org/llama.cpp リポジトリを repos/llama.cpp にクローンする
- ビルドは
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON -DGGML_ACCELERATE=ON で構成する
- その後
cmake --build build --config Release -j でリリースビルドを行う
- テストしたビルドは
GGML_METAL=ON、GGML_ACCELERATE=ON、GGML_BLAS=ON、GGML_BLAS_VENDOR=Apple の設定を持つ
モデルファイルのダウンロード
- Python 3.11 の仮想環境を作成し、
huggingface_hub と hf_xet をインストールする
huggingface-cli download で Gemma 4 メインモデル、mmproj-BF16.gguf、MTP draft model をダウンロードする
- ダウンロード対象ファイルは
gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf、mmproj-BF16.gguf、MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf である
- 最終的なモデルフォルダーは
models/unsloth-gemma-4-26B-A4B-it-GGUF/ 配下に 3 つのファイルを含む
ローカルサーバーの起動
- 最終サーバーは
llama-server で実行し、メインモデル、MTP draft model、multimodal projector をすべて指定する
- 主なオプションは
--spec-type draft-mtp、--spec-draft-n-max 3、-ngl 999、-fa on、-c 65536、--parallel 1 である
- サーバーは
--host 127.0.0.1 --port 8080 で起動する
- OpenAI 互換エンドポイントは
http://127.0.0.1:8080/v1 である
start_server.sh ラッパーは tmux セッションでサーバーを実行し、ログを logs/llama-server-mtp.log に残す
chmod +x start_server.sh の後、./start_server.sh でサーバーを起動する
- サーバーが動作しているかは
curl http://127.0.0.1:8080/v1/models で確認する
Pi の設定
- Pi はモデルプロバイダー設定を
~/.pi/agent/models.json から読み込む
- ローカルプロバイダー
gemma4-local の baseUrl は http://127.0.0.1:8080/v1 を指す
api は openai-completions で、ローカルサーバーなので authHeader は false にしておく
- モデル ID は
gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf、名前は Gemma 4 26B-A4B Q4 + MTP に設定する
input は ["text", "image"] でなければならず、そうでない場合 Pi はモデルをテキスト専用として扱う
- コンテキストウィンドウは 65536、最大トークン数は 8192 に設定する
- 必要であれば
~/.pi/agent/settings.json で defaultProvider を gemma4-local、defaultModel を該当する GGUF ファイル名に指定する
pi --offline --list-models gemma を実行したとき、画像対応が yes と表示されることを期待する
- ローカルモデルの実行は
pi --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf で行う
- 非対話型実行は
pi -p --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf "Explain what this repository does" の形で行う
- スクリーンショット入力は
pi -p @"/path/to/screenshot.png" "Describe this image and point out anything relevant to the UI" の形で行う
最終構成
- 最終的な推論ランタイムは
llama.cpp
- macOS のアクセラレーションは Metal + Accelerate の組み合わせ
- メインモデルは
gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf
- draft model は
gemma-4-26B-A4B-it-Q8_0-MTP.gguf
- MTP 設定は
--spec-draft-n-max 3
- multimodal projector は
mmproj-BF16.gguf
- サーバーは
127.0.0.1:8080 の llama-server
- API は OpenAI 互換の
/v1
- コーディングエージェントは Pi で、Pi のモデル入力は
["text", "image"]
- MTP draft model はこの環境で Gemma 4 の生成速度を 58.2 tok/s から 72.2 tok/s に引き上げ、ローカルの OpenAI 互換サーバーとして動かすのに十分シンプルな構成だった
Qwen3.6 35B-A3B の代替案
- 一部では
Gemma 4 26B-A4B の代わりに Qwen3.6 35B-A3B の使用を勧めている
- 確認可能なベンチマークでは、Qwen は Gemma 4 よりはるかに優れたコーディングエージェントと評価されている
- ただし Qwen の構成はより遅く、
Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf、unsloth-Qwen3.6-35B-A3B-MTP-GGUF、mmproj-BF16.gguf の組み合わせで 55 tok/s を記録した
- 72 tok/s ではなく 55 tok/s という差は、ユーザーが待たされる場面ではかなり大きい
- Qwen モデルのダウンロードは
unsloth/Qwen3.6-35B-A3B-MTP-GGUF から Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf と mmproj-BF16.gguf を取得する形で行う
- Qwen サーバーは同じ
llama-server を使うが、--port 8081 で起動する
- Pi 設定での Qwen プロバイダー名は
qwen36-local、baseUrl は http://127.0.0.1:8081/v1
- Qwen モデル設定は
reasoning: true、input: ["text", "image"]、contextWindow: 65536、maxTokens: 8192 を使う
1件のコメント
Hacker Newsの意見
ベンチマークのプロンプトが「unified diffをパースして変更されたファイルパスを返す簡潔なPython関数を書き、エッジケースを2つ説明せよ」で、各ベンチマークが約128トークンを生成していたなら、良い結果を得るには 128トークン は少なすぎるように見える
MTP高速化は予測トークンがどれだけ頻繁に採用されるかに依存するが、経験上、出力の前半ほど採用率が高いので、短いテストは 偽陽性の高速化 を生む可能性がある
llama.cppには、サーバーを再起動してプロンプトを送る必要なく引数を一通り試せるベンチマーク専用ツールがある: https://github.com/ggml-org/llama.cpp/blob/master/tools/llam...
モデルダウンロードのセクションでも、llama.cppの
-hf引数で代わりにモデルを取得できる点に触れるべきだった。作者が経験を共有してくれたのはありがたいが、初心者にとって最善のガイドではないかもしれないUnclotheの「2倍高速」という発表を見て、「これなら実用になるくらい速くなるのか?」と思って自分で設定してみた
去年もDevstralのようなもので試したが、遅すぎるうえに賢くなくて使い続ける気にならなかったし、今回はついに速度と知能の両方で 実用的だ と感じるところまで来た
llama.cppにはそのためのツールがあり、きちんと測るにはトークン生成前に プリフィル(prefill) を入れる必要がある。32kや64kのような長いコンテキストでのトークン生成速度測定も、ますます重要になっている
以前、ollamaとopencodeを使って似たような記事を書いたことがある: https://blog.kulman.sk/running-local-llm-coding-server/
opencodeはシステムプロンプトがコンテキストを食いすぎていないか? ローカルモデルはコンテキスト制約が大きいが、記憶ではopencodeはそのうち10kほど、あるいはそれに近い量を使っていた
llama.cppだけを使うなら、何かをダウンロードするのに
huggingface-cliが必須というわけではなさそうだ。-hf ...を渡せばモデルを取得してくれるダウンロード先を変えるには
LLAMA_CACHEを設定すればよい:LLAMA_CACHE="models" ./llama-server \\-hf unsloth/gemma-4-31B-it-GGUF:UD-Q4_K_XL \\...-hfdを使えばよい統合メモリRAMは大きいがテラフロップスと帯域幅GB/sが中程度以下なら、通常は MoE が最も有望だ。自分の環境であるM2 Max 96GBでは、
(知能, tok/s, コンテキスト深度)の基準で現在の1位は DeepSeek-V4-Flash REAP25<65gb gguf+ ds4-server + pi agentもちろんクラウドAPIより優れているわけではないが、必要なら受け入れて使える程度にはなっている。インターネットのない4時間のフライトでも、ローカルLLMが60Wを消費していたのにバッテリーは十分もった
REAPをサポートするds4ブランチはこちら: https://github.com/ljubomirj/ds4/tree/reap-compact-support
DS4Fが 784Kコンテキスト になってようやく10 tok/s未満の実用外レベルまで落ちるという点が大きな違いを生んでいる
こうしたローカルモデルが、特定のプログラミング言語の専門家ではないユーザーに対しても本当に問題を解決してくれるのか気になる
インライン自動補完や単位実装を超えて、実際に動く 技術仕様 を設計して組み立てられるのか、確信が持てない
llama.cpp/serverを使ってローカルLLMを立ち上げ、Claude CodeやCodex-CLIと一緒に使うのは比較的簡単
必要なllama serverの設定があちこちに散らばっていることが多いので、人気のあるオープンLLMいくつかについての手引きをここで管理している: https://pchalasani.github.io/claude-code-tools/integrations/...
omlx.aiを使って自分のハードウェアに合う複数の MLXモデル をダウンロードし、そのモデルでオープンソースおよびクローズドなハーネス(Claude Code、Codex)を自動実行するのにかなりうまく使えた
WebでもデスクトップUIでも使えるので、個人的にはomlxを使えばブログ記事をなぞる必要はない
これまで見つけたGemma 4 MLXビルドは、同じ量子化でより遅く、MTPでははるかに遅かった
モデルを選んだ後はllama.cppの内蔵Web UIがかなり良く、あれこれ試すならLM Studioも悪くない
Gemma-4とQwen 3.6には、一般的なopencodeのシステムプロンプトの大きな塊はまったく不要で、むしろ外したほうがよい
antirezのds4で動かす DeepSeek v4 Flash はかなり印象的だった
「蓄積された知識」という面ではGPT-4級モデルのように感じるが、長い流れのツール呼び出しはGPT-4級モデルよりもうまくこなす
128GB MBP M4 Maxで生成は約24 t/s、プリフィルは約200 t/s出る。遅いだろうと思っていたし、コード生成のような作業では実際に遅いが、簡単な作業のための マシンオーケストレーター としては驚くほど有用だ
エージェント的でない用途でも会話するには十分良いモデルで、完全に自前で動かせて非公開にできる利点もある
[0]https://github.com/antirez/ds4
とにかく怠けたいなら、ターミナルでClaude Codeを開き、この記事を指して、ただ「やって」と言えばよい
一方でClaudeは、一発で処理するか、ほんの少し手直しするだけでやってくれる
知識と実行への入口は今や LLM であり、Google Searchは恐竜のように感じる
スマートフォン以上にすごいとさえ思え、1世紀ほど未来に来てしまったような感覚だ