9つのコーディングハーネス対あなたのラップトップ
Nine coding harnesses vs. your laptop (nasutton.notion.site)
要約
この記事は、ローカルモデルをコーディングハーネスで使用する際のラップトップでの開発体験について論じています。多くのハーネスはローカルモデルを想定して設計されていないため、長いシステムプロンプトやツールスキーマ、限られたコンテキストウィンドウ、バックグラウンドでのサイドリクエストなどがパフォーマンスを著しく低下させることが指摘されています。筆者は、これらの問題を分析するために9つのハーネスをテストし、ローカル環境での使用に適したハーネスとそうでないハーネスを分類しています。
全文翻訳
APIコールをローカルモデルに置き換えることを考えたことがあるなら、おそらく失望に終わったでしょう。
あなたは素早くllama-benchを実行し、毎秒Xトークンを期待したかもしれません。しかし、そのベンチマークは、実際にこれらのハーネスを使用する感覚を伝えてくれません。
開発体験は非常にばらつきがあります。応答を見る前に何分も待たされたかもしれません。あるいは、作業が進んでいる途中で突然遅くなり始めたかもしれません。
「このプリフィル(Prefill)は何だ、私の時間をすべて奪っている!」
それはあなたのせいではありません。ほとんどのコーディングハーネスは、ローカルモデルを想定して作られていません。
これは、他のチームの優れた仕事に対する批判ではありません。データセンターをlocalhostに置き換えた場合に何が起こるかを、私は信頼性をもって測定しようとしています。
問題に対して間違ったツールを使用しているというレベルは、ある程度予想されることです。塩を加えてください:私はchadという、Apple Silicon上のQwen 3.8 27Bに特化して最適化されたコーディングハーネスをいじっています。
ラップトップの物理学
ほとんどのハーネスは、3つの方法でlocalhostに不利に働きます。
大きなシステムプロンプトとツールスキーマ。
あなたのラップトップが毎秒90トークンで読み込み、約10トークンで書き込むと仮定しましょう。これらは、コーディングハーネスの各ループにおけるプリフィル(Prefill)と生成(Generation)の部分を表します。
この速度では、プロンプトの読み込み1,000トークンごとに、モデルが書き込みを開始する前にカーソルを見つめて約11秒かかることになります。
LLMが作業を行う前に、システムプロンプト全体とロードされたすべてのツールスキーマを読み込みます。
piハーネスでは、この組み合わせはQwen 3.8 27Bで2,008トークンでしたが、Opencodeでは合計で18,046トークンでした。
プリフィルレートが平均10k+トークン/秒のデータセンターGPUがあれば、大きな違いは感じないでしょう。これは0.2秒対1.8秒に収束します。
私のラップトップでは、piで22秒、Opencodeで226秒でした。耐え難い!
小さなコンテキストウィンドウ。
LLMがシステムプロンプトの読み込みを終えた後、作業を行うためにメモリに残されたコンテキストウィンドウは限られています。
それはあなたが思うよりも小さく、ハーネスはすでにその一部を使用しています。
どれだけのコンテキストが残るかは、開始時のメモリ量とモデルの重みのサイズによって異なります。
多くの変数がありますが、比較的良好なモデルと比較的良好なラップトップで残る32,000トークンというのは妥当な推測です。
この32,000トークン予算のうち、piハーネスのためにどれだけ残っているでしょうか? 94% - 管理可能に見えます。
Opencodeは? すでに18,046トークンが消費されているため、実際に作業できるコンテキストは44%しか残っていません。
サイドリクエストの習慣。
従来のローカルクライアント/リモートサーバーパターンでは、ハーネスはデータセンターサーバーに好きなだけサイドリクエストを行うことができます。
あなたのラップトップはクライアントでもありサーバーでもあります。
最良の場合、これらのサイドリクエストはローカルモデルをキューに入れ、待機させます。
最悪の場合、繰り返し長いプリフィルを引き起こします。
24のタスクで、opencodeは33、crushは51、dshは24(セッションタイトルと要約)のリクエストを送信しました。ほぼすべてのリクエストがエージェントのターンと重複していました。
モデルはopencodeとcrushで、壁時計時間の125%と114%の間で「ビジー」でした。これは、1つのGPUで2つのリクエストが同時に進行していたことを意味します。
ハーネスの結果
私は9つのハーネスを、それぞれ自動承認モードで8つのExercism演習にかけ、1つの同一の1文プロンプトを使用しました。
各タスクは、同じM4 MacBook Pro(24GB、macOS 26.6.2)を使用し、Qwen 3.8 27Bの3ビット量子化モデルをllama.cpp(ビルド10470)経由で提供しました。
すべてのハーネスは同じllama-serverを共有し、共通のプロキシがQwenに推奨されるサンプリング設定(temperature=1.0, top_k=20, top_p=0.95, min_p=0.05)を強制しました。
すべてのセッションで、4つのスロットにまたがって同じ32,768トークンのユニファイドキャッシュが使用されました。
以下に報告されるすべての数値は、プロキシ経由で読み取られたllama-server自身の記録であり、ハーネスの自己申告ではありません。
ただし、*でマークされた2行(in-process MLXエンジンを使用するchad、サーバーを観測できないため、同じ定義でchad自身のプリフィルトレースから取得)は例外です。
- 1回目のトークンまでの待機時間は、システムプロンプト、ツールスキーマ、および最初のリクエストに対するサーバーのプリフィル時間です。
- 後続ターンの待機時間は、最初のリクエスト以降の各ターンの同じ待機時間で、サイドリクエストは除外されます。
- キャッシュ再利用率は、後続ターンがプレフィックスキャッシュから提供された割合です。
- 経験トークン/秒は、プリフィルとツール時間を含めたタスク全体の壁時計時間で生成されたトークンを割った値です。
同じハーネスでも、実行ごとにこの列で最大50%変動しました(n=3)。そのため、軽量ハーネス間の小さな差はノイズと見なしてください。
- 合格はExercismタスクのゲートです。
順位として解釈しないでください。これらは単純なPythonタスクです。
Tは1,200秒のキャップでのタイムアウトを示します。
| harness | version | tools | tax: turn-1 prompt (tok) | wait before 1st token, turn 1 | wait / later turn (med · p90) | cache reuse | exp. tok/s | pass (gate) |
|---|---|---|---|---|---|---|---|---|
| mini-swe-agent | 2.4.6 | 1 | 1,171 | 12.2 s | 3.6 s · 21 s | 96% | 8.0 | 11/24 (14 T) |
| pi | 0.80.3 | 4 | 2,008 | 21.6 s | 1.3 s · 22 s | 99% | 8.1 | 19/24 (7 T) |
| cline | 3.0.60–61 | 26 | 5,876 | 64.1 s | 9.9 s · 52 s | 94% | 7.3 | 17/24 |
| codex | 0.151.0 | 10 | 7,804 | 87.8 s | 9.6 s · 28 s | 94% | 6.9 | 19/24 (5 T) |
| dsh | 0.1.1-rc.2 | 25 | 8,052 | 94.4 s | 2.2 s · 34 s | 99% | 7.2 | 18/24 |
| goose | 1.50.0 | 18 | 9,617 | 110.3 s | 1.0 s · 22 s | 100% | 8.0 | 22/24 (3 T) |
| crush | 0.92.0 | 26 | 16,263 | 199.8 s | 1.8 s · 40 s | 100% | 5.8 | 18/24 (8 T) |
| opencode | 1.17.12 | 10 | 18,046 | 225.7 s | 4.6 s · 44 s | 99% | 5.7 | 15/24 (13 T) |
| chad (llama.cpp) | 2.0.3 | 5 | 2,563 | 25.6 s | 0.8 s · 19 s | 99% | 7.9 | 24/24 |
| chad (MLX, serial) * | 2.0.3 | 5 | 2,566 | 4.7 s | 1.0 s · 19 s | 99% | 12.4 | 21/24 (3 T) |
| chad (MLX, dflash2) * | 2.0.3 | 5 | 2,562 | 4.6 s | 0.9 s · 36 s | 99% | 17.4 | 22/24 (3 T) |
私の経験では、ハーネスは3つのグループに分かれました。
軽量で安定(pi, mini-swe-agent, chad)。これらのハーネスは、システムプロンプトが簡潔で、キャッシュ再利用率が96~99%です。
データセンターまたはローカルモデルのバックエンドで、ほとんどの場合そのまま動作します。
唯一の注意点は、mini-swe-agentはExercismタスクの11/24しか合格せず、タイムアウトが最も多かったことです。
ヘビーだが規律正しい(dsh, cline, codex, goose)。長いシステムプロンプトまたは多数のツールスキーマの組み合わせ効果です。
それらのプレフィックスはバイト単位で安定しているため、一度開始すれば許容できる感覚になります。
(gooseはバージョン1.50.0でこのグループに入りました。以前のリリースでは、毎ターン1分解像度のタイムスタンプが最初のユーザーメッセージに再レンダリングされていました。これにより、キャッシュ再利用率が78%に低下しました)
開始がヘビー(crush, opencode)。アクティビティが見える前に3~4分待つことになります。
chadのケース
これらのハーネスのいずれも、設計が悪かったわけではありません。
Opencodeの18kシステムプロンプトは、APIの背後で展開される最先端モデルに役立つためです。
crushの26のツールスキーマは、200kのコンテキストがあり、プリフィルが瞬時に行われる場合には問題ありません。
これらの選択はすべて、プリフィルが事実上無料である環境で行われました。
ローカル速度では、これらすべてが崩壊します。