본문 바로가기

개발일지/디스코드 봇

디스코드 봇 개발 일지 2023-03-03 - OpenAI 업데이트 및 모델(GPT3.5-turbo) 교체

 

 

※ 작성자가 작성한 내용이 일부 틀릴 수도 있음 주의

 

※ 작성자가 코드 쓰다가 계속 코드 수정함 주의

 

 

 

OpenAI API 가 업데이트가 되었다.

 

 

그리고 아예 채팅 특화 모델인 gpt-3.5-turbo 가 생겼고,

 

기존에 쓰던 Davinci 모델은 1K 당 $0.02 다.

 

가격도 기존 davinci 모델에 비해 0.1배의 가격인 1k token 당 $0.002 로 책정되었다.

 

openai 자체는 버전이 대략 2.6.5 에서 2.7 버전으로 바뀐 것 같다.

 

일단은 OpenAI 파이썬 패키지를 업데이트 하자.

 

 

pip install openai --upgrade

 

그냥 평범하게 cmd 켜서 이걸 쳐주자.

 

2.7.0 버전인가 그게 설치되었다면 된 것이다.

 

 

 

 

 

(23.03.11 추가) GPT-3.5-turbo 로 바꾸기만 하면

 

모델만 바꾸면 아래의 에러 메시지가 뜬다.

 

혹시나 에러 메시지를 검색하게 될 사람들을 위해서 추가했다.

 

Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 229, in wrapped
    ret = await coro(*args, **kwargs)
  File "C:\Users\USER\Desktop\discordBot\ChatBot.py", line 81, in 챗
    completion = openai.Completion.create(
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\openai\api_resources\completion.py", line 25, in create
    return super().create(*args, **kwargs)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\openai\api_resources\abstract\engine_api_resource.py", line 153, in create
    response, _, api_key = requestor.request(
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\openai\api_requestor.py", line 226, in request
    resp, got_stream = self._interpret_response(result, stream)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\openai\api_requestor.py", line 619, in _interpret_response
    self._interpret_response_line(
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\openai\api_requestor.py", line 679, in _interpret_response_line
    raise self.handle_error_response(
openai.error.InvalidRequestError: This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 1349, in invoke
    await ctx.command.invoke(ctx)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 1023, in invoke
    await injected(*ctx.args, **ctx.kwargs)  # type: ignore
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 238, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: InvalidRequestError: This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?

 

그래서 아래와 같은 과정으로 바꾸어주면 된다.

 

 

 

 

 

답변을 받는 구조도 약간 바뀌었다.

 

 

기존에는 "prompt = String" 을 통해 채팅 내용을 넣어주었다.

 

하지만 gpt-3.5-turbo 는 "messages = List(Dict('role':(String), 'content':(String)))" 형태로 내용을 넣어줘야 한다.

 

기존의 davinci 모델이나 다른 모델의 경우 바꿔주지 않아도 되지만, 

 

더 싸고 특화한 놈이 있는데 바꾸는게 당연히 이득이다.

 

 

리스트에 들어가는 딕셔너리는 두 개의 key-value 원소를 가지며,

 

'role' 에는 역할인데, 'system', 'user', 'assistant' 로 나뉘며,

 

'system' 은 대화 상황, 'user' 는 사용자의 입력, 'assistant' 는 그에 따른 AI 의 답변을 의미한다.

 

'content' 에 해당하는 문장을 넣어주면 된다. 이를테면,

 

 

[{'role':'system', 'content':'너는 지금 능력은 개쩌는 부관이다'},
{'role':'user', 'content':'부관 밥줘'},
{'role':'assistant', 'content':'지금 저희 물자 상황 모르십니까? 아주 바닥이 났습니다!'},
{'role':'user', 'content':'그럼 빨리 일해서 밥 구해와 알겠어?'}]

 

정말 대충 써제낀 상황이지만 이런 식으로 넣는다고 생각하면 된다.

 

 

 

 

 

코드를 적당히 수정해주자.

 

 

기존 챗봇 코드를 보기 위해서는 아래의 글을 참고하자.

 

https://syerco0.com/33

 

디스코드 봇 개발 일지 2023-02-17 - OpenAI / ChatGPT / GPT-3 로 챗봇 만들기

※ 작성자가 작성한 내용이 일부 틀릴 수도 있음 주의 ※ 작성자가 코드 쓰다가 계속 코드 수정함 주의 어떻게 하면 최대한 적은 token을 소모할까? 확실한 거는 간단한 질문 답변 형태가 가장 적

syerco0.com

 

 

기존에 prompt 에 넣던 문자열을 기반으로 (위 글 참고)

 

dictionary 형태로 바꾸고, 이를 모아서 List 로 만든다음 messages 에 쏴줄 것이다.

 

전체 코드는 굳이 안 넣고, 크게 바뀐 것만 여기에 올릴 것이라서

 

여기 있는 것만 수정하면 안 돌아갈 수도 있으니 참고하길 바란다.

 

 

 

 

Prompt 불러오기 / 저장하기 (23.07.11 Python 3.6 이상을 위한 수정)

 

def LoadPrompts():
    global prompts

    f = open("ChatPrompt.txt", "r")
        
    rawStr = f.read()
    totalConv = rawStr.split("\n")
    prompts = []
        
    for conv in totalConv:
        pair = conv.split(": ")
            
        keys = []
        values = []
            
        keys.append("role")
        values.append(pair[0])
        keys.append("content")
        values.append(pair[1])
            
        prompts.append(dict(zip(keys, values)))
            
    f.close()
    
@bot.command()
async def 프롬프트저장(message):

    global prompts

    if chatType == "LearningChat":
        f = open("ChatPrompt.txt", "w")
        for prompt in prompts:
            f.write(f'{prompt["role"]}: {prompt["content"]}\n')
        f.close()
        await message.channel.send("Prompt를 저장 했습니다")
    else:
        await message.channel.send("LearningChat 타입일 때만 Prompt를 저장 가능합니다")

 

사실상 여기 두 개 바뀐 걸로 얼추 설명을 끝낼 수 있다.

 

기존에 문자열로 넣어주던 것을 형식에 맞는 Dictionary로 바꾸어 List 에 저장을 하거나

 

List 에 있는 원소들을 반대로 텍스트 파일에 문자열의 형태로 저장해주는 코드다.

 

타입 변경하면서 프롬프트를 불러오는 등의 과정을 수행하는 부분에서

 

이 수정한 코드들을 잘 반영해주면 된다.

 

 

 

앞서도 말했지만 이거만 수정한다고 다 되는 것이 아니다.

 

데이터 형식이 바뀌었기 때문에 전체적으로 고쳐야할 부분이 몇 개 더 있을 것이다.

 

 

 

 

 

답변을 얻는 방식의 변화

 

    completion = openai.ChatCompletion.create(
        model='gpt-3.5-turbo',
        messages=prompts,
        temperature=0.9,
        max_tokens=500,
        top_p=1,
        frequency_penalty=0.0,
        presence_penalty=0.6,
    )

 

 

기존 코드의 "챗" 함수에 들어있던, AI에게 대화 내용을 주고 답변을 얻는 부분이다.

 

openai.Completion에서 openai.ChatCompletion 으로 바뀌었고,

 

model 또한 gpt-3.5-turbo 로 바꾸어주어야 한다.

 

변수 이름 'prompt' 도 'messages' 로 바꿔주자.

 

그리고 기존에 뭐였지 "Human: " 이나 "AI: " 를 넣어줬던 변수는 messages 의 형식에 따라 필요가 없기 때문에

 

삭제를 해준다.

 

 

 

그렇게 나온 completion 은 다음과 같다. (openai API document 에서 발췌)

 

{
 'id': 'chatcmpl-6p9XYPYSTTRi0xEviKjjilqrWU2Ve',
 'object': 'chat.completion',
 'created': 1677649420,
 'model': 'gpt-3.5-turbo',
 'usage': {'prompt_tokens': 56, 'completion_tokens': 31, 'total_tokens': 87},
 'choices': [
   {
    'message': {
      'role': 'assistant',
      'content': 'The 2020 World Series was played in Arlington, Texas at the Globe Life Field, which was the new home stadium for the Texas Rangers.'},
    'finish_reason': 'stop',
    'index': 0
   }
  ]
}

 

여기에서 답변만 얻기 위해서는 다음과 같이 해주면 된다.

 

    response = completion['choices'][0]['message']['content']

 

dictionary 안에 list 안에 dictionary 안에 dictionary 안에 있는 string 을 꺼내주면 된다.

 

참으로 엄청난 데이터 형식이라고 생각한다.

 

 

 

 

messages 형식이 List 이기 때문에

 

기존의 문자열의 형태로 넣던 방식과는 달리,

 

굳이 prompt 를 지속적으로 추가하는 타입이 아니라면,

 

리스트의 맨 마지막 원소를 없애주는 것으로 간단하게 해결이 가능하다.

 

덕분에 코드 길이가 저번보다 짧아졌을 것이다.

 

 

 

 

 

나머지

 

로그 저장/불러오기 라던가 "챗" 함수의 수정부분은 여기에 안 올렸다.

 

위의 코드를 참고하여 알아서 적당히 수정하기를 바란다.

 

일일이 코드를 긁어오는 것이 편하지만, 코드의 구성에 대한 이해가 있어야

 

개인적으로 업데이트를 하는 데에 있어서 불편함이 없기 때문이다.

 

 

 

 

(23.03.22 추가) 전체 코드를 원하는 사람이 있어서

 

https://github.com/SYSTEMERRORCODE0/Opened-Source-for-My-Blog

 

GitHub - SYSTEMERRORCODE0/Opened-Source-for-My-Blog: Personal discord bot for just a interest

Personal discord bot for just a interest. Contribute to SYSTEMERRORCODE0/Opened-Source-for-My-Blog development by creating an account on GitHub.

github.com

 

앞으로 블로그에서 공개할(제가 공개하고 싶은) 전체 코드같은 경우에는

 

웬만해서는 깃허브에 올리는 것으로 하겠습니다.

 

하다보면 여기에 올리기에 너무 길어지는 경우가 있어서 그렇게 하는 것이 좋을 것이라 판단했습니다.

 

디코 봇 코드를 비롯해서 다른 것을 진행할 때도 깃허브를 이용하는 것으로 하겠습니다.

 

참고로 이 글의 챗봇 코드는 ChatBot3.5_public.py 에 있습니다.

 

 

 

 

 

여담

 

4월 1일 까지 남은 17달러만큼 챗봇 가지고 놀아야겠다.

 

OpenAI 시여 쫌만 더 주세용

 

힝잉잉

728x90