Sparks
ツンデレAIのDiscordボット (ITORO Bot)
OpenAIのAPIとVOICEVOXで作った自作のDiscord用AIボット。ツンデレ口調の会話・画像の認識/生成・チャットの音声読み上げを、Python(discord.py)とRaspberry Pi上のDockerで安定して動かしています。作った理由と仕組み、工夫した点をまとめました。
友人と集まる Discord サーバーに、もう一人“住人”を増やしたくて作った AI ボットです。話しかければツンデレな調子で雑談に付き合い、画像を見せれば写っているものを言い当て、「〇〇な絵を描いて」と頼めば描き、チャットの内容は VC(ボイスチャット)で読み上げる——そんな“何でも屋”を、OpenAI の API と音声合成を組み合わせて一から作りました。
作った理由
きっかけは「Discord をもっと面白くしたい」という、ただそれだけの動機でした。せっかくなら愛想のいいアシスタントより、ちょっと素直じゃない“ツンデレ”な相棒のほうが面白い。そう思って性格まで設定した常駐ボットに仕上げました。遊びで始めたものですが、作るうちに「外部 API をどう安定して組み合わせるか」という、仕事と地続きの課題がいくつも出てきました。
できること
- ツンデレ AI との会話 — メンションで話しかけると、ツンデレ口調で返してくれます。返信をたどって直前までのやり取りも覚えているので、会話の流れが続きます。
- 画像の認識 — 画像を添付して「これ何?」と聞くと、写っているものを説明します。
- 画像の生成 — 「〇〇を描いて」と頼むと、その場で画像を生成して返します。
- チャットの読み上げ — 指定したチャンネルの発言を、ずんだもんの声で VC に読み上げます。“ながら”でも会話に置いていかれません。
- 言葉だけで操作 — コマンドを暗記しなくても、「読み上げして」「音量下げて」のような普通の言葉で操作できます。
ほかにも、ゲーム仲間をランダムにチーム分けして VC に振り分ける小ネタ機能など、自分たちの“遊びの面倒”を自動化する道具も入れています。
人格は“プロンプト”で作る
ツンデレ口調は、AI への指示文(システムプロンプト)でキャラクターを指定して作っています。面白いのは、エラー時のセリフまで含めて世界観を崩さないようにしている点です。たとえば実際の返答はこんな調子です。
(うまく言葉が出なかったとき)
…… 何よ、言葉が出ないじゃない
(エラーが起きたとき)
ごめんなさい、エラーが発生したわ…別にあなたのためじゃないんだから、もう一度試してよね
エラーメッセージすらキャラクターのまま返すことで、不具合が起きても“それっぽく”見える、という副次的な効果もありました。
AI に“道具”を持たせる(function calling)
このボットは、ただ喋るだけの AI ではありません。会話の内容に応じて、AI 自身に「画像を生成する」「読み上げを操作する」といった“道具”を選ばせています。OpenAI の function calling という仕組みで、AI には「何をしたいか」を構造化された形(JSON)で返させ、実際の処理はこちら側のコードで実行します。おかげで「猫の絵を描いて」も「読み上げ止めて」も、ユーザーは自然な一言を投げるだけで済みます。
読み上げ(VOICEVOX)の仕組みと、止まらない工夫
音声読み上げには、ホスト型の VOICEVOX Web API(音声合成サービス)を使っています。自前で合成エンジンを動かすと非力なサーバーには重いので、合成だけ外部に任せる構成です。合成には数百ミリ秒〜数秒かかりますが、そこを滑らかに見せる工夫を入れています。
- 落ちても本体は無事:合成サービスが落ちても、ボット本体は止めず読み上げだけ黙ってスキップします(“あれば便利、無くても困らない”機能として切り離す設計)。
- 途切れさせない再生:VC では無音を流し続けてプレーヤーを生かしておき、合成が終わった瞬間に読み上げを差し込みます。鳴らすたびに再生を立ち上げ直すより滑らかです。
- 異常応答を弾く:外部から返ってきたデータが本当に音声か中身の先頭を見て確認し、エラーページ(HTML など)を再生処理に渡して壊れるのを防ぎます。
- 連投対策:読み上げ待ちが一定数(5 件)を超えたら、それ以上の新しい発言は読み上げをあきらめて、暴走を防ぎます。
つまずいた点・工夫した点
- 「絶対に落ちない」音声処理:音声を出力する心臓部のコードは、何があっても途中で止まらない(例外を投げない)ように作っています。ここが一度でも落ちると音声機能が丸ごと無言になるためで、地味ですが安定稼働のいちばんの要でした。返信の送信が失敗したときも、添付を外して本文だけ送り直すなど、一つの失敗で全体を道連れにしない作りにしています。
- 重い処理を裏で並行処理:AI への問い合わせ(別スレッド)や音声合成(ノンブロッキングな通信)は時間がかかるので裏で処理し、その間もボットが他の発言にすぐ反応できるようにしています。
- コストの暴走を防ぐ:AI への問い合わせや画像生成は使うほど費用がかかります。誰か一人が連打しても費用が膨らまないよう、短時間の連続呼び出しは受け流し、画像生成には「1 時間あたりの上限」を設けています。API の応答が固まったときに備えて、待ち時間にも上限を入れています。
- 設定ミスでも止まらない:設定ファイルの値が壊れていても既定値に戻して起動を続けます。ちょっとした設定ミスでボット全体が立ち上がらない、という事故を防ぎます。
動かし続けるための仕組み(インフラ・運用)
- 自宅の Raspberry Pi(手のひらサイズの小型コンピュータ)上の Docker で、24 時間つけっぱなしにしています。
- コードを更新(
mainブランチに push)すると、GitHub Actions という仕組みが自動で Pi に反映(デプロイ)します。事前の自動チェックに通ったときだけ反映されるので、構文エラーのあるコードがそのまま動くことはありません。 - 停電や再起動の後も、自動で立ち上がり直して復帰します。
- サーバーごとの設定(音量など)は、書き込み途中で壊れないやり方でファイルに保存し、再起動後も引き継ぎます。
使った技術
Python 3.13 / discord.py / OpenAI API(会話・画像認識・画像生成。会話まわりは Responses API + function calling)/ VOICEVOX(tts.quest の Web API)/ FFmpeg / Docker / GitHub Actions / Raspberry Pi
趣味で作りましたが、仕事と同じ技術です
このボットは遊びで作ったものですが、中身は「外部の API を実際に動くサービスへ組み込み、コストや障害を見越して安定して動かし続ける」という、ふだんの受注のお仕事とまったく同じ技術でできています。たとえば、お問い合わせの一次対応や FAQ の自動応答、送られてきた画像の自動仕分け、社内ツールへの AI 組み込みなども、同じ考え方で作れます。「うちのこれ、自動化できる?」といったご相談はお気軽にどうぞ。