ローカルLLMへ切り替えた直後、最初の返事が出るまで数分待つことがありました。

一応コンテキスト窓には収まっています。なので最初は「モデルが小さいから遅いのかな」と思っていました。

実際には、セッション開始時に渡していたものが多すぎました。共通ルール、リポジトリのルール、使わないプラグイン、MCPのschema、過去の会話まで合わせて約200KBありました。

これを約4KBまで落とすと、最初のツール実行がかなり早くなりました。

単にトークンを節約したというより、いまの仕事と関係ない話を先に読ませるのをやめた、という方が近いです。

長い入力は、入ればよいわけではなかった

最初はClaude Code側の環境を、できるだけそのままQwen側へ再現しようとしていました。

ルールやツールを減らすと能力も落ちそうだったので、一旦全部渡しておく方が安全だと思っていたためです。

ただ、動かしてみると次のようなことが起きました。

コンテキスト窓の上限を超えたわけではありません。

入ってはいるけど、いま必要な情報の割合がかなり薄くなっていました。小さいモデルほど、この影響を受けやすそうです。

約200KBの入力を必要性フィルターで約4KBへ圧縮し、必要時だけ追加取得する流れ

図1: 最初から全部読ませず、開始に必要なものだけ渡して、続きは作業中に取りに行きます。

残したものと、後回しにしたもの

「大事そうか」で選ぶと、だいたい全部大事に見えます・・・!

なので、開始時にいるかどうかで3つに分けました。

分け方 扱い 例
開始時に必要 handoffへ短く入れる いまの目的、残りTODO、作業場所、完了条件
必要になったら読む パスや探し方だけ渡す 詳細仕様、過去の議論、関連コード、長いログ
今回はいらない 読み込まない 未使用プラグイン、別領域のルール、全MCP schema

ローカル継続用のモードでは、通常フック、指示ファイルの自動読込、プラグイン、MCP、自動メモリを外しました。

使える道具もRead、Edit、Bashだけです。

その代わり、次の短いルールは残しています。

全ツールの説明を置くより、今回迷いそうな分岐だけを書いた方が動きやすかったです。

200KBから4KBへ減らした順番

まず、何が大きいかを分けて測る

合計だけ見ても削る場所が分からないので、発生元ごとにサイズを出しました。

global instructions   46 KB
project instructions  61 KB
rules                  18 KB
tool / MCP schemas     52 KB
handoff / history      23 KB
----------------------------
total                 200 KB

この数字は環境によって変わります。

自分が見たかったのは、全体が何KBかより「昨日より増えたのはどこか」でした。セッション開始時に内訳を記録しておくと、プラグイン追加などで急に重くなったときに分かります。

ルールを常駐させない

フロントエンドを触るときだけ必要なルールや、公開直前だけ必要な手順まで、毎回読み込む必要はありません。

ローカル側には対象作業で使う分だけ短く渡し、残りは索引とファイルパスを置きました。

必要になった時点で読めればよいので、開始時の負担はかなり減ります。ただ、参照先が権限の外にあると詰まるので、ローカル側から本当に読めるかは先に確認しています。

ツールも一旦3つまで減らす

ツールが多いとできることは増えますが、schemaも選択肢も増えます。

ローカルでファイル作業を続けるだけなら、ブラウザ操作やメール送信は必要ありません。Read、Edit、Bashがあれば、調べる、直す、確認するところまでは進められます。

もちろんBashを何でも実行してよいわけではなく、別の安全ゲートを置いています。

会話をそのまま渡さない

会話には、途中で捨てた案や同じ説明がかなり入っています。

引き継ぎ時は全文をコピーせず、最後の依頼、残りTODO、Gitの状態、直前出力の末尾へ変換します。

ここはシリーズ第3回のhandoffの記事で、もう少し詳しく書いています。

足りないものは、その場で取りに行く

4KBだけで最後まで作業するわけではありません。

最初は4KBで現在地をつかみ、必要なファイルだけ後から読みます。

小さいhandoffを読む
  ↓
最初のTODOを一つ選ぶ
  ↓
対象ファイルを検索して読む
  ↓
変更して確認する
  ↓
次のTODOへ進む

結果として、一度も使わなかった情報は最後までコンテキストへ入りません。

モデル設定は1箇所だけ見てもダメだった

自分の構成では、メイン処理、軽量処理、サブエージェントでモデル設定が分かれていました。

メインだけローカルモデルへ変えても、別経路にクラウド用のモデル名が残っていると、Ollamaへ存在しないモデルを要求して止まります。

起動前に、全部の経路が許可したモデルへ向いているか確認するようにしました。

main_model       = local-model
small_model      = local-model
subagent_model   = local-model
allowed_models   = [local-model]

設定ファイルだけでなく、実際のAPIリクエストへ出たモデル名もログで見ています。ここは設定した気になりやすいところでした。

削りすぎたときの見分け方

もちろん短ければ短いほどよいわけではありません。

必要な情報へたどり着けないと、モデルは推測で埋め始めます。

自分は次の動きが増えていないか見ています。

増えた場合も、元の200KBへ戻すことはしません。

何が足りなかったかを確認して、handoffか索引へ一つ足します。この調整を何度か繰り返して、いまは約4KBになっています。

何を比較しているか

入力だけ短くなって完了率が落ちたら、普通に削りすぎです。

少し入力が増えても、最初の正しい行動が早くなって手直しが減るなら、そちらを選んでいます。

いまの確認項目

200KBを4KBへ減らした、と書くと圧縮の話に見えます。

実際にやったのは、最初に全部教えるのを諦めたことでした。現在地だけ渡して、必要なものはファイルから取りに行ってもらう。この形の方が、少なくとも自分のMacではローカルLLMが素直に仕事を始めてくれています。

ローカルLLM設計・全8回

MacでローカルLLMを実際に動かして、うまくいかなかったところを8つに分けて書いています。

  1. PART 01仕事の配置
  2. PART 02コンテキストの削減
  3. PART 03handoff
  4. PART 04冪等性
  5. PART 05プロセス管理
  6. PART 06復帰監視
  7. PART 07安全ゲート
  8. PART 08完了率の計測