📄

社内の議事録作成を半自動化した話

2024/06/10に公開

TLDR;

  • 社内で行われるMTGの議事録作成をLLMsを使用して自動化した
  • エンジニア部署以外でも使用しやすいようにSlack から呼べるツールとしてリリースした
  • GPT4oとClaude-sonetの二つの議事録を返してくれるようにしたら生産性が上がった

はじめに

社内会議の議事録作成は時間と手間がかかり、特にエンジニア以外の部署では大きな負担なっていました。
この問題を解決するために、最新の言語モデル(LLMs)を活用して議事録作成を自動化することを半自動で行えるようにしました。

システムの構成と実装方法

システム構成図
主要な技術スタック:

  • 音声認識:Whisper
  • 言語モデル:GPT-4o, Claude-sonet
  • インターフェース:Slack Bolt API
  • ホスティング:AWS Lambda

システムの主な流れは以下です。

  1. ユーザーがSlackから議事録作成botに入力ファイル(vtt, txt, m4a)を@mentionを使用してわたす
  2. @mentionをトリガーとして、lambdaが動き出す
  3. 入力ファイルがm4aなどの音声ファイルであった場合は、OpenAIのWhisperを使用して文字起しをする
  4. vtt, txtファイルであった場合はGPT4o, Claude Omniに投げて議事録を作成する
  5. 議事録はユーザーがファイルを送信したスレッドにメッセージとして返却される

具体的な実装方法は参考にしたサイトをご参照ください。

出来上がったもの

minute-bot screenshot

アップロードした会議のデータに対してGPT-4o, Claude Omi がまとめた議事録を取得します。
二つの視点の議事録があることで、ユーザーは議事録から取捨選択して素早くクオリティの高い議事録を素早く作成できます。

つまったポイント

25MB以上のファイルはWhisper APIが対応していないため分割が必要

長時間のMTGなどの音声データはpydubなどのライブラリを使用して分割する必要があります。

Whisper, LLMsのレスポンス時間が遅いのでLazy Listenerを設定する必要がある。

slack boltの制約上3秒以内にレスポンスがない場合はリクエストを再送してしまうため、一旦リクエストを受け取ったことことを返すよう必要があります。 Slack bolt APIにはLazyリスナーが実装されているので、それを使用しました。
また、Lambdaの生存時間も5mほどに設定しておいた方が良いです。

## Lazy リスナーのサンプル https://slack.dev/bolt-python/ja-jp/concepts
from slack_bolt import App
 from slack_bolt.adapter.aws_lambda import SlackRequestHandler
 
 # FaaS で実行するときは process_before_response を True にする必要があります
 app = App(process_before_response=True)
 
 def respond_to_slack_within_3_seconds(body, ack):
     text = body.get("text")
     if text is None or len(text) == 0:
         ack(":x: Usage: /start-process (description here)")
     else:
         ack(f"Accepted! (task: {body['text']})")
 
 import time
 def run_long_process(respond, body):
     time.sleep(5)  # 3 秒より長い時間を指定します
     respond(f"Completed! (task: {body['text']})")
 
 app.command("/start-process")(
     ack=respond_to_slack_within_3_seconds,  # `ack()` の呼び出しを担当します
     lazy=[run_long_process]  # `ack()` の呼び出しはできません。複数の関数を持たせることができます。
 )
 
 def handler(event, context):
     slack_handler = SlackRequestHandler(app=app)
     return slack_handler.handle(event, context)

専門的な用語は文字起こしができない

議事録の作成であるため、社内用語などは拾ってほしいのですが文字起こしの段階で情報落ちてしまう場合がありました。
Instructionのカスタマイズで、ある程度誤った文字起こしから社内用語を拾ってもらうように調整できるますが、さらに精度を上げるにはSpeech to Textのカスタマイズなども必要になります。

・### 専門用語
・1. **ラボベース(LabBase)**: 自社名
・2. **ラボベース就職**: LabBaseが提供している就職支援サービス
・3. **ラボベースナウ(ナウ)**: LabBaseが提供しているイベント
・4. **ラボベースエキスポ(エキスポ)**: LabBaseが提供しているイベント
・5. **ラボベースサミット(サミット)**: LabBaseが提供しているイベント

参考: プロンプティングで制度が向上できる。
https://platform.openai.com/docs/guides/speech-to-text/prompting

議事録作成の運用コスト

30分のMTGの録音データを使用した場合で約40円で議事録が作成できます。
また応答時間は1~3分程度でした。

input: 3258 tokens
output: 1172 tokens

mode linput output 概算
gpt-4o $5.00/1M tokens 15.00/1M tokens 5.1円
claude-sonet $3.00/1M tokens 15.00/1M token 4.1円
whisper $0.006/minute 27円

文字起こしファイルを使用することで、1回10円くらいに抑えることもできます。

使用感について

自動生成した議事録で手直しが必要な情報は2割程度と言った所感です。
二つの議事録を比較できるため、大事な数値情報は手元にメモをしておき、ちょっと修正というようなワークフローで議事録が作成できるようになったため、だいぶ大きく工数削減ができるようになりました。

Discussion