はじめに
諸般の事情により、AWSを使ってデモ発表することになりました。その中で「Amazon Pollyを使って何か面白いことができないか?」と考え、「AIが考えた漫才ネタを機械音声で実演させてみよう!」というアイデアにたどり着きました。
本記事では、ChatGPTで漫才台本を自動生成し、それをAWS LambdaからAmazon Pollyで音声化し、S3に保存するまでの一連のフローについて紹介します。
この記事でわかること
- (1)AWS→OpenAI API(ChatGPT)との連携
- (2)Amazon Pollyで2人のキャラクターによる漫才の掛け合い音声を生成
工夫したポイント
Amazon Pollyで音声を生成する際に、2種類の音声を交互に喋らせ、コンビ漫才をしているかのようにしたことです。
というのも、Amazon Pollyは、本来1人分の音声を生成するサービスです。
日本語だと標準2種類(ニューラルだと4種類)の話者が選べますが、1つのテキストで複数話者を喋らせることはできません。
そこで、ChatGPTに「芸人A」「芸人B」と話者を明記した台本を作らせ、セリフごとにPollyで声種を切り替えて音声を生成・合成することで、漫才の掛け合いを再現しています。
処理の流れはざっくり以下のとおり:
(1)AWS→OpenAI API(ChatGPT)との連携
1.ChatGPTで漫才ネタの台本を自動生成する際に、セリフの先頭に「芸人A」/「芸人B」を付与するよう指示
(2)Amazon Pollyで2人のキャラクターによる漫才の掛け合い音声を生成
2.台本を行ごとに分割し、それぞれのセリフが「芸人A」か「芸人B」かを判定
3.話者が「芸人A」ならMizukiの音声、「芸人B」ならTakumiの音声を使用
4.セリフから話者名(例:「芸人A:」)を除き、音声を生成し順番に配列へ追加
5.最後に、すべての音声データを結合し、1つの音声ファイルとして出力
このようにして、「芸人A → 芸人B → 芸人A → ...」と交互に再生される掛け合い音声が完成します。
では、具体的な手順を見ていきましょう。
(1) AWS→OpenAI API(ChatGPT)との連携
事前準備1 APIキー準備
今回はAWSからOpenAI APIと連携するため、ChatGPT(gpt-3.5-turbo)を利用します。
あらかじめOpenAIの公式サイトでAPIアカウントを作成し、APIキーを取得しておきましょう。
ChatGPT OpenAI公式サイト
※ChatGPTのOpenAI APIは重量課金となりますので、使い過ぎに注意してください。
なお、今回は手早く構築するため、取得したAPIキーはLambdaの環境変数に設定しています。
事前準備2 ネタ台本保管用S3
ChatGPTで生成した漫才ネタはテキストファイルとしてS3に保存します。
そのため、事前にS3バケットを作成し、バケット名もLambdaの環境変数に設定しておきましょう。
事前準備が済んだら、いよいよコーディングです。
lambda関数でChat GPTを呼び出し、漫才ネタを作ってみる
まずはChatGPTに渡すプロンプト(指示文)を決めておきます。今回、以下のように指定しました。
- ネタの長さは1分程度で披露できる内容にする
- 二人組の漫才師(芸人A、芸人B)の台本形式で、各セリフの冒頭に発言者名を明記する
プログラム作成
実際のコードはこうなります。
(Javaで作成しました)
//初期設定
const AWS = require('aws-sdk'); // AWSサービス操作用
const axios = require('axios'); // HTTP通信(OpenAI API呼び出し用)
const s3 = new AWS.S3(); // S3クライントインスタンス
// 環境変数から取得
const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // ChatGPTのAPIキー
const NETA_BUCKET_NAME = process.env.NETA_BUCKET_NAME; // ネタ台本保管用バケット
exports.handler = async (event) => {
try {
// リクエストのボディからお題を取得
const body = JSON.parse(event.body);
const { topic } = body;
// OpenAIにリクエストして漫才ネタを生成
// - Systemロール、Userロールで指示
const openAiPayload = {
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: 'あなたはお笑いのプロです。' },
{ role: 'user', content: 次のお題で1分の漫才ネタを考えてください: ${topic} },
],
max_tokens: 500,
};
// OpenAI APIへHTTP POSTでリクエストを送信
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
openAiPayload,
{
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${OPENAI_API_KEY}, // 認証トークン(Bearer接頭辞必須)
},
}
);
// ChatGPTが生成した台本テキストを取得
const generatedText = response.data.choices[0].message.content;
console.log('生成されたネタ:', generatedText);
// 生成したネタ台本をテキストファイルとしてS3に保存
const textFileName = comedy-${Date.now()}.txt;
await s3.putObject({
Bucket: NETA_BUCKET_NAME,
Key: text/${textFileName},
Body: generatedText,
ContentType: 'text/plain',
}).promise();
console.log(ネタをS3に保存しました: text/${textFileName});
// 台本の各行をセリフ単位で分割(改行区切り・空白除外)
const lines = generatedText.split('\n').filter(line => line.trim() !== '');
// それぞれの発話者ごとにセリフを整理(後続のPolly音声合成等に利用)
const dialogue = { comedianA: [], comedianB: [] };
for (const line of lines) {
if (line.startsWith('芸人A:')) {
dialogue.comedianA.push(line.replace('芸人A: ', '').trim());
} else if (line.startsWith('芸人B:')) {
dialogue.comedianB.push(line.replace('芸人B: ', '').trim());
}
}
lambda実行
作成したLambda関数をデプロイしたら、テスト実行で動作を確認します。
漫才ネタのお題は、LambdaコンソールのイベントJSONに設定します。
{
"body": "{\"topic\": \"ここにお題を設定\"}"
}
これで実行すると、設定したお題をもとに漫才ネタが自動生成され、S3バケットに台本テキストファイルが保存されるはずです。
AIが考えた漫才ネタが面白いかどうかは、ぜひ実際に試してみてください。
(2)Amazon Pollyで2人のキャラクターによる漫才の掛け合い音声を生成
次に、ChatGPTで作成した漫才ネタをAmazon Pollyを使って実際に喋らせてみます。
事前準備1 音声ファイル保管用S3
Amazon Pollyで作成した音声は、mp3ファイルとしてS3に保存します。
あらかじめS3バケットを作成し、バケット名をLambdaの環境変数に設定しておきましょう。
機械音声で漫才を実演させてみる
せっかくなので、2人の異なる声で掛け合いを再現し、より本物の漫才に近づけてみます。
Amazon Pollyの日本語音声から「Takumi(男性音声)」と「Mizuki(女性音声)」を使用します。
それぞれの音声でセリフを生成し、最後に1つの音声ファイルとして合成します。
(★ここがポイント!:2つの声を交互に使うことで雰囲気がかなりリアルになります)
先ほど作成した漫才台本生成用Lambdaに、音声生成・合成機能をアドオンして実装します。
まず、初期設定や環境変数まわりに下記内容を追加します。
//初期設定
const AWS = require('aws-sdk');
const axios = require('axios');
const s3 = new AWS.S3();
// ★追加★
const polly = new AWS.Polly(); // Amazon Polly クライアント設定
// 環境変数から取得
const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // ChatGPTのAPIキー
const NETA_BUCKET_NAME = process.env.NETA_BUCKET_NAME; // ネタ台本保管用バケット
// ★追加★
const POLLY_BUCKET_NAME = process.env.POLLY_BUCKET_NAME; // 音声保管用バケット
次に、漫才ネタ作成の処理の続きにPollyでの音声合成処理を追記します。
(エラーハンドリングも追加しています)
// Pollyで音声を生成
// 2人のキャラクターごとに VoiceId(Mizuki/Takumi)を割り当て
const voices = { comedianA: 'Mizuki', comedianB: 'Takumi' }; // 日本語の女性と男性の声
const audioStreams = [];
for (const line of lines) {
// セリフの先頭で話者を判定し、VoiceIdを動的に切り替え
// こうすることで「AとBが交互に喋る漫才」を再現
const speaker = line.startsWith('芸人A:') ? 'comedianA' : 'comedianB';
const params = {
// '芸人A:'/'芸人B:' という話者表記をセリフから除いて音声生成
Text: line.replace(/芸人[AB]: /, ''),
VoiceId: voices[speaker],
OutputFormat: 'mp3',
};
// Pollyで音声データ(mp3)を生成し、ストリームとして格納
const response = await polly.synthesizeSpeech(params).promise();
audioStreams.push(response.AudioStream);
}
// すべてのセリフの音声ストリームを順番に結合
const combinedAudio = Buffer.concat(audioStreams);
// 合成した音声データをmp3ファイルとしてS3に保存
const audioFileName = textFileName.replace('.txt', '.mp3');
await s3.putObject({
Bucket: POLLY_BUCKET_NAME,
Key: audio/${audioFileName},
Body: combinedAudio,
ContentType: 'audio/mpeg',
}).promise();
console.log(音声ファイルをS3に保存しました: audio/${audioFileName});
// 正常終了レスポンスを返却
return {
statusCode: 200,
body: JSON.stringify({
message: 'ネタ生成と音声作成成功',
topic: topic,
textFile: text/${textFileName},
audioFile: audio/${audioFileName},
}),
};
} catch (error) {
// エラーレスポンス&エラーログ
console.error('エラーが発生しました:', error);
return {
statusCode: 500,
body: JSON.stringify({
message: '処理中にエラーが発生しました。',
error: error.message,
}),
};
}
};
これで、指定したS3バケットに漫才の掛け合い音声(mp3)が作成されるはずです。
おわりに
発表の場でお題をもらいデモ実演することで、ライブ感を演出でき、本番はとても盛り上がりました。
OpenAIでコンテンツを生み出し、Pollyで“声”にするという流れは、まさに生成AIの実用例として面白い体験でした。
今回は、生成AIと音声合成、そしてAWSのサーバーレス技術を組み合わせることで、「ネタを作って喋らせる」という一連の流れを自動化できました。
真面目な用途にも転用できそうですが、こうした遊び心のあるユースケースこそ、技術に対する理解や応用力を育てるチャンスだと感じています。
もしこの記事が面白いと思ったら、ぜひ試してみてください!