ガイド · 1 分で読めます

SwiftアプリにPythonインタプリタを234MB同梱する:MinuteAIがオプションのMLランタイムをリリースするまでのパイプライン

話者分離機能そのものはひとつの物語です。その裏にある743MBのPython/PyTorchランタイムをビルドし、検証し、リリースするのはまた別の物語です。そのダウンロードがユーザーのMacに届く前に、MinuteAIが実行しているパイプラインを解説します。

SwiftアプリにPythonインタプリタを234MB同梱する:MinuteAIがオプションのMLランタイムをリリースするまでのパイプライン

語りやすいのは「機能」の話まで

以前、MinuteAIがなぜ話者分離をアプリ本体に同梱せず、234MBのオプションダウンロードとして提供するのかについて書きました。あの記事が扱ったのはアーキテクチャです——サブプロセスとして動くPythonランタイムを、必要なときだけ取得し、SwiftUIのバイナリから意図的に距離を置くという設計でした。あの記事が扱わなかったのは、その234MBのファイルがそもそもどうやって作られるのか、という部分です。機能紹介には出てこない部分ですが、1バイトでもユーザーのMacに届く前に、毎回正しく完了していなければならない工程です。

MinuteAIはリリース用の成果物を、アプリのソースコードとは別のリポジトリに置いています。これはプロジェクトの経緯による偶然ではなく、意図的な分離です。アプリ側のリポジトリは、ランタイムをダウンロードし、展開し、駆動するSwiftコードを持っています。一方リリース側のリポジトリは、ビルド済みのtarballをGitHub Releaseのアセットとしてホストし、それをどう再現するかを文書化するためだけに存在します。リリースリポジトリの中身を読んでも、話者分離が実際に何をしているかはわかりません——それはアプリがダウンロードするもの「そのもの」を作るための手順書です。

ビルドし、それから動くことを証明する

その手順書は固定の3段階からなる手順で、CIランナーではなく人間が順番に実行することを前提としています——ランタイムをビルドし、検証し、それからパッケージ化する、という順序です。ビルド段階では、Python 3.13.11、PyTorch 2.10.0、pyannote.audio 4.0.4を組み込んだ自己完結型の環境を組み立てます。この工程は冪等(べきとう)であるよう設計されている、つまり再実行しても安全だと文書化されています。というのも、クリーンな状態からのビルドは依存関係のダウンロードとコンパイルにおよそ5〜10分かかり、パッケージ化する前の時点でおよそ712MBに達するからです。

ビルドとパッケージ化のあいだで起きていることこそ、じっくり見る価値があります。単なる形式的な手続きではないからです。検証段階は5つのチェックからなるゲートです——Pythonと各MLライブラリが期待通りのバージョンでインポートできるかを確認し、実際のHugging Faceアクセストークンを使ってモデルを一時ディレクトリにダウンロードし、2人の話者による音声を合成し、そのモデルを実行し、少なくとも1つのセグメントにわたって2人の話者を実際に検出できるかを確認する、という流れです。この音声合成のステップは、見た目以上に重要です。手順書は、テスト音声がmacOS自体のテキスト読み上げエンジンから生成されたものでなければならず、生成した正弦波のトーンであってはならないと明記しています。なぜなら、このモデルは合成トーンを音声として確実には認識しないからです。偽の音声を使った検証は、何も証明していないのに、見た目上はクリーンな結果を返してしまいます。5つのチェックすべてが成功と報告するまで、パッケージ化には進みません。

二度書き留める価値のある2つのバグ

手順書のトラブルシューティング欄には、一度実際に起きたことがあり、二度と起きないようにするコストが低い、2つの具体的な失敗が記録されています。ひとつは依存関係の刈り込みに関するバグです。以前のバージョンのビルドスクリプトは、使われていないと想定したパッケージを刈り込んでいましたが、その対象にsklearnnetworkxが巻き込まれてしまいました。しかし実際にはpyannote.audio 4.xはこの両方に依存しており、これらを刈り込んだ結果、ModuleNotFoundError: No module named 'networkx'というインポートエラーで失敗するランタイムが出来上がってしまいました。これはパッケージ化がすでに完了したあとにしか表面化しない種類の失敗であり、だからこそ一回限りの修正ではなく、トラブルシューティング文書に恒久的な一行として記録されているのだと考えられます。

もうひとつは、MinuteAI側ではなく上流の問題です。pyannote.audio 4.xは自身のパイプラインが返す値そのものを変更し、古いコードが直接期待していたAnnotationオブジェクトの代わりに、DiarizeOutputというオブジェクトで結果をラップするようになりました。これにより3.x系のAPIを前提に書かれたコード——MinuteAI自身の推論スクリプトが依存していたitertracks()の呼び出しも含めて——が動かなくなりました。この移行の痛みはこのプロジェクト固有のものではありません。同じDiarizeOutputの変更は、pyannoteを基盤とする他の話者分離ツール、たとえばWhisperXも壊しており、そのGitHubのIssueトラッカーには今も開いたままのスレッドが存在するほどです。MinuteAIの修正——結果オブジェクトからspeaker_diarizationを取り出してから旧来のメソッドを呼び出す——は、pyannoteコミュニティ全体が行き着いたのと同じパターンです。この点は、このパイプライン全体をどう捉えるかについて、ひとつの見方を与えてくれます。ここで防がれていることの大半は、MinuteAI固有の脆さではなく、変化の速いMLライブラリに依存することの通常のコストが、見えない形で吸収されるのではなく、目に見える形で現れているだけなのです。

なぜAppleの仕組みをそのまま使わないのか

そもそも、743MBというオプションのペイロードを、GitHub Releasesと通信する自作のダウンローダー経由で取得しているのはなぜなのか、と問う価値はあります。インストール後にコンテンツを取得するために存在する、Apple自身のOn-Demand Resourcesという仕組みがあるにもかかわらず、です。これを除外する理由は2つあります。On-Demand Resourcesは、アプリスライシング後のリソースタグを512MBまでに制限しており、理想的な目安として64MBという数字が挙げられています。Python・PyTorch・pyannoteを含む環境は、「メディアアセット」と「独自のsite-packagesツリーを持つ実行可能なランタイム」の違いを考慮する以前の段階で、この上限に収まりません。さらに別の理由として、AppleはOn-Demand Resourcesを近年のOSリリースを通じて段階的に非推奨とし、より新しいBackground Assetsフレームワークへの移行を進めており、サイズの問題を抜きにしても、これから依存を築くには縮小しつつある対象になっています。GitHub Releases経由で自己完結型のtarballを配布し、アプリケーションコード側からダウンロードを駆動するというやり方は、この両方の問題を回避します——ただしその代償として、Apple自身のインフラであれば無料で処理してくれたはずの、リソースタグの管理などを自前で持つことになります。

このトレードオフは、パイプラインの別の箇所にも現れています。GitHub Releasesは大きなバイナリをホストするのに便利な場所です——ファイル自体にサイズ上限はなく、公開者側に課金されるような帯域制限もありません——しかし、専用のCDNのような「リリースホスティングプラットフォーム」として設計されたものではありません。ダウンロード解析機能はなく、「最新版」をプログラム的に解決する仕組みも組み込まれておらず、長く壊れやすいURLしかありません。MinuteAIのSwiftコードは、後者の問題を直接的なやり方で回避しています——ダウンロードURLは実行時に解決されるのではなく、ソースコード内で特定のリリースタグにバージョン固定されています。これはつまり、ランタイムを新しいpyannoteやPyTorchのバージョンへ引き上げることが、サーバー側の設定変更ではなく、アプリ側のコード変更とリビルドを意味するということです。ランタイムとアプリのリリースサイクルはすでに互いに切り離されているとはいえ、これは話者分離ランタイムが単独でどれだけ頻繁に更新できるかに対する、現実の制約として残ります。

アーキテクチャ図には現れない部分

ここまでの話は、話者分離機能そのものとして実際に出荷されたものを何ひとつ変えるものではありません。これは別の主張です——「同梱せずオプションダウンロードにする」というのは、一見一行で済むアーキテクチャ上の判断に見えても、その裏にははるかに長い運用上の裏付けがある、という主張です。冪等であり続けなければならないビルドスクリプト、トーンジェネレーターを信用せず実際の音声を生成しなければならない検証ゲート、一度すでに起きた2つの具体的なバグ、そしてAppleの標準ツールに収まらないという理由と、GitHubの標準ツールに収まるという理由の両方から選ばれた配布方式。MLの依存関係をオプションにすると決めること自体は、誰にでもできます。モデルやランタイムを更新するたびに、その判断を安全に繰り返せるようにしておくことこそが、目に見えにくい仕事です。


MinuteAIはApp StoreからmacOSおよびiOS向けに入手できます。詳しくはgetminute.appをご覧ください。特定のワークフローへの組み込み方についてご質問があれば、hello@aitytech.comまでお気軽にどうぞ。

実績を見る

MinuteAIからAgentKitsまで — 私たちが提供した製品とプロジェクトをご覧ください。

ポートフォリオを見る

関連記事

ガイド

AppleとGoogleが無料で通話の文字起こしを始めた。それでもZoomのタブには手が届かない

iOS 26とGoogleのPixel Recorderは、無料でオンデバイスの通話文字起こしと要約を行うようになりました。しかしどちらのプラットフォームも越えていない境界線があります — それがまさにMinuteAIのChrome拡張機能が動いている場所です。

ガイド

日本の「最大12兆円」レガシー警告は、COBOL人材不足の話ではない。文字コードの話だ。

経済産業省の「2025年の崖」は、人材の引退や一括置き換えのコスト計算として語られがちだ。しかし移行プロジェクトを最初に静かに壊すのは、もっと小さく見落とされやすい問題だ——EBCDICとShift-JISは、英字と数字のどちらを先にソートするかすら一致しない。Legacy Dragonが文字コードを、後付けの前処理ではなくパーサー本体の関心事として扱っている理由。

ガイド

料金ページも従量課金もない理由 — PrivateAIが無料である経済的な仕組み

クラウドAIがトークン単位で課金されるのは、1回のクエリごとにベンダー側で実際の計算コストが発生するからだ。オンデバイスのツールにはその請求書が存在しない。この構造的な違いがPrivateAIのようなプロダクトに何をもたらし、何をもたらさないのかを解説する。