医師と患者の会話を録音してSOAP形式に自動でまとめてくれるプログラム（Python+ChatGPT）

## 作ったもの
医師と患者の会話を録音してSOAP形式に自動でまとめてくれるプログラムを作りました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">医師と患者の会話を録音してSOAP形式に自動でまとめてくれるプログラムを作りました。<br><br>①　GUIを作成<br>②　録音<br>③　録音の内容をテキスト化<br>④　テキストの内容をSOAPに整形<br><br>をPythonのライブラリとChatGPTを使ってやりました。 <a href="https://t.co/oiPYbjgkNX">pic.twitter.com/oiPYbjgkNX</a></p>&mdash; 森　維久郎（生成AI/Python勉強中） (@iryogpt) <a href="https://twitter.com/iryogpt/status/1688152972537335808?ref_src=twsrc%5Etfw">August 6, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## アプリケーションの動作フロー

アプリケーションの動作フローは以下の通りです。

1. ユーザーは「録音開始」ボタンをクリックします。
2. 録音が開始され、録音中は「録音終了」ボタンが活性化されます。
3. ユーザーは必要な音声を録音します。
4. ユーザーは「録音終了」ボタンをクリックして録音を停止します。
5. 録音が停止すると、音声はファイルに保存されます。
6. 音声ファイルがテキストに変換され、変換結果がテキストエリアに表示されます。
7. 変換されたテキストはカルテ記載の素材として使用されます。
8. プロンプトが生成され、生成された素材とプロンプトがAPIに送信されます。
9. APIから返された翻訳結果がテキストエリアに表示されます。

このアプリケーションでは、OpenAIのAPIを使用して音声をテキストに変換し、その内容に基づいたカルテ記載を生成することができます。

## アプリケーションの仕組み

基本はPythonのライブラリの組み合わせです。

まずデスクトップから起動する形にしたいのでGUIを作成。

Speech to textとしてWhisper APIで会話をテキスト化して、その後GPT3.5turboでその会話のテキスト情報をSOAP形式に変更するプロンプトを入れました。

GPT4だとほぼ修正がいらない精度になりました。

```
import os
import tkinter as tk
import threading
import pyaudio
import wave
import openai

class AudioTranscriberApp:
    def __init__(self, root):
        self.root = root
        self.root.title("音声録音＆文字起こしアプリ")
        self.root.geometry("400x900")

        self.is_recording = False
        self.audio_frames = []
        self.output_filename = "output.wav"

        self.start_button = tk.Button(root, text="録音開始", command=self.start_recording)
        self.start_button.pack(pady=10)

        self.stop_button = tk.Button(root, text="録音終了", command=self.stop_recording, state=tk.DISABLED)
        self.stop_button.pack(pady=10)

        self.transcript_text = tk.Text(root, wrap=tk.WORD, height=5, width=40)
        self.transcript_text.pack(pady=10)

        self.translated_text = tk.Text(root, wrap=tk.WORD, height=30, width=40)
        self.translated_text.pack(pady=10)

        # OpenAIのAPIキーを設定します
        openai.api_key = ''

    def start_recording(self):
        self.is_recording = True
        self.start_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)

        threading.Thread(target=self.record_audio).start()

    def stop_recording(self):
        self.is_recording = False
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)

    def record_audio(self):
        CHUNK = 1024
        FORMAT = pyaudio.paInt16
        CHANNELS = 1
        RATE = 44100

        p = pyaudio.PyAudio()

        stream = p.open(
            format=FORMAT,
            channels=CHANNELS,
            rate=RATE,
            input=True,
            frames_per_buffer=CHUNK
        )

        self.audio_frames = []

        while self.is_recording:
            data = stream.read(CHUNK)
            self.audio_frames.append(data)

        stream.stop_stream()
        stream.close()
        p.terminate()

        self.save_audio()

    def save_audio(self):
        p = pyaudio.PyAudio()
        wf = wave.open(self.output_filename, "wb")
        wf.setnchannels(1)
        wf.setsampwidth(p.get_sample_size(pyaudio.paInt16))
        wf.setframerate(44100)
        wf.writeframes(b"".join(self.audio_frames))
        wf.close()
        p.terminate()
        print("録音が終了しました。")

        # Whisperを使って音声ファイルをテキストに変換
        self.transcribe_audio()

    def transcribe_audio(self):
        try:
            # 音声ファイルを開きます
            audio_file = open(self.output_filename, "rb")

            # 音声ファイルをテキストに変換します
            transcript = openai.Audio.transcribe("whisper-1", audio_file)

            # 変換されたテキストをテキストエリアに表示します
            self.transcript_text.insert(tk.END, transcript.text)

            # 翻訳のプロンプトを生成します
            prompt = self.generate_prompt(transcript.text)

            # 翻訳
            translated_text = self.translate_to_english(prompt)

            # 翻訳されたテキストを新たに作成したテキストエリアに表示します
            self.translated_text.insert(tk.END, translated_text)
        except Exception as e:
            print("音声のテキスト変換に失敗しました:", e)

    def generate_prompt(self, text):
        return f"""#Position
            あなたは信頼性の高いカルテ記載の専門アシスタントで、私は医師です。

            #タスクの詳細
            医師と患者の間の会話を基に、医療カルテ（SOAP形式：Subjective、Objective、Assessment、Plan）のたたき台を出してほしい

            #医師と患者の会話をテキスト化したもの
           {text}


            #具体的な手順
            Step1: 医師と患者との会話のテキストを理解。
            Step2: S)に相当する箇所は患者の視点で書かれるべきなので、口語体で、「」を使用してその表現を引用形式にする。
            Step3: O)、A)、P)に相当する箇所は文語形式の表現にする。
            Step4:カルテの記載を、S)***、O)***、A)***、P)***というフォーマットで行なう。

            #ルール
            ・この会話で話されていないことを勝手に追加しないでください。

            #Output
            ・step4の内容のみで良い。
            """

    def translate_to_english(self, prompt):
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": prompt},
            ]
        )

        # 最後のメッセージ（翻訳結果）を取得します
        translated_text = response['choices'][0]['message']['content']

        return translated_text

if __name__ == "__main__":
    root = tk.Tk()
    app = AudioTranscriberApp(root)
    root.mainloop()

```

プロンプトをもう少しチューニングすればGPT3.5-turboでも良い感じになりそうです。

次はHTML+JavaScript+Pythonでやってみようと思います。最終的にデプロイしてみたい。