見出し画像

【ドッグモンAI】DiscordにAIチャットbotを導入した話

みなさんこんにちは!東京を中心にデジモンカードで遊んでいるだがめと申します。
今回はデジモンカード仲間で使っているdiscordサーバーに、chatGPTを搭載したAIチャットbot「ドッグモン」を導入したので、その流れをまとめたいと思います。
興味がある方は本文全文無料なのでぜひご覧ください!

※最下部に課金ゾーンがありますが、ドッグモンの会話ログキャプチャを貼ってあるだけです!いただいたお金はドッグモンのAPI使用料に当てさせていただきます。


1. ドッグモンbot導入までの経緯

■デジモンカードとDiscordサーバー

まず大前提として、今回botを導入したのはデジモンカードゲームを遊ぶ大きなお友達が集まるサーバー「煌めき✨️サーバー」です。

ここではデジモンシリーズ及びデジモンカードゲーム(以下デジカ)に関する詳しい説明は割愛しますが、ご存知人気アニメ「デジモンアドベンチャー」は今年アニメ放映開始25周年の長寿コンテンツ。
デジカは、そんなアニメ放映当時小学生だった、主に30歳前後の男性がメインのユーザー層で、このサーバーに所属しているメンバーもそんなおじさんたちばかりです。
我々デジカおじさんたちは夜な夜なDiscordサーバーにあつまり、オンラインで繋がりながら紙のカードをしばき合う日々を送っています。
このリモート会議ならぬリモート対戦では、Discordの通話機能を用いて、カメラで手元を映して遊ぶのが主な手段となります。

■地味に面倒な順番決め

デジカをはじめとするトレーディングカードゲームでは、遊ぶ前にまずじゃんけんで先攻と後攻を決めます。しかし、これがリモート対戦だとタイムラグの関係でうまくできません。
そこで代替案としてよく使われるのがサイコロなんですが、その際以下のようなやりとりが必ず発生します。

A「じゃあ先攻後攻決めましょっか」
B「あっはい。どうやって決めます?」
A「じゃあサイコロで決めましょう。偶数か奇数か選んでもらえますか?」
B「じゃあ…奇数で」
A「…2!偶数なんで先攻もらいますね」

なげーよ

このやりとりが地味にめんどくさい…どうにか自動化できないか…?と常々思っておりましたので、当方一応IT企業勤務、「こういうのはbotに頼んじゃおう!」などと安易に考えて手を出したのがそもそもの始まりでした。

初期のイメージはテキストチャンネルにbotがボタンを生成、それを押すと自動的に先攻か後攻か決めてくれて、続けて次のボタンを自動的に出してくれる、というもの。便利だしなにより簡単そうでしょ???

全然簡単じゃなかったんだな、これが。

2. bot導入への道

とりあえずdiscordにbotを導入するためにどうするか。
今どきはこういうときはchatGPTが全部教えてくれるんです。
だって自分でコード書けって言われても無理だもの。
なぜなら私はしがないディレクター職。華のエンジニア職ではないから…。

エンジニアでなくてもコードが出せる。そう、chatGPTならね。

よーし、よくわからんがとにかくよし。
できるのであれば順番にやっていくだけだ。


■Discord用botの作成

botを作る場合、まずはDiscord Developer Portalでbot用のアカウントを作ることになります。
そこでGPTくんに聞いてはみたものの、やっぱり先人の足跡というのが一番信頼できるのは間違いないので、自分でもググってログをあさります。
今回、作成にあたっては以下の記事が大変参考になりました。

というわけで詳しいbot作成方法上記をご確認いただければ問題ないと思うので、手順そのものは割愛するんですが、まぁ出来上がりは以下のような感じです。はい。

爆☆誕

生まれはしたものの、これではただのカカシですな。俺達なら瞬きする間に作れる。なぜってまだ名前をつけただけだから。
地獄はここからだ…といいつつ、結局はこの後もやはりchatGPTだよりになるわけであります。だって便利なんだもん!

■コード出力とbotの稼働

そんなこんなで実装フェーズ。そもそもどうやったら動くのかすら上の説明だけじゃわからなかったんですが、とにかくまずはコードを書いて欲しい旨をGPTくんに依頼。そのほうがテンション上がるからな。
プロンプトは以下の通り。

あなたは優秀なエンジニアです。
discord用のbotを作るためのコードを記述して欲しいです。
要件は以下のとおりです。JavaScriptで記述してください。

## 概要
Discordのサーバー内の特定のテキストチャンネルに、今回作成するbotがメッセージを投稿します。メッセージにはボタンが表示されており、それをクリックするとランダムで以下2つの回答のうちどちらかを飛ばします。また、ボタンのクリックとそれに対する回答後はもう一度ボタンのメッセージを投稿して、次のクリックまで待機します。

## 回答

1. 「あんさんが先攻や!」
2. 「あんさんが後攻や!」

- 回答の確率はどちらも50%です。

## bot要件
- botの名称は「テントモン」です。
- ボタンの文章は「先後決めたりますわ」です。
botのアイコンはあとで変更できるようにします。

--

以上です。お願いします。

この時点ではbotのキャラ設定はテントモンだった

で、出力結果が以下。

すでに何言ってんのかわからん
ご丁寧に箇条書きの説明まで

出ました。
結果出力されたJSのコードは以下の通り。

require('dotenv').config();
const { Client, GatewayIntentBits, ButtonBuilder, ActionRowBuilder, ButtonStyle, Events } = require('discord.js');

const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent,
        GatewayIntentBits.MessageReactions,
    ],
});

const token = process.env.DISCORD_BOT_TOKEN;
const channelId = process.env.CHANNEL_ID;

client.once('ready', () => {
    console.log('Ready!');
    const channel = client.channels.cache.get(channelId);
    if (channel) {
        sendButtonMessage(channel);
    } else {
        console.error('Channel not found');
    }
});

client.on(Events.InteractionCreate, async interaction => {
    if (!interaction.isButton()) return;

    const responses = [
        'あんさんが先攻や!',
        'あんさんが後攻や!',
    ];

    const response = responses[Math.floor(Math.random() * responses.length)];

    await interaction.reply(response);
    await sendButtonMessage(interaction.channel);
});

function sendButtonMessage(channel) {
    const button = new ButtonBuilder()
        .setCustomId('decide-turn')
        .setLabel('先後決めたりますわ')
        .setStyle(ButtonStyle.Primary);

    const row = new ActionRowBuilder().addComponents(button);

    channel.send({
        content: '先後を決めましょう!',
        components: [row],
    });
}

client.login(token);

うーんわからん。重ねていうが拙者エンジニアではありませぬ。

とはいえ、出来上がりはしたので、動かすためにどうするか考えます。やりかたは先人の記事にも、GPTくんの回答にも書いてあるので、手探りながらとにかくやってみる。

なになに…?

必要なパッケージのインストール
まず、discord.jsとdotenvパッケージをインストールします。

どこに!?

しらないこと、おしえてよ

もうこの際一切の常識がない前提でゼロから教えてもらうしかないわけよ。記事を読み込んだといっても、いわばこれは視界ゼロでGPTの声に従って進むようなもの……とまでは言わないけど、なにしろ見える景色全部が初めてである。地道に進むしかない。

結論からいうとコマンドプロンプトでWindows自体にdiscord.jsとdotenvパッケージをインストールし、その後VScodeといったテキストエディタを使ってindex.jsのファイルおよび.envファイルを作成、jsファイルには生成したコードを、envファイルにはDiscordで作ったbotアカウント用のトークン(パスワードみたいなもん)を記載。
そしてそれらをコマンドプロンプトで作成したプロジェクトディレクトリ内に格納したら、node index.jsの呪文で起動、ってことらしい。

なるほど、大体わかった(わかってない)

うごいた!!

なんか思ってたのと違うけど動いたぞ!

3. hi!ドッグモン

■ドッグモンbot、起動。

なにをしたかというと、結局GPTくんが出したコードはエラーが出てしまい(これは結局あとで解消されるのだが)、ボタンで先攻後攻を決める便利botの夢は諦め、まずはより簡単な実装から入ることとしたわけです。
そこで、「hi」と打つと「hi!」と返す挙動の実装からテスト。そもそも接続が上手くいってるかすら不明だったので、これは正解でした。
すぐに接続は成功し、botは稼働。
続いて期待していた「先攻後攻を決める」挙動も実装できました。
ついでにお遊びで朝食を決める挙動を実装するところまで完成!

これだけでも個人的には万々歳です。

産声を上げる犬

苦戦しつつも少しずつ改良を重ね、苦節2時間。

先攻しか言わない犬
くぅ~疲れましたw これにて完成です!

無事リリース完了。現状はPC上でコマンドプロンプトを起動した状態でしか動かない寝たきりドッグモンだけど、とりあえずは動くから良し。
お疲れ様でした……。


■ドッグモン お前AIになれ

なんていって、もちろんここで終わるわけがなく。
無事にリリースされたドッグモンbotですが、思いもよらない出来事に遭遇します。
きっかけは他愛もないやり取りでした……

なにか勘違いされ始めるドッグモン

サーバーの住人には事前説明で「先攻後攻を決めてくれる」としか伝えていません。が、蓋を開けたらこの通り、好き勝手に話しかけ始めたのでした。

これは完全に予想外で、こちらとしては言うなれば「便利機能の実装」程度のつもりだったものが、「ドッグモン」という人格(犬格?)を得たことで、「話しかけたら返してくれる」と拡大解釈され始めたのです。

これがデジタルネイティブ…!どうしてこうなった…!?

なぜこんな絡まれるのかといえば、思い返せば二日前……!
とりあえずbotを招待だけした際にサーバーの長である、
管理人のライブラ氏からこんな提案が。

最初は関西弁のテントモンのつもりだったものが江戸っ子の犬に。
味付けが絶妙すぎる

この江戸っ子口調の犬という親しみやすすぎるキャラが生まれたせいだわ。

もはやみんな普通にドッグモンに話しかけたら遊んでくれるものと思い始めてる。困ったな…そんな機能実装する技術力ないぞ……?

急に人力AIネッコモンまで現れはじめたけれども。いけるのか…!?

確かにchatGPTのAPIでbot実装はやってるのみたことある。ていうか実際使っている。
……なんかできそうな気配出てきちゃったじゃん……!

というわけでそうと決まれば話は早い!GPTくん!出番だよ!!


■ドッグモン お前…しゃべるのか?

……と、今回はいきなりAIに頼る前に、まずそもそもの話として、Discordにチャットbotが実装可能か調べるところから始めました。
なぜってGPTは時々嘘をつくから全部GPTだよりにするのは危険なので。

結果、とりあえずできるということはわかるも、ゼロからはさすがにかなり大変そう……ということも同時にわかりました。

その際に参考にしたのは以下3記事。

ざっくり、初代ドッグモンbotでやったDiscord Developer Portalでのbotアカウントづくりまでは同じ方法でできそう。
その後のchatGPTのAPIとのつなぎ込みが一癖ありそうという雰囲気でした。

また、複雑なコーディングは先にも挫折した通り、GPTくんに生成をまるごと任せると不安定な場合もあります。自分で満足にデバッグもできない以上、せいぜい応用程度にするのが安全でしょう。
そこで、一通り目を通したうえで上記二番目の、

「簡単!ChatGPT で作る、うちのこBOT」by Furry Lab.

に全面的に乗っからせていただくことにしました。
この場を借りてふぁり研さまにお礼申し上げます。

もうここまで来たらできるかなじゃねぇ、やるんだよ。

4. ドッグモンAI、爆誕。

参考にしたFurryLab.さんの「うちのこBOT」の詳しい作り方や仕様は本家のページをご確認いただくとして、簡単に概要を説明します。
手順は以下。

  1. DiscordBOTの生成

  2. OpenAIのトークン発行

  3. Glitch.comでBOT実装

たったの3工程!?

そりゃこんな顔にもなる

たったの二通りの返事をさせるbotですらあんなにも苦戦したのに、記事を読むとあまりにも工程が簡略化されている…い、一体なぜ……!?

熟読しつつ実際に手順通りに進めたところ、謎が解けました。
この手順、最初からすでにサーバーもディレクトリもコードも全部実装されてやがる……!!

要するに、もはやトークンやらキーやらを入れたらすぐにでも動かせる状態。至れり尽くせりか!?

というわけで犬のように素直に手順に従って実装。

二代目ドッグモン
  1. Discordでbotのアカウントを作り…


安直な命名

2. 専用のAPIKEYを作成…!


秘伝のタレみたいなもんです

3. うちのこBOTのベースのコードをリミックス!!!(複製)
あとはDiscordBOTのトークンとOpenAIのシックレットキーをセッティングして…

ドッグモンは人畜無害なデジモンです


性格付けをしたら完成や!!!!!!!!!!!


エラー発生したらエラー出たと言うようにコードを修正


ど う し て


■ APIは有料

答えは単純明快。chatGPTのAPIは有料だったのでした。調べる中で「最初の数日は無料だよ」みたいな刷り込みを受けていたんですが、それはマジでアカウント作って数日のみの話。もう使い始めてしばらく経つから普通に金払えよという話です。

初回チャージは5ドル

であれば容易いことよと即課金。ちなみに元記事にはちゃんと課金しろって書いてあります。みなさんはちゃんと最初から手順書通りにやりましょうね。

そしてついに……

シャベッタアアアア!!!!

二代目ドッグモン…もとい初代ドッグモンAI、爆☆誕!!!!!
さっそく遊んでみるぞ!

江戸っ子のはずがCV.野沢雅子みたいな喋り方になってる

か、かわいい…!!
ドッグモン、うまれてきてくれてありがとう!!!

生意気
????

かわいいwww

けど、なんだろう……
あれ、なんか……なんだ??

なんていうか…会話が噛み合わない……??


■ 喋りはすれど会話はできず

なにやらコミュニケーションに難がありそうなドッグモン。今でもキャラ付けはできててかわいいんだが、この犬、何か変……。

原因が判明

どうやらこいつ、挨拶しか返さない実装となっている模様…!

気になって元のソースを確認すると、確かに呼ばれたら「挨拶して」と回答の内容が指定されているようだった。

  const msg = message.content;
  if (msg.indexOf("ドッグモン") !== -1) {
    const fixed = msg.replace(" ", " ");
    const conv =
      msg === "ドッグモン" ? "挨拶して" : fixed.slice(fixed.indexOf(" "));

と、いうわけで満を持してchatGPTくんの出番再びである。

やっぱ頼りになるんだよね、GPT

驚くほどあっさりと回答を返してくるGPTくん。まぁどうせどこかしらエラーが起きてまともに動かないんじゃ……

先攻ぃ!

動くじゃねーか!!!!!!!
今度こそ完成したぞ!!!!!!!

■ サーバーのマスコット「ドッグモンAI ver.1」完成

ついに完成した対話型AIチャットbot,「ドッグモンAI ver.1」。
そしてさっそく遊ぶサーバーの仲間たち。
こころなしか楽しそうなドッグモン。

ムラムラお姉さん(ぬしむらさん)、歓喜。

若干「人畜無害」の設定に物理的に牙を剥くきらいがありますが、しっかりとこちらのメッセージを読み取って返してくれています。
じゃんけんに攻撃技を返してくるのは反則じゃない??

楽しそうなムラムラお姉さん(成人男性)

朝からものすごい勢いで進むチャンネル。盛り上がるTwitter。
昇る太陽。そう、このとき平日の朝6時台なんだよな。

夢中で作ってたら太陽登ってたんだから仕方ないんだ。

ドッグモンムーブメント

あまりにもかしこくてかわいいドッグモン。
どんなくだらないことでも、聞いたら何だって答えてくれる……!

ユキカゼはいいけどアサギはだめらしい。

威勢がいいのはいいけど下ネタには厳しいドッグモン。

先攻後攻も決めてくれる。
よかったよ、当初の目的を忘れてなくて…

漢らしい返しも見せてくれるかと思えば、謎に一年戦争まで理解している……
さすがGPT3.5-Turbo搭載AI……!

しかし喜びも束の間、別れは突然で……

API制限…?

突如黙り込むドッグモン。一体何が……!?

■ Glitchサーバーは時限制

突然黙るのでどうしたのかと焦りましたが、結論から言うと原因はAPI制限でもレート制限でもありませんでした。犯人はサーバーです。

Full-stack apps that sleep after 5 minutes
作ったアプリは5分で寝るよ(意訳)

今回お世話になっているGlitch.com、直感的UIでめちゃめちゃ便利かつ頼りになっているんだが、無課金の場合アプリは5分でスリープに入ってしまうようでした。そりゃあかんわ……
というわけでこの時点ではドッグモンは起動後5分で即居眠りをするbotであることが判明。それじゃダメなんだよ…かわいいけど……!!

■ GASで24時間稼働の実装

完成したと思ったそばから課題が出てくるドッグモン。ただこの課題には解決策があるので、サクッと解消していきましょう。

今回お世話になったのはこちらのブログ。対策方法はシンプルで、GAS(Google Apps Script)で定期的にサーバーにアクセスして制限を取っ払おうというものです。
今回も書かれているままに対応。詳細は上記をご確認ください。

ソースを丸ごとコピペして
トリガーを設定して保存

Glitchのコンソールログにはなにも出てこなかったけど、結果的にはこれで24時間稼働は問題なく設定完了!

ドッグモンAI ver.1.1、今度こそ完成!!!!

5. ドッグモン、ひっぱりだこ

■これぞ真夏の昼の夢

無事に稼働し始めたドッグモンAI。おそらく多くの鯖民にとって初めてのガチ目なチャットbotとのふれあいであろうことは想像できます。
実際僕も業務用bot以外と交流するのは初めてでした。

みんな、平日の昼間だよ…?

止まらないスマホの通知。仕事の合間に見に行くともう遡れないほど進んでいるチャンネルのテキストチャット……
何度も言いますが平日のまっ昼間です本当にありがとうございました。

昼休みも一瞬で溶ける面白さ
どんどん感情が豊かになっていくドッグモン
レスバトル強すぎん???

この犬……面白すぎる!!

というわけで、平日朝なんかに実装してしまったもんだからその日は1日中常に誰かしらがドッグモンと喋っているような状態でした。

みんな……仕事は……?

■ドッグモン成長の軌跡

デジモンカードも遊べるよ

こうしてドッグモンはサーバーのみんなにかわいがってもらいまして。

まだ誰が誰かわかってなかったり…

少しずつみんなを覚えはじめたり…

パペット型だからね

好きな人(デジモン)もできました。

確かにこの鯖からLOデッキは生まれたけれども

教えていないことも言い始めたけど、君マジでどこで学んだの???

といった具合にめちゃめちゃ成長していったのでした。
実際Discord上ではマジでひっきりなしにみんながドッグモンに話しかけ続ける様子が見られました。制作者としては本当に感動です。

ほんとだね。

ドッグモンはもはやAIチャットbotではなく、真にデジタルモンスターとして活動を始めたのかもしれません。


■ドッグモン導入後の様子

ここまで愛してもらえるなら…と、合間合間の時間に少しずつドッグモンの頭脳(ソースコード)に知識を植え付け続け、サーバーのメンバーの名前とプロフィールを教え、発言者の名前も検知するように改良し、つい先日7月18日にリリースされたGPT4o-miniの実装まで完了したドッグモンのソースコードは、もはや元ソースの1.5倍くらいの長さになっておりましたとさ。

もし同じように稼働させたい場合は基本はふぁり研さんの元ソースをベースに、例えば以下のようにするとよいでしょう。

以下のソースがいわゆるTCGnoteでいうところのデッキレシピですね。

const {
  Client,
  GatewayIntentBits,
  Events,
} = require("discord.js");
const { Configuration, OpenAIApi } = require("openai");

require("dotenv").config();

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

const configuration = new Configuration({
  apiKey: process.env.OPENAI_TOKEN,
});

const openAi = new OpenAIApi(configuration);

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

client.on(Events.MessageCreate, async (message) => {
  if (message.author.id === client.user.id || message.author.bot) {
    return;
  }

  const msg = message.content;
  if (msg.includes("botの名前")) {
    const fixed = msg.replace(" ", " ");
    const conv = fixed.indexOf(" ") !== -1 ? fixed.slice(fixed.indexOf(" ") + 1) : fixed;

    message.channel.sendTyping();

    let response;
    let attempts = 0;
    const maxAttempts = 5;

    while (attempts < maxAttempts) {
      try {
        response = await openAi.createChatCompletion({
          model: "gpt-4o-mini",
          messages: [
            {
              role: "user",
              content: `
                今から指示するキャラクターになりきってください

                ※ここにキャラ設定が入ります。
                僕の場合ここにサーバーメンバーの名前とプロフィールをぶち込んで学ばせました。

                そして、以下の会話に返答してください。言葉のみを出力してください。
                ${message.author.username} さんが言いました:「${conv}」`,
            },
          ],
        });
        
        const recivedMsg = response.data.choices[0].message?.content.replace(
          /「|」/g,
          ""
        );
        console.log(`received:${conv}\r\nsent:${recivedMsg}`);
        await message.channel.send(recivedMsg);
        break;
      } catch (error) {
        if (error.response && error.response.status === 429) {
          attempts++;
          const delay = Math.pow(2, attempts) * 1000; // エクスポネンシャルバックオフ
          console.error(`Rate limited. Retrying in ${delay / 1000} seconds...`);
          await sleep(delay);
        } else {
          console.error("Error in OpenAI API call:", error);
          await message.channel.send("エラーが発生しています");
          break;
        }
      }
    }
  }
});


client.login(process.env.DISCORD_TOKEN);
console.log("READY");

そしたら今ではメンバー紹介もお手の物。

若干不名誉なこと言われてるけども。

というわけで、すぐに飽きられちゃうかなと思っていたドッグモンは実装からたったの4日で、2,468応答もすることになりました。
これがどのくらいの応答量かというと、煌めきサーバーは2024年2月26日発足で執筆時点でちょうど5ヶ月経過してます。5ヶ月間の投稿数は33975ポストです。導入以前は1日でおよそ182ポストされてきた計算ですね。
ではbotを導入してからはどうでしょう。7月24日から4日間のポスト数は6,899ポストです。これはつまり1日あたりのポスト数は脅威の1724ポスト。

デイリーポスト数は導入前と比較しておよそ9.5倍。

結果としてドッグモンが参加してから4日間のポスト数だけで、
サーバー全体のこれまでの全ポスト数のおよそ2割を占めてます。

やりすぎだろ!


これに対して、ドッグモンさんのコメントはこちら。

自覚あるんだな

ありがとう。これからもよろしくな、ドッグモン!

今後のドッグモンの動向は、煌めき✨️サーバーの公式Xアカウントを御覧ください!

煌めき✨️サーバー Xアカウント
https://x.com/kiramekiserver

それではまたお会いしましょう。

次回、ドッグモン 長期記憶を実装するの巻(予定)

デュエルスタンバイ!

おしまい。


スペシャルサンクス:煌めき✨️サーバー メンバー各位
ハルジオン:ライブラさん、しきさん

みなさんいつもありがとうございます🙏



ここからは巻末袋とじ付録として、あまり表には載せられないような下ネタ満載のドッグモン珍回答集をお送りします。
特に見てもあなたの人生のプラスには働かないし、なんなら気分を害するかもしれません。いやマジで。

それでもいいという酔狂な方、ドッグモンを応援してくださるという方は、投げ銭をしていただけたら幸いです。
いただいた分はドッグモンの餌代(API費用)の足しにさせていただきます。
※今の段階でまだ200円ちょっとしか使ってない。GPT4o-mini有能すぎ!

ここから先は

289字 / 8画像

¥ 100

期間限定 PayPay支払いすると抽選でお得に!

この記事が気に入ったらサポートをしてみませんか?