備忘
# %%
import discord
import openai
import os
import tiktoken
import logging
def num_tokens_from_messages(messages, model="gpt-4-0314"):
"""Returns the number of tokens used by a list of messages.
from https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
"""
try:
encoding = tiktoken.encoding_for_model(model)
except KeyError:
print("Warning: model not found. Using cl100k_base encoding.")
encoding = tiktoken.get_encoding("cl100k_base")
if model == "gpt-3.5-turbo":
print("Warning: gpt-3.5-turbo may change over time. Returning num tokens assuming gpt-3.5-turbo-0301.")
return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0301")
elif model == "gpt-4":
print(
"Warning: gpt-4 may change over time. Returning num tokens assuming gpt-4-0314.")
return num_tokens_from_messages(messages, model="gpt-4-0314")
elif model == "gpt-3.5-turbo-0301":
# every message follows <|start|>{role/name}\n{content}<|end|>\n
tokens_per_message = 4
tokens_per_name = -1 # if there's a name, the role is omitted
elif model == "gpt-4-0314":
tokens_per_message = 3
tokens_per_name = 1
else:
raise NotImplementedError(
f"""num_tokens_from_messages() is not implemented for model {model}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.""")
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message
for key, value in message.items():
num_tokens += len(encoding.encode(value))
if key == "name":
num_tokens += tokens_per_name
num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
return num_tokens
def cgpt(message_content, message_history):
# 環境変数 OPENAI_API_KEY を取得して、 openai.api_key にセットする
openai.api_key = os.environ["OPENAI_API_KEY"]
# その他、パラメータを設定する
MODEL_NAME = 'gpt-4'
MAX_TOKENS = 1200 # 1回のリクエストで返ってくるトークン数の上限
model_token_limit = {
'gpt-4': 8192,
'gpt-3.5-turbo': 4096,
}
# 入力を message_history に user として追加する
message_history.append({"role": "user", "content": message_content})
# message_history の token 数を数える
num_tokens = num_tokens_from_messages(message_history)
print(f"num_tokens before: {num_tokens}")
# ここで num_tokens + max_tokens が model_token_limit[model_name] を超える場合は、
# num_tokens が model_token_limit[model_name] を下回るまで、 message_history の先頭を削除する
while num_tokens + MAX_TOKENS > model_token_limit[MODEL_NAME]:
print(f"trimming message_history: {message_history[0]}")
message_history = message_history[1:]
num_tokens = num_tokens_from_messages(message_history)
print(f"num_tokens after trimming: {num_tokens}")
# OpenAI GPT に入力を渡す
r = openai.ChatCompletion.create(
model=MODEL_NAME,
messages=message_history,
temperature=0.9,
max_tokens=MAX_TOKENS,
top_p=1,
frequency_penalty=0.5,
presence_penalty=0.5,
)
# 返答を表示する
res_text = r.choices[0].message.content
# 返答をメッセージに追加する
message_history.append({"role": "assistant", "content": res_text})
# message_history の token 数を数える
num_tokens = num_tokens_from_messages(message_history)
print(f"num_tokens after: {num_tokens}")
return res_text, message_history
class ChatGPTDiscord():
def __init__(self) -> None:
self.message_history = []
def chatevent(self):
# 環境変数 DISCORD_BOT_SIMPLE を取得して TOKEN にセットする
TOKEN = os.environ["DISCORD_BOT_SIMPLE"]
# インテントの生成
intents = discord.Intents.default()
intents.message_content = True
# クライアントの生成
client = discord.Client(intents=intents)
# discordと接続した時に呼ばれる
@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 == client.user:
return
# メッセージを返す
res_text, message_history = cgpt(
message.content, self.message_history)
self.message_history = message_history
await message.channel.send(res_text)
# クライアントの実行
client.run(token=TOKEN, log_level=logging.ERROR)
c = ChatGPTDiscord()
c.chatevent()
point
- できるだけ長く文脈を保つように、 token の数をちゃんと計算するようにする
- system のところに「あなたは優秀な助手です」みたいなことを入れてたけど、 gpt-4 がすでにその使命を帯びていて意味ないのでやめた。