見出し画像

Discord bot with chatGPT 作成日記

前置き

おはようございます、えるです❢

最近始めたdiscord bot作成方法を、自分のためにもまとめて見ることにしました 

誰かの参考にもなれば嬉しいです


Discord アカウントを作成

※とりあえず省略

Discord botアカウントを作成

Botアカウント作成 (discordpy.readthedocs.io) の通りに作成します

ーえるさんの日記ー
ー完ー

…とか書くとnoteにならないので、以下は実際に作ってみたやつです❢

事前準備

discordにログインしておきます
自分用のdiscordサーバーも作っておきます


そしてdiscordのApplicationページにいきます


discord Applicationページ

右上の「New Application」をポチッと

作成済みなのはいつも愛でているエリちゃんbot


bot名入力→規約確認→create 

薄く見えているのはいつも愛でているエリちゃん(略

 CreateするとBOTの設定ページに遷移します

general infomationページ

まずこのページに来ますが、そのままBotタブへ移動

Botページ

1) botアイコンにイラストを設定したい場合、ICONで画像をアップロードします
2) Reset Token ボタンを押し、表示されるトークンをコピーしておきます これは後ほど使用します
3) PUBLIC BOTをオフにします これはまあ、お好みで

トークン生成前


トークン生成後はこんな感じ


4) MESSAGE CONTENT INTENTをオンにします
※3つともオンでも可

理由は後述

BOTページでやることは以上です
OAuth2タブに移動します

OAuth2ページ

URL Generatorに行きます


とりあえずメッセージ送受信の権限を追加します


右下のコピーを押して、URLをコピーします
これが、BOTを自サーバーに追加するURLになります


コピーしたURLを適当なWEBブラウザで開きます
自分のサーバーを選択→はい


→認証


→人間です!

追加できました! 

サーバーに追加しただけではBOTが反応出来ないので、適当なチャンネルにBOTを呼び込みます

私の場合は新しくdebugチャンネルを作り、「メンバーまたはロールを追加」からBOTをメンバー追加しました

BOTを追加しただけでは何も出来ないので、次に
BOTを動かすためのコードを書いていきます


Discord bot コード:Hellow 

Google Colabで作ります

1) discord.pyをインストール

!pip install discord.py


2) トークンを入力
トークンはdiscordのbotページで取得したものです

import os
token = "ここにトークンを入力"#@param {type:"string"}
os.environ['TOKEN'] = token


3) 実行
main.pyはテキストで別に作成して、colabにアップロードしておきます

!python3 main.py


main.pyの中身

#main
import discord
import os

#initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

token = os.environ['TOKEN']

@client.event
async def on_ready():
  print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
  if message.author.bot == True:
    return

  else:
    await message.channel.send("hellow!")

client.run(token)


Discordで動作確認

出来ました❣

【注意事項】
その1)
discord botページの 4) MESSAGE CONTENT INTENTをオン しない場合、main.py実行後にエラーで止まります

その2)
main.pyの記述をファイル化せず、Colabのセルに記述を貼り付けて実行するとエラーで止まります

#まだ原因よくわかってないです… Python versionとか?


OpenAI APIを取得

※とりあえず省略

Discord botコード:chatGPT

相変わらず、colabで作ります

1) インストール

!pip install discord.py
!pip install openai


2) DiscordトークンとOpenAIのAPI Keyを入力

import os
token = "ここにdiscordトークンを入力"#@param {type:"string"}
os.environ['TOKEN'] = token

openai_apikey = "ここにOpenAIのAPI Keyを入力"#@param {type:"string"}
os.environ['OPENAI_API'] = openai_apikey


3) 実行
前回同様にmain.pyはテキストで別に作成して、colabにアップロードしておきます

!python3 main.py


今回のmain.py

# main
import discord
import os
import openai

#initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

token = os.environ['TOKEN']
openai.api_key = os.environ['OPENAI_API']

@client.event
async def on_ready():
  print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
  if message.author.bot == True:
    return

  else:
    text = message.content
    response = openai.ChatCompletion.create(
      model="gpt-3.5-turbo",
      messages=[
        {"role": "system", "content": "あなたはAIアシスタントです"},
        {"role": "user", "content":text}
      ]
    )

    await message.channel.send(response['choices'][0]['message']['content'])

client.run(token)

【補足】
”あなたはAIアシスタントです” の部分を変更すると、AIの性格を方向付けできます

Discordで動作確認


動きました❣


Discord botコード:短期記憶

chatGPT APIはweb版とは異なり、そのままだと会話内容を覚えていません

悲しい回答

覚えてほしいので、短期的な記憶を実装していきます
手段はきっと色々あるのですが、とりあえずlangchainさんに頑張ってもらいます

Welcome to LangChain — 🦜🔗 LangChain 0.0.152

そして今日もcolabで作ります

1) インストール

!pip install discord.py
!pip install openai
!pip install langchain
!pip install tiktoken


2) DiscordトークンとOpenAIのAPI Keyを入力

import os
token = "ここにdiscordトークンを入力"#@param {type:"string"}
os.environ['TOKEN'] = token

openai_apikey = "ここにOpenAIのAPI Keyを入力"#@param {type:"string"}
os.environ['OPENAI_API_KEY'] = openai_apikey


3) 実行
今までと同様に、main.pyは別に作成してアップロードします

!python3 main.py

main.pyの中身は以下の通り
使用するmemory.txtは空ファイルで良いのでアップロードしておきます

# main

#import
import discord
import os
import openai

#langchain import
from langchain.prompts import (
  ChatPromptTemplate, 
  MessagesPlaceholder, 
  SystemMessagePromptTemplate, 
  HumanMessagePromptTemplate
)
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.chains.conversation.memory import ConversationSummaryBufferMemory

#Discord initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

#token and API seting
token = os.environ['TOKEN']
openai.api_key = os.environ['OPENAI_API_KEY']

#langchain initial
system_settings = """あなたはAIアシスタントです"""
prompt = ChatPromptTemplate.from_messages([
  SystemMessagePromptTemplate.from_template(system_settings),
  MessagesPlaceholder(variable_name="history"),
  HumanMessagePromptTemplate.from_template("{input}")
])

use_model = "gpt-3.5-turbo"
S_conversation = ConversationChain(
  memory=ConversationSummaryBufferMemory(
    return_messages=True,
    llm=ChatOpenAI(model_name=use_model),
    max_token_limit=500
  ),
  prompt=prompt,
  llm=ChatOpenAI(model_name=use_model),
  verbose=True
)

# buffer load
f = open('memory.txt', 'r')
memory_text = f.read()
f.close()
S_conversation.predict(input=memory_text) 

#Discord event
@client.event
async def on_ready():
  print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
  if message.author.bot == True:
    return

  else:
    text = message.content
    S_text = S_conversation.predict(input=text) 
    await message.channel.send(S_text)

    S_memory_text = S_conversation.memory.load_memory_variables({})
    Sf = open('memory.txt', 'w')
    Sf.write(str(S_memory_text))
    Sf.close()

client.run(token)

だんだん長くなってきました。。

Discordで動作確認
先ほどと同じやりとりをしてみます

覚えてくれました❣

空ファイルで作成したmemory.txtですが、会話後は会話内容が記載されます

Bot立ち上げ時も最初に前回の会話内容をinputしにいくため、会話内容をある程度保持しておくことができます

Colabにアップロードしたファイルは削除されてしまいますが、GoogleDriveをマウントしそちらに保存することで解決できるかと思います

【注意事項】
記憶のためのデータのやり取りはもっと効率の良いやり方があるはずですが、現状の方法でもある程度可能なためひとまず良いかと思っています。

Discord botコード:Streaming

chatGPT API はデフォルトだとメッセージを全文作成後に応答します
一方でchatGPT のweb版はストリーミングテキストなので、初動レスポンスが速いです
上記に憧れたのでStreaming機能を試してみました

結果
↓ 

これはひどい

これは求めていたものと違う…
と思ったのでいろいろ調べてみましたが、discordはメッセージ送信時に改行が入ってしまうので、web版風は今のところ出来ていません。。

とは言え、なんとかならないかなーと頑張ってみました
以下が現状実装です まだ試行錯誤中なのでcolabでは無いです

import asyncio
import json
import aiohttp
import os
import discord

bot_token = os.environ['KEMI_BOT_KEY']
openai_key = os.environ['GPT_KEY']


url = "https://api.openai.com/v1/chat/completions"

### discord initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

message_chunks = []

@client.event
async def on_ready():
    print(f'{client.user.name} has connected to Discord!')

@client.event
async def on_message(message):
    print(message.content)
    if message.author == client.user:
        return

    async with aiohttp.ClientSession() as session:
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {openai_key}",
        }
        data = {
            "model": "gpt-3.5-turbo",
            "messages": [
                {"role": "system","content": "あなたは優秀なアシスタントです。質問内容に対して可能な限り簡潔に回答してください",},
                {"role": "user","content": message.content,},
            ],
            "stream": True,
        }
        async with session.post(url, headers=headers, data=json.dumps(data)) as resp:
            async for line in resp.content:
                if line.strip() == b'data: [DONE]':
                    print("Stream finished")
                    response_message = "".join(message_chunks)
                    if response_message.strip():
                        await message.channel.send(response_message)
                    message_chunks.clear()
                    break
                else:
                    try:
                        response = json.loads(line[6:])  # skip the 'data: ' part of the SSE
                        content = response["choices"][0]["delta"].get("content", "")
                        if content:
                            message_chunks.append(content)
                            if "\n" in content or "。" in content:
                                response_message = "".join(message_chunks)
                                await message.channel.send(response_message)
                                message_chunks.clear()
                        #if content:
                        #    await message.channel.send(content)
                    except json.JSONDecodeError as e:
                        print(f"Failed to parse JSON: {line[6:]}"+"content:"+content)

client.run(bot_token)

できました❣
文章途中で送信(=改行)すると変なところで改行されてしまうので、
改行コードや句点で送信するようにしています
全文待ちよりは体感かなり良い感じになったかと、個人的には満足しています

でもほんとはweb版のようなStreamingをdiscordでもしたい。。
何か方法はあるのかなあ

ーーー
ひとまずはここまでです❢ 以降は考え中の内容も含むため、時間を要する気がします。

省略箇所も記載要望があれば追記を考えたいと思います


Discord botコード:長期記憶

※実装方法考え中

Discord botコード:ボイス

※作成中


いいなと思ったら応援しよう!