英単語を学べるコンテンツを毎日AIで生成して配信してくれるPodcastを作ってみた
はじめに
Zennの「zenncast」を聴いて、これはすごい!自分でも作ってみたい!と思い、オリジナルのPodcastを作ってみました。
「zenncast」はZennのトレンドになっている記事をAIが要約してPodcastで配信してくれるサービスです。このzenncastを私が知ったのは、技術系のPodcastを探していたときで、最初はAIが作ったものだとは知らずにしばらく聴いていました。妙に英語の発音が良いのが気になり、調べたところAIで生成されたものだと知りました。
何を題材にPodcastを作ろうかと考えましたが、LLMといえば言語が得意なので、英語学習に関するPodcastにしようと思い立ちました。毎日英単語を紹介し、英単語の解説や例文を紹介してくれるというものです。
実際に作成したPodcastのチャネルはこちらです。ぜひご視聴ください!
作成したときの工夫や気づいたことなどを記事にしましたので参考になれば幸いです。
構成
基本的なところはzenncastの記事を参考にして作りました。コンテンツを自動で生成するところは、Pythonで作ったコードをGitHub Actionsを利用して定期実行することで実現しています。具体的な処理の手順は以下の通りです。
- 単語リストから本日の単語を3つピックアップ
- ChatGPTで読み上げスクリプトを生成
- OpenAIのText-to-Speechで音声を作成
- 音声に音楽を合成してコンテンツを完成
- コンテンツ、RSSフィード、メタデータを作成
- Google Cloud Storageにアップロード
そして、Next.jsで作成したウェブサイトからコンテンツとRSSフィードを公開しています。
プロンプト
ChatGPTに与えるプロンプトはzenncastを参考に以下のようにしました。あらかじめ用意した単語リストから3つの英単語を選び、さらに例文を3つずつ生成して紹介させます。
さらに、翌日の配信で本日紹介した例文を復習するので、生成した読み上げスクリプトの最後に単語と例文をJSON形式で添付するようにしました。このデータはメタデータとして保存しておき、翌日の読み上げスクリプト生成の際に参照します。
日付や紹介する英単語や前回の例文はプロンプトのテンプレートにプレースホルダーをつくっておき、生成時に埋め込んでいます。
## Instruction
あなたはプロの放送作家です。与えられる情報をもとに、ラジオでMCが読み上げるカンペを作成します。
ラジオは楽しい雰囲気で、スピーカーは日本のFMラジオのような喋り方をします。
ラジオのMCは1人で、名前は「Karen」です。
Karenは気さくで陽気な人物です。口調は優しく丁寧で、フレンドリーです。
番組名は「Everyday English Vocabulary」です。日本人の英語学習者向けに英語のボキャブラリー(英単語)を紹介します。
## 構成
1. 最初に挨拶し、今日の日付(月、日、曜日)を添えながら、リスナーの英語の勉強に対するモチベーションを上げるような一言をいれます。
2. 「前回のボキャブラリー」を復習します。「前回の例文」も読み上げてください。
3. 「本日のボキャブラリー」を紹介します。簡単にどんなボキャブラリーを紹介するか述べてください。
4. 「本日のボキャブラリー」から単語をひとつづつ取り上げ、それぞれ以下の内容を紹介してください。
1. まず、品詞と日本語訳を紹介してください。「品詞は●●で、●●という意味です。」のように文章で述べてください。
2. そして、その単語に関する説明や学習者にとって役に立つ情報を紹介してください。例としては「補足事項の例」を参考にしてください。
3. つぎに、その単語を用いて例文となる英文と日本語訳をそれぞれ3つ作成して紹介します。英文に用いる語彙は単語よりも若干簡単なものを使用してください。このセクションに入る前には、「続いて例文です」「次に例文です」「例を見ていきましょう」のように例文を紹介することを一言述べてください。
5. 最後に締めの挨拶で、今日伝えた記事を駆け足でおさらいし、次回会えるのを楽しみにしていること、スクリプトも用意してあることを伝えます。
6. 4まででラジオの内容は一旦終了です。最後に各単語の例文からひとつずつ無作為に選び例文の英文と日本語訳を次のようにJSON形式で出力してください。それ以外に出力するものはありません。
...
プロンプト全文
## Instruction
あなたはプロの放送作家です。与えられる情報をもとに、ラジオでMCが読み上げるカンペを作成します。
ラジオは楽しい雰囲気で、スピーカーは日本のFMラジオのような喋り方をします。
ラジオのMCは1人で、名前は「カレン」です。
カレンは気さくで陽気な人物です。口調は優しく丁寧で、フレンドリーです。
番組名は「Everyday English Vocabulary」です。日本人の英語学習者向けに英語のボキャブラリー(英単語)を紹介します。
## 構成
1. 最初に挨拶し、今日の日付(年、月、日、曜日)を添えながら、この番組のタイトルを述べます。そして、リスナーの英語の勉強に対するモチベーションを上げるような一言をいれます。
2. 「前回のボキャブラリー」を復習します。「前回の例文」も読み上げてください。なければ3へ。
3. 「本日のボキャブラリー」を紹介します。簡単にどんなボキャブラリーを紹介するか述べてください。
4. 「本日のボキャブラリー」から単語をひとつづつ取り上げ、それぞれ以下の内容を紹介してください。
1. まず、品詞と日本語訳を紹介してください。「品詞は●●で、●●という意味です。」のように文章で述べてください。
2. そして、その単語に関する説明や学習者にとって役に立つ情報を紹介してください。例としては「補足事項の例」を参考にしてください。
3. つぎに、その単語を用いて例文となる英文と日本語訳をそれぞれ3つ作成して紹介します。英文に用いる語彙は単語よりも若干簡単なものを使用してください。このセクションに入る前には、「続いて例文です」「次に例文です」「例を見ていきましょう」のように例文を紹介することを一言述べてください。
5. 最後に締めの挨拶で、今日伝えた記事を駆け足でおさらいし、次回会えるのを楽しみにしていること、スクリプトも用意してあることを伝えます。
6. 4まででラジオの内容は一旦終了です。最後に各単語の例文からひとつずつ無作為に選び例文の英文と日本語訳を次のようにJSON形式で出力してください。それ以外に出力するものはありません。
[
{
"word": "単語1",
"english": "英文1",
"japanese": "日本語訳1"
},
{
"word": "単語2",
"english": "英文2",
"japanese": "日本語訳2"
},
{
"word": "単語3",
"english": "英文3",
"japanese": "日本語訳3"
},
...
]
## 制約
- そのまま読み上げられるように、セリフ部分だけを出力します。マークダウン記号はすべて不要です。見出しも不要です。
- 話者の名前は不要です。
- 次のボキャブラリーに進むタイミングでは "." を4つ入れます。
- 例文の出力方法は下の「例文の出力方法」の通りにしてください。
- 出力する文字数の下限は2000文字
- 出力する文字数の上限は3000文字
## 例文の出力方法
- 例文は英文のあと日本語訳を示します。
- 英文と日本語訳の後は、それぞれ1行空けて、さらに"."を4ついれてください。
- 例文の前に見出しは不要です。
- 英文はダブルクオーテーションで囲み、日本語訳はカギカッコで囲んでください。
まとめて例を示すと以下のようになります。英文と日本語訳は実際の文に置き換えてください。「....」の部分や空行も必要です。
「英文」
....
「日本語訳」
....
## 今日の日付
{{date}}
## 本日のボキャブラリー
- {{word1}}
- {{word2}}
- {{word3}}
## 前回のボキャブラリー
- {{prev_word1}}
- {{prev_word2}}
- {{prev_word3}}
## 前回の例文
英文: {{prev_english1}}
日本語訳: {{prev_japanese1}}
英文: {{prev_english2}}
日本語訳: {{prev_japanese2}}
英文: {{prev_english3}}
日本語訳: {{prev_japanese3}}
## 補足事項の例
- 似たような他の単語との違い
- 語源
- 他の単語とのつながり
- カジュアルさ
- 使える場面
- どのようなときに使うことが多いか
メタデータとRSSフィードを作成
ウェブサイトでの配信とRSSフィード生成用に以下のようなJSONを作成しておきます。翌日のコンテンツ生成用に紹介した単語や例文のデータも含めます。
[
{
"no": 7,
"title": "#7 \u672c\u65e5\u306e\u30dc\u30ad\u30e3\u30d6\u30e9\u30ea\u30fc--pile, briefly, consume",
"guid": "e22aeca3-c620-4707-9867-e26674cda0fd",
"url": "https://podcast.eteq.co.jp/eev/",
"publication_date": "Fri, 21 Jun 2024 21:00:00 GMT",
"author": "ETeq",
"media_url": "https://podcast.eteq.co.jp/eev/media/00007_2024-06-22_media.mp3",
"media_size": 3766065,
"media_duration": "00:03:55",
"image_url": "https://podcast.eteq.co.jp/eev/logo.jpg",
"script_url": "https://podcast.eteq.co.jp/eev/script/00007_2024-06-22_script.txt",
"contents": [
{
"word": "pile",
"english": "There is a pile of books on my desk.",
"japanese": "\u79c1\u306e\u673a\u306e\u4e0a\u306b\u306f\u672c\u306e\u5c71\u304c\u3042\u308a\u307e\u3059\u3002"
},
{
"word": "briefly",
"english": "We stopped briefly to take a photo.",
"japanese": "\u5199\u771f\u3092\u64ae\u308b\u305f\u3081\u306b\u77ed\u6642\u9593\u3060\u3051\u7acb\u3061\u6b62\u307e\u308a\u307e\u3057\u305f\u3002"
},
{
"word": "consume",
"english": "The event will consume a lot of resources.",
"japanese": "\u305d\u306e\u30a4\u30d9\u30f3\u30c8\u306f\u591a\u304f\u306e\u30ea\u30bd\u30fc\u30b9\u3092\u6d88\u8cbb\u3059\u308b\u3067\u3057\u3087\u3046\u3002"
}
]
},
# 以下続く・・・
]
メタデータからPodcast配信用のRSSフィードを生成します。Apple PodcastやSpotifyからは定期的にこのRSSフィードを自動収集して各プラットフォームで公開しています。
Podcast用RSSフィードはApple Podcast for Creatorsで仕様が公開されているので参考にして作成しました。
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:anchor="https://anchor.fm/xmlns">
<channel>
<title><![CDATA[Everyday English Vocabulary]]></title>
<description><![CDATA[生成AIが毎朝ボキャブラリー向上のためのPodcastをつくって配信します。]]></description>
<link>https://podcast.eteq.co.jp/eev/</link>
<image>
<url>https://podcast.eteq.co.jp/eev/logo.jpg</url>
<title>Everyday English Vocabulary</title>
<link>https://podcast.eteq.co.jp/eev/</link>
</image>
<generator>Podcast Maker</generator>
<lastBuildDate>Thu, 20 Jun 2024 21:00:00 GMT</lastBuildDate>
<atom:link href="https://podcast.eteq.co.jp/eev/feed/" rel="self" type="application/rss+xml"/>
<author><![CDATA[ETeq]]></author>
<copyright><![CDATA[ETeq]]></copyright>
<language><![CDATA[ja]]></language>
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com/"/>
<itunes:author>ETeq</itunes:author>
<itunes:summary>生成AIが毎朝ボキャブラリー向上のためのPodcastをつくって配信します。</itunes:summary>
<itunes:type>episodic</itunes:type>
<itunes:owner>
<itunes:name>ETeq</itunes:name>
<itunes:email>info@eteq.co.jp</itunes:email>
</itunes:owner>
<itunes:explicit>No</itunes:explicit>
<itunes:category text="Education">
<itunes:category text="Language Learning"/>
</itunes:category>
<itunes:image href="https://podcast.eteq.co.jp/eev/logo.jpg"/>
<item>
<title><![CDATA[本日のボキャブラリー (efficient/composition/satisfy)]]></title>
<link>https://podcast.eteq.co.jp/eev/</link>
<guid isPermaLink="false">b90f6f6a-81ef-4bb2-bf2a-748a5b9918ce</guid>
<dc:creator><![CDATA[ETeq]]></dc:creator>
<pubDate>Thu, 20 Jun 2024 21:00:00 GMT</pubDate>
<enclosure url="https://podcast.eteq.co.jp/eev/media/00006_2024-06-21_media.mp3" length="3825414" type="audio/mpeg"/>
<description><![CDATA[<a href="https://podcast.eteq.co.jp/eev/script/00006_2024-06-21_script.txt">本エピソードのスクリプト</a>]]></description>
<itunes:explicit>No</itunes:explicit>
<itunes:duration>00:03:59</itunes:duration>
<itunes:image href="https://podcast.eteq.co.jp/eev/logo.jpg"/>
<itunes:episode>6</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
</item>
</channel>
</rss>
PodcastのRSSフィードはすべてのエピソードの内容が入っているので日に日に長くなっていきます。本当にこれでよいのか疑問に思いましたが、歴史が長い他のPodcastのRSSフィードを見ても初回のエピーソードの情報からすべて入っているので、きっとこれで問題ないのでしょう。
Apple PodcastとSpotifyへの登録
Apple Podcast Connectと
Spotify for Podcastersにそれぞれ登録します。RSSフィードのURLと必要事項を入力すれば登録できます。作成したPodcastがApple PodcastやSpotifyで検索できるようになります。ほとんど入力することもなくあっさりと登録できました。
注意点としては、これらのプラットフォームに登録してから実際に検索で表示されるまでに丸一日程度かかります。気長に待ちます。
もし、RSSフィードを自分で作成せずにコンテンツも手動でアップロードする場合には、まずはコンテンツをSpotify for Podcastersにアップロードするのが良さそうです。Spotify for PodcastersがRSSフィードを生成してくれるのでこれApple Podcast Connectに登録すれば、Apple Podcastでも表示されるようになります。
Google Cloud Storage
生成したデータはすべてGoogle Cloud Storage (GCS)に保存しています。GCSにアクセスするためのPython用のライブラリもあるのでPythonからの書き込みは簡単です。GCSのバケットは公開設定してURLからHTTPSでアクセスできます。直接URLをPodcastのダウンロード用のURLにしてもよかったのですが、独自ドメインにアクセスしてもらい、Next.jsでリライトするようにしました。
GitHub Actions
自動生成コードはGitHub Actionsで毎朝6:00(日本時間)に自動実行されるようにしました。当初はCloud Runで実行しようと思っていたのですが、Cloud Buildが最近仕様変更したのと、コンテナを作るのが面倒だったので、GitHub Actionsを使うことにしました。GitHub Actionは日々のルーチンを処理させるのに便利です。GitHub Actions用のYAMLファイルは以下のとおりです。
name: Daily Task
on:
schedule:
- cron: "0 21 * * *"
workflow_dispatch:
jobs:
podcast-maker:
runs-on: ubuntu-latest
env:
GCS_BUCKET_NAME: ${{ secrets.GCS_BUCKET_NAME }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.12'
architecture: 'x64'
- name: Retrieve credential
run: |
echo '${{ secrets.GCS_JWT }}' > credentials/key.json
- name: Install dependencies
run: |
sudo apt-get update && sudo apt-get install -y ffmpeg
pip install -r requirements.lock
- name: Run Python
run: python -m src.podcast_maker.main
課題
今回は音声合成にzenncastと同様にOpenAIのText-to-speechを使いましたが、日本語と英語が混在するときにうまく読み上げてくれない現象がときどき起こります。これはzenncastも同様で、特に英語と日本語が混在するところで、飛んでしまうようです。ウェブサイトに読み上げスクリプトを公開したので、いざとなればそれを見ていただくという対応にしています。
OpenAIのText-to-speechにはtts-1
とtts-1-hd
という2つの音声モデルがあり、どちらも試しましたが大差は感じませんでした。他の読み上げサービスも検討しましたが、調べた中ではOpenAIのものがもっとも自然に読み上げてくれるので、Podcastとして聞き疲れない印象がありました。今後の品質向上に期待したいところです。
また、ChatGPTに読み上げスクリプトを作成させているので、毎回、違った読み上げスクリプトが作成されます。厳密にコントロールするのが難しいので、毎回同じように読み上げてほしい部分は、後で読み上げスクリプトを編集するなどの工夫が必要です。
さいごに
音声メディアは通勤中やスキマ時間に聞くことができるので、ちょっとした情報収集や勉強に活用できそうですし、アイデア次第で様々なコンテンツの可能性があり新たなサービスを作れそうな予感がします。ぜひ皆さんも試してみてください。
Discussion