自分がはてブした記事をPodcastにして配信する環境を作った
ということで作った。
サイト
Podcast URL
ソースコード
Overcastでも普通に聴けてて便利。
動機
最近の情報収集方法はもっぱら、はてブのお気に入りフィードとXのおすすめフィードだ。特にXのおすすめフィードはうまく調教出来ていると日英問わず興味深い記事ばかり大量に流れてくる。
今まではそれらの記事をはてブしてとりあえず"あとで読む"に入れておくという運用にしていた。ところがご存じの通りほとんどの人間は"あとで読む"に入れてしまうとあとで読まないことがわかっており、ご多分に漏れず自分も"あとで読む記事"がどんどん溜まっていき消化できなくなっていた。
最近NotebookLMを使っていて、WebページでもPDFでもとにかくコンテンツはなんでも突っ込んでおけば、いい感じにPodcastが作成されるという体験がうらやまし〜と思っていたところだったので、じゃあこれの自分のはてブ版作るか〜となり試してみたという経緯。
実装
こういうノリで欲しい〜と思ったソリューションを、今までだったら1,2日ガッと気合い入れれば自前でスクラッチできたわけだけど、加齢に伴い"ガッ"とやっていく体力が落ちており、面倒だし来週やるか〜となり2週間,1ヶ月,半年と経過し、気づいたら忘却の彼方へ...という状態になっていた。が、現在我々にはコーディングエージェントなる怠惰のための神器が用意されている。
大体システム構成は脳内にすぐ描けるし大体出来ることはわかってるが手を動かすのがとにかくだるい、という案件にCline + Claudeは最適である。
まずはmkdir hatebu-audio
みたいなディレクトリを作り、この前作っておいたプロジェクトルール作成リポジトリで.clinerules
を生成。
そんで下記の初期プロンプトをこしらえる。
私がはてなブックマークした記事やPDFの内容を取得して内容を少し長めに要約してまとめて、それらをPodcastのような会話形式で音声ファイル化する仕組みを作りたいです。用途は自分が通勤中などに聴くためです。サーバーを用意したりせず自分のローカルPCで実行できるだけでも良いです。データベースが必要であればSQLiteを使ってください。技術スタックとしてはTypeScriptが良いです。
私のはてなブックマーク一覧が取得できるURLは以下です。
- HTTP URL: https://b.hatena.ne.jp/razokulover/bookmark
- RSS URL: https://b.hatena.ne.jp/razokulover/bookmark.rss
Cline + Claude 3.7 sonnetをplanモードにして何度か考えさせる。途中で下記のような感じでCline側から質問が来るので何回かラリー。
1. 要約と会話形式への変換には、OpenAI APIの利用を想定していますが、これは問題ないでしょうか?(APIキーとコスト発生が必要)
2. 音声合成にはどのようなサービスを使いたいですか?
OpenAI TTS API
Google Cloud Text-to-Speech
Amazon Polly
ローカルで動作する音声合成ライブラリ
3. 会話形式のPodcastでは、何人の話者を想定していますか?(例:2人のキャラクターによる対話)
4. 生成された音声ファイルはどのような形式で保存したいですか?(MP3, WAV, AAC等)
5. 一度に処理するブックマーク数の上限や、どの期間のブックマークを対象にするかなどの条件はありますか?
ある程度良さげなPlanができたら、そこまでの過程を踏まえてREADME.mdとDESIGN.mdを作成させる。
あとはAutoApproveのRead/Edit/MCPあたりだけチェックを入れてactモードに切り替えて待つ。
最初のうちは暴走する可能性があるので見張ってる必要があるけど、ある程度既定路線の実装パターンに入ったらスマホでもいじってればいい。それで1セッションが終わるのをとりあえず待つ。
試行錯誤
セッションを分ける
流石にこの規模のシステムになると1セッションで作り切るのは難しい。というか最初は思ったような機能が出来てないといったこともあるだろう。
そういう時は一旦cancelして止めて全体を確認する。そんでそこまでの進捗をREADME.mdやDESIGN.mdに書き出させてgitにcommitさせる。あとはセッションを新しく作成し直す。
そうしたら下記のようなプロンプトを書いて実装を仕切り直してもらう。
このリポジトリにはREADME.mdやDESIGN.mdの設計に基づいてラジオの音声ファイルを生成しPodcastを配信する機能を実装中です。contentsテーブルの内容を要約する機能の実装から始めてください。
これの繰り返し。暴走して意味わからんこと始めたらcancel→やり直し、を繰り返す単純作業。
幸い今回のシステムは「はてブの取得」「記事の抽出」「要約」「音声ファイル化」「フィード生成」「website構築」「deploy」みたいな感じで、AIがワークフローの中の各機能をnpm run
でコマンド実行できるように実装してくれていたので個別実行しながら進捗を確認しやすかった。
テストを書かせない
AIにテストを生成させるのが便利みたいなことを先々週くらいまで言ってた気がしたけど、あれは一概には言えないということがわかってきた。
機能の実装を書かせてから、その実装のテストを書かせるという流れで確かにそれなりのテストは出来るんだけどある程度のサイズのシステムを実装させようとするとテストの実装の試行錯誤に結構な時間を取られる。簡単にいうとtokenをめちゃくちゃ消費するので(すげー)金がかかる。
個人開発のシステムなんて自分が満足できる品質が保証されていれば良いんだからテストはそれこそ全ての実装が終わった後の残タスク消化タイムにでもやればいい。まずは全部実装させて自分の望む当たり前品質を満たすまでは機能実装に集中してもらおう。
残りの1割
実装がある程度終わった状態で下記の機能は大体出来きてた。
- はてなブックマークのRSSフィードからブックマーク情報を取得
- Webページやからの記事本文抽出
- PDFからのテキスト抽出
- OpenAI APIを使用した要約生成
- OpenAI APIを使用したナレーション生成
- Google Cloud TTSを使用した音声合成
- 複数の音声ファイルを一つのMP3ファイルに結合(音声ファイル間に2.5秒の無音を挿入)
- ラジオ風の挨拶と結びを自動的に追加(process-allコマンド実行時)
- 音声ファイルをCloudflare R2にアップロードしてPodcastとして配信
- Podcast用のRSSフィードを自動生成
- 過去の配信を一覧表示するWebサイトの自動生成とデプロイ
だけど完璧ではなくて人間が手を加える必要がある部分もいくつか残っていた。
具体的には、
- 音声の台本テキストを作成するためのプロンプト調整
- 生成される音声ファイルのコンテンツ調整(より聞きやすい音声にするための色々)
- 音声ファイル/websiteのデプロイ準備
- Podcastとして配信するための準備
- 絶妙に気づきにくいバグ取り
あたり。特にコンテンツの調整は苦労した。実装としては出来ているが、自分が聞いた時にためになるレベルの記事の詳細度はどの程度なのか、要約しすぎるとつまらないし、深掘りしすぎると記事読んだ方が早い、となってしまう。その辺はプロンプトの調整や音声生成&合成の仕方をいくつも試す必要があった。
あとはデプロイ関連の処理、具体的にはアカウントを作ったり環境変数に入れるトークンとかを準備したりなど。どうしても人がやらないといけない。あとはPodcastに公開するには何が必要なのか?とかrequirementを読んで画像を準備したりもやった。
画像の生成にはDALL・E3を使い、画像の調整(テキスト入れたり)にはGoogle AI Studioを使った。
全体像
フロー
今回作ったシステムでやってることはざっくり下記。
- はてなブックマークRSSから最新のブックマーク情報を取得(特定のタグ付きのもののみ)
- 取得したブックマーク情報をデータベースと比較し、新規ブックマークを特定
- 新規ブックマークのURLからコンテンツを抽出
- 抽出したコンテンツを1人のナレーターによる解説形式に変換
- 解説テキストを音声に変換
- 音声ファイルをMP3形式で保存
- 処理結果をデータベースに記録
- 挨拶と結びの音声ファイルを生成(初回のみ)
- 未処理の音声ファイルを挨拶→本編→結びの順で結合
- 結合された音声ファイルをCloudflare R2にアップロード
- エピソードのメタデータを自動生成
- RSSフィードを更新
- Webサイトを生成してCloudflare Pagesにデプロイ
運用
はてブしたコンテンツの音声ファイル化は自動化できるならした方が楽だとは思いつつ、DB必要だしFFmpegも必要だし定期実行するためのschedulerの仕組みがないといけないしサーバの管理がだるいので今回はひとまずローカル環境で運用することにした。つまり、ある程度はてブが溜まったら自身のPCでコマンド実行するみたいな運用。
websiteに関してはNext.js+SQLiteでSSGしてCloudflare Pagesでホスティングさせている。SSGにするとbuild通ればバグることがほぼなく安定運用できるためまじで楽。
音声コンテンツはR2に置いている。デフォルトでByteRangeのリクエストに対応してるからPodcastのコンテンツ配信も特別な設定が不要。S3より安いのも良い。
残タスク
もう大体自分の要件は満たせているが、いくつかやっておきたいこともあるのでメモ。
- SQLiteのバックアップ
- 現状はローカルPCで運用してるからPCが死んだら終わる
- R2にでもsyncさせておくか
- 対応コンテンツ方式の拡充
- 現状はテキストタイプの記事とPDFしか対応していない
- Youtubeの動画とかも文字起こしを取得して要約できたらコンテンツの幅が広がりそう
- タイムスタンプがズレてる
- Podcast用にタイムコードをdescriptionに生成するようにしてみたけど、なぜか微妙にズレてる。バグっぽいけど個人的にはあんま困りはしてないので直すかはわからん...
- descriptionに各記事へのリンク追加
テストとかも書いた方がいいけど、今後の実装修正頻度を考えるそのためにどこまで金かけるか?みたいな気持ちはある...
実装にかかったコスト
時間
git logをみたら先週の日曜にInitしてるっぽい。ただ平日は仕事もあるし全然稼働できてないからざっと15時間くらい?ただ、Clineが動いてる時間は実質2時間もないと思う。自分がコンテンツの質を上げるために試行錯誤した時間がおそらく一番長く、あとはPodcastの仕様を調べたりCloudflare R2やPagesの仕様を読んだりしてる時間が長かった。人間がボトルネック。。。
金
今回はコーディングにClaude、テキスト要約/生成にGPT、Text to SpeechにGoogleを使っている。音声化に関してはOpenAIのTTSを使ってもいいんだけど、個人的にはあっちの自然な喋りよりGoogleの機械っぽい喋りの方が頭に入ってきやすかったのでGoogleにした(あと安い)。
コーディングに関しては$10くらい。Claude 3.7 Sonnet使ってるのに意外と安く済んだ。多分テストとかちゃんとやりながらだと倍はかかってると思う。
テキスト要約/生成ではgpt-4o-miniを使ってたのもあって、あんなに何度も生成したり実験繰り返したのに$2しか減ってない。Text to Speechも12円しか使ってなかった。
コーディング以外はほぼタダみたいなもん。なので1回のPodcastエピソードの作成に数円しかかからなそう。月10回くらいの配信頻度になる気がしてるから年間数百円?
まとめ
自分のはてブした消化しきれないブクマ記事をPodcastにして聴けるようにする仕組みを作った。自前で実装するとなると精神的にダルいような問題をコーディングエージェントに丸投げすることで手早く形にできるのは本当いい時代になったと感じる。
流石に今回作ったものを非エンジニア with AIで作り切れるかというと結構厳しい気はする。残り1マイルの状態まででさえ、自分による途中の技術的な指示や軌道修正の手助けがあってやっとたどり着けている感じ。このレベルの仕組みは仕事としてみたらかなり小規模なシステムだし、この程度でエンジニアの助けが要るのだからまぁ専門的な知識はまだある程度必要なんだとは思う。
とはいえ確実にアウトプットを生み出す敷居は低くなっているので本当に便利。
Discussion