Whisper API + ChatGPTで議事録作成するLINEボットを作ってみた

2023.05.08
OpenAIより公開されている音声認識システムであるWhisper APIとChatGPTを使って、音声データから議事録作成するLINEボットを作ってみました(ボットの友達追加URLは公開していません)。既に同じようなことをしているブログもいくつかありますが面白そうだったのでやってみました。

作ったもの

ユーザーがLINEのトーク画面から音声ファイルを送ると、議事録が返信されます。以下のスクショはクラスメソッドがYoutubeに出している動画(15分でわかるクラスメソッド)の冒頭3分間程度の音声データを渡してみた結果です。

https://www.youtube.com/watch?v=IgBqV7joJ0E

議事録の内容には間違った内容が書かれている場合もあったため注意が必要です。機密情報なども渡してはいけません。3分間ほどの音声で返信が得られるまでに30秒ほどかかりました。

処理の流れ

Whisper APIとChatGPTの他にLINE Messaging APIも利用しています。処理の流れは、

  1. LINEボットにトーク画面から音声ファイルを送ると、webhookを通してLambda関数が呼び出される。
  2. Lambda関数上で、LINE Messaging APIを使ってユーザーが送ってきた音声データを取得、一時ファイルとして保存
  3. そのファイルをWhisper APIに渡して音声データを文字起こし
  4. 文字起こししたテキストデータからChatGPTが議事録作成してLINEで返信

という流れです。インフラはLambda + API Gatewayで、AWS CDKを使って構築しました。

今回作成したプロジェクトのGithubリポジトリです。

手元で動かす際はline-whisper/.envの作成と、Lambdaのパッケージをinstallする必要があります。

# line-whisper/.env
LINE_CHANNEL_ACCESS_TOKEN='LINEチャネルアクセストークン'
LINE_CHANNEL_SECRET='LINEチャネルシークレット'
OPENAI_API_KEY='OpenAI API key'
cd line-whisper/lambda && pip install -r requirements.txt -t lambda/python

プロジェクトの説明

cdk initでCDKプロジェクトを作成して、stack作成のコード(line-whisper/lib)とLambdaのコード(line-whisper/lambda)を追加しました。

mkdir line-whisper && cd line-whisper
cdk init --language python

CDKではスタック作成のコードもpythonで書きました。プロジェクトの構成は以下のようになっています。

line-whisper
├── lib (stack作成のコード)
├── lambda
│   ├── src (lambdaで実行されるコード)
│   └── python (lambdaで利用する外部ライブラリ)
└── ...

Lambdaで実行しているコードの抜粋と説明です。

# line-whisper/lambda/src/lineWebhook.py
...
# API keyを環境変数から読み込み
LINE_CHANNEL_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]
LINE_CHANNEL_SECRET = os.environ["LINE_CHANNEL_SECRET"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
...
# 成功時とエラー時のレスポンス
okResponse = {
    "statusCode": 200,
    ...
errorResponse = {
    "statusCode": 400,
    ...

mimeTypeToExtension = {"audio/x-m4a": "m4a", "audio/mpeg3": "mp3", "video/mp4": "mp4"}

def lambda_handler(event, context):
    signature = event["headers"]["x-line-signature"]
        # リクエストボディ取得
    body = event["body"]

    try:
                # 署名検証とwebhookの処理
        handler.handle(body, signature)
    except InvalidSignatureError:
        return errorResponse

    return okResponse

# 音声、動画が送られてきた時の処理
@handler.add(MessageEvent, message=(AudioMessage, FileMessage, VideoMessage))
def handle_audio_message(line_event):
    # メッセージIDを使って音声、動画データの取得
    message_content = line_bot_api.get_message_content(line_event.message.id)
    # ファイル形式(拡張子)を取得できないため、MIMEタイプから拡張子に変換
    extension = mimeTypeToExtension[message_content.content_type]
    # 一時ファイルの作成
    with tempfile.TemporaryDirectory() as tmp_dir:
        tmp_file = os.path.join(tmp_dir, f"{line_event.message.id}.{extension}")
        with open(tmp_file, "wb") as fd:
            for chunk in message_content.iter_content():
                fd.write(chunk)
        audio_file = open(tmp_file, "rb")
        # whisperを使って音声をテキストに変換
        transcript = openai.Audio.transcribe("whisper-1", audio_file)
    # whisperから得られたテキストをChatGPTで要約
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": """渡された文章に見出しをつけたり、箇条書きにして議事録を作成して下さい"""},
            {"role": "user", "content": transcript.text},
        ],
    )
    gpt_response_text = completion.choices[0].message.content
    # ChatGPTからのレスポンスを返信として送る
    line_bot_api.reply_message(
        line_event.reply_token, TextSendMessage(text=gpt_response_text)
    )

LINE DevelopersでBot用のチャネル作成も必要です。作成後に「Messaging API設定」タブを押してチャネルアクセストークンを取得、「チャネル基本設定」タブからチャネルシークレットを取得して、環境変数としてenvファイルの作成をします。Whisper APIも利用するのでOpenAIのSECRET KEYも必要です。自分は直接envファイルを作成しましたが、AWS Systems Managerパラメータストアなどを使うとより安全にAPI Keyの管理ができると思います。

詰まった箇所

Lambdaレイヤー

今回はLambda上で動かすコードをpythonで書いたのですが、外部ライブラリを使ったためLambdaレイヤーを追加しました。その時外部ライブラリをインストールするディレクトリ名を適当にpackagesなどの名前にしていると認識されませんでした。ローカルではエラー表示が出ず実行時にエラーが出ます。外部ライブラリを保存するディレクトリ名はpythonにする必要があるようです。

line-whisper/lambda/python
(このディレクトリ名がpython以外だとLambda上で認識されない)

AWS Lambdaでレイヤー追加してもエラーが解決しないのはフォルダ名が問題だった

音声データの受け取り方

詰まったわけでは無いのですが、LINEでユーザーが送ってきた音声データをLambdaで受け取る方法についてです。最初、ユーザーが音声データを送った時に、webhookのlambda関数が直接音声データを受け取るのかと思いました。webhookで受け取ったPOSTリクエストのbodyに音声データが含まれているのかと思いましたがそうではなく、音声データはLINEのAPIから受け取れるようです。Lambda関数が受け取れるペイロードサイズは6MBという制限があるので一瞬思い過ごしで、6MB以上の音声ファイルがLINEボットに送られてくるとLambda上では読み込めないのかも、と勘違いしました。実際はLambda関数上でLINE Messaging APIを呼び出す形で音声データを受け取るので6MB以上でも受け取れます。Webhookで受信したメッセージIDを使って、ユーザーが送信した音声データを取得できます。

Messaging APIリファレンス
AWS Lambdaの呼び出しタイプ、ペイロード制限など

OpenAIの無料枠

OpenAIではアカウント作成後に$5の無料枠がもらえます(以前は$18分の無料クレジットがもらえたのですが、現在$5に減額されています)。無料枠の残りはこのページで確認できます。(https://platform.openai.com/account/usage)

OpenAIでは同じ電話番号で複数アカウント作成しても2つ目以降のアカウントは、無料枠がもらえません。自分は最初個人のメアドで登録していて、その後会社のメアドでも登録したので、会社のメアドでは無料枠が付与されませんでした。API keyを取得しにいく時なんで無料枠がないんだろうと思ってました。

感想

Youtubeの動画など日本語だけでなく海外の動画でも文章化して翻訳すると、ブログ記事の作成に役立つかもと思いました。自分は仮想通貨ゲームにハマっているのですが、ゲームの運営元がtwitterのスペースで英語で公開ミーティングを行ったりします。仮想通貨など最新の情報で相場が変化するので、そこでも要約された文章が手に入ると役立つかもしれません。