自分が配ったテンプレが、自動最適化を殺していた — `-ngl 99` と WARN 1行の話 CodeRouter v2.13.0
TL;DR: 2026-08-07 の v2.13.0 は、カレントディレクトリの `providers.yaml` を親切に読む機能が、実行ファイル名を指す設定ごと乗っ取られる経路だったので opt-in に落とした回だ。2日後に llama.cpp のオプションを見直すと、同じ形の話が出た。上流は 2025-12-15 から自動メモリフィット(`-fit`)を既定 on にしていて、配っていた `-ngl 99` 型のテンプレは、その自動配分を丸ごと止める指示になっていた。しかもエラーにならない ── WARN 1行だ。山場は、裏取りの過程でその日の自分の修正にも穴が2つ見つかったこと。
あらすじ — 39話目です
初コミットは 2026-04-19、本記事の作業日は 2026-08-09 で 113日目。前話(第38話・v2.12.0)は 108日目だった。この5日に、根の同じ出来事が2つ入る ── v2.13.0(08-07)と、llama.cpp 起動オプションの全面刷新(08-09)。通しているのは、便利のために受け取っていた入力と、親切のつもりで配った既定値が、あとから毒になるという一本だ。
v2.13.0 — 親切に読んでいた設定が、乗っ取りの経路だった
出どころは 2026-08-04 のマルチエージェントレビュー(Critical 0 / High 14 / Medium 約24 / Low 約30)。第38話は H-5 の話で、v2.13.0 は残っていた H-1・H-2・H-3・H-14 を潰した(PR #81、テストは 2273 passed / 5 skipped → 2329 passed / 6 skipped)。
破壊的変更は3つで、どれも同型だ。CodeRouter はカレントディレクトリに `providers.yaml` があれば黙って読んでいた ── 置いた場所で `coderouter serve` すれば動く、という親切である。ところがこの設定には `restart_command` や `launcher.backends[*].binary` が書ける。どれも「実行ファイル名」を指す項目だ。敵対的な YAML が1個そこにあれば足りたので、`CODEROUTER_ALLOW_CWD_CONFIG` の opt-in に落とした。
残り2つも同じ筋だ。`shell=True` だった `restart_command` は `shlex.split` + `shell=False` にし、シェルメタ文字を拒否する。sweep API はリクエストの `bench_command` をやめ、定義済みの `bench_preset` 名だけ通す。便利のために開けていた口が、任意プログラム実行の入口だった。
2日後 — 「何も書かない」がいちばん正しい設定だった
08-09 に llama.cpp 側へ手を伸ばした。Launcher は `llama-server` の argv を自分で組み立てるので、上流の CLI に追随するしかない。読んでいて手が止まった。
`-fit` / `--fit` は PR #16653、マージ 2025-12-15、初出タグ b7410。PR 本文の原文は "New CLI argument `--fit [on|off]` ... enabled by default."。導入時から既定 on だ。8か月近く前からそうで、気づいていなかったのは自分だった。同じ流れで `-ngl` の既定は auto、`-c` は 4096 から 0(= 学習長)へ、`-fa` は bare フラグから値必須へ変わっている。
fit の契約 — 書いた項目は触らない、そして黙って諦める
`common/fit.h` のコメントが契約を宣言している ── "only parameters that have the same value as in `llama_default_model_params` are modified"。既定値のままの項目だけ触る。書いた項目には手を出さない。

行儀はいい。割れるのはその先だ。`-c` を書くと、その項目の調整だけ諦める(`context size set by user to 32768 -> no change`)。fit 自体は生きている。ところが `-ngl` / `-ts` / `-ncmoe` / 複数GPU時の `-sm row` を書くと、fit 全体が abort する。捕捉側は `LOG_WRN("%s: failed to fit params to free device memory: %s\n", ...)` ── 警告1行で起動は続く。
一次記録もある。Issue #21801(2026-04-12 起票、b8763)は、デュアル AMD Radeon 8060S で `--fit on` にしているのに fit が効かない、というバグ報告だ。
llama_params_fit: failed to fit params to free device memory:
n_gpu_layers already set by user to -2, abort`-2` は `-ngl all` の内部表現だ。「全層 GPU に載せたい」と書いたつもりが、fit には「手動指定=触るな」の合図になり、自動配分が丸ごと止まる。報告は closed as not planned。バグではなく仕様だ。自分がテンプレに置いてきた `-ngl 99` も、この `-2` と同じ位置に立っている。
山場 — 裏取りをしたら、その日の自分の修正に穴が2つあった
まず `57d35fb` で `examples/providers.llamacpp-vllm.yaml` を書き直した。+275 / -121、llama.cpp のプロファイルは9個から13個へ。推奨プロファイルの args は1項目だけになる。
# 旧テンプレ (README にまだ載っている形)
"-ngl": 99
"--ctx-size": 65536
# 新しい推奨「おまかせ (自動フィット・推奨)」
"-fitc": 32768ここで満足していた。記事のために一次ソースを当て直したら、同じ日の自分の修正に穴が2つ空いていた。
1つめ。`-ncmoe` は fit を abort させる側だった。 「MoE: エキスパートを CPU 退避」で `-fitc 32768` と `-ncmoe 8` を併記していたが、`-ncmoe` を書いた時点で fit は動かず `-fitc` は効かない。そのうえ `-c` 未指定 = 既定 0 なので学習長いっぱいを取りに行く。縮小役が死んだ状態で最大 ctx を要求する = OOM する構成を、推奨として配ろうとしていた。`-fitc` を外し `--ctx-size 32768` を明示する手動モードに直した。
2つめ。`--mlock` の置き換え先を `-lm mlock` と書いていた。正しくは `-lm mmap+mlock` だ。`--load-mode` への統合は PR #20834 だが、その4日後の PR #26135 で `mlock` の定義が「mmap しない mlock」へ変わっている。旧 `--mlock` は mmap + mlock 相当なので、素直な置き換えが意味を変える。訂正は `b27e675`、+76 / -18。
第38話の山場は、「全テスト緑」と報告してきた修正を敵対的検証エージェントに叩かせたら復元バグが出た話だった。今回の検証役は「記事を書くこと」だ。出典を1行ずつ当て直す作業が、そのままレビューになった。
オチ — 直したのは、1ファイルだけ
`57d35fb` と `b27e675` が触ったのは、その1ファイルだけだ。`README.md` の192行付近には `"-ngl": 99` / `"--ctx-size": 4096` が残っている。`docs/backends/launcher.md` に11箇所、`llamacpp-direct.md` には `-ngl 999` の起動コマンド例。英語版 `.en.md` にも同型が並ぶ。
いちばん効いたのは過去記事だった。`note-llamacpp-coderouter-zero-cost-2026.md` は、304行付近で `"-ngl": 99` / `"--ctx-size": 65536` を推奨プロファイル例として掲載したままだ。しかも同じ記事の79行目では、`--no-mmap` の非推奨化を自分で訂正済みである。1か所直して、その下の同じ問題を通り過ぎていた。
コード側も未修正で残した。UI の「⚙ 推奨値」ボタンは今も `-ngl {ngl} --ctx-size {ctx} --threads {threads}` を返す。これは追加オプション欄に入り、argv の最後で後勝ちする ── 押すと自動フィットが必ず死ぬ。足したのは警告コメントだけだ。
まとめ — 期限切れは通知されない
1つめ。上流が賢くなると、過去の親切がそのまま逆を向く。 `-ngl 99` は書いた当時は正しかった。変わったのはこちらではなく向こう側だ。ベストプラクティスには賞味期限があり、切れたことは誰も教えてくれない。
2つめ。WARN 1行の失敗は、実質的に無音だ。 起動するし応答も返る。取り残されるのは配分だけになる。第38話の「発火しない安全装置」と形が同じで、正常時と見分けの付かない失敗がいちばん長く生き残る。
3つめ。配布物はコードより遅れて腐る。 テストは 2329 通っているが、README の YAML も過去記事のコード例もテストされない。設定例は実行されないコードで、CI が守ってくれない。公式が `llama-fit-params`(fit の出した引数を印字するツール)を用意しているのは、手書きテンプレの居場所がそこへ移ったからだろう。
──あなたが去年ドキュメントに書いた「推奨設定」は、いまの上流でも推奨のままですか。確かめる仕組みはありますか。よかったらコメントで聞かせてください。
CodeRouter は MIT ライセンスの OSS です: https://github.com/zephel01/CodeRouter
`pip install coderouter-cli` / `uvx coderouter-cli serve` で動きます。
#CodeRouter #ローカルLLM #llamacpp #ClaudeCode #設定ファイル #セキュリティ #Python #OSS #個人開発 #AIエージェント
いいなと思ったら応援しよう!
サーバー代とコーヒー代になります☕ 役に立ったら応援よろしくお願いします!