これは「ケイラボAIラジオ」というデスクトップ常駐型の自律AIラジオを、Mac版からWindows版へ移植した記録です。
前回、Mac版の開発記を書きました。そこでは「最初に作ったWindows版でなぜ失敗したのか」「その反省を踏まえてMac版でどう設計を立て直したのか」を書いています。
今回はその続編です。立て直した設計を手に、もう一度Windowsへ戻ってきた話になります。
先に結論を書いてしまうと、移植は拍子抜けするほど簡単でした。そして「移植」というフェーズこそ、いまのAI開発が一番威力を出す場面だと、作りながら確信しました。なぜそう思ったのかを、最後に書きます。
ケイラボAIラジオとは
PC作業中のバックグラウンド利用を想定した、タスクトレイ常駐型の自律AIラジオです(Mac版はメニューバー常駐)。専用ウィンドウは持ちません。
AIのDJが気ままに喋り、Spotifyの曲をかけます。論理的な番組進行(番組フォーマット)と、LLMのカオス的な生成を両立させているのがこのプロダクトの肝です。喋りっぱなしでも、無秩序でもなく、「ちゃんと番組として成立しているけれど、毎回ちょっと違う」状態を目指しています。
主な機能はこんな感じです。
- 時刻・季節・曜日・記念日に応じた挨拶やリード文
- ニュースと天気(取得した内容をDJ同士の会話に変換)
- お便りコーナー、ゲストコーナー、アーティスト特集
- 曜日替わりのメインDJ、複数キャラの掛け合い
- 週をまたいで前回を覚えている長期記憶(ステーション・ジャーナル)
- アーティスト名の読み辞書の自動同期
技術スタックは、
- 音声合成:VOICEVOX(複数キャラ)
- 台本生成:Gemini
- 音楽:Spotify Web API(OAuth PKCE・検索/再生とも)
- ニュース・天気:Google News RSS・気象庁API
そしてWindows版だけの追加要素として、**AquesTalk1(初代・いわゆる「ゆっくりボイス」)**を2つ目の音声合成エンジンとして載せ、霊夢・魔理沙を追加しました。ここは後で出てきます。
設計について
アーキテクチャは3層です。
App(トレイUI + DI配線)
→ Infrastructure(VOICEVOX / Gemini / Spotify / 音声再生 / 設定ローダ …)
→ Core(抽象 + ドメイン型 + 放送進行ロジック・外部依存ゼロ)
依存方向は外側から内側への一方向だけ。そして一番内側の Coreは「完全にプラットフォーム非依存」 です。Coreは時刻もファイルもネットワークも直接触らず、すべて抽象(interface)越しにアクセスします。
設計の原則はシンプルで、
- interfaceは「実差し替えが本当に起きる境界」にだけ引く(音声・LLM・音楽・再生+テスト用の時計やHTTP)。実装が1個しかない抽象は作らない。
- 設定ファイルのローダは汎用1個に集約する(ファイルごとにローダを量産しない)。
- 進行ロジックは1つのエンジンに集約しつつ、コンストラクタ引数が膨らまないように、段を小さなステップ列で表現する。
地味ですが、この「Coreがプラットフォーム非依存」という1点が、後で移植のときに効いてきます。
振り返り:最初のWindows版で、何を失敗したか
実は、このプロダクトを最初に作ったのはWindows版でした。そして、見事に失敗しました。
失敗の本質は、設計の方針そのものではなく、**「設計を細かく整えることが目的化して、肝心のプロダクトから目が離れたこと」**でした。「1つの仕様 = 1つのnamespace = 新しいクラス群」と機械的に展開した結果、281ファイル・59仕様にまで膨れ上がり、Godオブジェクトと乱立したinterface、21個もの設定ローダが残りました。
設計図としては綺麗なのに、「実際に流れているラジオ」から目が離れていた。
このあたりの詳しい分析は前回の記事に書いたので、ここでは深追いしません。
その経験を活かして設計を立て直したMac版(詳しくは前回の記事で)
この失敗を踏まえ、Mac版では設計を一から立て直しました。要点だけ挙げると、
- 縦スライス:毎回「動く形」で機能を足す。各スライスの終わりに必ず「前より少し賢くなったラジオ」が手元に残る。
-
仕様駆動:実装の前に
docs/specs/<機能>.mdを書いて凍結する。 - 目的のための抽象化:差し替えやテストが必要な箇所にだけ抽象を引く。
- スコープ管理:「やらないこと」を決める勇気。
詳細はこちらに書きました。
- Qiita: https://qiita.com/KLaboratory/items/8a2de7da618d01f5740c
- Zenn: https://zenn.dev/tokumeishatyo/articles/3a062d46e24f86
この立て直しのおかげで、Mac版は素直に完成まで進みました。
そして、もう一度Windowsへ
Mac版がきちんと動くようになったところで、改めてWindows版を作り直すことにしました。一周回って、出発点に戻ってきたわけです。
今回のリポジトリは3つ並べました。
- Mac版(Swift)……挙動の「正」。読み取り専用の参照。
- 失敗した旧Windows版(C#)……下回りの実装だけ資産として流用。過剰分解した設計は捨てる。
- 新しいWindows版(C# / .NET 10 + Avalonia)……今回作るもの。
進め方は、Mac版の各スライス(s0〜s19)を、Windows版の各スライス(W0〜W19b)として1つずつ忠実に移植していく形です。仕様駆動も踏襲し、「仕様を凍結 → 実装 → dotnet test グリーン → ドキュメントへミラー」を毎スライス回しました。
言語とプラットフォームの置き換えは、対応関係を決めてしまえば淡々とした作業でした。
| Mac版(Swift) | Windows版(C# / .NET) |
|---|---|
AppKit NSStatusItem
|
Avalonia TrayIcon
|
AVFoundation |
NAudio |
URLSession |
HttpClient |
| Keychain | DPAPI(暗号化保管) |
| Yams(YAML) | YamlDotNet |
| 構造化並行(Task/TaskGroup) | 明示的な CancellationToken
|
そして、ここで一番驚いたことがあります。
Coreのドメインロジックと、YAMLの設定ファイルは、ほぼ1:1でそのまま移りました。 設定ファイル(番組フォーマット、DJ定義、コーナー定義…)に至っては、基本そのまま流用です。Coreがプラットフォーム非依存だったので、やることは「言語を書き換える」だけ。ロジックの構造も、責務の境界も、Mac版がそのまま設計図になっていました。
逆に言うと、今回「本当に新しく設計する必要があった」のは、Windows固有の差分だけでした。
- 単一インスタンス化/VOICEVOXの自動起動/セッション終了時の後始末(Windowsライフサイクル)
- AquesTalk1の「ゆっくりボイス」(声種ごとに別DLLを動的ロードする層)
- その上に乗せた、複数話者の掛け合いOP+同時発話(PCMミックス)と、霊夢→魔理沙の掛け合いトーク
ここだけは、Mac版に存在しないので、改めて仕様を起こして設計しました。けれど全体から見れば、ごく一部です。
成果としては、dotnet test が614件グリーン・警告ゼロ。トレイに常駐して実際に喋り、曲をかけるところまで動き、配布用の簡易インストーラ(Inno Setup)も用意できました。
気付き:移植は「設計さえちゃんとしてれば」簡単で、AIの威力が最大化する
今回の移植を通して一番強く感じたのは、これです。
移植の難易度は、移植元の設計の質でほぼ決まる。
なぜでしょうか。良い設計は「各部品が何をするか」「境界がどこにあるか」を明確にします。それは裏を返せば、そのまま移植の仕様書になっているのです。
そして今のAI(私はClaude Codeを使いました)は、「明確な仕様に沿って、忠実に・網羅的に変換する」のが圧倒的に得意です。
ゼロから「いい感じに作って」と丸投げすると、AIも人間と同じように発散します。最初のWindows版がまさにそれでした。でも、「このMac版のスライスを、この設計のまま、この境界で、C#へ移してほしい」と頼めるなら、AIは淡々と、正確に、端から端まで移植してくれます。途中で力尽きたり、飽きて手を抜いたりしません。
仕様駆動(先に docs/specs/ を凍結する)が効いたのも、同じ理由だと思います。移植元の挙動 = 仕様 = AIへの契約。契約が明確であるほど、AIの出力は安定します。
言い換えると、設計と仕様がAIにとっての「手すり」になるということです。手すりがしっかりしているほど、AIは速く、遠くまで走れます。移植というタスクは、その手すりが最初から全部揃っている、AIにとって理想的な仕事でした。
まとめ:とにかく、設計が大事
一周回って同じプロダクトをWindows→Mac→Windowsと作り直してみて、確信しました。
プロダクトの寿命を決めるのは、機能の数でも、使っている言語でもなく、設計の質です。
設計が綺麗なら、別プラットフォームへの移植も、機能追加も、AIとの協働も、すべてが楽になります。逆に設計が崩れていると、AIを使っても同じだけ崩れます。むしろ、AIの速度のぶん、速く崩れます。
「設計を細かくすること」と「プロダクトが前に進むこと」は別物――前回学んだこの教訓は、移植というフェーズでもそのまま効きました。そしてもう一つ付け加えるなら、良い設計は、AIにとって最高の道しるべになるということです。
同じように「移植が地獄だった」「AIに任せたら余計こんがらがった」で詰まったことのある人に、何か届けば嬉しいです。
- GitHub(Windows版): https://github.com/tokumeishatyo/AIRadio_Win
