HN 日本語サマリー

← 一覧へ戻る
プログラミング

Qwen3.5-122BをMac Studioで日常的に使えるようにした3つのバグ修正

Fixed three bugs that made Qwen3.5-122B a daily driver on Mac Studio (mrzk.io)

45 pointsby marzukia21 コメント

要約

著者はMac StudioでQwen 3.5 122Bという大規模言語モデルを日常的に使用するために、3つのバグを修正しました。特に、長文コンテキストでの応答速度低下やキャッシュの不具合を解消し、モデルの応答性を劇的に改善しました。これにより、ローカル環境でのAI開発やペアプログラミングが快適に行えるようになりました。

全文翻訳

目次 50,000トークンの会話に対するフォローアップの質問は、最初のトークンが表示されるまでに3〜5分かかりました。完全な回答ではありません。最初のトークンです。それはチャットボットではなく、バッチジョブであり、それが考えている間にコーヒーを淹れに行くようなものです。 Mac Studioを最大限に活用することに夢中になっている他の十数人のギークたちへ、良い知らせがあります。良い知らせとは、あなたがデバッグする必要がないように、私がキャッシュリークを3週間かけてデバッグしたということです。 これは、コンシューマーハードウェアで大規模モデルを実行するための素晴らしい実験であるantirez/ds4から始まりました。2ヶ月前、私はM3 Mac Studio Ultraを衝動買いしました。RAMの価格が96GB構成の入手をすぐに不可能にするだろうと恐れていました(ネタバレ:その通りになりました)、そして育児休暇中にローカル推論スタックに深く入り込みたいと思っていました。目標はシンプルでした:単一のマシンで最先端のモデルを実行し、それをウォーム状態に保ち、コンテキストを実際に理解する会話型AIを持つことです。 それはそれよりも複雑であることが判明しました。モデルはユニファイドメモリに収まり、首尾一貫して応答しますが、数週間の日常使用の後、私は大きな方針転換をしました:DS4 Flashを諦め、Qwen 3.5 122Bに切り替えました。その後、2つの別々のことが起こりました、そしてそれらを分けておく価値があります。モデルの切り替えは、速度と適合性の決定でした。新しいモデルを実際に使用可能にするためには、どのモデルを実行したかとは無関係な、私のサービングスタックにあった3つのバグを修正する必要がありました。 なぜモデルを切り替えたのか DS4 Flashは本当に良いモデルであり、antirezのスタックは素晴らしい作品です。それは単に私の用途に合っていませんでした。これは適合性の話であり、欠陥の話ではありません。 私のユースケースは、長文コンテキストのエージェントコーディングです。モデルが数千トークンの会話、コード、ツール出力を保持するペアプログラミングであり、フローを維持するためにほぼ瞬時の応答が必要です。その特定のワークフローでは、プリフィルレイテンシが決定的な要因でした。50kトークンを超えると、単純なフォローアップは最初のトークンが表示されるまでに3〜5分かかり、お茶を淹れるのを待たせるモデルではペアプログラミングはできません。それが追いつく頃には、私はすでに次の問題に移っていました。それはDS4が絶対的な意味で遅いのではなく、そのセットアップが長文コンテキストのプリフィルを私のハードウェアでどのように処理するかと、私のワークフローのニーズとのミスマッチです。 Qwen 3.5 122Bは、私が気にかけていたすべての点でM3 Ultraにより適しているように見えました: 最先端、完全にローカル。API呼び出しなし、レート制限なし、マシンからデータが漏れることもありません。完全にオフラインで動作する、プロプライエタリシステムに匹敵する122Bモデル。 Ultraの帯域幅に適したアクティブパラメータサイズ。M3 Ultraのメモリ帯域幅は大きいです。約10Bのアクティブパラメータを持つ122B MoEは、GPUがコンピートユニットにストールさせずに供給できるスイートスポットにあります。 ツール呼び出しと推論の良好なバランス。複雑なコードロジックのための十分な推論深度、信頼性の高い関数呼び出しとエージェントオーケストレーションのための十分な堅牢性。 ユニファイドKVキャッシュに適したサイズ。96GBのユニファイドメモリで、低ビット幅の122Bモデルは、ディープSSDバックのKVキャッシュのための十分なヘッドルームを残します。これは、スワップなしで長文コンテキストを保持するためのすべてです。 見つけられた最も近いサービングスタックはrapid-mlxでした。しかし、それはハイブリッドアテンションで異なる方向へ進んでいました。その分岐を考慮して、私はそれをフォークしました。結果はqMLXで、GitHubで入手可能です。フォークはハイブリッドアテンションに特化しています(詳細は後述)。 本当の仕事:3つのバグを潰す モデルは収まりましたが、モデル自体とは無関係な別の理由で、箱から出してすぐに使用できませんでした。すべてのフォローアップメッセージが会話全体を最初から再処理していました。130kトークンのコンテキストでは、モデルが単語を1つ emit する前に数分待つことになります。 原因は、GatedDeltaNet (SSM) レイヤーと密なアテンションの混合であるハイブリッドアテンションアーキテクチャです。SSMレイヤーのリカレント状態は巻き戻したり、以前の位置にトリミングしたりできないため、メモリリークを避けるために、インメモリキャッシュはそのレイヤーを含むエントリをすべてドロップします。このモデルでは、インメモリプレフィックスキャッシュは毎回ヒットしません。通常のウィンドウでは、ディスクヒット109回に対してインメモリヒットはゼロでした。モデルをウォーム状態に保つ唯一のものはディスクキャッシュです:アテンションKVをSSDにチェックポイントし、次のターンでそれを復元します。ディスク復元はフォールバックではなく、キャッシュ全体です。そしてそれは、最後のバグの陰に隠れるように、3つの異なる方法で壊れ続けました。 バグ1:システムプロンプトのタイムスタンプ KVの再利用はバイト単位で正確です。プロンプトがわずかに変更されても、最初の違いでマッチが失敗し、それ以降すべてが再計算されます。 エージェントフレームワークは、ターンごとに一意のメッセージIDをシステムプロンプトにスタンプしていました。130kトークンのプロンプトの上部近くにあるその一意の値は、プロンプトがバイト単位で安定しないことを意味しました。ターン2は、最初の数百トークン内でターン1と異なりました。キャッシュされたエージェントは破棄され、システムプロンプト全体が再構築され、マッチは早期に分岐しました。ターンごとに、コールド状態でした。 修正は、その行を削除することでした。メッセージIDは装飾的なもので、誰もそれを読み戻さず、エージェントはすでにターンごとのユーザーメッセージを通じてIDを持っていました。ターンごとにユニークなものは、変更されるべきプロンプトの部分に入れるべきではありません。 バグ2:決して起こらなかった返信 システムプロンプトは修正され、しばらくは機能しましたが、その後、会話の奥深くで再び壊れました。 モデルがまだ返信中にメッセージを送信すると、エージェントは実行を中断します。正しい動作です。しかし、割り込みパスで、ストリーミング済みの返信を保存せずにループを抜けました。推論サーバーはすでにそのトークンをKVキャッシュにデコードしていましたが、履歴にはアシスタントのターンが欠けていました。分岐、深い本体、コールドフィル。 データベースに対してそれを証明しました:アシスタントのターンなしで連続して保存された4つのメッセージと、画面にストリーミングされた返信が履歴に存在しないこと。それは決して書き込まれませんでした。修正は、割り込みパスでストリーミングされた返信を、ブレークする前に永続化します。これは、ネットワークストリームのドロップに対してコードがすでに実行していたのと同じ回復です。ボーナスとして、エージェントは自分が言ったことを忘れなくなります。 一般的なルール:生成されたトークンがサーバーのキャッシュに到達できる場合、その生成は、乱雑なパスを含むすべての終了パスで履歴にコミットされなければなりません。 バグ3:チェックポイントストアの毒 最初の2つの修正の後、キャッシュはターンごとにウォーム状態を保ちましたが、ツールを使用したターンや中断されたターンで正確に1回コールド状態になり、その後回復しました。 2人のライターがチェックポイントストアに触れていました。一方は本物を書き込みました:次のターンで復元される、プロンプトにキーが付けられたチェックポイントです。もう一方は、トークンキーなしで256トークンごとにフルチェックポイントを書き込むバックグラウンドフックでした。そのため、マッチさせることも復元することもできませんでした。デッドウェイトです。そして、それはディスクキャップに対してカウントされていました。長いツール使用ターンは大量のトークンを生成し、それらのジャンク書き込みを大量にトリガーし、ストアをキャップを超えさせ、エビクションポリシーは古いものから順にドロップし、ジャンクと一緒に1つの良いチェックポイントも削除しました。ディスク上には、単一のディレクトリに27GBのマッチ不可能なボディがあり、重要なチェックポイントを圧迫していました。修正は、エビクションにまずマッチ不可能なチェックポイントをドロップするように教え、復元がオンになっている場合はジャンクライターを完全に無効にします。良いチェックポイントは生き残り、次のターンで復元され、コールドフィルは停止します。 どこに着地したか 以前はターンごとに30kトークンをコールドフィルしていたのと同じ会話が、修正後、31kから57kトークンに増加しました: uid=58 HIT cached=53267 prefill=670 uid=59 HIT cached=54009 prefill=33 uid=60 HIT cached=54113 prefill=1671 uid=61 HIT cached=55867 prefill=45 uid=62 HIT cached=55996 prefill=1869 すべてのターンで前のコンテキストが復元され、新しいメッセージのみがプリフィルされます。以前は数分かかっていたものが、サブ秒になりました。チェックポイントディレクトリはクリーンな状態に落ち着き、すべてのチェックポイントがマッチ可能で、ジャンクはありません。 キャッシュは助けるだけでなく、問題の形状を変えます。キャッシュオンとオフでの同じ繰り返しプロンプト、秒単位のプリフィル時間です。