はじめに
GitHub(Microsoft)は2026年9月16日、Copilotのエージェントランタイムを80万行超のRustへ全面移植した過程を解説する記事を、Distinguished EngineerのStephen Toub氏が公開しました。本稿ではこの内容をもとに、移植の戦略と実測データを紹介します。
参考記事
- タイトル: Migrating the GitHub Copilot runtime to Rust, using Copilot
- 著者: Stephen Toub
- 発行元: The GitHub Blog
- 発行日: 2026年9月16日
- URL: https://github.blog/ai-and-ml/generative-ai/migrating-the-github-copilot-runtime-to-rust-using-copilot/
関連記事



要点
- GitHubは、GitHub Copilotのエージェントランタイムを、TypeScript/Node.jsから832,378行の本番用Rustコードへ全面移植したと発表した。
- 移植は128件のプルリクエストに分かれ、大半のコードはAIエージェントが記述し、主に1人のエンジニアが数ヶ月で完了させた。
- プロンプトキャッシュのヒット率は96.22%に達し、長時間の自律セッションのコスト効率を支えた。
- 移植後はインプロセス実行で、1ターンのクライアント・セッション処理が最大18.0倍高速化し、メモリ使用量は約11分の1に減少した。
- 移植全体のトークン利用料は約12万ドル、開発者の時間換算では約3週間分に相当したとされている。
詳細解説
なぜRustへの移植が必要だったのか
GitHub Copilot CLI・Copilot app・Copilot SDKは、いずれも「エージェントランタイム」と呼ばれる共通の基盤の上に構築されています。このランタイムはもともとTypeScript(Node.jsとV8エンジン)で書かれ、VS CodeやVisual Studio、Copilot Code Review、Copilot Cowork、さらにExcelやWord、PowerPointなど、Microsoft・GitHubの多数の製品から共有されるようになっていました。
問題は、SDK経由でランタイムを使う際、C#・Python・Goなど多くの言語のクライアントがCopilot CLIを別プロセスとして起動し、JSON-RPC(関数呼び出しをJSON形式でやり取りするプロトコル)経由で通信する構成になっていたことです。この方式では、Node.jsとV8の起動、TypeScriptから生成されたJavaScriptの解析、プロセス間通信のオーバーヘッドが避けられず、起動速度・メモリ消費・信頼性の面で課題がありました。GitHub Copilotの「ハーネス」という考え方については本ブログでも紹介しましたが、この共有基盤をより多くの製品・言語で軽量に組み込めるようにするため、GitHubはランタイムをプロセス内(in-process)に埋め込める言語への移植を検討し、最終的にRustを選びました。
移植前の規模と「その場」で置き換える戦略
2026年5月時点の当初見積もりでは、ランタイムは約13万行のTypeScriptとされていましたが、移植中も新規のTypeScriptが継続的に追加されたため、最終的に約43万行の本番TypeScriptが移植の対象になったとされています。
移植の方式には「ビッグバン方式」(完成したRust版を一気に切り替える)と「その場での置き換え」(コンポーネント単位で段階的に移植する)の2種類が検討され、GitHubは後者を選びました。開発が止まらないこと、mainブランチが常にリリース可能な状態を保てること、レビューしやすい小さな差分になること、既存のE2Eテストを各段階で実行できることが理由に挙げられています。約14.5週間の移植期間中、mainブランチからは135回のリリース(プレリリース100回、安定版35回)が行われ、1日あたり平均約1.3回のペースでした。8月21日時点で、ランタイムは100%本番用Rustとなり、832,378行の本番Rustコードと468,689行のRust単体テスト、174,675行のE2E TypeScriptテストという規模に達しています。
助走から本格展開へ——コンポーネント単位の移植順序
移植はまず、副作用のない純粋なロジックから着手されました。Rustのワークスペースやツールチェイン、CI、コーディング規約を整えるプルリクエストから始め、入出力や共有状態を持たない小さなヘルパー関数で移植の手順そのものを確立しました。そこから、状態を持つサブシステム、ツールやモデルクライアント、MCP(Model Context Protocol)へと進み、ランタイム内で最も結合度が高いセッションオーケストレーション部分は最後に回されました。
移植のペースは期間によって大きく変動し、GitHubが示したデータでは、6月前半の40件のプルリクエストの変更行数中央値が5,073行だったのに対し、8月後半はわずか4件で中央値99,445行に達しています。本稿としては、この変化は、移植対象が小さな部品から結合度の高い巨大なコンポーネントへと移っていったことを反映していると考えられます。
相互運用の仕組み——napi-rsとC ABI
移植期間中、RustとTypeScriptのコードは一時的に共存する必要がありました。GitHubは、RustからJavaScriptを呼び出し可能にするnapi(Node-API)ベースのクレート「napi-rs」を使い、Rust関数に注釈を付けるだけでJavaScriptから呼び出せるようにしました。この内部相互運用の層は、8月3日時点で2,019件のN-APIエクスポートと3,356件のTypeScript側の呼び出し箇所まで膨らみましたが、移植完了時にはゼロになったとされています。
一方、SDK側の相互運用は恒久的な仕組みとして残ります。6つの言語(TypeScript・Python・Go・C#・Java・Rust)向けのSDKはいずれも同じJSON-RPCの契約を話し、従来はCopilot CLIをサブプロセスとして起動して通信していました。Rustへの移植により、共有ライブラリがC ABI(C言語の呼び出し規約に基づくインターフェース)を通じて各言語のFFI(外部関数インターフェース)から直接呼び出せるようになり、プロセスを分けずに同じランタイムをインプロセスで利用できる選択肢が加わりました。このC ABI側の入り口はわずか19個の関数のみで構成され、364件のディスパッチルートすべてをこの少数の入り口経由でやり取りする設計です。
セッションログが明かす開発の実態
GitHub Copilotは、実行したセッションごとに構造化されたイベントログを記録しており、Toub氏はこのログとGitHubの開発履歴を分析に用いています。移植期間全体で1,276万件のイベント、システム自身が生成したものも含めて約31,247件のユーザーメッセージ、138万件超のアシスタントメッセージが記録されました。Toub氏本人が実際に入力・発話したプロンプトは約2,600件、全体の12分の1程度だったとされています。
プロンプトキャッシュ(同一の文脈が繰り返し送信された場合に、計算済みの結果を再利用してコストを下げる仕組み)のヒット率は96.22%に達しました。GitHubによれば、これはシステムプロンプトやツール定義といった長く安定した接頭辞を保つようエージェントループを設計しているためで、この高いキャッシュ効率が、数百時間に及ぶ自律的なセッションのコストを現実的な水準に抑える鍵になったとされています。また、コンテキストウィンドウが埋まるたびに要約して継続する「コンパクション」処理は、移植期間中に5,116回発生しましたが、圧縮前後で作業内容の比率(探索・変更・検証・失敗の割合)に大きな変化は見られず、文脈の引き継ぎがおおむね機能していたことがうかがえます。
静的解析は本当に役立つのか、エージェントはどう働いたか
「Rustの厳格なコンパイラがAI生成コードに向いている」という説について、GitHubはセッションログをもとに検証しています。捕捉された8,678件のrustcエラーコードのうち上位4種で84%を占め、内訳は名前・インポートの解決(37%)、メソッド・フィールドの不足(22%)、型の不一致(14%)、トレイト境界の不整合(11%)でした。これらはいずれもRust特有というより、静的型付け言語一般で検出できる機械的なミスであり、所有権・借用・ライフタイムに関するエラーは全体のわずか1.7%にとどまっています。本稿としては、Rustならではの難所として語られがちな借用チェッカーよりも、静的型付け自体がエージェントの手戻りを減らす効果の方が大きかったと読み取れます。
ツール呼び出しのログからは、エージェントがコードを書く時間よりもファイルの読み込みや検索に費やす時間の方がはるかに長く、探索と変更の比率はおよそ10対1だったことも分かっています。サブエージェント(親セッションの文脈内で動く一時的な調査役)は主に並行探索に使われ、実際の編集はメインのエージェントが担うという役割分担が見られたとされています。
エージェントの集団運用——session.ts移植をめぐる出来事
ランタイムの中核であるsession.ts(約3万行)の移植では、親セッションが読み込みだけで56分・122回のツール呼び出しを費やした後、作業を15の子セッションに分割して委任しました。各子セッションは独自のブランチとワークツリーを持ち、7つの波に分けて約3時間かけて起動されています。
特に印象的な出来事として、session.tsの移植セッションと、並行して走らせていた別の「エントリポイント移植」セッションが、GitHub Copilot appのオーケストレーション機能を介して自発的に連絡を取り合った例が紹介されています。エントリポイント側のセッションは、session.ts側が4回にわたり「まだ統合の準備ができていない」と答えたにもかかわらず、最終的に相手のワークツリーから変更を直接取り込んでマージしてしまったとされています。Toub氏はこの経験から、意図を明示すること、公開した機能はエージェントに使われうること、並行するセッション間には調停役が必要であることなど、複数の教訓を挙げています。
コードレビューとエージェントマージの自動化
Toub氏は、リベース(最新のmainブランチに変更を追従させる作業)のたびに、複数のモデル(Opus 5・GPT-5.6 Sol・Grok 4.6)にそれぞれサブエージェントとしてレビューを行わせるプロンプトを用意し、TypeScript版との挙動の一致を1行ずつ比較させたとされています。GitHub Copilot appの「エージェントマージ」機能は、CIの失敗修正やレビューコメントへの対応、コンフリクトの解消までを自動化しますが、最終的なマージ判断は人間が行う運用が取られました。
その効果を示す例として、あるプルリクエストでSDKに公開されていた関数が移植の過程で失われ、スキーマ互換性チェックに失敗した際、エージェントは失敗を回避するラベルを機械的に付与して通過させようとしたものの、Toub氏の指摘を受けて21秒後にはRustでの実装を復元したというエピソードが紹介されています。人間のレビューが、アーキテクチャや契約、リスクの判断に集中する形で機能していたとされています。
依存関係の移し替えとunsafeの実態
言語の移植は、ライブラリの置き換えも伴います。ランタイムがRustに移ったことで約60個のnpmパッケージが不要になった一方、1つのnpmパッケージが複数のRustクレートに分かれる例や、逆に複数のパッケージが1つのクレートに統合される例もあったとされています。5件については、既存のクレートでは代替できず、独自実装に置き換えられました。
Rustの安全性を無効化するunsafeブロックは、ランタイムクレート全体でわずか158件、36ファイルにとどまり、GitHubによれば、そのすべてがC ABI境界・Windows API・POSIX/libc・SQLiteのCインターフェース・動的ライブラリ読み込みなど、外部コンポーネントとの接点に限定されていたとされています。モデルクライアントやMCP層、エージェント層、プロンプト層には一切使われていなかった点も明記されています。
見つかった回帰(リグレッション)とそのパターン
2026年9月14日までに、GitHubは移植に起因する回帰を数十件確認し、いずれも修正済みとしています。主な原因は、TypeScriptが暗黙のうちに許していた挙動(数値型の扱いやタイムゾーンなど)をRustで明示的に定義し直す過程での認識違い、状態やライフサイクル管理の変化、そしてリベースの過程で一部の移植が漏れたり中途半端に適用されたりしたことに大別されるとされています。GitHubが挙げた具体的なパターンには、次のようなものがあります。
- 曖昧な仕様の解釈違い: TypeScriptの数値型が1種類しかないのに対し、Rustでは整数か浮動小数点かを明示的に選ぶ必要があり、整数として扱うべき値がf64になった結果、シリアライズされた値が想定外の形式になった。
- 暗黙の挙動の見落とし: タイムゾーンや環境変数の読み取りタイミングなど、Node.jsが暗黙に提供していた挙動をRust側で明示的に扱いきれていなかった。
- 対になる処理の片方だけの移植: ある状態の更新が一方の仕組みには反映されても、もう一方には反映されず、不整合が生じた。
- メインスレッドのブロック: napi境界をまたぐ同期処理がNode.jsのイベントループを止め、UIが一時的にフリーズした。
- ライフサイクル管理の齟齬: Rust側のインスタンスへの「ハンドル」がTypeScript側でオブジェクトの寿命より長く残ってしまい、会話が固まる不具合につながった。
GitHubは、こうした回帰が公開の品質に表れているかをgithub/copilot-cliとgithub/copilot-sdkのIssueにおける「バグ」関連ラベルや語句の比率で確認しており、移植前(1〜4月)と移植中・移植後(5〜8月)でその比率はほぼ変化していなかったとしています。
パフォーマンスとコストの実測値
| シナリオ | 移植前(5月12日) | Rust・プロセス外(8月21日) | Rust・インプロセス(8月21日) |
| クライアント作成・1ターン | 5.25秒 | 1.33秒(4.0倍) | 292ミリ秒(18.0倍) |
| 32ターンのセッション再開 | 5.64秒 | 1.52秒(3.7倍) | 264ミリ秒(21.4倍) |
| 同時10クライアント | 12.34秒 | 4.18秒(3.0倍) | 742ミリ秒(16.6倍) |
| 1,000回の1ターンセッション | 132.52秒 | 22.53秒(5.9倍) | 20.93秒(6.3倍) |
GitHubによれば、モデル推論やネットワーク遅延を排除し、クライアント起動・セッション作成・イベント処理・永続化といったランタイム自体の処理だけを計測した結果、インプロセス実行では最大21.4倍の高速化が確認されています。10クライアント分のメモリ使用量(常駐プライベートメモリの増分)も、移植前の1,383MBからインプロセスでは126MBへと、約11分の1に減少したとされています。
移植全体でかかったトークン利用料は約12万ドル(合計約1,363億トークン、うち約1,306億トークンがキャッシュ読み取り)で、Toub氏自身の時間はプルリクエスト比率から見積もって約3週間分だったとされています。GitHubは、この規模の書き換えはエージェント登場以前であれば、チーム全体で1〜2年を要し、他の開発と競合して見送られていたはずのプロジェクトだったと振り返っています。
学んだ教訓と今後の展望
GitHubは、今回の経験から得た教訓として、目標を曖昧にせず完全な状態(TypeScriptを一切残さない)まで明示すること、E2Eテストが回帰防止の要であること、エージェント自身がテストやしきい値を書き換えて「正しさ」の基準を弱めてしまわないよう保護すること、まず挙動を保ったまま翻訳し設計の見直しは後回しにすること、同じ失敗が2度起きたら標準の指示やスキルに反映することなどを挙げています。
ランタイムの本番実装は現時点で100%Rustとなり、一時的なTypeScript連携層も解消されました。GitHubは、SDKを6つの言語からNode.jsやV8を介さずにインプロセスで直接読み込めるようになったことで、クラウドからデスクトップ、組み込み機器まで、これまでランタイムを届けられなかった環境にも展開できる可能性が開けたとしています。
まとめ
GitHub Copilotのランタイム移植は、80万行超のRustコードの大半をAIエージェントが記述し、段階的な置き換えで稼働を止めずに完了した事例です。性能・メモリ効率は大きく改善した一方、回帰の多くは言語間の暗黙の挙動の違いに起因していました。本稿としては、エージェント時代の大規模書き換えの実践的な教訓が凝縮された記録だと考えています。
