メインコンテンツへスキップ
見出し画像

会議中の「あれ、なんだっけ?」をゼロにする。Pythonで作る自分だけの「リアルタイム文字起こし&要約アシスタント」

    こんにちは、高橋です!

    Web会議や対面の打ち合わせ中、
    「今の発言、メモしたっけ?」「あれ、納期っていつと言っていたかな?」と不安になることはありませんか?

    ZoomやTeamsにも文字起こし機能はつきましたが、

    • 「ホスト(主催者)しか使えない」

    • 「あとで読み返すのが大変なほど精度が低い」

    • 「対面の会議では使えない」 といった課題があります。

    そこで今回は、
    「PCのマイクで音を拾い、高精度に文字起こしをし続け、最後に要点をまとめてくれる自分だけの議事録ボット」
    をPythonで作ります。

    使うのは、OpenAIが誇る最強の音声認識モデル「Whisper」と、
    高速・安価な「GPT-4o mini」。
    これらを組み合わせることで、月額数千円の議事録ツールと同等以上の機能を、自分のPC上で実現できます。

    これさえあれば、もう会議中に必死でキーボードを叩く必要はありませんね(*'▽')


    今回作るものの仕組み

    仕組みはシンプルです。
    あなたのPCを「聞き耳」として使い、聞いた内容をAIに投げ続けます。

    1. 録音: PCのマイクから音声を一定間隔(例: 30秒ごと)で録音する。

    2. 文字化: 音声データをOpenAIの「Whisper API」に送り、テキストにする。

    3. 表示: 画面にリアルタイムで会話内容を表示していく。

    4. 要約: 会議終了後、溜まったテキストを「GPT-4o mini」に渡し、きれいな議事録にする。

    コード スニペット

    [マイク/スピーカー] 
          ↓ (音声データ)
    [Pythonスクリプト]
          ↓
    [Whisper API] ──→ テキスト化 ──→ [画面に表示]
                                           ↓
                                      (終了後)
                                           ↓
    [GPT-4o mini] ──→ [要約・To-Do抽出] ──→ [議事録ファイル.md]
    

    この構成の最大のメリットは「場所を選ばない」ことです。Zoomでも、Google Meetでも、あるいは会議室での対面ミーティングでも、PCのマイクさえオンにしておけば機能します。


    実装手順:環境構築から実行まで

    今回も、Pythonのパッケージ管理ツール uv を使って、サクッと環境を作っていきましょう。

    1. プロジェクトの作成

    適当な作業用フォルダを作り、必要なライブラリをインストールします。 今回は音声を扱うための pyaudio と、OpenAI公式ライブラリを使います。

    Bash

    # プロジェクト作成
    mkdir meeting-bot
    cd meeting-bot
    uv init
    
    # ライブラリの追加
    # ※Windowsの場合
    uv add openai pyaudio keyboard soundfile numpy
    
    # ※macOSの場合 (事前に brew install portaudio が必要な場合があります)
    uv add openai pyaudio keyboard soundfile numpy
    

    ⚠️ Macユーザーの方へ: pyaudio のインストールでエラーが出る場合、Homebrewで brew install portaudio を実行してから、再度 uv add pyaudio を試してみてください。

    2. 環境変数の設定

    OpenAIのAPIキーを設定します。

    Windows (PowerShell):

    PowerShell

    $env:OPENAI_API_KEY="sk-..."
    

    macOS / Linux:

    Bash

    export OPENAI_API_KEY="sk-..."
    

    3. ボットの実装 (main.py)

    フォルダ内の hello.py を main.py にリネーム(または新規作成)し、
    以下のコードをコピペしてください。

    このコードは、
    「スペースキーを押している間だけ録音」するトランシーバー方式と、
    「常に録音し続けて一定間隔で切る」方式のいいとこ取りで、
    「Enterキーを押すと録音開始/停止を切り替える」仕様にしています。

    コード スニペット

    import os
    import time
    import wave
    import threading
    import keyboard  # キー入力を検知
    import pyaudio
    from openai import OpenAI
    from datetime import datetime
    
    # === 設定 ===
    API_KEY = os.environ.get("OPENAI_API_KEY")
    if not API_KEY:
        raise ValueError("OPENAI_API_KEYが設定されていません")
    
    client = OpenAI(api_key=API_KEY)
    
    # 録音設定
    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 44100
    WAVE_OUTPUT_FILENAME = "temp_chunk.wav"
    
    # グローバル変数
    is_recording = False
    transcript_log = []  # 全会話ログを保存するリスト
    
    def record_audio(stop_event):
        """録音を実行するスレッド用関数"""
        p = pyaudio.PyAudio()
        stream = p.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)
    
        frames = []
        print("\n🔴 録音中... (Enterを押すと停止・文字起こし開始)")
    
        while not stop_event.is_set():
            data = stream.read(CHUNK)
            frames.append(data)
    
        print("✅ 録音終了。文字起こし中...")
    
        stream.stop_stream()
        stream.close()
        p.terminate()
    
        # 一時ファイルとして保存
        wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(b''.join(frames))
        wf.close()
    
    def transcribe_audio():
        """保存された音声をWhisperで文字起こしする"""
        if not os.path.exists(WAVE_OUTPUT_FILENAME):
            return
    
        try:
            with open(WAVE_OUTPUT_FILENAME, "rb") as audio_file:
                transcript = client.audio.transcriptions.create(
                    model="whisper-1",
                    file=audio_file,
                    language="ja" # 日本語を指定
                )
            
            text = transcript.text
            if text:
                print(f"\n📝 {text}")
                transcript_log.append(text)
            else:
                print("❌ 音声が認識できませんでした")
                
        except Exception as e:
            print(f"Error: {e}")
    
    def generate_summary():
        """GPT-4o miniで議事録を作成する"""
        if not transcript_log:
            print("会話ログがないため、要約をスキップします。")
            return
    
        full_text = "\n".join(transcript_log)
        print("\n🧠 議事録を作成中...")
    
        system_prompt = """
        あなたは優秀な議事録作成アシスタントです。
        提供された会話ログをもとに、以下のフォーマットで議事録を作成してください。
        
        # 会議議事録
        ## 1. 決定事項
        - (決定したことを箇条書き)
        
        ## 2. To-Do / ネクストアクション
        - [ ] (誰が、いつまでに、何をするか)
        
        ## 3. 会話の要約
        (議論の流れを簡潔に)
        """
    
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f"以下の会話ログを要約してください:\n\n{full_text}"}
            ]
        )
    
        summary = response.choices[0].message.content
        
        # ファイルに保存
        filename = f"minutes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
        with open(filename, "w", encoding="utf-8") as f:
            f.write(summary)
        
        print(f"\n✨ 議事録が完成しました: {filename}")
        print("-" * 30)
        print(summary)
        print("-" * 30)
    
    def main():
        print("=== AI議事録ボットへようこそ ===")
        print("操作方法: [Enter]で録音開始/停止を切り替えます。[Esc]で終了して要約します。")
    
        while True:
            # キー入力を待機
            key = keyboard.read_event()
            
            if key.event_type == keyboard.KEY_DOWN:
                if key.name == 'enter':
                    # Enterが押されたら録音サイクルを回す
                    stop_event = threading.Event()
                    
                    # 録音スレッド開始
                    t = threading.Thread(target=record_audio, args=(stop_event,))
                    t.start()
                    
                    # もう一度Enterが押されるまで待つ(簡易的な実装)
                    time.sleep(0.5) # チャタリング防止
                    keyboard.wait('enter')
                    
                    # 録音停止&文字起こし
                    stop_event.set()
                    t.join()
                    transcribe_audio()
                    
                elif key.name == 'esc':
                    print("\n終了シグナルを検知しました。")
                    break
        
        generate_summary()
    
    if __name__ == "__main__":
        main()
    

    いざ、実行!

    準備ができたら、以下のコマンドで実行します。

    Bash

    uv run main.py
    

    ※ Macなどでキー入力監視(keyboardライブラリ)に管理者権限が必要な場合があります。その場合は sudo を使うか、ターミナルのセキュリティ許可を確認してください。

    使い方の流れ:

    1. 開始: アプリが起動したら、会議の開始と同時に Enterキー を押します。

    2. 録音中: 🔴 録音中... と出ている間は、PCのマイクが会話を聞いています。

    3. 区切り: 話題が変わったり、少し間が空いたタイミングで再び Enterキー を押します。

    4. 文字化: 数秒で 📝 (発言内容) が画面に表示されます。

    5. 終了: 会議が終わったら Escキー を押します。

    6. 完成: 自動的にAIが要約を行い、minutes_2026xxxx.md というファイルに完璧な議事録を出力してくれます。

    工夫したポイント

    • あえての「手動区切り」: ずっと録音しっぱなしだと、Whisperの処理時間が長くなり、リアルタイム感が失われます。「話がひと段落したらEnter」というリズムにすることで、サクサク文字起こしが表示される気持ちよさを重視しました。

    • GPT-4o miniの採用: 議事録の要約程度なら、最高性能のGPT-4oである必要はありません。miniを使うことで、コストを10分の1以下に抑えています。1時間の会議でも数円〜数十円程度です。

    応用:システム音声(相手の声)を拾うには?

    上記のコードは「マイク入力」を使っているため、対面の会議や、
    スピーカーから音を出しているWeb会議では問題なく機能します。

    しかし、イヤホンをしてWeb会議をする場合、相手の声はPC内部で完結してしまうため、マイクには入りません。これを解決するには、以下のどちらかの方法を使います。

    1. VB-Cable (仮想オーディオデバイス) を使う: PCの音声をマイク入力としてループバックさせる無料ソフトです。これを入れると、Zoomの相手の声もこのPythonスクリプトで拾えるようになります。

    2. スマホを横に置く: 一番アナログですが、最強の方法です。スマホでZoomに入り、スピーカーオンにして横に置いておけば、このスクリプトは全員の声を拾ってくれます。

    まとめ

    たった100行未満のコードですが、これで「議事録係」から解放されます。 録音データは一時ファイル以外残らないため、クラウド上のツールに音声データを預けるのが不安な方にもおすすめです!

    ぜひ次回のミーティングで、こっそり起動してみてください。
    「あれ、なんでそんなに詳しく覚えてるの?」と驚かれるはずですよ!

    また、今回は黒い画面(CUI)で動かしましたが、
    Gradio や Streamlit を使うと、ブラウザ上で録音ボタンを押せるリッチなアプリに進化させることができます。
    興味がある方は「Streamlit Audio Recorder」などで検索して、拡張に挑戦してみてください!

    それでは今回はここまで(*'▽')ノシ


    おまけ

    ここでは、記事中で紹介したコードの補足情報と、トラブルシューティングを記載します。

    ディレクトリ構成

    Plaintext

    meeting-bot/
    ├── .venv/               # uvが作成する仮想環境
    ├── main.py              # メインスクリプト
    ├── pyproject.toml       # 依存関係定義
    └── temp_chunk.wav       # 一時保存される音声ファイル(実行時に生成)
    

    トラブルシューティング

    Q. ModuleNotFoundError: No module named 'keyboard'

    • A. インストールがうまくいっていません。uv add keyboard を実行してください。Macの場合、セキュリティ設定(入力監視)でターミナルアプリを許可する必要がある場合があります。

    Q. OSError: [Errno -9999] Unanticipated host error

    • A. マイクが見つからない、またはアクセス権限がない場合のエラーです。

      • Windows: 「設定 > プライバシー > マイク」でアプリのアクセスを許可してください。

      • Mac: ターミナルにマイクへのアクセス権限を与えてください。

    Q. 相手の声が文字起こしされない

    • A. イヤホンを使っていませんか? このスクリプトは「マイク」から入る音しか聞き取れません。イヤホンを外してスピーカーにするか、別途「VB-Cable」などを導入してシステム音声をマイク入力にミックスする必要があります。

    Q. 文字起こしの精度が低い

    • A. マイクの性能に依存します。PC内蔵マイクよりも、USB接続の会議用マイクやヘッドセットを使うと劇的に改善します。また、transcribe_audio 関数内の model="whisper-1" の部分で prompt="会議の議事録です。専門用語: 〇〇, ××" のようにプロンプトを追加すると、社内用語の認識率が上がります。

    この記事が参加している募集

     
     
    ダイアロゴスの仲間たちが交代でつぶやくnote。 AIやITを「むずかしい」から「わかりやすい」へ。 身近な視点で楽しくお届けします。

    あなたへのおすすめ