🎓 講義音声からAIナレッジBotを自作してみた①

# 🎓 講義音声からAIナレッジBotを自作してみた話

大学で受けた講義、自分で録音した勉強会の音声…。  
それらを「文字起こし → ナレッジ化 → 自然言語で質問できるBot」に変えたくて、  
Whisper × LangChain × FAISS × Streamlit を使って、RAG（Retrieval-Augmented Generation）構成のローカルBotを作りました。

本記事では、その全手順をまとめます。

---

## 🧭 ゴール

- `.mp3` の講義音声を文字起こし
- 自動でテキスト整理（フォルダ構成付き）
- テキストをベクトルDBに変換（FAISS）
- LLMと組み合わせて Q&A Bot 化
- 最終的に Web UI（Streamlit）で操作可能に

---

## 🛠 使用技術

| 技術 | 役割 |
|------|------|
| Python | 全体の実装 |
| Whisper | 音声文字起こし（openai/whisper） |
| LangChain | ナレッジ処理・検索 |
| FAISS | ベクトルDB（Meta製） |
| HuggingFace Embedding | 日本語対応の文ベクトル化 |
| Streamlit | Web UI作成 |
| Ubuntu + CUDA + NVIDIA RTX4060 | ローカルGPUで高速処理 |

---

## 📁 プロジェクト構成

```plaintext
ai_lecture_bot/
├── transcribe_and_organize.py      # Whisper実行＋整理
├── ingest.py                       # テキスト → ベクトルDB
├── query_bot.py                    # CLI型Bot
├── app.py                          # Streamlit UI
├── data/
│   ├── lecture_01/
│   │   ├── lecture_01.txt, .mp3...
│   └── ...
└── db/                             # FAISS保存先
```

# ① 音声 → テキスト化（Whisper）
Whisperを使って .mp3 ファイルを文字起こしし、さらに自動で講義ごとのディレクトリに整理するスクリプトです。GPUを最大活用するため、multiprocessing を使って並列実行しています。

```# transcribe_and_organize.py
# mp3ファイルをWhisperで文字起こしし、lecture_xxフォルダに整理するスクリプト

import subprocess
import os
import shutil
from multiprocessing import Pool

def transcribe_and_organize(index):
    # 対象のファイル名（例：lecture_01）
    filename = f"lecture_{index:02}"
    mp3_file = f"{filename}.mp3"
    print(f"🚀 GPUで文字起こし中: {mp3_file}")

    # Whisperを使って文字起こしを実行
    subprocess.run([
        "whisper",
        mp3_file,
        "--language", "Japanese",
        "--model", "medium",
        "--device", "cuda"  # GPUを使用
    ])

    # 出力ファイルを lecture_xx/ ディレクトリに整理
    output_dir = os.path.join("data", filename)
    os.makedirs(output_dir, exist_ok=True)

    # mp3 + Whisperの出力ファイルをすべて移動
    for ext in [".mp3", ".txt", ".json", ".srt", ".vtt"]:
        src = f"{filename}{ext}"
        dst = os.path.join(output_dir, src)
        if os.path.exists(src):
            shutil.move(src, dst)

if __name__ == "__main__":
    num_files = 62          # 処理対象のmp3ファイル数（lecture_01〜lecture_62）
    max_processes = 2       # 並列実行数（GPU性能に応じて調整）
    with Pool(processes=max_processes) as pool:
        pool.map(transcribe_and_organize, range(1, num_files + 1))
```

# 🔍 出力例
```
data/
├── lecture_01/
│   ├── lecture_01.mp3
│   ├── lecture_01.txt
│   ├── lecture_01.json
│   ├── lecture_01.srt
│   └── lecture_01.vtt
├── lecture_02/
│   └── ...
```

# 💡 補足
GPUの使用状況は別ターミナルで watch -n 1 nvidia-smi を実行すると確認できます。

medium モデルは1プロセスあたり約3〜4GBのVRAMを使うため、RTX 4060（8GB）なら2並列がベストバランスです。

# ② テキスト → ベクトルDB化（LangChain + FAISS）
次に、文字起こしされた .txt ファイルをベクトルDBに変換します。チャンク分割したテキストを日本語対応の埋め込みモデルでベクトル化し、FAISSに保存します。
```# ingest.py
# 整理された講義テキストをチャンク分割してベクトルDB（FAISS）に保存するスクリプト

from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings

# data/ 以下の lecture_xx/*.txt をすべて読み込む
loader = DirectoryLoader("data", glob="**/*.txt")
docs = loader.load()

# テキストを約500文字ごとに分割（チャンク）
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
split_docs = splitter.split_documents(docs)

# 日本語対応の埋め込みモデルを使用（E5-small）
embedding = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-small")

# 分割テキストをベクトル化し、FAISSに保存
db = FAISS.from_documents(split_docs, embedding)
db.save_local("db")  # 保存先は ./db ディレクトリ
```

# ③ 対話Bot構築（LangChain + Ollama）
構築したベクトルDBをもとに、自然言語で質問できるBotを作成します。ローカルLLM（Ollama）とLangChainの RetrievalQA を組み合わせます。

```# query_bot.py
# ローカルベクトルDBを使ってユーザーの質問に回答する対話Bot（CLI形式）

from langchain.chains import RetrievalQA
from langchain.llms import Ollama
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings

# ベクトルDBの埋め込みと検索設定
embedding = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-small")
db = FAISS.load_local("db", embedding)

# OllamaのローカルLLM（例：mistral）を使って回答生成
qa = RetrievalQA.from_chain_type(
    llm=Ollama(model="mistral"),
    retriever=db.as_retriever()
)

# ユーザーが質問を入力するループ
while True:
    query = input("💬 質問を入力してください: ")
    print("🧠 回答:", qa.run(query))
```

# ④ Web UIで対話（Streamlit）
最後に、StreamlitでUIを構築し、ブラウザ上で対話できるようにします。
```# app.py
# StreamlitでWeb UIを構築し、質問→回答までをブラウザで実行できるようにするスクリプト

import streamlit as st
from query_bot import qa  # 上で定義した RetrievalQA を読み込む

# タイトル表示
st.title("🎓 講義AIナレッジBot")

# ユーザーの入力欄
query = st.text_input("質問をどうぞ（例：『DNAとは何か？』など）")

# 質問が入力されたら回答を生成
if query:
    with st.spinner("考え中..."):
        answer = qa.run(query)
        st.success(answer)
```

## 今後のステップ


### ステップ0：ベーシックな手動フローの完成
- `transcribe_and_organize.py` で文字起こし＋整理  
- `ingest.py` でベクトルDB化  
- `query_bot.py`／`app.py` で対話まで動作  
  - まずは Docker やエージェントを使わず、ターミナルで順に実行できれば OK  
    ```bash
    python transcribe_and_organize.py
    python ingest.py
    streamlit run app.py
    ```
- **成功条件**  
  - 各ステップで想定どおりのファイル・DB が更新されている  
  - UI から質問 → 回答まで問題なく返ってくる  
---

### ステップ1：環境の固定化＋自動化
- 各サービスに `Dockerfile` を追加してコンテナ化  
- `docker-compose.yml` で複数コンテナを一括起動  
- `agent/watch_and_run.py` でフォルダ監視を組み込み、自動実行  

---

### ステップ2：CI/CD／Observability の導入
- **GitHub Actions** でビルド → テスト → リリースを自動化  
- **OpenTelemetry + Grafana** で処理時間・エラーを可視化  
- **Slack 通知** などのアラート設定を追加  

---
### 拡張アイデア
- lecture_data.csv を活用して meta.json を生成し、講義タイトル・日付などのフィルターを可能に
- ノートや回答ログの保存、Notion連携




#  まとめ
音声を知識化し、ベクトルDBで検索、ローカルLLMで回答、そしてUIまで一貫して構築できるこの構成は、まさに「未来の自分用知識アーカイブBot」。

同じように学習や仕事に活用したい人の参考になれば幸いです。
コメント・質問大歓迎です！🙌

# 🧷 GitHub
準備中（またはURLをここに記載）

# 🙏 Thanks
AIエージェント駆動型の開発に興味があるのでこれからも学習を続けて行こうと思っています！！