3BエージェントモデルをApple Siliconで本当に動かす──Nanbeige4.2-3Bの5つの落とし穴とメモリ2.7倍化テクニック
はじめに:Apple SiliconでAIエージェントは動くのか?
「ローカルLLMでAIエージェントを動かしたい」——この願いを持つエンジニアは少なくない。クラウドAPIに依存せず、自分のMac上でエージェントがツールを呼び出し、コードを書き、ファイルを操作する姿を想像する。ところが、Apple Silicon(MPSバックエンド)にはCUDA環境とは根本的に異なる制約がある。特にユニファイドメモリはGPUとCPUで共有されるため、大規模なプロンプト処理ではすぐにメモリの壁にぶつかる。
Nanbeige4.2-3Bは、こうした期待と制約の交点に現れたモデルである。3Bパラメータ(非埋め込みパラメータ)というコンパクトな規模ながら、SWE-Bench Verifiedで63.6%、GPQA-Diamondで87.4%を記録し、Qwen3.5-9BやGemma4-12Bといった大規模モデルを凌駕するエージェント性能を誇る。Looped Transformer(LT)アーキテクチャを採用し、同一のレイヤースタックを2回通過させることでパラメータ効率を極限まで高めた設計が、この驚異的なスコアを実現している。
しかし、理論上の性能と実際のデプロイ可能性の間には深い溝がある。本稿では、この「理想のモデルを現実のApple Siliconで動かすと何が起きるか」を、バグ修正・メモリ最適化・ベンチマーク評価の3段階で検証する。そのままでは動かない5つのデプロイバグ、Looped Transformerが招くメモリ消費の倍増、そしてシステムプロンプトに隠された回帰バグ——これらすべてを乗り越えた先に、Nanbeige4.2-3BはあなたのMacで真価を発揮する。あなたのMacでも、このモデルは動くはずだ。ただし、そのままでは動かない。
Nanbeige4.2-3Bとは?──Looped Transformerがもたらすパラメータ効率の革命
コンパクトなエージェントモデルの現在地
Nanbeige4.2-3Bは、非埋め込みパラメータがわずか3B(約30億)のコンパクトなエージェント特化型モデルである。28Tトークンのプレトレーニングに加え、MCPツール呼び出し・コードエージェント・オフィスワークフローといったエージェントタスク向けのSFT(教師ありファインチューニング)とRL(強化学習)パイプラインを経て調整されている。注目すべきは、この3Bという規模でありながら、以下のベンチマークでQwen3.5-9BやGemma4-12Bといった9〜12Bクラスのモデルを凌駕する結果を出している点である。
| ベンチマーク | Nanbeige4.2-3B | Qwen3.5-4B | Qwen3.5-9B | Gemma4-12B |
|---|---|---|---|---|
| SWE-Bench Verified | 63.6% | — | 54.0% | — |
| GPQA-Diamond | 87.4% | 84.8% | 86.2% | 78.3% |
| MMLU-Pro | 75.8% | 74.5% | 76.9% | 71.9% |
(出典:Nanbeige Lab, arXiv:2607.22083)
3Bパラメータで9Bクラスと同等以上の性能を達成できる理由は、次に説明するアーキテクチャ上の工夫にある。
Looped Transformer(LT)の仕組み
Looped Transformer(LT)は、Bae et al.(2025, "Mixture-of-Recursions")によって提案されたアーキテクチャで、同一のレイヤースタックを2回通過させることで、パラメータ数を一切増やさずに実効的なネットワークの深さを2倍にする。Nanbeige4.2-3Bでは22層のトランスフォーマーレイヤーがループ構造になっており、以下のフローで動作する。
flowchart LR
A["入力トークン\n(埋め込み後)"] --> B["レイヤースタック\n(22層, 1回目)"]
B --> C["中間隠れ状態"]
C --> D["同じレイヤースタック\n(22層, 2回目)"]
D --> E["最終ロジット"]
subgraph F["標準Transformer(比較)"]
G["入力"] --> H["22層\n(1回のみ)"] --> I["最終ロジット"]
end
1回目のパスで計算された隠れ状態は、2回目のパスで同じ重みをもう一度適用して再計算される。これにより、パラメータ数は22層分のまま、実効的には44層の深さを持つモデルとして振る舞う。この「パラメータ効率」が、先述のベンチマークで3Bモデルが9Bクラスに匹敵する性能を発揮する原動力である。
ただし、このアーキテクチャには明確なトレードオフがある。LTは同じアテンション計算を2回実行するため、ピーク時のメモリ消費量が標準Transformerの2倍になる。アテンションスコア行列はプロンプト長Lに対してO(L²)で増加するため、長いコンテキストを扱うほどメモリ負荷が深刻化する。つまり、「パラメータは少なく、計算は2倍」という性質が、Apple Siliconのようなメモリ制約環境では大きな課題となる。
なぜ今、3Bスケールのエージェントモデルが重要なのか
3Bスケールのエージェントモデルが注目される背景には、クラウドAPI依存からの脱却という大きな流れがある。ローカル推論には、データが外部に出ないプライバシー面のメリット、ネットワークレイテンシがゼロになる応答速度、そしてAPI従量課金と比較した長期的なコスト削減という3つの利点がある。MacBookやRaspberry Piクラスのエッジデバイス上でエージェントが動作すれば、オフライン環境でもツール呼び出しやコード生成を実行できるようになる。
しかし、理論上の可能性と実際のデプロイ可能性の間には深い溝がある。LTがもたらすメモリ2倍の代償は、Apple Siliconのユニファイドメモリ(最大128GiBとはいえOSや他プロセスと共有)の上で無視できない制約となる。この論文(Halloran, arXiv:2608.13987)が取り組むのは、まさにこの溝を埋める実践的な修正と最適化である——理想的なアーキテクチャを、現実のハードウェアで動く形に変換するプロセスこそが、本記事の主題となる。
Apple Silicon(MPS)で直面した5つのデプロイバグ
Nanbeige4.2-3BをApple Silicon上で実際に動作させるまでに、合計5つのデプロイバグを修正する必要があった。これらのバグはtransformersのバージョン非互換、MPS(Metal Performance Shaders)特有の挙動、モデルコードの設計上の問題に起因する。以下、発見が困難なものから順に解説する。
バグ1:RoPEバッファの「静かなるゼロ化」(最重要)
問題:モデルの位置エンコーディングに使われるinv_freqバッファがpersistent=Falseで定義されているため、device_map="auto"によるメタデバイスロード後に値が復元されず、すべてゼロのままになった。
結果:位置情報が付与されないため、モデルは文法的に流暢でありながら位置的に無意味なテキストを生成する。クラッシュしないため、出力を注意深く観察しない限り異常に気づけない。この「サイレントバグ」は、MLXポートの開発中に偶然発見された。
修正:モデル読み込み後にsibling-fileモンキーパッチでバッファの永続性フラグを書き換え、再度計算し直すことで解決する。
バグ2:RoPE設定ディスパッチのKeyError
問題:カスタムモデリングコード内でRoPEの種別をディスパッチする処理が、一部の設定値に対応する分岐を持たずKeyErrorを送出する。これはMPS固有の問題ではなく、デバイスを問わずモデルのロードを完全にブロックする。
修正:ディスパッチ辞書に不足しているキーのハンドラを追加する。
バグ3:削除されたキャッシュAPIの呼び出し
問題:DynamicCache.from_legacy_cache(...)というAPIが現在のtransformersバージョンで削除されているにもかかわらず、モデルのforward()メソッドから呼び出されている。generate()を経由せず直接forward()を呼び出すコードパスでのみ発火するため、標準的な推論では気づかれにくいバグである。
修正:該当箇所を現在のAPIに置き換える。
バグ4:MPS固有のposition_ids再トリムクラッシュ(Metalアサーション)
問題:MPSデバイス上でposition_idsの再トリミング処理を実行すると、Metalアサーション違反が発生し、SIGABRTでプロセスが強制終了する。CPU(CUDA含む)ではまったく再現しない、MPS特有のクリティカルなバグである。
修正:再トリミング処理をMPSデバイスではスキップする条件分岐を追加する。
バグ5:tied-weightsキー形式の非互換性
問題:save_pretrained()を実行すると、_tied_weights_keysの形式がネストされたリスト構造になっているためAttributeErrorが発生する。修正を試みても再保存できない状態に陥る。
修正:キー形式をフラットなリストに変換する。
バグ修正の原則:transformersパッケージそのものは一切編集しない
これらの修正はすべて、元のtransformersパッケージには一切手を加えず、sibling-fileモンキーパッチで対応している。修正済みのチェックポイントはjohnhalloran/Nanbeige4.2-3B-mps-fixとしてHugging Faceで公開されており、誰でも即座に利用可能である。ただし、この修正済みチェックポイントが解決するのは上記5つのバグのみであり、後述するメモリ問題やシステムプロンプトの問題は別途対処が必要である。
Looped Transformerのメモリ代償──なぜバグ修正だけでは足りないのか
LTのメモリ問題の本質
5つのデプロイバグを修正しても、Nanbeige4.2-3BをApple Siliconで実用的に動かせるわけではない。その根本原因は、Looped Transformer(LT)アーキテクチャ自体が持つメモリ消費の問題にある。
標準Transformerでは、プロンプト長Lに対してO(L²)のアテンションスコア行列を1回だけ計算する。一方LTは、同一のレイヤースタックを2回通過させるため、同じプロンプトに対して2回のO(L²)計算が発生する。ピーク時のアテンションスコア行列は標準の2倍となり、メモリ消費が実質2倍になる。3Bパラメータ(bf16)のモデルであっても、Apple Siliconの32GiB共有メモリでは約8,000トークンでプロセスがアボートする。以下に、Naive PrefillとLTのデータフローの違いを示す。
flowchart TB
subgraph Standard["標準Transformer(Naive Prefill)"]
A1["入力プロンプト(Lトークン)"] --> B1["単一パス<br>O(L²)アテンション"]
B1 --> C1["出力"]
end
subgraph LT["Looped Transformer(Naive Prefill)"]
A2["入力プロンプト(Lトークン)"] --> B2["1回目のパス<br>O(L²)アテンション"]
B2 --> C2["2回目のパス(同一重み)<br>O(L²)アテンション"]
C2 --> D2["出力"]
end
Standard -.->|"ピークメモリ = 基準"| M1["✓ 標準メモリ"]
LT -.->|"ピークメモリ = 2倍"| M2["✗ 約8,000トークンでOOM"]
Apple Siliconのユニファイドメモリの特殊性
CUDA環境と異なり、Apple SiliconのユニファイドメモリはOSや競合プロセスと物理メモリを共有する。ページアウト(スワップ)機構はCUDAほど強力ではなく、メモリ不足が発生するとプロセスレベルのメモリ劣化(OOMによる後続タスクのカスケード障害)を引き起こす。このため、単純なバグ修正だけでは実運用に耐えうる安定性を確保できない。
チャンク化プリフィル(Chunked Prefill)による解決
この問題に対する解決策がチャンク化プリフィル(Chunked Prefill)である。従来のNaive Prefillではプロンプト全体を一度にモデルに入力するため、長大なアテンションスコア行列が一気に確保される。チャンク化プリフィルでは、プロンプトを固定サイズ(デフォルト256トークン)のチャンクに分割し、KVキャッシュを段階的に構築する。これにより、アテンションスコア行列の最大サイズがチャンクサイズに制限されるため、ピークメモリが劇的に削減される。
結果とトレードオフ
32GiBメモリのM2 Maxで評価した結果、チャンク化プリフィルによって最大コンテキスト長は以下のように改善された。
| 方式 | 最大コンテキスト長 | 倍率 |
|---|---|---|
| Naive Prefill(従来) | 4,096トークン | 基準 |
| Chunked Prefill(改善後) | 11,231トークン | 2.7倍 |
その代償として、スループットは22.8〜40.9%低下する。ただし、この低下率はバッチサイズが大きいほど償却される性質を持つ。ビット単位でNaive Prefillと同一の出力が確認されており、品質を犠牲にせずにメモリ制約を緩和できる点が最大の利点である。
システムプロンプトの落とし穴──気づかれにくい回帰バグ
chat_template.jinjaの「暗黙の置き換え」
Nanbeige4.2-3Bのツール呼び出し機能は、モデル内部にハードコードされた中国語のシステムプロンプトに依存している。このシステムプロンプトにはツールスキーマのフォーマット定義や呼び出し規則が記述されており、モデルが正確な<tool_call>タグを生成するための基盤となっている。
問題は、ユーザーが任意のシステムメッセージを指定したときに発生する。Hugging Faceのchat_template.jinjaは、ユーザーからシステムメッセージが渡されると、モデル本来のツール呼び出し用システムプロンプトを上書きして消してしまう。この結果、モデルはツール呼び出しのフォーマットを一切知らされないまま推論を行うことになり、出力が破綻する。
さらに厄介なのは、この上書きが発生しない自動挿入ブランチにも問題が潜んでいることである。システムメッセージが自動で挿入される場合、テンプレートの分岐ロジックによって末尾の改行が欠落する。わずか1バイトの改行有無が、モデルの出力フォーマットに決定的な影響を与え、生成された<tool_call>タグがパース不能になるケースが確認されている。
修正方法はシンプルである。chat_template.jinjaからシステムメッセージ分岐を完全に削除し、テンプレートレンダリング後に呼び出し元のコードでシステムコンテンツを結合する形に変更する。これにより、モデル本来のツール呼び出し用プロンプトが常に保持され、かつ改行の欠落も防げる。
llama.cppコミュニティでの独立報告
この問題はtransformersに固有のものではなく、llama.cppコミュニティでも独立して報告されている。PR #26324では、Nanbeige4.2-3Bのツール呼び出しの約25%で<tool_call>タグの後に不要なスペースが挿入され、パースに失敗する現象が報告された。この問題はllama.cppのb10335以降で修正されている。
注目すべきは、同じchat_template.jinja構造を共有するQwen3-CoderやQwen3.5-4Bにも同様の潜在的な問題が存在する点である。ツール呼び出しの信頼性がテンプレート上のわずかな空白文字や改行の有無に依存しているという事実は、エージェントモデルのデプロイにおける「フォーマットの脆さ」を如実に示している。関数呼び出しの正確性を担保するには、モデルの重みだけでなく、テンプレートレンダリングのバイトレベルの挙動まで考慮する必要がある。
MPSメモリバグ──一度のOOMが評価プロセスを死滅させる
問題の症状
MPS(Metal Performance Shaders)バックエンドでOOM(Out of Memory)が発生すると、一度メモリが枯渇したプロセスは二度と正常な状態に戻らない。torch.mps.empty_cache()を呼び出しても、GPUドライバに返却されるメモリ量は限定的であり、OSが管理するユニファイドメモリのページング状態が復旧しない。その結果、同じPythonプロセス内で後続のタスクを実行しようとすると、最初のタスクより少ないメモリ使用量であっても即座にOOMで失敗する——いわば、プロセスがメモリ障害のゾンビ化する現象が発生する。
この振る舞いはCUDAとは根本的に異なる。NVIDIA GPUの場合、torch.cuda.empty_cache()によって確保済みのキャッシュアロケータは解放され、次のアロケーションで新たに割り当て直される。CUDAではプロセスレベルのメモリ劣化は起こらない。一方、MPSではMetalがアロケーションを管理する階層がOSのメモリプレッシャーと直結しており、一度OOMを経験したプロセスはMetalデバイスのメモリ予算が恒久的に縮退した状態となる。
カスケード障害の実態
ベンチマーク評価の実践では、この問題が致命的な影響を及ぼす。たとえばBFCLの30タスクを連続実行する場合、途中の1タスクでOOMが発生すると、その後の全タスクが次々に失敗するカスケード障害が発生する。OOMの発生タイミングはコンテキスト長やプロンプトの内容に依存するため再現性が低く、「時々動くが、動かないときは全部動かない」という厄介な挙動を示す。論文の評価プロセスでも、この問題を解決するまでは安定したベンチマーク測定が事実上不可能であった。
回避策:サブプロセス分離によるプロセス再起動
唯一確実な回避策は、タスクごとに独立したサブプロセスを起動し、1タスク完了後にプロセスごと終了させる方法である。具体的には、以下のアプローチを取る。
flowchart LR
A[評価スクリプト\nメインプロセス] -->|subprocess.Popen| B[タスク1用\nサブプロセス]
A -->|subprocess.Popen| C[タスク2用\nサブプロセス]
A -->|subprocess.Popen| D[タスクN用\nサブプロセス]
B -->|終了・メモリ完全解放| A
C -->|終了・メモリ完全解放| A
D -->|終了・メモリ完全解放| A
各サブプロセスは独立したPythonインタプリタとして起動されるため、OOMが発生してもそのプロセスだけがクラッシュし、メインプロセスや他のサブプロセスには影響が及ばない。タスク完了後にサブプロセスが終了すれば、OSによってすべてのメモリが完全に回収される。この方式の欠点は、モデルのロードをタスクごとに繰り返すためオーバーヘッドが生じることだが、MPSのメモリ劣化問題を根本的に回避できる唯一の手段である。
このバグはMPSバックエンドの実装に起因する問題であり、アプリケーション側で完全に回避することはできない。そのため、Apple Silicon上でNanbeige4.2-3Bのようなメモリ要求の高いモデルを連続実行する場合、サブプロセス分離は必須の設計パターンとなる。
評価結果:修正後のNanbeige4.2-3Bはどこまで使えるのか
MCPMark(ファイルシステム操作、Easy Tier)
MCPMarkはMCP(Model Context Protocol)ツールの使用能力を評価するベンチマークである。ファイルシステム操作のEasy Tierにおいて、修正前はバグ2(RoPE設定ディスパッチのKeyError)によってモデルのロードすら不可能だった。修正後は10タスク中3タスク(30%)の成功を達成した。パスしたのはlargest_rename(最大ファイルのリネーム)、txt_merging(テキストファイルのマージ)、file_reorganize(ファイル整理)の3つである。
失敗の主因はマルチターン対話におけるコンテキスト蓄積に伴うタイムアウトとメモリ超過である。特に重要なのは、実用的な律速要因がプリフィル(プロンプト処理)ではなくデコード(トークン生成)にある点だ。コンテキスト長5,120トークン時点でデコードスループットは15.5トークン/秒から2.1トークン/秒へと約86%低下する。長い推論トレースを必要とするタスクでは、生成速度自体がボトルネックとなる。
BFCL(Berkeley Function Calling Leaderboard)
Berkeley Function Calling Leaderboard(BFCL)は、関数呼び出しの正確性を4つの観点で測定する。修正後のNanbeige4.2-3Bの結果は以下の通りである。
| カテゴリ | 内容 | スコア |
|---|---|---|
| irrelevance | ツール不要の判断(呼び出すべきでない場面で呼び出さない) | 30/30(100%) |
| simple_python | 単一ツール呼び出し | 19/30(63.3%) |
| multiple | 複数ツールの順次呼び出し | 13/30(43.3%) |
| parallel | 複数ツールの同時呼び出し | 1/30(3.3%) |
| parallel_multiple | 複数ツールの同時+順次呼び出し | 9/30(30.0%) |
irrelevanceが100%という結果は、このモデルが「ツールを呼び出すべきでない状況」を正確に認識できることを示している。単一ツール呼び出し(simple_python)の63.3%も実用的な水準にある。一方、複数ツールの並列呼び出し(parallel)が3.3%と極端に低い点は、フォーマットレベルの本質的な制約を示唆している。失敗の大半は誤った呼び出し数(多すぎる・少なすぎる)に起因しており、モデルが複数のツール出力を同時に整形する能力に限界があることを意味する。
論文が明らかにした「3Bエージェントモデルの真実」
一連の評価結果から、Apple Silicon上のNanbeige4.2-3Bには明確な適用範囲がある。単一ツール呼び出しとツール不要判断は十分実用的であり(BFCL irrelevance 100%、simple_python 63%)、「軽量なタスクをローカルで完結させたい」というニーズに応えられる。一方で、複数ツールの連携や長い推論トレースを要するマルチターンエージェントには、メモリ制約とフォーマット制約の二重の壁が立ちはだかる。Looped Transformerによるパラメータ効率の高さ(3Bで9Bクラスのベンチマーク性能)と、Apple Siliconの限られた共有メモリ上での実用性(メモリ消費2倍・デコード速度低下)のトレードオフは、依然として完全には解決されていない。修正後のモデルは「小さなモデルでどこまでできるか」という問いへの現在最良の答えの一つであり、単一ツールのローカルエージェント用途に照準を絞れば実戦投入可能な水準にある。
Nanbeige4.2-3Bを自分のMacで動かすには
Nanbeige4.2-3BをApple SiliconのMacで実行する方法は、求める修正範囲と実行環境に応じて3つの選択肢がある。以下ではそれぞれの手順と適用される修正内容を解説する。
修正済みチェックポイントを直接使う(5つのバグのみ)
最もシンプルな方法は、Hugging Faceで公開されている修正済みチェックポイントjohnhalloran/Nanbeige4.2-3B-mps-fixを利用することである。以下のコードでモデルとトークナイザーをロードできる。
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "johnhalloran/Nanbeige4.2-3B-mps-fix"
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype="auto",
device_map="auto",
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
この方法では、5つのデプロイバグ(RoPEバッファのゼロ化、KeyError、キャッシュAPI非互換、MPSクラッシュ、tied-weights非互換)はすべて修正されている。ただし、チャンク化プリフィルによるメモリ最適化とシステムプロンプトの修正は適用されない点に注意が必要である。そのため、長いコンテキストを扱うタスクではプロセスが強制終了する可能性がある。
GitHubリポジトリのharnessサーバーを使う(すべての修正を含む)
バグ修正・メモリ最適化・システムプロンプト修正のすべてを適用したい場合は、GitHubリポジトリに含まれるOpenAI互換サーバー(harness)を使用する。セットアップ手順は以下の通りである。
# リポジトリのクローンとインストール
git clone https://github.com/johnhalloran321/Nanbeige4.2-3B-mps-fix.git
cd Nanbeige4.2-3B-mps-fix
pip install -e .
# サーバーの起動
python harness/nanbeige_harness_server.py --port 8100
サーバー起動後は、OpenAI API互換のエンドポイントとして利用できる。
curl http://127.0.0.1:8100/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Nanbeige4.2-3B-mps-fix",
"messages": [{"role": "user", "content": "Hello"}]
}'
harnessサーバーには以下の修正がすべて含まれている。
| 修正項目 | 内容 |
|---|---|
| 5つのデプロイバグ修正 | RoPEゼロ化・KeyError・キャッシュAPI・MPSクラッシュ・tied-weights |
| チャンク化プリフィル | プロンプトを256トークン単位で分割し、コンテキスト長を4,096→11,231トークンに拡大 |
| システムプロンプト修正 | ツール呼び出し用システムプロンプトが上書きされないようテンプレートを修正 |
| MPSメモリ分離 | タスクごとにサブプロセスを再起動し、OOMのカスケード障害を防止 |
llama.cpp(GGUF量子化)で使う
より軽量な実行環境を求める場合、llama.cppのGGUF量子化フォーマットを利用する方法もある。Nanbeige公式のnanbeige42ブランチでビルドすることで対応可能である。Q4_K_M量子化を適用すればメモリ使用量をさらに削減でき、8GiBメモリのMacでも動作の可能性が広がる。
llama.cppでは、ツール呼び出しのフォーマット問題(<tool_call>の後に不要なスペースが挿入されるバグ)が独立して報告されていたが、PR #26324にて修正され、b10335以降のバージョンで解決済みである。llama.cpp版ではtransformers版とは異なるコードベースで動作するため、チャンク化プリフィルによるメモリ最適化は不要であるが、その代わりにGGUF量子化による精度低下が生じる点を考慮する必要がある。
各方法の比較
| 方法 | デプロイバグ修正 | メモリ最適化 | システムプロンプト修正 | セットアップの容易さ |
|---|---|---|---|---|
| 修正済みチェックポイント | ✅ | ❌ | ❌ | ★★★(コード3行) |
| harnessサーバー | ✅ | ✅ | ✅ | ★★(サーバー起動必要) |
| llama.cpp GGUF | ✅(別系統) | ✅(量子化) | ✅(b10335以降) | ★★(ビルド+量子化) |
どの方法を選ぶかは、求める修正範囲と環境に応じて判断してほしい。短いプロンプトで単純な推論を行うだけなら修正済みチェックポイントで十分であるが、エージェントタスクを本格的に試す場合はharnessサーバーが適している。メモリが限られている環境ではllama.cppのGGUF量子化が有力な選択肢となる。
よくある質問
Nanbeige4.2-3BはM2/M3/M4 Macのどれで動く?
理論上すべてのApple Silicon(M1〜M4)で動作する。論文の評価はM2 Max(32GiBメモリ)で実施されており、16GiB以上のメモリ搭載機種を推奨する。8GiBモデルの場合、チャンク化プリフィルを適用してもコンテキスト長が大幅に制限されるため、実用的なエージェントタスクの実行は難しいだろう。
Looped Transformerと通常のTransformer、どちらが良いの?
パラメータ効率ではLooped Transformer(LT)が優位である。3Bパラメータで9Bクラスのベンチマーク性能を達成できる反面、推論時のアテンション計算が2回発生するためピークメモリが倍増する。大規模GPU環境であればLTの恩恵を純粋に享受できるが、Apple Siliconのようなメモリ制約環境ではチャンク化プリフィルが必須の対策となる。
BFCLのスコアはどの程度信頼できる?
単一ツール呼び出し(simple_python 63%)とツール不要の判断(irrelevance 100%)は十分な信頼性がある。一方、複数ツールの並列呼び出し(parallel 3.3%)や並列・複数ツールの組み合わせ(parallel_multiple 30%)は著しく低く、マルチツールエージェント用途にはさらなる改善が必要である。MCPMarkの30%という数値も、単一ファイル操作タスクに限定された結果である点に注意してほしい。
llama.cppでも同じ問題が起きる?
起きる。llama.cppでもNanbeige4.2-3Bのツール呼び出しに同様のフォーマット問題が報告されていた(PR #26324)。約25%のツール呼び出しで<tool_call>タグの後に余分なスペースが挿入され、パースに失敗する現象である。ただしb10335以降のバージョンで修正済みであり、transformers版とは別系統のバグであるが、chat_templateの空白文字処理に起因する点で共通している。
そもそも3Bモデルでエージェントは実用的なの?
用途に大きく依存する。単一のツール呼び出し(天気取得、計算、ファイル操作など)で完結するケースでは十分実用的である。実際にBFCLのツール不要判断は100%、単一ツール呼び出しは63%の成功率を示している。ただし複数ツールの連携や長い推論トレースが必要なタスクでは、メモリ制約とマルチツール生成の弱さが顕在化する。「軽量なタスクをローカルで完結させたい」というユースケースに最も適したモデルと言えるだろう。
まとめ
Nanbeige4.2-3Bは、3Bパラメータというコンパクトな規模ながら、Looped Transformerの採用によりQwen3.5-9BやGemma4-12Bに匹敵するエージェント性能を発揮する画期的なモデルである。しかし、Apple Silicon(MPS)上でそのまま動作させることは不可能であり、5つのデプロイバグ(RoPEバッファの永続性問題、RoPE設定ディスパッチのKeyError、削除済みキャッシュAPIの呼び出し、MPS固有のposition_idsクラッシュ、tied-weightsキー形式の非互換性)が立ちはだかる。これらのバグはクラッシュしないサイレントなものからプロセスを強制終了するものまで多岐にわたり、いずれもtransformersパッケージ自体を編集せず、sibling-fileモンキーパッチで修正可能な形で対処されている。
バグ修正だけでは不十分な理由は、Looped Transformerのアーキテクチャ自体が持つメモリ代償にある。同一レイヤースタックを2回通過するLTは、標準Transformer比でアテンションスコア行列が2倍になり、32GiBのApple Silicon共有メモリでは約4,096トークンでプロセスがアボートする。この問題に対しては、プロンプトを256トークンのチャンクに分割して段階的にKVキャッシュを構築するチャンク化プリフィルが有効で、最大コンテキスト長を11,231トークン(2.7倍)に拡大できる。スループットは22.8〜40.9%低下するものの、バッチサイズが大きいほどコストは償却される。
システムプロンプトの回帰バグやMPSメモリのカスケード障害といった追加の課題を解決した上で実施したベンチマーク評価では、MCPMark(ファイルシステム操作)で30%のタスク成功、BFCL irrelevance(ツール不要判断)で100%、simple_python(単一ツール呼び出し)で63.3%を達成した。一方で、複数ツールの並列呼び出し(3.3%)やマルチターンタスクは本モデルの本質的な限界であり、3Bスケールのモデルでどこまでできるかの現実的な線引きを示している。
これらの成果はすべてGitHubリポジトリ(修正済みチェックポイント、harnessサーバー、実験スクリプト)およびHugging Faceで公開されており、読者自身のMacで再現可能である。「小さなモデルでどこまでできるか」という問いへの現在最良の答えの1つとして、Nanbeige4.2-3BはApple Silicon上でのローカルAIエージェント実行の可能性を切り開くと同時に、その現実的な制約も明確に示している。
参考資料
- Nanbeige4.2-3B on Apple Silicon: Fixing Deployment Bugs and Decreasing Looped Transformer Memory Overhead
- Nanbeige4.2-3B: Unlocking Agentic Capabilities in a Compact Model
- Mixture-of-Recursions
- GitHub: johnhalloran321/Nanbeige4.2-3B-mps-fix
- Hugging Face: Nanbeige/Nanbeige4.2-3B
- Hugging Face: johnhalloran/Nanbeige4.2-3B-mps-fix
- llama.cpp PR #26324 — Nanbeige4.2-3B tool calls fix
- MCPMark
- Berkeley Function Calling Leaderboard (BFCL)