Whisper+LangChain（text-davinci-003)で音声ファイルからAIを使って要約・箇条書きにするまで

# 音声を聞かされたくない
皆さん、以下の様なことが思い当たりませんか？
1. 仕事で誰かとの話し合いの結果が録音ファイルで共有される。
2. 会議議事として録音ファイルが提供される。
2. ドキュメントと称して操作のスクリーンキャプチャー動画を渡される。
2. 内容を再確認するのに文字になってないので時間がかかってしまう。

ないよりはずっといいですが、「文字になってくれ！議事録にまとめてくれ！！」と思うこと、
皆さんとてもよくあるでしょう。私もとてもとてもよくあります。やーね、もう。

そこで、今流行りのAIとAIを組み合わせて労せず議事の箇条書きを作ってみよう！というのがこの記事の趣旨です。
使用するのはどちらもOpenAIの[Whisper](https://github.com/openai/whisper)とLangChain経由GPT-3になります。

# さあ、実行しよう
セットアップやAPIコードの払い出しは後で書く（書かない）。

## 音声のテキスト起こし
Whisperを使用します。コードをざっと示します。この例ではLargeモデルを使ってますが、Middiumでもあまり大差はないかもしれません。
他の方のサンプルを切り貼りしてる部分があるので見覚えのあるコードもあるでしょう。mp3やm4aに対応しているのでファイルを作りやすいでしょう。このサンプルではものぐさなので同じ階層に音声ファイルを置いています。
```python:DoWhisper.py
# whisperのインポート
import whisper

print("loading model.")
# Largeモデル
model = whisper.load_model("large")

# 中間ファイルに書き出し
f = open('transcription.txt', 'w', encoding='UTF-8')
# 日本語指定で音声ファイル読み込み
result = model.transcribe("sample.m4a", verbose=True, language="ja")
for segment in result["segments"]:
    id, start, end, text = [segment[key] for key in ["id", "start", "end", "text"]]
    # セグメントの区切りごとに読点と半角スペースと改行を挿入する
    output = segment["text"] + "。 \n"
    print(output)
    # 途中まででも書き込めるように1行ずつ
    f.write(output)
f.close()

print("end recognation.")
```

# 要約
ここではOpenAI APIを利用します。OpenAIからAPIキーを払い出しておくのを忘れないようにしてください。
長い音声からの書き起こしを使うのであれば課金したほうがいいかもです。
Fetch方法をRefineにすると長いプロンプトをチャンクごとに呼び出して合体するそうです[^1]。その分トークン数は激増します。
なお、Whisperで書き起こしたこれまた同じ階層にあるテキストファイルを参照しています。
```python:DoGPTSummary.py

# coding: UTF-8
import openai
from langchain.text_splitter import CharacterTextSplitter
from langchain.docstore.document import Document
from langchain.llms import OpenAI
from langchain.chains.summarize import load_summarize_chain
import time

# APIキーの設定
import os
os.environ["OPENAI_API_KEY"] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

print("prepare OpenAI.")
llm = OpenAI(model_name="text-davinci-003", temperature=0.1)

text_splitter = CharacterTextSplitter(separator = "。 ",
    chunk_size=500,  # チャンクのトークン数
    chunk_overlap=0  # チャンクオーバーラップのトークン数
    )

#要約するための文字起こしファイルを開く
with open('transcription.txt',encoding="utf-8") as f:
    state_of_the_union = f.read()

print("start estimation")
start_time = time.perf_counter()

print("prepare documents")
texts = text_splitter.split_text(state_of_the_union)
docs = [Document(page_content=t) for t in texts]

# stuffのload_summarize_chainを準備
chain = load_summarize_chain(llm, chain_type="refine")

# サマライズ実行
summarize =chain({"input_documents": docs}, return_only_outputs=True)
print(summarize)

print("End execution.")
```
## 箇条書き
ここに関しては、手前の実装でどうにかするよりChatGPTで対話的に箇条書きにしてもらったほうが楽です。
手元でどうしてもやりたいのであれば、先の英文要約をこのコードに掛けると得られます。

```python:DoGPTSummaryWithPrompt.py
# coding: UTF-8
import openai
from langchain.text_splitter import CharacterTextSplitter
from langchain.docstore.document import Document
from langchain.llms import OpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate

# APIキーの設定
import os
os.environ["OPENAI_API_KEY"] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

print("prepare OpenAI.")
llm = OpenAI(model_name="text-davinci-003", temperature=0.1)

text_splitter = CharacterTextSplitter(separator = "。 ",
    chunk_size=1500,  # チャンクのトークン数
    chunk_overlap=0  # チャンクオーバーラップのトークン数
    )


# ここに要約文を入れる
state_of_the_union = '''This is a summary sample text.
Change replace here!!!
'''
print("start estimation")

texts = text_splitter.split_text(state_of_the_union)
docs = [Document(page_content=t) for t in texts]
print("prepare documents")

prompt_template = """議事録の要約を作成せよ。
決定事項について経緯を書くこと。


{text}


日本語で箇条書きで記述すること"""

# PromptTemplateの生成
PROMPT = PromptTemplate(template=prompt_template, input_variables=["text"])

# stuffのload_summarize_chainを準備
chain = load_summarize_chain(llm, chain_type="stuff", prompt=PROMPT)

# 質問応答の実行
summarize =chain({"input_documents": docs}, return_only_outputs=True)
print(summarize)

print("End execution.")
```
（ここで詰まっていたのをうみゆきさんに教えてもらいました。感謝！）
以上、ぜひ試してみてください。
私の環境では40分ほどの録音を15分強で処理しています。

[^1]:[LangChain の HOW-TO EXAMPLES (4) - データ拡張生成](https://note.com/npaka/n/nb9b70619939a) より