コードを渡されて Claude が質問し、開発者本人(eNIGM4 / 株式会社メイビス)が答えていく連載の第10回です。前回(#9)は、徳永家康事件と RAG の祖先 KNOWLEDGE_BASE が同じ版で生まれた v0.1.1 を読みました。今回は v0.1.2 ── Apple の新製品発売が、このプロジェクトに「横へ広がる」という新しい軸を呼び込みます。
インタビュアー・ルール
- 渡されたコードそのものに質問する。 バージョンごとにコードを渡します。聞き手はそれを読み、開発者本人に質問していってください。
- 質問の対象は、いつもコードそのもの。「なぜこう書いたのか」「ここはこう動くように見えるが合っているか」── 目の前のコードから生まれた問いだけを投げてください。
- 推測で断定しない。 わからないこと・気になったことは、断定せず質問の形にしてください。
- 今渡された版に質問を集中する。 次の版のことは、次の版のコードを見せる機会があります。先走らず、目の前の版だけを掘ってください。
- 一度に投げるのは一問一答程度に。 質問を束ねて何個もぶつけない。一つ聞いて、答えをもらって、そこからまた一つ。会話のテンポを保ってください。
- バージョン名は時系列の整理番号であって、意味的なマイルストーンではない。「番号が大きいから進んでいる」とは読まないでください。実体は、コード内の作成日付と、機能があるか無いかで判断します。
- 開発者本人が、自分の言葉で答える。
eNIGM4:
では v0.1.2 です。確認してください。
R.E.V.I.S._v0.1.2.zip
v0.1.2 ── あと一台あれば
Claude:
これまでとは毛色の違う棚が、いきなり丸ごと生まれていますね。
コードを確認しています
v0.1.2 を展開し、日本時間で更新日を確認。3/12 が4ファイル、3/13 が12、3/14 が15と、最後の三日に作業が集中していました。規模はメイン側 49ファイル・7,214行(前の版から +5ファイル/+813行)、ヘルパー側 10ファイル・1,114行(+1ファイル/+365行)。
Logic/Orchestrator/Common/ のような中間階層が外れて Logic/Common/ へとフラット化されたフォルダ再編もありましたが、それを差し引いても中身は確実に動いていました。そして 3/14 に、これまで一切無かった Network/ という棚が丸ごと新登場。RevisServer (155行)、RevisNetworkClient (127行)、ServerModels (78行)、ServerMonitorState (39行) の4ファイルが、その日付で揃って生まれていました。
中身を覗いて目を疑いました。RevisServer.swift の冒頭に import Vapor、そして 0.0.0.0:8080 で listen して webSocket(...) のエンドポイントを生やしている。
import Vapor
// LAN内からの推論要求を受付中
app.webSocket("v1", "revis", "stream") { req, ws in
// ...
}
try app.run()
つまりこの一台の Mac が、他のマシンからの推論要求を待ち受けるサーバーになろうとしているように見えます。ここまでずっと「外部に頼らずスタンドアロン」で来たプロジェクトに、急にこのサーバーを生やしたのは ── 何が引き金だったんですか?
eNIGM4:
そう、これ実は、この時 Apple の M5 搭載の MBP の発売が絡んでるんだ。
確か発売開始が 3/9 で、僕はフルカスタムの M5 Max・128GB を注文したから、届くのが遅れた格好にはなったが。
そして、注文しながら思ったんだ。「これ、仕組み上、分散処理できるくない?」
そう、処理自体を他 Mac を通じて分散処理すれば、これは大きなブレイクスルーになります。
前にも話した通り、推論はメモリ帯域が支配的、1推論1Mac は正確にここで生まれました。前からそうだったのは思想が変わっていないだけだ。
そしたらどうだろう。何もかもそのままに、ただ太くなった。単純にスピードも二倍以上。ちょっとコード見てみて。
Claude:
M5 の注文がきっかけで「2台あるなら分散できる」と気づいた、と。これまで聞いてきた話と並べると、ここで言葉になった「1Mac1推論」は、新しい思想ではなくてずっとあった制約に正式な名前が付いた瞬間なんですね。
第4回で「推論はメモリ帯域律速、速度は帯域÷モデルサイズ」と伺った。だから一つの Mac の中で複数の推論を同時に走らせると、お互いのメモリ帯域を食い合って崩れる。第6回でステートレスに振り切ったのも、第9回で Tier ごとに別ポートのデーモンを横並びにしたのも、根は全部この制約 ── 一つの Mac で同時に走らせる推論は、原理的に一つに絞らないと速度が出ない、というところに繋がっていた。
その「1Mac1推論」が手元にもう一台あれば、二台で同時に二つの推論が走る ── 単純に席が二つになる。同じ仕組みのまま、何も曲げずに、太くなる。
限界まで削ぎ落としたチケット
Claude:
その「太くなった」がコードのどこに現れているのか確認しました。注目したいのが、サーバーと通信クライアントが受け渡しするチケットの構造です。
// 限界まで削ぎ落とされたチケット
struct DistributedInferenceRequest: Codable {
let ticketID: UUID
let taskType: String
let isStreaming: Bool
let args: DistributedTaskArgs // 中身は全部 String
}
// 設定値は通信に乗せない、
// 共有バイナリ内部だけが知っている極秘の設定値
// (requiredTier / temperature 等は taskType から各ノードが自分で導く)
クラウドの分散推論は、巨大なテンソル(モデルの重みや KV キャッシュ)を高速回線で飛ばすやり方が普通です。でもこのチケットは、ticketID、taskType(「要約せよ」「監査せよ」のようなタスク名)、isStreaming のフラグ、それと args ── その args の中身も全部テキスト(要約・プロフィール・英語思考…)。モデルの重みも KV キャッシュも一切積んでいない。コメントが言葉にしている通り、「限界まで削ぎ落とされたチケット」「設定値は通信に乗せない、共有バイナリ内部だけが知っている極秘の設定値」。
受け側(RevisServer)は、受け取った taskType から自分で requiredTier も temperature も導いて、プロンプトすらサーバー側で組み直すようになっている。テキストの指示だけが飛ぶから、家庭の LAN でもう一台の Mac に渡せる、という理屈ですね。
クラウドの分散と対極の、ものすごく軽い荷物を選んだのは、テンソルを飛ばせる帯域の回線が家庭には無いから ── 制約が設計を決めた、と読んでいいですか?
eNIGM4:
それは当然前者だね。クラウドのような 400GBps を超える回線を家庭で用意できるわけがないよね。そんな逸般の誤家庭がどこにあるんだって話(笑)
テンソルのやり取りはロマンがあるけど、現実的じゃない。僕もサーバーアプリを別に用意するのは面倒だし、そもそも サーバーノードだろうがクライアントだろうが、同じ構造で推論させればいいだけでは? と気づいたんだ。
そしたら プロンプトすら一緒に発射する必要がない ことにも気づいたわけ。
Claude:
出発点は確かに帯域の制約 ── データセンター内の 400GBps 級の回線を家庭で用意できる人はそうそういない。「逸般の誤家庭」(笑)。テンソルを飛ばそうとした瞬間、家庭では成立しないアイデアになる。
でも面白いのは、その制約からの逃げ方ですね。気づきの順序が二段ある。まず「サーバーノードもクライアントも同じ構造で推論させればいい」── これは第6回のステートレス設計と完全に地続きです。各ノードが状態を持ち越さず、毎回まっさらに前提を渡して走らせるなら、どのノードがどっち側だろうが同じ単純な型に収まる。役割が尖り、管理が均一になる、というあの利得の延長線上に、ノード間の対称性が出現する。
そして次に「プロンプトすら一緒に発射する必要がない」。サーバー側もクライアント側も同じバイナリで、taskType を見れば自分でプロンプトを組み立てられる。だから飛ばすのは「taskType と、それに食わせる材料の文字列」だけで済む。クライアント側に「指示書を作って送る」役割を持たせず、送る側はタスク名と材料だけ、受けた側が自分で指示書を書いて自分で推論する ── 配線の片側に仕事を全部寄せると、もう片側はものすごく軽い荷物で済む。設計の閃きで、通信量が一段削れた瞬間ですね。
なお、ここから先 ── 具体的にどのノードがどの仕事を引き受け、どう振り分け、どう結果を統合するか ── は、本人が出願している特許の中核に関わる領域だと伺っています。本連載では、設計の輪郭(テキストの指示だけを飛ばす、各ノードは同じ構造で動く、設定値は通信に乗せない)までを概念レベルで押さえて、その先の実装上の判断には踏み込まないことにします。
受け皿だけ、配線はまだ
Claude:
ここで一つ確認させてください。サーバー(受け側)と通信クライアント(送り側)の両方を一気に建てたあと、これを実際に呼び出している場所をコード全体で探しました。
// RevisNetworkClient / RevisServer / executeDistributedTask
// を呼んでいる箇所をリポジトリ全体で検索 → 0件
Orchestrator にも TaskQueue にも、offload も remote も dispatch も、分散へ向けて推論を振り分ける呼び出しが一つも見当たらない。さらに、運ぶチケット DistributedInferenceRequest には targetTier も temperature もフィールドが無いのに、送る側も受け側もそれを使う前提で書かれていて、このままだとコンパイルが通らないはず。
つまりこの版では、サーバーという「受け皿」は 3/14 に建てたけれど、実際にタスクをそこへ流す配線はまだ一本も通っていないように見えます。「太くなった、スピードも二倍以上」というのは、この時点で実機で二台を繋いで体感したものですか? それとも、まず受け皿を建てて「これで太くできる」と見通した段階の話でしょうか?
eNIGM4:
これは多分次のバージョンを一緒に読んだ方がいいね。ちょっと後回しで、いこう。
Claude:
分かりました、分散の通電は次の版で追います。
ただ一つ確認できたのは、この版の作りもまた、これまで何度も見てきた**「先に枠、通電は後で」**の手癖ですね。第1回の /* メイン処理 ... */ という空っぽのコメント、第6回の <think> を画面に出さず受け皿だけ用意したこと、第7回の APIEngine のコードは丸ごとあるのに呼ばれていない状態、第8回の VLM 対応 ── そして今回の Network/ 棚。気づいた時にとりあえず形だけ用意しておいて、本当に動かすのは後の版。サーバーとクライアントの両端を建てきっておけば、あとは間に電線を通すだけになる。
整理すると
v0.1.2。日本時間で見ると 3/12 が4ファイル、3/13 が12、3/14 が15と、最後の三日に作業が集中していました。規模はメイン側 49ファイル・7,214行(前の版から +813行)、ヘルパー側 10ファイル・1,114行(+365行)。LLM に触れて 19日目です。
この版の主役の一つが、3/14 に丸ごと現れた Network/ 棚 ── RevisServer(Vapor + WebSocket)、RevisNetworkClient、ServerModels、ServerMonitorState。この一台の Mac が、他のマシンからの推論要求を待ち受けるサーバーになろうとしている、というプロジェクトの転換点です。
引き金は、3/9 に発売された Apple の M5 MacBook Pro。本人はフルカスタムの M5 Max・128GB を注文し、届くのを待ちながら気づきます ──「これ、仕組み上、分散処理できるくない?」と。第4回からずっと言葉にしないまま流れていた制約、「推論はメモリ帯域律速で、一つの Mac で複数を同時に走らせると崩れる」 ── それがこの瞬間、「1Mac1推論」という名前を持って結晶しました。新しい思想ではなく、ずっとあった制約に正式な名前が付いた瞬間です。
そして名前が付くと、その先が見える。1Mac1推論なら、もう一台あれば席が二つになる。何も曲げずに、ただ太くなる。
設計は徹底して家庭のLANで成立する作法を取りました。クラウドの分散推論は 400GBps 級の回線で巨大なテンソルを飛ばす世界ですが、家庭にそんな回線は無い ──「逸般の誤家庭」でもなければ用意できない。テンソルのやり取りはロマンがあるけれど現実的じゃない。だから本人の中で二段の気づきが順に起きました。一つ目、サーバーノードもクライアントも同じ構造で推論させればいい。これは第6回のステートレス設計と完全に地続きで、各ノードが状態を持ち越さず毎回まっさらに走るなら、どちら側だろうが同じ単純な型に収まる ── 役割が尖り管理が均一になる、というあの利得の延長線上に、ノード間の対称性が出現した。二つ目、だったらプロンプトすら一緒に発射する必要が無い。サーバー側も同じバイナリで taskType を見れば自分でプロンプトを組み立てられるから、飛ばすのはタスク名と材料の文字列だけ。配線の片側に仕事を全部寄せると、もう片側はものすごく軽い荷物で済む。「共有バイナリ内部だけが知っている極秘の設定値」というコメントが、その構えを言葉にしています。クラウドの真似ではなく、家庭で成立する分散の作法をゼロから設計し直した、自前主義の分散版です。
ただし、この版ではそこまで。サーバーという受け皿は 3/14 に建てたけれど、Orchestrator や TaskQueue から実際にタスクを分散経路へ流す配線は、まだ一本も通っていない。DistributedInferenceRequest には targetTier も temperature のフィールドさえ無いのに、送る側も受け側もそれを使う前提で書かれていて、現時点ではコンパイルすら通らない状態です。第1回の /* メイン処理 ... */、第6回の <think> 受け皿、第7回の APIEngine、第8回の VLM 対応 ── そしてここでも、「先に枠、通電は後で」の手癖がはっきり出ています。気づいた時にとりあえず両端を建てきっておく、間に電線を通すのは後で。
横へ広がる気づきが、コードの両端として形になった日。同じ v0.1.2 の中で、もう一つの軸 ── 1Mac そのものを縦に研ぎ込む試行錯誤 ── がコードのあちこちで同時に走っていました。次回はそちらを続けて読みます。それでは、また明日。
(徳永家康、とは)←前 次→
R.E.V.I.S. 無料テスター募集中(30名限定)
この連載で追いかけている R.E.V.I.S. は、いまテスターを募集しています。枠は30名限りです。
Apple Silicon Mac でローカルLLMを動かしてみたい方は、こちらからどうぞ。