PythonでAI Tuberを作る|OpenAI API・VOICEVOX・OBS・YouTubeをつなぐハンズオン

エンジニアリング

AI Tuberを作ってみたい。

そう思って調べていたところ、『AI Tuberを作ってみたら生成AIプログラミングがよくわかった件』という書籍を見つけた。

今回はこの書籍を参考にしながら、Python、OpenAI API、VOICEVOX、OBS Studio、YouTube Liveを組み合わせて、YouTube LiveのコメントにAIが自動で回答し、その回答を音声で読み上げるAI Tuberを実際に作ってみた。

最終的に作る仕組みは次のようなもの。

YouTube Live
    │
    │ コメント
    ▼
Python
    │
    ├─ OpenAI API
    │      └─ 回答を生成
    │
    ├─ VOICEVOX
    │      └─ 回答を音声化
    │
    ├─ OBS Studio
    │      └─ 質問・回答を画面表示
    │
    └─ VB-CABLE
           └─ 音声をOBSへ渡す

単にOpenAI APIへ質問して文章を生成するだけではない。

YouTubeから入力を受け取り、生成AIで処理し、VOICEVOXで声に変換し、OBSから映像・音声として配信するところまで、一つのシステムとしてつないだ形で書いていこうと思う。

実際に作ってみると、生成AIアプリ開発ではLLMそのもの以上に、周辺のサービスをどう分離し、どう連携させるかが重要だと分かった。

この記事では、環境構築からPythonコード、OBSの設定、YouTube Liveの設定、実際の配信まで順番に進める。

この記事について

本記事は実際にハンズオンを行った環境・ソースコードをベースにしています。
OpenAI API、YouTube、Pythonライブラリなどは変更される可能性があります。
特に外部サービスを利用する部分については、実行時点の公式情報も確認してください。


  1. 1. 今回作るAI Tuber
    1. 使用する主な技術
    2. 動作確認環境
  2. 2. Pythonの開発環境を作る
    1. Pythonをインストールする
    2. 作業フォルダを作る
    3. venvで仮想環境を作る
  3. 3. OpenAI APIで回答を生成する
    1. OpenAI APIを利用できるようにする
    2. APIキーを.envへ保存する
    3. AI Tuberの人格をprompt_system.txtへ書く
    4. OpenAIAdapterを作る
    5. OpenAI APIの動作を確認する
  4. 4. VOICEVOXで回答を音声化する
    1. VOICEVOXをインストールする
    2. VOICEVOXのAPIを確認する
    3. requestsをインストールする
    4. VoicevoxAdapterを作る
  5. 5. PythonからVOICEVOXの音声を再生する
    1. 使用できるサウンドデバイスを調べる
    2. PlaySoundを作る
    3. まずPCのスピーカーで確認する
  6. 6. VB-CABLEでPythonの音声をOBSへ渡す
  7. 7. OBS StudioでAI Tuberの配信画面を作る
    1. OBSのシーンを作る
    2. OBSへVOICEVOXの音声を入力する
  8. 8. OBSにQuestionとAnswerを作る
  9. 9. OBS WebSocketを設定する
  10. 10. PythonからOBSへ接続する
    1. .envへOBS設定を追加する
    2. OBSAdapterを作る
  11. 11. OBSにYouTube Liveのコメント欄を表示する
  12. 12. 背景とAI Tuberの画像を配置する
  13. 13. YouTube Liveからコメントを取得する
    1. pytchatをインストールする
    2. YoutubeCommentAdapterを作る
  14. 14. YouTube Liveの配信枠を作る
    1. VIDEO IDを確認する
  15. 15. ここまで作った機能を整理する
  16. 16. AITuberSystemで全機能を連携する
  17. 17. AI Tuberを常時動作させる
  18. 18. 最終的なファイル構成
  19. 19. 実際にYouTube LiveでAI Tuberを動かす
    1. 1. VOICEVOXを起動する
    2. 2. OBS Studioを起動する
    3. 3. YouTube Liveの配信枠を作る
    4. 4. YouTube StudioでVIDEO IDを取得する
    5. 5. OBSのコメント欄を更新する
    6. 6. .envを更新する
    7. 7. Pythonの仮想環境を有効にする
    8. 8. AI Tuberを起動する
    9. 9. OBSからライブ配信を開始する
    10. 10. YouTube Liveへコメントする
  20. 20. 配信を終了する
  21. 21. 動かない場合に確認するポイント
    1. OpenAIの回答が返らない
    2. VOICEVOXへ接続できない
    3. 音が出ない
    4. OBSのQuestion / Answerが変わらない
    5. YouTubeコメントを取得できない
  22. 22. AI Tuberを作って分かったこと
    1. 面白かったのは「AI」よりも「連携」だった
  23. 23. ただし、このままでは本格的なAI Tuberではない
  24. 24. 「人間らしい」ではなく、一人の存在にしたい
  25. まとめ

1. 今回作るAI Tuber

まず、これから何を作るのか整理する。

YouTube Liveへ視聴者がコメントを投稿すると、Pythonがそのコメントを取得する。

取得したコメントをOpenAI APIへ送り、回答を生成する。

回答テキストは2方向へ送る。

一つはOBS Studioで、質問と回答を配信画面上へ表示する。

もう一つはVOICEVOXで

回答を音声へ変換し、VB-CABLEという仮想オーディオデバイスを経由してOBSへ入力する。

処理の流れを整理すると、

1. YouTube Liveのコメントを取得
            ↓
2. OpenAI APIへコメントを送信
            ↓
3. AIから回答を取得
            ↓
4. VOICEVOXで回答を音声化
            ↓
5. OBSへ質問と回答を表示
            ↓
6. 音声をVB-CABLE経由でOBSへ出力
            ↓
7. YouTube Liveへ映像・音声を配信

となる。

使用する主な技術

技術用途
Pythonシステム全体の制御
OpenAI APIコメントに対する回答生成
VOICEVOX回答テキストの音声合成
requestsVOICEVOX APIとの通信
sounddevice音声データの再生
soundfileWAVデータの読み込み
VB-CABLEPythonからOBSへの仮想音声入力
OBS Studio配信画面と音声の統合
OBS WebSocketPythonからOBSを操作
YouTube Liveコメント入力とライブ配信
pytchatYouTube Liveコメント取得

この時点で重要なのは、OpenAI APIがシステム全体では 「回答を考える一つの部品」 にすぎないこと。

これから各部品を一つずつ作り、最後に統合していく。

動作確認環境

項目動作確認環境
OSWindows
Python3.11.5
OBS Studio32.0.2
VOICEVOX0.25.0
OpenAI Python SDKバージョン未記録
動作確認時期2025年11月

2. Pythonの開発環境を作る

今回はWindows上でPythonを使用した。ハンズオンではPython 3.11.5。

Pythonをインストールする

Pythonをインストールするときは、

Add Python.exe to PATH

を有効にしておく。

インストール後、コマンドプロンプトを起動して確認。

python --version

次のようにバージョンが表示されればOK。

Python 3.11.5

作業フォルダを作る

今回は、

C:\workspace\aituber

を作業フォルダとした。

cd C:\workspace\aituber

以降、このフォルダを基準に進める。

venvで仮想環境を作る

Pythonではプロジェクトごとにライブラリ環境を分離できるvenvを利用する。

python -m venv .venv

これで、

C:\workspace\aituber\.venv

が作成される。

そして、仮想環境を有効にする。

.venv\Scripts\activate.bat

コマンドプロンプトの先頭に、

(.venv)

と表示されれば有効になっている。

仮想環境から抜ける場合は、

deactivate

この記事では、以降のpip installやPythonプログラムの実行を、基本的にこの仮想環境内で行う。


3. OpenAI APIで回答を生成する

最初に、AI Tuberの「考える部分」を作る。

最終的にはYouTubeのコメントを渡すが、まずはコマンドラインから入力した文章に対して回答できるところまで作る。

OpenAI APIを利用できるようにする

OpenAI API側でAPIキーを発行。

作成したAPIキーは外部へ公開しないよう注意。

Python側には必要なライブラリをインストール。

pip install openai
pip install python-dotenv

APIキーを.envへ保存する

APIキーをPythonコードへ直接書くのは避けるため、

プロジェクト直下に、

.env

を作成。

OPENAI_API_KEY="sk-*******************"

最終的にはOBSやYouTubeの設定もこのファイルへ追加する。

尚、.envにはAPIキーやパスワードが入るため、外部へ公開しないよう注意。

AI Tuberの人格をprompt_system.txtへ書く

AIへのシステムプロンプトもPythonコードから分離。

prompt_system.txt

を作成。

ここには、

あなたはYouTubeで配信を行っているAIキャラクターです。

どういう人格なのか
どういう話し方をするのか
何が好きなのか
何が嫌いなのか
どの程度の長さで回答するのか

などを定義します。

ユーザーからキャラクター設定を変更する指示を受けても、
設定された人格を維持してください。

といった内容を書く。

今回作成したAI Tuberでは、ここにキャラクター「結音ゆの」の人格設定を書いていく。

Pythonを変更しなくても、このファイルを変えることでキャラクター性を調整できる。

OpenAIAdapterを作る

OpenAI APIとの通信処理を、

openai_adapter.py

へ分離します。

import os

from dotenv import load_dotenv
from openai import OpenAI, APIError


class OpenAIAdapter:
    def __init__(self, model: str = "gpt-4o-mini"):
        # .env読み込み
        if not load_dotenv():
            print(
                "⚠️ .envファイルが見つかりません。"
                "環境変数から読み込みを試みます。"
            )

        api_key = os.getenv("OPENAI_API_KEY")
        if not api_key:
            raise ValueError("❌ OPENAI_API_KEY が設定されていません。")

        self.client = OpenAI(api_key=api_key)
        self.model = model

        prompt_path = os.path.join(
            os.path.dirname(__file__),
            "prompt_system.txt"
        )

        # システムプロンプトを読み込み
        try:
            with open(prompt_path, "r", encoding="utf-8") as f:
                self.system_prompt = f.read()

        except FileNotFoundError:
            raise FileNotFoundError("❌ prompt_system.txt が見つかりません。")

    def _create_message(self, role: str, content: str) -> dict:
        return {"role": role, "content": content}

    def _create_system_message(self, content: str) -> dict:
        return self._create_message("system", content)

    def _create_user_message(self, content: str) -> dict:
        return self._create_message("user", content)

    # ChatGPTに質問を送り、返答を取得する
    def create_chat(self, question: str) -> str:
        messages = [
            self._create_system_message(self.system_prompt),
            self._create_user_message(question)
        ]

        try:
            response = (
                self.client.chat.completions.create(
                    model=self.model,
                    messages=messages
                )
            )

            content = (
                response.choices[0].message.content
            )

            if not content:
                return ""

            return content.strip()

        except APIError as e:
            print(f"❌ OpenAI APIエラー: {e}")
            return "(エラーが発生しました)"

        except Exception as e:
            print(f"❌ 予期せぬエラー: {e}")
            return "(予期せぬエラーが発生しました)"

if __name__ == "__main__":
    adapter = OpenAIAdapter()
    question = input("質問を入力してください: ")
    answer = adapter.create_chat(question)
    print("🤖 回答:", answer)

ここでポイントとなるのは、

OpenAIAdapter

の外側からは、

answer = openai_adapter.create_chat(question)

と呼ぶだけでよいこと。

OpenAI API固有の処理を、このクラス内部へ閉じ込めている。

OpenAI APIの動作を確認する

テスト用に、

openai_test.py

を作る。

import sys

from openai_adapter import OpenAIAdapter


if len(sys.argv) < 2:
    print(
        "usage: "
        "python openai_test.py <question>"
    )
    sys.exit(1)

question = " ".join(
    sys.argv[1:]
)

openai_adapter = OpenAIAdapter()

response = (
    openai_adapter
    .create_chat(question)
)

print(response)

実行。

python openai_test.py こんにちは!

設定したキャラクターとして回答が返ってくれば成功。

これで、

テキスト
   ↓
OpenAI API
   ↓
AIの回答テキスト

まで完成。

OpenAI APIについて

このコードはハンズオン時に使用したChat Completions APIをベースにしています。
OpenAI APIはモデルや推奨APIが変更されるため、実際に試す際は利用可能なモデルや最新のAPI仕様を確認してください。

この記事ではAPIの最新機能を紹介することより、「OpenAIとの通信をAdapterとしてシステム本体から分離する」という構成を重視しています。

2026年9月追記

OpenAI APIを組み込む場合はResponses APIを選択肢として挙げるようです。
実際に試す際は利用可能なモデルや最新のAPI使用を確認してください。


4. VOICEVOXで回答を音声化する

OpenAIから文章を取得できるようになったので、次はその文章をAI Tuberにしゃべらせる。

今回はVOICEVOXを使用。

VOICEVOXをインストールする

VOICEVOX公式サイトからWindows版をダウンロードしてインストール。

インストール後、VOICEVOXを起動。

まずはVOICEVOX単体で適当な文章を入力して、正常に読み上げられることを確認しておく。

Pythonから接続する前に単体動作を確認しておくことで、問題が起きたときに原因を切り分けやすくなる。

VOICEVOXのAPIを確認する

VOICEVOX起動中にブラウザから、

http://127.0.0.1:50021/docs

へアクセス。(Port番号は違うことがあるので各自確認してください)

API一覧が表示されればOK。

VOICEVOXではローカルでAPIが公開されており、

audio_query
synthesis
speakers

などを利用できる。

今回の処理は、

読み上げる文章
      ↓
audio_query
      ↓
音声合成パラメータ
      ↓
synthesis
      ↓
WAV形式の音声データ

という流れ。

使用できる話者については、

http://127.0.0.1:50021/speakers

から確認できる。

今回のコードではspeaker_id=8を使用していますが、利用したいVOICEVOXキャラクターに合わせて変更すること。

requestsをインストールする

PythonからVOICEVOX APIへHTTP通信するため、

pip install requests

を実行。

音声データを扱うため、次もインストールしておく。

pip install numpy
pip install soundfile

VoicevoxAdapterを作る

VOICEVOXとの通信も、

voicevox_adapter.py

として分離。

import io
import json
import sys

import requests
import soundfile as sf


class VoicevoxAdapter:
    URL = "http://127.0.0.1:50021"

    def __init__(self) -> None:
        pass

    def __create_audio_query(
        self,
        text: str,
        speaker_id: int
    ) -> dict:

        post_url = (
            f"{self.URL}/audio_query"
        )

        response = requests.post(
            post_url,
            params={
                "text": text,
                "speaker": speaker_id,
                "enable_katakana_english":
                    "true"
            },
            timeout=30
        )

        response.raise_for_status()

        query_data = response.json()

        # 好みに合わせて調整
        query_data["speedScale"] = 1.01
        query_data["pitchScale"] = 0.01
        query_data[
            "intonationScale"
        ] = 1.01
        query_data["volumeScale"] = 1.0

        return query_data

    def __create_request_audio(
        self,
        query_data: dict,
        speaker_id: int
    ) -> bytes:

        post_url = (
            f"{self.URL}/synthesis"
        )

        response = requests.post(
            post_url,
            headers={
                "Content-Type":
                    "application/json"
            },
            params={
                "speaker": speaker_id,
                "enable_interrogative_upspeak":
                    "true"
            },
            data=json.dumps(
                query_data
            ),
            timeout=30
        )

        response.raise_for_status()

        return response.content

    def get_voice(
        self,
        text: str,
        speaker_id: int
    ):
        query_data = (
            self.__create_audio_query(
                text,
                speaker_id
            )
        )

        audio_bytes = (
            self.__create_request_audio(
                query_data,
                speaker_id
            )
        )

        data, sample_rate = sf.read(
            io.BytesIO(
                audio_bytes
            )
        )

        return data, sample_rate


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(
            "usage: "
            "python voicevox_adapter.py "
            "<text> <speaker_id>"
        )
        sys.exit(1)

    text = sys.argv[1]
    speaker_id = int(
        sys.argv[2]
    )

    voicevox_adapter = (
        VoicevoxAdapter()
    )

    data, sample_rate = (
        voicevox_adapter.get_voice(
            text,
            speaker_id
        )
    )

    print(
        f"Sample rate: "
        f"{sample_rate}"
    )

実行すると、

テキスト
 ↓
VOICEVOX
 ↓
WAVデータ
 ↓
Python上の音声データ

まで作れるようになる。

ただし、まだ音を出力していないため、この時点では耳で聞くことはできない。

次は生成した音声を実際に再生します。


5. PythonからVOICEVOXの音声を再生する

Pythonから音声を再生するため、

pip install sounddevice

をインストールする。

使用できるサウンドデバイスを調べる

Windowsには、

  • スピーカー
  • マイク
  • HDMI
  • 仮想オーディオデバイス

など複数のデバイスがあります。

Pythonから一覧を確認してみます。

make_list_sound_device.py

を作ります。

import sounddevice as sd


with open(
    "sound_device.txt",
    "w",
    encoding="utf-8"
) as file:
    file.write(
        str(sd.query_devices())
    )

実行。

python make_list_sound_device.py

sound_device.txtへサウンドデバイス一覧が出力される。

例えば、

Microsoft サウンド マッパー
スピーカー (Realtek(R) Audio)
CABLE Input (VB-Audio Virtual Cable)
...

といったデバイスを確認できる。

デバイス番号そのものはPCごとに異なるため、固定値ではなく名前から検索するようにする。

PlaySoundを作る

音声再生を担当する、

play_sound.py

を作成。

import sounddevice as sd


class PlaySound:
    def __init__(
        self,
        output_device_name=(
            "CABLE Input "
            "(VB-Audio Virtual Cable)"
        )
    ) -> None:

        output_device_id = (
            self._search_output_device_id(
                output_device_name
            )
        )

        # 入力デバイスは使用しない
        input_device_id = None

        sd.default.device = [
            input_device_id,
            output_device_id
        ]

    def _search_output_device_id(
        self,
        output_device_name,
        output_device_host_api=None
    ) -> int:

        devices = sd.query_devices()

        for index, device in enumerate(
            devices
        ):
            is_target_name = (
                output_device_name
                in device["name"]
            )

            has_output = (
                device[
                    "max_output_channels"
                ] > 0
            )

            if (
                is_target_name
                and has_output
            ):
                if (
                    output_device_host_api
                    is None
                    or device["hostapi"]
                    == output_device_host_api
                ):
                    return index

        print(
            "output_deviceが"
            "見つかりませんでした。"
        )

        print("利用可能なデバイス:")

        for index, device in enumerate(
            devices
        ):
            print(
                f"{index}: "
                f"{device['name']}"
            )

        raise RuntimeError(
            "出力デバイスが"
            "見つかりません。"
        )

    def play_sound(
        self,
        data,
        rate
    ) -> bool:

        try:
            sd.play(
                data,
                rate
            )

            sd.wait()

        except Exception as e:
            print(
                f"❌ 音声再生エラー: {e}"
            )

            return False

        return True

まずPCのスピーカーで確認する

OBSへ接続する前に、普通のスピーカーで動作確認。

voicevox_test.py

を作成。

from play_sound import PlaySound
from voicevox_adapter import (
    VoicevoxAdapter
)


input_text = (
    "おはようございます!"
)

voicevox_adapter = (
    VoicevoxAdapter()
)

data, sample_rate = (
    voicevox_adapter.get_voice(
        input_text,
        8
    )
)

play_sound = PlaySound(
    "スピーカー (Realtek(R) Audio)"
)

play_sound.play_sound(
    data,
    sample_rate
)

ここに指定するスピーカー名は、自分の環境の名前へ変更してください。

実行。

python voicevox_test.py

VOICEVOXの声で、

おはようございます!

と再生されれば成功。

これで、

Python
 ↓
VOICEVOX
 ↓
音声生成
 ↓
sounddevice
 ↓
PCのスピーカー

まで動いた。


6. VB-CABLEでPythonの音声をOBSへ渡す

現在はPCのスピーカーから音を出している。

しかし、最終的にはVOICEVOXの音をOBSへ渡してYouTubeへ配信したいため、音声の出力先を変える必要がある。

そこで使用するのがVB-CABLE。

VB-CABLEは仮想的なオーディオケーブルをWindows上へ作るソフトウェア。作ってくれた方に圧倒的感謝を。

インストールすると、

CABLE Input
CABLE Output

というデバイスが追加される。

名前だけ見ると少し分かりにくいのですが、今回の構成は、

Python
  │
  │ 音声を出力
  ▼
CABLE Input
  │
  │ VB-CABLE内部
  ▼
CABLE Output
  │
  │ OBSが入力
  ▼
OBS Studio

となる。

Python側では、

PlaySound(
    "CABLE Input "
    "(VB-Audio Virtual Cable)"
)

へ出力する。

OBS側では後ほど、

CABLE Output

をマイクとして受け取る。

これによってPythonが生成したVOICEVOXの音声を、そのまま配信へ乗せられるようになる。


7. OBS StudioでAI Tuberの配信画面を作る

音声を作れるようになったので、次は配信画面を作る。

OBS Studioをインストール。 → https://obsproject.com/ja/download

初回の自動構成では、今回の用途であれば配信向けに設定する。

ハンズオンでは、

配信のために最適化し、録画は二次的なものとする

解像度:
1920 × 1080

フレームレート:
30 または 60

サービス:
YouTube - RTMPS

サーバー:
Primary YouTube ingest server

とした。

PC性能や配信環境に合わせて調整してください。

OBSのシーンを作る

今回のOBSのシーンには、主に次の要素を配置する。

AITuber シーン

├─ 背景
├─ AI Tuberの画像
├─ Question
├─ Answer
├─ YouTubeコメント欄
└─ 音声入力
    └─ CABLE Output

ここから一つずつ設定します。


OBSへVOICEVOXの音声を入力する

OBSの「ソース」へ、

音声入力キャプチャ

を追加する。

名前は分かりやすく、

マイク

などにしておきます。

プロパティを開き、デバイスへ、

CABLE Output
(VB-Audio Virtual Cable)

を指定する。

VOICEVOXのテストプログラム側では、出力を、

CABLE Input

へ変更して実行します。

この状態で音声を再生し、OBSの音声ミキサーが反応すれば、

Python
 ↓
CABLE Input
 ↓
CABLE Output
 ↓
OBS

がつながっていることが確認できる。


8. OBSにQuestionとAnswerを作る

YouTubeコメントとAIの回答をOBSへ表示したい。

OBSの「ソース」からテキストを2つ追加する。

今回の名前は、

Question
Answer

とする。

今回のPythonコードでは、この名前でOBSのソースを検索するため。

役割は、

Question
    YouTube Liveから取得した質問

Answer
    OpenAI APIが生成した回答

フォントサイズや位置、色などは配信画面に合わせて調整してください。

例えば配信中には、

Question
今日のおすすめゲームは何ですか?

Answer
今日はじっくり遊べるRPGなんてどう?

という形で表示される。

この文字列を後ほどPythonから書き換える。


9. OBS WebSocketを設定する

PythonからOBSを操作するためにOBS WebSocketを使用する。

OBSの、

ツール
 ↓
WebSocketサーバー設定

を開く。そして、設定を行う。

WebSocketサーバーを有効にする:
ON

サーバーポート:
表示されているポート番号を使用

サーバーパスワード:
生成ボタンなどから設定

ポート番号とパスワードはPython側から接続するときに使用するため控えておく。

設定後、

適用

を押下。


10. PythonからOBSへ接続する

PythonからOBS WebSocketを操作するため、

pip install obsws-python

を実行。

.envへOBS設定を追加する

先ほど作った.envへOBSの設定を追加する。

OPENAI_API_KEY="sk-*******************"

# OBS
OBS_WS_HOST=localhost
OBS_WS_PORT=設定したポート番号
OBS_WS_PASSWORD="設定したパスワード"

# YouTube
YOUTUBE_VIDEO_ID=@@@@@

YOUTUBE_VIDEO_IDについては後ほど設定するため、一旦ステイ。

OBSAdapterを作る

obs_adapter.py

を作成する。

import os

import obsws_python as obs
from dotenv import load_dotenv


class OBSAdapter:
    def __init__(self):
        if not load_dotenv():
            print(
                "⚠️ .envファイルが"
                "見つかりません。"
            )

        host = os.getenv(
            "OBS_WS_HOST"
        )

        port_text = os.getenv(
            "OBS_WS_PORT"
        )

        password = os.getenv(
            "OBS_WS_PASSWORD"
        )

        if not host:
            raise ValueError(
                "❌ OBS_WS_HOST が"
                "設定されていません。"
            )

        if not port_text:
            raise ValueError(
                "❌ OBS_WS_PORT が"
                "設定されていません。"
            )

        if not password:
            raise ValueError(
                "❌ OBS_WS_PASSWORD が"
                "設定されていません。"
            )

        port = int(port_text)

        self.client = obs.ReqClient(
            host=host,
            port=port,
            password=password
        )

        print(
            "✅ OBSに接続しました。"
        )

    def set_text(
        self,
        source_name: str,
        text_content: str
    ):
        try:
            self.client.set_input_settings(
                name=source_name,
                settings={
                    "text":
                        text_content
                },
                overlay=True
            )

            print(
                f"✅ OBSソース "
                f"'{source_name}' "
                f"を更新しました。"
            )

        except Exception as e:
            print(
                f"❌ OBSソース "
                f"'{source_name}' "
                f"更新エラー: {e}"
            )

    def set_question(
        self,
        text_content: str
    ):
        self.set_text(
            "Question",
            text_content
        )

    def set_answer(
        self,
        text_content: str
    ):
        self.set_text(
            "Answer",
            text_content
        )


if __name__ == "__main__":
    try:
        obs_adapter = OBSAdapter()

        obs_adapter.set_question(
            "OBSへの質問表示テスト"
        )

        obs_adapter.set_answer(
            "OBSへの回答表示テスト"
        )

    except ValueError as e:
        print(e)

    except Exception as e:
        print(
            f"❌ エラー: {e}"
        )

OBS Studioを起動した状態で、

python obs_adapter.py

を実行する。

OBS上の、

Question
Answer

が、

OBSへの質問表示テスト
OBSへの回答表示テスト

へ変更されれば成功。

これで、

Python
 ↓
OBS WebSocket
 ↓
Question / Answer

がつながった。


11. OBSにYouTube Liveのコメント欄を表示する

AI Tuberへの質問と回答だけでなく、実際のYouTube Liveのコメント欄も配信画面へ表示してみる。

OBSの「ソース」から、

ブラウザ

を追加する。

URLには、

https://www.youtube.com/live_chat?is_popout=1&v=VIDEO_ID

を指定する。

VIDEO_ID部分は、実際のYouTube Live配信枠を作成したときに取得する。

まだ配信枠を作っていない場合は、ここでは仮の値にしておく。

幅・高さはOBSのレイアウトに合わせて調整する。

YouTubeコメントをOBS上へ表示するときは、カスタムCSSを使用して見た目を変更することもできます。

既存の記事で使用したCSS生成サービスを利用する場合は、そのサービスのライセンスや利用条件も確認してください。

参考 : https://css4obs.pyoko.xyz


12. 背景とAI Tuberの画像を配置する

次に見た目を調整。

OBSの「ソース」へ画像を追加し、

背景
AI Tuber本人

を配置する。

今回のハンズオンでは、AI Tuber「結音ゆの」の画像を作り、背景と組み合わせた。

この部分はAI Tuberの仕組みそのものには必要ない。

技術的な動作確認だけが目的であれば、背景やキャラクター画像を作り込まず、

Question
Answer
コメント欄

だけでも問題ない。

ただし、自分で作ったキャラクターが実際に返事をしているように見えると、かなりAI Tuberらしくなるためオススメ。


13. YouTube Liveからコメントを取得する

ここまでで、

OpenAIによる回答生成
VOICEVOXによる音声生成
Pythonからの音声再生
OBSへの音声入力
OBSへのQuestion / Answer表示

までできた。

残っているのは、

YouTube Live
 ↓
Python

今回はpytchatを使ってYouTube Liveのコメントを取得した。

pytchatをインストールする

pip install pytchat

pytchatについて

pytchatは今回実際にハンズオンで使用したライブラリなので、その構成をそのまま掲載しています。

ただし長期的にメンテナンスされているライブラリではなく、YouTube側の仕様変更によって動作しなくなる可能性があります。

本格的なシステムとして運用する場合は、YouTube Data APIなど別のコメント取得方法も検討した方がよいでしょう。

今回コメント取得処理をYoutubeCommentAdapterとして分離するのも、将来この部分だけ置き換えられるようにするためです。

YoutubeCommentAdapterを作る

youtube_comment_adapter.py

を作る。

import time

import pytchat


class YoutubeCommentAdapter:
    def __init__(
        self,
        video_id: str
    ):
        self.video_id = video_id

        self.chat = pytchat.create(
            video_id=video_id
        )

    def get_comment(self):
        comments = (
            self.get_comments()
        )

        if not comments:
            return None

        return comments[0][
            "message"
        ]

    def get_comments(self):
        comments_data = []

        # pytchat側が停止した場合は
        # 再接続を試みる
        if not self.chat.is_alive():
            self.chat = pytchat.create(
                video_id=self.video_id,
                interruptable=True
            )

        try:
            chat_data = (
                self.chat.get()
            )

            for comment in (
                chat_data.items
            ):
                comments_data.append(
                    {
                        "id":
                            comment.id,

                        "datetime":
                            comment.datetime,

                        "message":
                            comment.message,

                        "author": {
                            "name":
                                comment
                                .author
                                .name,

                            "isChatOwner":
                                comment
                                .author
                                .isChatOwner,

                            "isVerified":
                                comment
                                .author
                                .isVerified,
                        }
                    }
                )

        except Exception as e:
            print(
                "❌ コメント取得中に"
                f"エラー: {e}"
            )

        return comments_data


if __name__ == "__main__":
    test_video_id = (
        "実際のVIDEO_ID"
    )

    adapter = (
        YoutubeCommentAdapter(
            test_video_id
        )
    )

    print(
        "YouTubeライブコメントの"
        "取得を開始します。"
    )

    try:
        while True:
            comments = (
                adapter.get_comments()
            )

            for comment in comments:
                print(
                    f"[{comment['datetime']}] "
                    f"{comment['author']['name']}: "
                    f"{comment['message']}"
                )

            time.sleep(1)

    except KeyboardInterrupt:
        print(
            "コメント取得を"
            "停止しました。"
        )

    except Exception as e:
        print(
            f"エラーが発生しました: {e}"
        )

実際のYouTube LiveのVIDEO IDを指定して実行する。

python youtube_comment_adapter.py

配信中にコメントを投稿してみる。

[日時] ユーザー名: こんにちは

のように表示されれば、コメント取得まで成功している。

今回の実装上の制約

get_comment()では、一度に取得したコメントのうち先頭1件をAIへの質問として利用しています。

コメントが大量に投稿される本格的な配信を想定する場合は、キューに保存して順番に処理する仕組みなどが必要になります。

今回はAI Tuberの一連の仕組みを確認するハンズオンなので、シンプルな実装としています。


14. YouTube Liveの配信枠を作る

ここからYouTube側も設定する。

OBSの、

コントロール
 ↓
配信の管理

を開き、配信情報を設定して配信枠を作成する。

YouTube Studioを開き、

コンテンツ
 ↓
ライブ配信

を選択する。

作成した配信予定を確認し、

ライブ管理画面で見る

を開く。

VIDEO IDを確認する

YouTube StudioのURLが、

studio.youtube.com/video/XXXXXXXXXXX/livestreaming

となっている場合、

XXXXXXXXXXX

の部分がVIDEO ID。

このVIDEO IDは2か所で使用する。

一つはOBSのコメント欄。

ブラウザソースのURLを、

https://www.youtube.com/live_chat?is_popout=1&v=XXXXXXXXXXX

へ変更する。

もう一つは.env。

YOUTUBE_VIDEO_ID=XXXXXXXXXXX

へ更新する。

これでYouTube LiveとPython側が同じ配信を参照するようになる。


15. ここまで作った機能を整理する

ここまでに5つの機能を作った。

YoutubeCommentAdapter
    │
    └─ YouTubeコメント取得


OpenAIAdapter
    │
    └─ AIによる回答生成


VoicevoxAdapter
    │
    └─ 回答を音声化


PlaySound
    │
    └─ 音声をVB-CABLEへ出力


OBSAdapter
    │
    └─ Question / Answerを更新

各外部サービスを別クラスへ分離している。

これらを直接バラバラに実行するのではなく、最後に、

AITuberSystem

からまとめて制御する。

構造としては、

                    AITuberSystem
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
YouTubeComment OpenAI VOICEVOX
Adapter Adapter Adapter
│ │ │
│ │ ▼
│ │ PlaySound
│ │ │
└─────────┼─────────┘
│
▼
OBSAdapter

というイメージ。

OpenAI、VOICEVOX、OBS、YouTubeはすべて別々のサービスだが、Python側ではそれぞれを一つの部品として扱っている。


16. AITuberSystemで全機能を連携する

aituber_system.py

を作る。

import os

from dotenv import load_dotenv

from obs_adapter import OBSAdapter
from openai_adapter import (
    OpenAIAdapter
)
from play_sound import PlaySound
from voicevox_adapter import (
    VoicevoxAdapter
)
from youtube_comment_adapter import (
    YoutubeCommentAdapter
)


class AITuberSystem:
    def __init__(self):
        load_dotenv()

        video_id = os.getenv(
            "YOUTUBE_VIDEO_ID"
        )

        if not video_id:
            raise ValueError(
                "❌ 環境変数 "
                "YOUTUBE_VIDEO_ID が"
                "設定されていません。"
            )

        self.youtube_comment_adapter = (
            YoutubeCommentAdapter(
                video_id
            )
        )

        self.obs_adapter = (
            OBSAdapter()
        )

        self.voicevox_adapter = (
            VoicevoxAdapter()
        )

        self.openai_adapter = (
            OpenAIAdapter()
        )

        self.play_sound = PlaySound(
            output_device_name=(
                "CABLE Input "
                "(VB-Audio Virtual Cable)"
            )
        )

        print(
            "✅ AITuberSystem "
            "が初期化されました。"
        )

    def talk_with_comment(
        self
    ) -> bool:

        print(
            "💬 コメントを取得中..."
        )

        comment = (
            self
            .youtube_comment_adapter
            .get_comment()
        )

        if not comment:
            print(
                "⚠️ コメントが"
                "見つかりませんでした。"
            )

            return False

        print(
            f"🗨️ コメント: {comment}"
        )

        try:
            # OpenAIで回答生成
            response_text = (
                self
                .openai_adapter
                .create_chat(comment)
            )

            print(
                f"🤖 応答: "
                f"{response_text}"
            )

            # VOICEVOXで音声生成
            data, rate = (
                self
                .voicevox_adapter
                .get_voice(
                    response_text,
                    speaker_id=8
                )
            )

            # OBSへ質問・回答を表示
            self.obs_adapter.set_question(
                comment
            )

            self.obs_adapter.set_answer(
                response_text
            )

            # VB-CABLEへ音声出力
            self.play_sound.play_sound(
                data,
                rate
            )

            print(
                "🎤 再生完了。"
            )

            return True

        except Exception as e:
            print(
                "❌ 対話処理中に"
                f"エラー: {e}"
            )

            return False

talk_with_comment()の中を見てみると、処理そのものはかなり単純。

YouTubeコメントを取得
        ↓
OpenAIへ送る
        ↓
AIの回答を取得
        ↓
VOICEVOXへ送る
        ↓
音声データを取得
        ↓
OBSへQuestionを設定
        ↓
OBSへAnswerを設定
        ↓
VB-CABLEへ音声を出力

これまで一つずつ作ってきた部品を順番に呼んでいるだけ。


17. AI Tuberを常時動作させる

最後にAITuberSystemを一定間隔で繰り返し実行する。

run.py

を作る。

import sys
import time
import traceback
import msvcrt

from aituber_system import (
    AITuberSystem
)


def main(
    polling_interval: int = 5
):
    print(
        "🚀 AITuber 起動中..."
    )

    try:
        aituber = AITuberSystem()

    except Exception:
        print(
            "❌ システム初期化中に"
            "エラーが発生しました。"
        )

        print(
            traceback.format_exc()
        )

        return

    print(
        "✅ 初期化完了。"
        "コメント監視を開始します。"
    )

    while True:
        # Windows環境。
        # Escキーで終了する。
        if msvcrt.kbhit():
            key = msvcrt.getch()

            if key == b"\x1b":
                print(
                    "\n🛑 Esc押下 → "
                    "終了します"
                )

                break

        try:
            success = (
                aituber
                .talk_with_comment()
            )

            if not success:
                print(
                    "⏸ コメントなし。"
                    "再試行します..."
                )

            # Escキーを確認できるよう
            # 0.1秒単位で待つ
            for _ in range(
                polling_interval * 10
            ):
                time.sleep(0.1)

                if msvcrt.kbhit():
                    key = msvcrt.getch()

                    if key == b"\x1b":
                        print(
                            "\n🛑 Esc押下 → "
                            "終了します"
                        )

                        print(
                            "✅ AITuber "
                            "正常終了"
                        )

                        sys.exit(0)

        except Exception:
            print(
                "❌ 実行中にエラーが"
                "発生しました。"
            )

            print(
                traceback.format_exc()
            )

            time.sleep(5)

    print(
        "✅ AITuber 正常終了"
    )


if __name__ == "__main__":
    main()

msvcrtを使っているため、この終了処理はWindows向け。

Escキーを押すとプログラムを終了できる。


18. 最終的なファイル構成

ここまで作ると、

C:\workspace\aituber
│
├─ .venv
│
├─ .env
├─ prompt_system.txt
│
├─ openai_adapter.py
├─ openai_test.py
│
├─ voicevox_adapter.py
├─ voicevox_test.py
│
├─ make_list_sound_device.py
├─ play_sound.py
│
├─ obs_adapter.py
│
├─ youtube_comment_adapter.py
│
├─ aituber_system.py
└─ run.py

という構成になる。

主要ライブラリをまとめると、

pip install openai
pip install python-dotenv
pip install requests
pip install numpy
pip install soundfile
pip install sounddevice
pip install obsws-python
pip install pytchat

GitHubリポジトリを使わなくても、この記事に掲載しているコードから一通り構築できるようにしている。


19. 実際にYouTube LiveでAI Tuberを動かす

それでは全機能をつないで動かす。

起動する順番を整理しよう。

1. VOICEVOXを起動する

VOICEVOX APIをPythonから使用するため、VOICEVOXを起動しておく。

ブラウザから、

http://127.0.0.1:50021/docs

へアクセスできることも確認しよう。

2. OBS Studioを起動する

AI Tuber用のシーンを表示する。

確認するのは、

Questionがある
Answerがある
CABLE Outputが音声入力になっている
WebSocketサーバーが有効

こと。

3. YouTube Liveの配信枠を作る

OBSの、

配信の管理

から配信を作成する。

4. YouTube StudioでVIDEO IDを取得する

YouTube Studioのライブ管理画面を開く。

URLからVIDEO IDを取得します。

5. OBSのコメント欄を更新する

ブラウザソースのURLを、

https://www.youtube.com/live_chat?is_popout=1&v=VIDEO_ID

へ変更する。

6. .envを更新する

YOUTUBE_VIDEO_ID=取得したVIDEO_ID

とする。

7. Pythonの仮想環境を有効にする

cd C:\workspace\aituber

.venv\Scripts\activate.bat

8. AI Tuberを起動する

python run.py

正常に初期化されると、

🚀 AITuber 起動中...
✅ OBSに接続しました。
✅ AITuberSystem が初期化されました。
✅ 初期化完了。コメント監視を開始します。

といったログが表示される。

9. OBSからライブ配信を開始する

OBSの、

配信開始

を押下。

YouTube Studio側で映像が届いていることを確認する。

10. YouTube Liveへコメントする

YouTube Studioのチャット欄から、例えば、

今日のおすすめゲームは何ですか?

と投稿する。

正常に動けば、

YouTube Liveへコメント
        ↓
Pythonがコメント取得
        ↓
Questionへ表示
        ↓
OpenAIが回答生成
        ↓
Answerへ表示
        ↓
VOICEVOXが回答を音声化
        ↓
VB-CABLEへ音声出力
        ↓
OBS
        ↓
YouTube Live

という一連の処理が動きます。

確認したいのは次の5点です。

・OBSへYouTubeコメント欄が表示される
・投稿したコメントをPythonが取得する
・コメントがQuestionへ反映される
・OpenAIの回答がAnswerへ反映される
・VOICEVOXの音声がYouTube Liveから聞こえる

すべて確認できれば完成。


20. 配信を終了する

確認が終わったら、

YouTube Studio
    ↓
ライブ配信を終了

OBS
    ↓
配信終了

Python
    ↓
Escキー

の順で終了。

VOICEVOXも必要なければ終了。


21. 動かない場合に確認するポイント

複数のソフトウェアやAPIをつないでいるため、問題が起きたときに全部を一度に確認しようとすると原因が分かりにくくなる。

今回のようなシステムでは、各機能を単体で確認する方が切り分けやすくなる。

OpenAIの回答が返らない

まず、

python openai_test.py こんにちは

だけを実行する。

ここで動かなければ、OBSやYouTubeではなくOpenAI API側の問題。

.envのAPIキーやモデル指定を確認してください。

VOICEVOXへ接続できない

ブラウザで、

http://127.0.0.1:50021/docs

が開けるか確認する。

開けない場合、PythonではなくVOICEVOX側を確認する。

音が出ない

最初に、

VOICEVOX
 ↓
PCスピーカー

でテストする。

それが動いたら、

VOICEVOX
 ↓
CABLE Input
 ↓
CABLE Output
 ↓
OBS

を確認する。

OBSの音声ミキサーが動いているかを見ると分かりやすい。

OBSのQuestion / Answerが変わらない

python obs_adapter.py

だけを実行。

OBS WebSocketの、

ポート
パスワード

と.envの設定が一致しているか確認する。

また、OBS上のソース名が正確に、

Question
Answer

になっていることも確認する。

YouTubeコメントを取得できない

まずVIDEO IDを確認。

OBSのブラウザソース
.env
YoutubeCommentAdapter

がすべて同じ配信のVIDEO IDを参照している必要がある。

また、今回使用しているpytchatはYouTube側の仕様変更によって動作しなくなる可能性がある。

他の機能が正常でYouTubeコメント取得だけ失敗する場合、この部分を疑った方がよい。


22. AI Tuberを作って分かったこと

今回のハンズオンで特に印象に残ったのは、

「生成AIアプリを作ること」と「OpenAI APIを呼ぶこと」は同じではない

ということ。

最初は、

OpenAI API
 ↓
AI Tuber

くらいに考えていた。

しかし実際に作ってみると、

入力
 └─ YouTube Live

思考
 └─ OpenAI

音声
 └─ VOICEVOX

音声経路
 └─ VB-CABLE

画面・配信
 └─ OBS Studio

制御
 └─ Python

という構成。

OpenAI APIは確かにAI Tuberの重要な部分ですが、システム全体から見ると一つの部品だとわかった。

面白かったのは「AI」よりも「連携」だった

OpenAI APIへ文章を送り、回答を取得するだけならコード量はそれほど多くない。

今回面白かったのは、

YouTube
 ↓
OpenAI
 ↓
VOICEVOX
 ↓
OBS
 ↓
YouTube

という、本来まったく別のサービスが自分のプログラムを中心につながっていくところだった。

それぞれを、

YoutubeCommentAdapter
OpenAIAdapter
VoicevoxAdapter
OBSAdapter
PlaySound

へ分離したことで、

AITuberSystem

から見ると各サービスの細かな仕様をあまり意識する必要がない。

例えば将来、

OpenAIを別のLLMへ変更する

VOICEVOXを別の音声合成へ変更する

pytchatをYouTube Data APIへ変更する

という場合でも、そのAdapterを中心に変更できる構成になる。

今回のハンズオンで得られたものは、生成AIのAPI利用方法だけではなかった。

異なるサービスを組み合わせて一つのアプリケーションとして成立させる考え方を実際に試せたことが大きかったと思う。


23. ただし、このままでは本格的なAI Tuberではない

今回のシステムで、

コメントを読む
 ↓
考える
 ↓
回答する
 ↓
しゃべる

ところまでは実現できた。

しかし、実際にAI Tuberとして継続的に活動させることを考えると、スタートラインにも立てていない。

極端に言えば現在は、

OpenAIが質問に回答する仕組み

+

VOICEVOX

+

OBS

+

YouTube

をつないで豪華に出力している状態。

キャラクターを「一人の存在」と感じられるところまで育てようとすると、例えば、

・キャラクター固有の人格を長期間維持する

・過去の会話を記憶する

・配信へ来てくれた人を覚える

・視聴者ごとの関係性を持つ

・状況によって感情を変える

・感情に合わせて表情や動きを変える

・会話の流れを理解する

・自分から話題を出す

・オリジナルの声を持つ

・2D・3Dモデルとして動く

・自分で歌う

・自分の好きなものを紹介する

といった機能が欲しくなる。(特にやってみたいのが「記憶」)

例えばデータベースへ、

視聴者A

├─ 名前
├─ 前回来た日時
├─ 過去に話した内容
├─ 好きなもの
└─ AI Tuberとの関係性

といった情報を保存する。

そうすれば、

「また来てくれたんだ」

「この前話していた○○、どうなった?」

といった会話ができるようになる。

単に毎回OpenAIへ質問を投げるだけのシステムとは、かなり印象が変わるはず。


24. 「人間らしい」ではなく、一人の存在にしたい

今回作成したAI Tuberは「結音ゆの」というキャラクター。

せっかく自分でAI Tuberを作るのであれば、

人間らしい回答を生成するAI

で終わらせるより、

一人のキャラクターとして継続的に存在しているように感じられるAI

まで育ててみたいと思った。

VOICEVOXの音声についても、もっとキャラクター固有の声にしたい。

表情や動きも付けたい。

2D・3Dモデルとして動かしたい。

自分で曲を作り、歌詞を考え、歌ってもらうのも面白そう。

そして、自分の好きなものを一緒に発信してくれる存在にしてみたい。

今回作ったものは、そこへ進むための最初の土台なのかもしれない。


まとめ

『AI Tuberを作ってみたら生成AIプログラミングがよくわかった件』を参考に、PythonでAI Tuberを作ってみた。

今回扱ったのは、

Pythonの仮想環境

OpenAI API

システムプロンプト

VOICEVOX API

音声データ

sounddevice

VB-CABLE

OBS Studio

OBS WebSocket

YouTube Live

YouTubeコメント取得

各サービスを分離するAdapter

各機能を統合するAITuberSystem

OpenAI APIの使い方を学べたことも大きかったが、それ以上に、

生成AIを実際のアプリケーションへ組み込むとはどういうことなのか

を体験できたことがよかった。

LLMだけではアプリケーションにはならない。

入力する仕組みがあり、

考える仕組みがあり、

出力する仕組みがあり、

外部サービスがあり、

それらをつなぐプログラムがある。

今回作ったAI Tuberは小さなシステムだが、

入力
 ↓
生成AI
 ↓
外部サービス
 ↓
出力

という構造は、生成AIを利用するさまざまなアプリケーションでも使える考え方だと思う。

そして何より、自分で作ったキャラクターが、自分のプログラムによってコメントを読み、考え、声を出して返事をするところまで動いたのは純粋に面白い体験だった。

今回作成した「結音ゆの」は、まだ質問へ回答してしゃべれるようになっただけ。

ここから記憶、感情、表情、声、2D・3Dモデルなどを追加しながら、少しずつ育てていければと思う。

AI Tuberを作ってみたら生成AIプログラミングがよくわかった件
著者 : Sald_ra様
– https://qiita.com/sald_ra
– https://x.com/sald_ra