Amazon Transcribe, ChatGPT, Amazon Pollyで作る自動音声対話システム

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125062/123de507-edc0-4dc2-561d-54190f199f42.png)

近年、AI技術の進化により、自動音声対話システムがますます注目を集めています。音声入力をリアルタイムでテキストに変換し、AIが適切な応答を生成し、それを音声出力するシステムは、さまざまなシーンで活躍しています。今回、私は AWS の機能と OpenAI API を利用して、このような自動音声対話システムのプロトタイプを構築しました。

このプロジェクトでは、 AI 部分に OpenAI の [ChatGPT](https://openai.com/blog/chatgpt) を使用しました。 ChatGPT は、 OpenAI API を通じてアクセスでき、自然な会話を実現するために特別に設計されたモデルです。また、 [Amazon Transcribe](https://aws.amazon.com/jp/transcribe/) を音声認識に、 [Amazon Polly](https://aws.amazon.com/jp/polly/) を音声合成に使用しました。

本プロトタイプでは、クラウドサービスを利用してマイクロサービスアーキテクチャを採用しています。このアーキテクチャは、システムを疎結合にすることで、以下のメリットが得られます。

1. **他のシステムへの組み込みが容易**: 疎結合なシステムは、他のシステムとの連携や統合が容易で、機能追加や変更にも柔軟に対応できます
2. **従量課金によるコストメリット**: クラウドサービスは従量課金制であり、リソース使用量に応じて課金されるため、スタートアップなどで利用しやすく、運用コストを抑えられます

これらのメリットにより、システム全体の効率と柔軟性が高まり、将来の拡張や改善にも対応しやすくなります。
本記事では、この自動音声対話システムの構築手順を詳しく解説し、皆さんが自分自身で同様のシステムを作成できるようになることを目指します。さあ、始めましょう！

---

### ChatGPTへの入出力制御

本セクションでは、ChatGPTの入出力を制御するプログラムについて説明します。このプログラムは、チャットの入力と出力を標準入力（stdin）と標準出力（stdout）で扱えるように設計されたツールです。これにより、システム全体の柔軟性が向上し、他のプログラムやツールとの連携が容易になります。

このツールは OpenAI API への HTTP アクセスをラップしていたらなんでも良いのですが、GitHubリポジトリ [kamataryo/chatgpt-cli](https://github.com/kamataryo/chatgpt-cli) で Rust の実装サンプルを公開しています。標準入出力を介して ChatGPT への入力と出力が行われるため、他のツールやプログラムと連携しやすくなります。

```shell
# 使用例
$  echo 'こんにちは！' | chatgpt-cli 
kamataryo >> こんにちは！
assistant << こんにちは！私はAIアシスタントです。何かお手伝いできることがありますか？
```

なお、このプロンプト表示は、 `"$(whoami) >> こんにちは！"` `"assistant << "` までを標準エラー出力、`"こんにちは！私はAIアシスタントです。何かお手伝いできることがありますか？"` を標準出力に設定していますので、ChatGPT からの返答のテキストをそのまま他のプログラムに渡すことができます。

### Amazon Transcribe を使った音声認識

次にこのセクションでは、マイクからの音声入力をテキストに変換するステップについて説明します。Node.js での簡易な[サンプルのコード](https://github.com/kamataryo/chatgpt-cli/blob/main/nodejs-client/transcribe.mjs)を作りましたので、そこで採用した手順について説明します。

3. **S3 バケツと IAM 設定**: 音声データを S3 バケツ上に一時的に配置する必要があります。 また、データは、Amazon Transcribe のサービスアカウントからからアクセスされ、文字認識処理が行われるため、適切なIAM設定が必要です。以下は S3 バケツを作成して必要な IAM ポリシーを設定するコマンドです
    
    ```shell
    $ aws s3 mb s3://<YOUR_BUCKET_NAME>
    $ aws s3api put-bucket-policy \
      --bucket <YOUR_BUCKET_NAME> \
      --policy '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "transcribe.amazonaws.com"
                },
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::<YOUR_BUCKET_NAME>/*"
            }
        ]
    }'
    ```

1. **マイクからの入力を MP3 に変換**: [node-audiorecorder](https://www.npmjs.com/package/node-audiorecorder) という npm パッケージを使用して、マイクからの音声入力を MP3 形式に変換します

2. **MP3 を S3 バケツにアップロード**: 変換した MP3 ファイルを、所定の Amazon S3 バケツにアップロードします

4. **文字認識の実行**: Amazon Transcribe の [StartTranscriptionJob](https://docs.aws.amazon.com/ja_jp/transcribe/latest/APIReference/API_StartTranscriptionJob.html) コマンドを使用して、文字認識を実行します

5. **ポーリング**: [GetTranscriptionJob](https://docs.aws.amazon.com/ja_jp/transcribe/latest/APIReference/API_GetTranscriptionJob.html) コマンドを定期的に実行することでポーリングを行い、文字認識のジョブが終わるまで状況を確認します。ジョブのの終了は、 `getTranscribeJobCommandOutput.TranscriptionJob.TranscriptionJobStatus` のプロパティで確認することができます

6. **結果のダウンロード**: 文字認識が終了したら、サービスアカウントでプリサインされたダウンロードの URL が発行されます。この URL から、文字認識の結果をダウンロードできます

7. **標準出力に書き込む**: ダウンロードしたテキストを標準出力に書き込みます

このプロセスの難点は、ポーリングにかなりの時間がかかることです。通常、30秒程度かかってしまいます。Amazon Transcribeにはストリーミングに対応した「[リアルタイムトランスクリプション](https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/streaming.html)」という機能があり、これを使用すればもっと素早く処理ができそうですが、現時点ではまだ試すことができていません。

[transcribe.mjs](https://github.com/kamataryo/chatgpt-cli/blob/main/nodejs-client/transcribe.mjs) として作成した上記のプログラムを以下のように実行することで文字認識の結果を標準出力に書くことができます。なお、リンク先の実装ではコマンド引数として録音時間の秒数を指定できるようにしています。

```shell
$ node transcribe.mjs 10 # こんにちは🎤👨
こんにちは
```

また、この時点で音声で ChatGPT に入力できるようになりました。

```shell
$ node transcribe.mjs　10 | # こんにちは🎤👨
  chatgpt-cli
kamataryo >> こんにちは！
assistant << こんにちは！私はAIアシスタントです。何かお手伝いできることがありますか？
```

### Amazon Polly を使った音声合成

このセクションでは、テキストから音声合成までのプロセスについて説明します。こちらも Node.js での簡易な[サンプルのコード](https://github.com/kamataryo/chatgpt-cli/blob/main/nodejs-client/synthesize.mjs)を作りましたので、そこで採用した手順について説明します。

1. **標準入力からテキストを受け取る**: プログラムが標準入力からテキストを受け取ります
2. **Amazon Polly の SynthesizeSpeech コマンドに渡す**: 受け取ったテキストを　 Amazon Polly の [SynthesizeSpeech](https://docs.aws.amazon.com/ja_jp/polly/latest/dg/API_SynthesizeSpeech.html) コマンドに渡します
3. **MP3 ファイルの作成**: Amazon Polly が、テキストを音声に変換し、 MP3 ファイルとして出力します
4. **音声の再生**: `afplay` コマンドを使用して、MP3 ファイルを再生します。開発環境がMacOSの場合、`afplay`コマンドが利用できます。他の環境をお使いの場合は、適切なコマンドやツールを探してみてください。

これらの手順により、テキストから音声合成までのプロセスが実現されます。Amazon Polly を利用することで、テキストを自然な音声に変換し、音声対話システムに組み込むことができます。

[synthesize.mjs](https://github.com/kamataryo/chatgpt-cli/blob/main/nodejs-client/synthesize.mjs) として作成した上記のプログラムを以下のように実行することで標準入力から音声合成を行うことができます。

```shell
echo 'こんにちは！' | node synthesize.mjs # こんにちは🎤💻
```

また、この時点で ChatGPT の出力を音声で再生できるようになりました。

```shell
$ echo 'こんにちは！' | chatgpt-cli | node synthesize.mjs
kamataryo >> こんにちは！
assistant << こんにちは！私はAIアシスタントです。何かお手伝いできることがありますか？ # 🤖🔈
```

### コマンド統合

最後に上記で作成した transcribe.mjs、 synthesize.mjs を統合します。

```shell
$ node transcribe.mjs 10 | # こんにちは🎤👨
  chatgpt-cli | node synthesize.mjs
kamataryo >> こんにちは！
assistant << こんにちは！私はAIアシスタントです。何かお手伝いできることがありますか？ # 🤖🔈
```

これで、一連の自動音声対話が実現できました！
動画も以下にアップロードしています。（ポーリングの時間をカットしていたりしてレスポンスが速いように見えてしまいますが、実際のレスポンスはもっと遅いです💦）

<iframe width="256" height="455" src="https://www.youtube.com/embed/R4Hi7HzPgck" title="Amazon Transcribe, ChatGPT, Amazon Pollyで作る自動音声対話システム" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## コストの試算

最後にコストに関して以下の表で各サービスの単価を試算しています。これにより、システムを構築する際にコスト面での検討がしやすくなることを目指しています。

| サービス名          |単位| 単価 | 摘要 |
| ------------------- |---| ---- | ---- |
| Amazon Transcribe   | 分あたり |0.024USD| ap-northeast-1 リージョン、 Tier 1 (最初の 250,000 分) |
| ChatGPT             |1K tokens| 0.002USD      |モデルは gpt-3.5-turbo|
| Amazon Polly        |100万文字|4.00USD      | 標準 TTS コスト |

今回の開発で利用した各サービスのリソースは、以下の通りでした。
| サービス名          | リソース|
| ------------------- |---|
| Amazon Transcribe   | 173秒 (概算) |
| ChatGPT             |3,140 tokens　|
| Amazon Polly        |300文字 (概算)|

これを音声1時間あたりに均すと以下のようにコストの合計が試算できました。

| サービス名          | 従量| コスト |
| ------------------- |---| ---- |
| Amazon Transcribe   | １時間 |1.4400 USD|
| ChatGPT             |65.34k tokens　|0.1307 USD |
| Amazon Polly        |6,243文字|0.0250 USD |
|**合計**||**1.5957 USD**|

この他に S3 などのストレージや、実際にサービス化する場合のプログラムの実行環境 (EC2、Lambda、ApiGateway　など)、ネットワークトラフィックのコストが掛かります。