はじめに
RSSリーダーを長く使っていると、だんだん同じ問題にぶつかる。情報は追いたい。でも全部読むのは重い。記事が増えれば増えるほど未読は積み上がるし、移動中や作業中にまで画面を見続けるのは正直しんどい。
それなら、読むのをやめて聞けばいい。そう考えて、SiftoにRSS記事を音声化し、Podcastとして配信する機能を実装した。さらに、生成済みの音声を聞くだけでなく、要約記事をその場でストリーミング再生する機能も追加した。これによって、RSSは単に「読むもの」ではなく、「聞くもの」に変わった。
実装してみると、これは単純な読み上げ機能ではなかった。記事を取得して、そのまま機械的に読み上げるだけでは、とても聞ける体験にはならない。要約の作り方、台本の構成、音声の間、チャンク分割、音声ファイルの結合、保存先の選定、Podcastとしての配信方法まで、全部がつながって初めて成立する機能だった。
この記事では、Siftoの中でこの仕組みをどう組んだのか、TTSとはそもそも何が難しいのか、そしていろいろ試した結果なぜAivis Cloud APIに落ち着いたのかを書いていく。
Siftoの中でどういうワークフローになっているのか
まず全体の流れから書いておく。今回作った機能は、大きく分けると「記事を読むためのデータを作る流れ」と、「そのデータを音声体験に変える流れ」に分かれている。
最初にSiftoがRSSから記事を取り込む。これは普通のRSSリーダーと同じで、各フィードから記事のメタデータと本文を収集するところから始まる。ただし、ここで終わらない。取り込んだ記事はそのままユーザーに見せるだけでなく、LLMで要約を生成し、Siftoの中に「読むための短い情報」として蓄積する。
次に、その直近の要約記事の中から、音声化に向いたものをいくつかピックアップする。全部を読み上げても情報量が多すぎるし、番組としても散らかるので、ここではある程度の取捨選択が必要になる。新しさや重要度、話題のまとまりを見ながら、一定本数の記事を選んでいる。
選ばれた要約記事は、そのまま連結するのではなく、ポッドキャストとして聞ける形に再構成する。ここでLLMに対して、オープニング、導入、個別記事の紹介、全体のまとめ、エンディングといった番組の流れを持った台本を生成させる。単に記事を順番に説明するだけだと音声として退屈なので、流れや呼吸を持った一つの番組として組み立てることが重要になる。
その後、生成された台本をそのまま一発でTTSに流すのではなく、チャンク単位に分割する。これは技術的にも品質的にも必須だった。長文を丸ごと投げると失敗しやすいし、成功しても一部分だけ読みがおかしい時に全部やり直しになる。だから文脈を壊しすぎない範囲で台本を細かく区切り、それぞれを個別に音声化する。
チャンクごとのテキストはAivis Cloud APIに送られ、それぞれ音声ファイルになる。このとき、ただ音を出すだけではなく、無音や間の取り方もAivis側のパラメータで調整している。ここは地味だけどかなり効く。文章の区切れ目や話題の切り替わりに応じて間が自然に入ることで、音声としての聞きやすさが大きく変わる。
生成されたチャンク音声は、その場で捨てずに一時ファイルとしてCloudflare R2に保存している。最終的な完成音声だけでなく、中間生成物も残しておく設計にした。こうしておくことで、一部のチャンクだけ再生成したり、結合処理だけやり直したり、不自然な箇所を後から確認したりできる。運用していると、こういう逃げ道がない設計はだいたい自分の首を絞める。
その後、Cloud Run Jobs上で動く小さな音声結合用の処理が、R2上のチャンク音声を集めてFFmpegで結合する。結合処理をTTSと分離したのは正解だった。責務が分かれるし、必要なときだけジョブとして起動できるので、常駐サービスを抱える必要もない。結合が終わった最終音声もR2に保存し、そこからPodcast用のRSSで配信できる状態にする。
この一連の流れによって、Siftoは「RSSをためる場所」から、「要約されたニュースを聞ける場所」へ変わった。しかもそれが、定期的に自動生成される。ここまでできると、かなり体験が変わる。
ストリーミングTTSでさらに体験が変わった
Podcast化とは別に、記事要約をその場でストリーミング再生する機能も作った。これも地味に効く。完成済みの番組を待って聞くだけではなく、その瞬間に読みたい、いや聞きたい要約を即座に音声化して再生できるようになったからだ。
この機能を入れると、情報との距離感が変わる。未読記事を開いて目で追うのではなく、再生して耳で取るようになる。作業しながらでもいいし、移動中でもいい。RSSリーダーを開いたときの行動が、「読む」から「流す」に変わる。これは想像以上に大きかった。
そもそもTTSとは何か
ここで少しTTSそのものの話も入れておきたい。TTSはText-to-Speechの略で、テキストを音声に変換する技術のことを指す。言葉だけ聞くと単純だが、実際に使ってみると、そんな軽い話ではない。
テキストを音にするだけならできる。しかし、自然に聞ける音声にするのは別問題だ。特に日本語はアクセントやイントネーション、文の区切り、読点の扱い、間の取り方などが自然さに強く影響する。短いサンプルでは良く聞こえても、五分、十分、二十分と流し続けると、少しの違和感が確実に積み重なってくる。
つまりTTSは、単なる変換エンジンではない。実際には、テキストをどう音声として体験させるかを決めるレイヤーでもある。発話の安定性、抑揚、間、読みの正確さ、長時間聞いたときの疲れにくさ。そういうもの全部を含めて評価しないと、本当に使えるかどうかは見えてこない。
実際にTTSをいろいろ試した
今回の実装では、いくつかの主要なTTSを試した。
VOICEVOXはローカルで動かせて、日本語も安定している。試作にはかなり向いているし、環境を自前で完結させやすいのも魅力だった。ただ、長尺で聞くとどうしても合成音声っぽさが残る。短い通知音声や補助的な用途なら十分だと思うが、番組として聞き続けるには少し厳しかった。
CoeFontは声の質が良く、ナレーションとして成立しやすい。ただし、プロダクトに組み込んで継続的に回すことを考えると、コストと運用のバランスが気になった。良い声ではあるが、気軽に大量生成する構成とは少し相性が悪かった。
ElevenLabsは英語では本当に強い。そこは疑いようがない。ただ、日本語で長く聞くと微妙な違和感が出ることがある。短いデモでは目立たないのに、長尺になるとズレが見える。これはかなり厄介だった。
OpenAIのTTSは全体的に優秀で、APIの扱いやすさも良かった。ただ、日本語の自然さについてはケースによって印象に差があり、最終的な決め手にはならなかった。
いろいろ試した結果、最終的に一番バランスが良かったのがAivis Cloud APIだった。
Aivis Cloud APIに落ち着いた理由
Aivis Cloud APIが良かった理由は、派手な一点突破ではなく、全体のバランスの良さにある。日本語が自然で、長尺でも比較的疲れにくく、APIとして素直に扱えて、しかも定額でコストが読みやすい。この四つが揃っていた。
実際のプロダクトでは、「一番すごい技術」より「毎日黙って働いてくれる技術」の方が重要になる。日本語TTSの比較をしていると、ついデモのインパクトで判断しがちだが、Podcastのように実際に聞き続ける前提では、安定して聞けることの方が圧倒的に大事だった。
結論として、Aivis Cloud APIは一番プロダクトに載せやすかった。かなり雑に言えば、最高だった。
TTSは失敗する前提で設計した方がいい
実装して分かったが、TTSは常に一発成功する処理ではない。長いテキストをまとめて投げると途中で失敗することもあるし、読みが一部だけおかしくなることもある。だからこそ、台本をチャンクに分割する構成が必要だった。
チャンク化しておけば、失敗した一部分だけを再生成できる。全体をもう一度やり直す必要がない。しかも、どこで問題が起きたかも特定しやすい。こういう設計は、動くかどうかより、壊れたときにどう戻せるかで価値が決まる。
音声は「間」で決まる
TTSを使っていて一番面白かったのは、音声の自然さは単に声質だけでは決まらないということだった。むしろ、間の取り方がかなり大きい。文章が流れすぎると息苦しいし、間が変だと一気に機械感が出る。
最初はFFmpegで無音を後付けしていたが、これはいまひとつだった。今はAivis側のパラメータで無音や間を制御している。これに変えてから、文の切れ目や段落の転換が自然になり、聞きやすさがかなり改善した。TTSはテキストから音を作る技術というより、音声の呼吸を設計する技術でもあると実感した。
中間生成物を残しておく設計はかなり重要だった
生成した音声は最終的な一本のファイルだけでなく、チャンクごとの一時ファイルも全部R2に保存している。Cloudflare R2は10GBまで無料で使え、しかもエグレス料金がかからない。音声のように配信で外に出ていくデータとの相性がかなり良い。
中間ファイルを残しておくと、不自然なチャンクだけ作り直したり、結合処理だけ再実行したりできる。あとで調整したくなったときにも効く。こういう部分は地味だけど、運用に入った瞬間に効いてくる。
音声結合はCloud Run Jobsに切り出した
音声結合はCloud Run Jobs上の小さな処理として分離した。FFmpegを使ってチャンクをまとめるだけの専用ジョブだ。これを別にしたことで、TTSと責務がきれいに分かれたし、必要なときだけ起動する構成にできた。常駐サービスで抱える必要がないのは精神衛生上も良い。
まとめ
今回Siftoに実装したのは、単なる読み上げ機能ではない。RSS取得、要約、記事の選定、台本化、チャンク分割、TTS、音声結合、R2保存、Podcast配信、そしてストリーミング再生までを一つの流れとして組み上げたことで、「読むためのRSS」が「聞くためのRSS」に変わった。
そして、その中心にあったのがTTSだった。TTSは単にテキストを音声にする技術ではなく、情報を耳で受け取れる体験に変換するための技術だった。いろいろ試した結果、Aivis Cloud APIはその用途に一番合っていた。日本語の自然さ、長時間再生のしやすさ、APIの扱いやすさ、コストの読みやすさ。この全部が揃っていたからだ。
実際にやってみると、難しかったのはAIそのものでもインフラそのものでもなかった。最終的に一番難しいのは、ちゃんと聞ける音声を作ることだった。そしてそこを越えると、RSSは本当に別の体験になる。




コメント