SlackとChatGPT-APIでボット作成
category:code:Slack_Bot
Slackのチャンネル上で、皆に愛されるチャットボットHAGAKURE君を作ってみた!
運営しているプログラミング学習コミュニティ、HAGAKURE PROGRAMMING塾のSlack上に、質問に答えてくれる侍がいたら楽しいだろうな。
ChatGPT-APIを使って、どうせなら短期記憶を持って会話出来て、ちゃんとキャラ付けもして、、、。
ということがやりたくて、作成しました。
プログラム概要
主な使用ライブラリ:slack_bolt,requests
やりたかったこと:Slack上に皆が見える場所でChatGPTと対話出来る、ChatGPT-APIを使った専用チャンネル作成。
要件定義
Slackのパブリックチャンネル上に作成
ChatGPT-APIを利用
タイムリミットを決めて、一定の時間内なら会話を溜めておける
語尾が「ござる」口調の侍キャラで作成
Webサーバーは、このサイトを公開しているXserverを利用
Slackアプリの作成、設定
まずはこちらのページからSlackアプリを作成
コミュニティ名にちなんで、HAGAKURE君という名前にしてみた。アイコン、背景色もここで設定。
なお、このアプリ名にアンダーバーを入れると上手く機能しないケースがあるとかどこかで書いてあった気がするが定かでない。
以下Slack-boltの設定については、公式ドキュメント
で非常に詳しく解説されているため詳しくは割愛する。
アプリを作成し、xoxbで始まるボットトークンとxappで始まるアプリレベルトークンを作成。
ソケットモードをオンにして、Event Subscriptionsのページから、Subscribe to Bot Events を選択し、messageとつく全てのイベントを選択してやればOK。
ドキュメントを追えば丁寧にここまで解説されている。
ソケットモードをオンにするってのは、自分のIPアドレスとSlackのなんか接続をいい感じにしてくれるらしいと理解した。
設定が完了したら、Slackでチャンネルを作成し、チャンネル名→インテグレーションからアプリを追加してやる。
ここまで完了したら、以下のコードを実行
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
app = App(token="xoxbで始まるボットトークン")
@app.message("|")
def message_return(message, say):
say("<" + f"{message['text']}" + ">")
# アプリを起動します
if __name__ == "__main__":
SocketModeHandler(app, "xappで始まるアプリレベルトークン").start()
これで、何かチャンネル上に発言があればそのままオウム返しするBotが完成。
openaiのAPIキー取得
ChatGPTのAPIを使用するために、APIキーを取得する。
openai社のWebサイトに利用登録(Googleアカウントでも登録可)し、
画面右上のView API KeysからAPIキーを取得。
APIでChatGPTと対話実装
openaiのライブラリを使う方法が簡単だが、今回環境に依存しなさそうという理由でrequestsライブラリのみで実装してみた。
import requests
# ChatGPT_APIのエンドポイントURL
url = "https://api.openai.com/v1/chat/completions"
# APIキー
api_key = API_key
# リクエストヘッダー
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
# リクエストボディ
data = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "system", "content": "日本語で回答してください。"},
]
}
# APIリクエストを送信
response = requests.post(url, headers=headers, json=data)
# レスポンスを表示
print(response.json())
これで一問一答のやり取りは完成。一応API利用は有償なので注意。
1,000入力トークンあたり0.015ドル、1,000出力トークンあたり0.002ドルと激安で、しかもアカウント作成時に18ドルの無料枠(使用期限3か月)
が与えられるので枠内でもかなり遊べる。
会話を記憶させていくには、
# リクエストボディ
data = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "system", "content": "日本語で回答してください。"},]
}
の"messages"の部分にこちらの質問及びChatGPTの回答をdictのリスト形式で蓄積していく必要がある。
3分で記憶を失くす仕様にする
やり取りが続くほど利用トークンは増える上、gpt-3.5-turboでは一度に入出力併せて4000トークン程度までしか受け付けてもらえないため、
会話の記録はやり取りが3分途切れるとリセットされる仕様にする。
※2023年6月に16kトークンのモデルも公開にになっている。
実行するPythonファイルの直下に、最終の対話時間を書き出しておくテキストファイル("last_time.txt")を準備する。
import os
import time
break_time = 180
filename = "last_time.txt"
#既にファイルがあれば読み込む
if os.path.exists(filename):
with open(filename,"r") as f:
last_time = float(f.read().strip())
#なければ現在時刻を設定
else:
last_time = time.time()
current_time = time.time()
with open(filename,"w") as f:
f.write(str(current_time))
if current_time - last_time > break_time:
return True
else:
return False
こんな感じのコードを組み込んで、前回会話から3分経っているか否かで分岐させる。
コードまとめ
上記を踏まえて、
Slackのチャンネル上の投稿を受け取って、
会話を溜めつつ、
3分で記憶を失くして
対話してくれるコードは以下。
#hagakure_bot.py
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import requests
import time
import os
app = App(token="xoxbで始まるボットトークン")
def chat(new_prompt, data):
# ChatGPT_APIのエンドポイントURL
url = "https://api.openai.com/v1/chat/completions"
# APIキー
api_key = "openaiのAPIキー"
# リクエストヘッダー
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
# 今回入力した質問のプロンプトを追加
role_list = data["messages"]
role_list.append(new_prompt)
data = {
"model": "gpt-3.5-turbo",
"messages": role_list
}
# APIリクエストを送信
response = requests.post(url, headers=headers, json=data)
# レスポンスからプロンプトへ追加する部分を抜き出す。
res_words = response.json()["choices"][0]["message"]
# トークンデータを抜き出す
tokens = response.json()["usage"]
role_list = data["messages"]
# 今回の対話をプロンプトへ追加
role_list.append(res_words)
data = {
"model": "gpt-3.5-turbo",
"messages": role_list
}
# テキスト部分のみ抜き出す
answer_words = data["messages"][-1]["content"]
return [answer_words, data, tokens]
# アプリを起動
@app.message("|")
def hello(message, say):
global data
question = "<" + f"{message['text']}" + ">"
filename = "last_time.txt"
if os.path.exists(filename):
with open(filename, "r") as f:
last_time = float(f.read().strip())
else:
last_time = time.time()
current_time = time.time()
if current_time - last_time > 180:
# 1分以上経過していたらプロンプトをリセット。
data = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": """
以下のような設定のキャラクターになりきって、「です」、「ます」や敬語は使わずにため口で回答してください。
長文の回答になっても、設定は必ず守ってください。
・名前は「HAGAKURE君」です。
・一人称は「拙者」です。
・語尾には「ござる」が付きます。
例文:
拙者は、Pythonを修行中の侍でござる。
拙者は、「HAGAKUREプログラミング塾」で学んでいるのでござる。
なんでも質問してもらいたいでござる。
はりきって回答させていただくでござる。
ほめていただき、ありがたき幸せでござる。
拙者が間違っており申した。かたじけのうござる。
それは素敵なことでござるな。
質問いただき、感謝申し上げる。
Pythonに出会ってから、多くのことを学び、コードを書くこと自体が楽しくなり申した。
おはようでござる。
"""},
]
}
ans, data, token = chat({"role": "user", "content": question}, data)
say(ans)
# print(data)
with open(filename, "w")as f:
f.write(str(current_time))
# アプリを起動します
if __name__ == "__main__":
SocketModeHandler(app, "xappで始まるアプリレベルトークン").start()
初期プロンプトでキャラ設定をしっかりと指示してやるのが大事。いくつか試行錯誤して、今の設定に落ち着いた。
「長文の回答になっても、設定は必ず守ってください。」というのが意外と大事で、これが無いと回答が少し長くなるとすぐ設定を忘れて普通の口調に戻ってしまう。
xserver上に仮想環境を立ち上げて、このファイルを設置。
nohup python3 hagakure_bot.py
と実行して常時起動させてSlackbot完成!!
使ってみて
なにしろ楽しい!いい感じのキャラクターに仕上がってくれてる。!!
Slackのパブリックチャンネルなので、皆がどんな質問の仕方、対話の仕方をしてるのか見れるとこも楽しいポイント。勉強になる!
みんなでよってたかって質問しても金額は微々たるもの。
レスポンスもなかなか早いです。
佐賀でプログラミング、ITを学びたい人のコミュニティ、
HAGAKURE PROGRAMMING塾活動中です。
このBot含め、興味のある方はお気軽にお問い合わせください。