이야기박스

Python. ChatGPT API를 활용한 간단한 예제 프로그램 본문

Computer & Data/Artificial Intelligence

Python. ChatGPT API를 활용한 간단한 예제 프로그램

박스님 2023. 4. 22. 18:35
반응형

 

들어가기에 앞서

오랜만에 글을 작성하게 되었습니다.

한발 늦은 감이 있지만, 얼마 전까지 ChatGPT로 한참 시끄러웠었죠.

특히 GTP4는 업무에 도움이 될 수준으로 답변을 주곤 했습니다. 

 

그래서 오늘은 openAI에서 제공하는 API로 재미 삼아 예제 프로그램을 만들어보고자 합니다.

 

오늘 작성된 코드는 Git repo에서도 확인할 수 있습니다.

 

API Key 획득

openAI에 회원 가입을 하여 API key 획득을 해야 합니다. 아쉽게도 2023년 4월 기준으로 gpt3.5 까지만 API로 지원을 하고 있습니다. gpt4 API는 별도 구매가 필요한 것 같아요. (https://openai.com/pricing)

OpenAI 사이트에서 개인 정보 수정에 들어가면, 이런식으로 API Key 생성해 두고 사용이 가능합니다. 

또한 아쉽게도.. 무료 버전에서는 분당 요청 건수 제한이 있습니다. 잘 조절해서 사용하여야 해요.

 

 

API Spec

오늘의 예제 코드는 모두 gpt-3.5-turbo 모델을 사용하였습니다. 자세한 내용은 공식 문서를 참고해주시면 좋을 것 같아요.

간단하게 주요 모델만 살펴보고 가겠습니다. (아래는 모두 제 나름의 이해를 바탕으로 작성한 것이라, 잘못된 내용이 있을 수도 있습니다.)

  • model: GPT-4, GPT-3.5, DALL·E 등.. 여러 모델들이 있는데요. GPT-3.5 가 무료 버전에서는 비교적 자유롭게 사용할 수 있어서 해당 모델을 사용하였습니다.
  • max_tokens: 자연어 분석에서 Token이라 함은 우리 문장을 컴퓨터가 분석 가능한 형태소로 분해해 둔 것으로 보면 될 것 같습니다. 여기서 API가 최대로 구분할 토큰의 개수를 지정할 수 있습니다.
  • n: 몇 개의 답변을 줄지 결정하는 모델입니다. 만약 n=2를 설정하였다면, 하나의 질문에 두 개의 답변을 주게 됩니다.
  • temperature: 매번 정해진 답변만 준다면 창의적인 답변을 주기 어렵겠죠. genetic algorithm에서 주는 변수와 같은 느낌입니다. 0 ~ 2 사이의 값을 주면 되는 것으로 보입니다.
  • top_p: temperature와 같이 답변에 변화를 줄 수 있는 옵션인데요. temperature가 전혀 새로운 답을 만들어 낼 수 있는 값이라면, top_p는 확률 분포를 기반으로 해당 범위를 넘어가는 답변을 샘플링하는 것으로 이해하였습니다.

 

저는 오늘 포스팅에서 gpt-3.5-turbo 모델을 사용하였는데요. 여기서 전달되는 messages에는 Role이라는 개념이 있습니다. 각 Role은 어떠한 주체로부터 메시지가 생성되었는지에 따라 나뉘게 됩니다.

  • system: chatGPT에게 역할을 줄 때 사용할 수 있습니다. 이 system 설정에 따라 메시지가 달라지게 됩니다.
  • user: 저희가 gpt에게 문의할 때 사용합니다. 저희가 사이트에서 채팅창에 입력하는 부분이라고 보면 됩니다.
  • assistant: GPT의 답변에 부여되는 역할입니다. 

그럼 이제 예제로 넘어가보도록 하겠습니다!!

 

단순 질문 예제

API Key를 받았으면, 가볍게 먼저 요청을 보내봅시다. 

from pathlib import Path

import openai
import yaml

if __name__ == '__main__':
    """
    The example with `gpt-3.5-turbo` model.
    """
    base_directory = Path(__file__).parent.parent

    with open(f"{base_directory}/openai.yaml", "r") as openai_yaml:
        config = yaml.load(openai_yaml, Loader=yaml.FullLoader)

    openai.api_key = config['OPENAI']['API_KEY']

    model_engine = "gpt-3.5-turbo"
    prompt = "this is test. what is the token? how many did you recognize it?"

    response = openai.ChatCompletion.create(
        model=model_engine,
        messages=[
            {"role": "system", "content": "You are my home manager."},
            {"role": "user", "content": "Can you recommend me the menu for dinner?"},
        ]
    )

    result = ''
    for choice in response.choices:
        result += choice.message.content

    print(result)
Sure, what kind of cuisine do you prefer?

 

연쇄 질문

chatGPT 사이트에선 대화를 하며 원하는 답을 이끌어내죠. 그렇게 하기 위해서는 ChatGPT가 지난 질문과 답변을 모두 기억하고 있어야 합니다. 이는 messages 항목을 통해 제출을 할 수 있는데요.

import json
from pathlib import Path

import openai
import yaml


def send_message_to_chatgpt(prev_messages, new_question):
    prev_messages.append({"role": "user", "content": new_question})
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=prev_messages,
        max_tokens=150,
        n=1,
        stop=None,
        temperature=0.8,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )

    assistant_message = response.choices[0].message

    answer = {'Question': new_question, 'Answer': assistant_message.content}
    print(json.dumps(answer, indent=4))
    prev_messages.append(assistant_message)
    return prev_messages


if __name__ == '__main__':
    """
    The example for chaining questions.
    """
    base_directory = Path(__file__).parent.parent

    with open(f"{base_directory}/openai.yaml", "r") as openai_yaml:
        config = yaml.load(openai_yaml, Loader=yaml.FullLoader)

    openai.api_key = config['OPENAI']['API_KEY']

    messages = [
        {"role": "system", "content": "You are a helpful assistant."}
    ]

    question1 = "What is the capital of France?"
    chain_messages = send_message_to_chatgpt(messages, question1)

    question2 = "What is the population of Paris?"
    chain_messages = send_message_to_chatgpt(chain_messages, question2)

    question3 = "What I asked you before? Tell me my all this conversation's questions and your answer again."
    chain_messages = send_message_to_chatgpt(chain_messages, question3)
{
    "Question": "What is the capital of France?",
    "Answer": "The capital of France is Paris."
}
{
    "Question": "What is the population of Paris?",
    "Answer": "As of 2021, the population of Paris is approximately 2.2 million people."
}
{
    "Question": "What I asked you before? Tell me my all this conversation's questions and your answer again.",
    "Answer": "Sure, here is a summary of our conversation:\n\n- Q: What is the capital of France?\n- A: The capital of France is Paris.\n\n- Q: What is the population of Paris?\n- A: As of 2021, the population of Paris is approximately 2.2 million people.\n\n- Q: What did I ask you before? Tell me my all this conversation's questions and your answer again.\n- A: Sure, here is a summary of our conversation."
}

 

연쇄 질문 - 채팅방 통합

여기서 더 나아가서 여러 개의 대화를 모두 기억하는 chatGPT AI를 구성해 보도록 하겠습니다.

간단하게 그동안의 대화를 하나의 messages로 합쳐줄 수도 있고, 다른 채팅의 messages를 문자열 형태로 변환하여 제공하여도 인식을 하였습니다.

 

아래는 문자열 형태로 변환하여 제공한 예제입니다.

import json
import time
from pathlib import Path

import openai
import yaml


def send_message_to_chatgpt(prev_messages, new_question):
    time.sleep(20)

    prev_messages.append({"role": "user", "content": new_question})
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=prev_messages,
        max_tokens=150,
        n=1,
        stop=None,
        temperature=0.8,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )

    assistant_message = response.choices[0].message

    answer = {'Question': new_question, 'Answer': assistant_message.content}
    print(json.dumps(answer, indent=4))
    prev_messages.append(assistant_message)
    return prev_messages


if __name__ == '__main__':
    """
    The example for chaining questions.
    """
    base_directory = Path(__file__).parent.parent

    with open(f"{base_directory}/openai.yaml", "r") as openai_yaml:
        config = yaml.load(openai_yaml, Loader=yaml.FullLoader)

    openai.api_key = config['OPENAI']['API_KEY']

    # first messages
    messages_1 = [
        {"role": "system", "content": "You are a helpful assistant what name is 'story'"}
    ]

    question1 = "What is the capital of France?"
    chain_messages = send_message_to_chatgpt(messages_1, question1)

    question2 = "What is the population of Paris?"
    chain_messages = send_message_to_chatgpt(chain_messages, question2)

    question3 = "What I asked you before? Tell me my all this conversation's questions and your answer again."
    chain_messages = send_message_to_chatgpt(chain_messages, question3)

    # second messages
    messages_2 = [
        {"role": "system", "content": f"You are a helpful assistant trained by ({json.dumps(chain_messages)})"}
    ]

    question2_1 = "Did you remember what I asked you? Please tell our conversation again."
    chain_messages = send_message_to_chatgpt(messages_2, question2_1)

    question2_2 = "Who are you? What is your name what I give you."
    chain_messages = send_message_to_chatgpt(chain_messages, question2_2)
{
    "Question": "What is the capital of France?",
    "Answer": "The capital of France is Paris."
}
{
    "Question": "What is the population of Paris?",
    "Answer": "As of 2021, the population of Paris is estimated to be approximately 2.1 million people."
}
{
    "Question": "What I asked you before? Tell me my all this conversation's questions and your answer again.",
    "Answer": "Sure! Here is a summary of our conversation so far:\n\n- Question: What is the capital of France?\n- Answer: The capital of France is Paris.\n\n- Question: What is the population of Paris?\n- Answer: As of 2021, the population of Paris is estimated to be approximately 2.1 million people."
}
{
    "Question": "Did you remember what I asked you? Please tell our conversation again.",
    "Answer": "Certainly! Here is a summary of our conversation so far:\n\n- Question: What is the capital of France?\n- Answer: The capital of France is Paris.\n\n- Question: What is the population of Paris?\n- Answer: As of 2021, the population of Paris is estimated to be approximately 2.1 million people.\n\n- Question: What I asked you before? Tell me my all this conversation's questions and your answer again.\n- Answer: You asked me to summarize our conversation so far, which I just did above."
}
{
    "Question": "Who are you? What is your name what I give you.",
    "Answer": "You named me \"story\". I am an AI-powered assistant designed to assist and help you with any questions or tasks you may have. How can I assist you today?"
}

이 것을 응용하면 여러 개의 대화방을 조합하여 보다 새로운 답을 이끌어내는 AI를 구성할 수 있을 것 같습니다.

 

 

코드 리팩토링

마지막으로 코드 리팩토링을 해주는 예제를 한번 만들어 보았는데요.

리팩토링이 필요한 코드 예제를 주고 AI가 이를 어떻게 정리해 주는지 한 번 보시죠.

 

지저분한 코드 예제

if __name__ == '__main__':
    # duplicated codes
    example_text_1 = "I'am storyparks. I lived in korea."
    print(f"split the sentence with blank and check the size: {example_text_1.split(' ')}")

    example_text_2 = "What's the clean code? I don't know what it should be."
    print(f"split the sentence with blank and check the size: {example_text_2.split(' ')}")

 

GPT에게 코드 정리를 요청

import json
import re
from pathlib import Path

import openai
import yaml


def send_message_to_chatgpt(prev_messages, new_question):
    prev_messages.append({"role": "user", "content": new_question})
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=prev_messages,
        max_tokens=150,
        n=1,
        stop=None,
        temperature=0.8,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
    )

    assistant_message = response.choices[0].message

    answer = {'Question': new_question, 'Answer': assistant_message.content}
    print(json.dumps(answer, indent=4))
    return assistant_message.content


def parse_message(message):
    pattern = r"```(.*?)```"
    matches = re.findall(pattern, message, re.DOTALL)
    return matches[0]


if __name__ == '__main__':
    """
    The example for chaining questions.
    """
    base_directory = Path(__file__).parent.parent

    with open(f"{base_directory}/openai.yaml", "r") as openai_yaml:
        config = yaml.load(openai_yaml, Loader=yaml.FullLoader)

    openai.api_key = config['OPENAI']['API_KEY']

    messages = [
        {"role": "system", "content": """You are a helpful assistant for develop. 
        You can refactor python code. 
        The entire of refactored code inside the '```' single block."""}
    ]

    ugly_codes = []
    with open('ugly_code.py', 'r') as ugly_code_files:
        ugly_codes = [line for line in ugly_code_files]

    ugly_code = '\n'.join(ugly_codes)

    print("The ugly code")
    print("=====")
    print(ugly_code)
    print("=====")

    asked = f"Refactor the duplication with below requested code. ```\n {ugly_code}\n```"
    response = send_message_to_chatgpt(messages, asked)

    refactored_code = parse_message(response)
    print(refactored_code)

    with open('refactored_code.py', 'w') as refactored_code_files:
        refactored_code_files.write(refactored_code)

 

GPT가 생성해 준 코드

def check_sentence(sentence):
    print(f"split the sentence with blank and check the size: {sentence.split(' ')}")


if __name__ == '__main__':
    example_text_1 = "I'am storyparks. I lived in korea."
    check_sentence(example_text_1)

    example_text_2 = "What's the clean code? I don't know what it should be."
    check_sentence(example_text_2)

사이트에서 질문하다 보면 다수의 코드를 동시에 체크받기가 어려운 점이 있었는데, 이런 식으로 API를 사용하면 프로젝트 전체 리팩토링도 가능할 것으로 보입니다.

 

마무리하며

GPT가 세상에 나타나면서 많은 변화가 일어나고 있는 것 같습니다. 저도 더욱 많은 것들을 할 수 있게 된 세상이 설레면서도 많은 분들이 그러듯이 일자리를 빼앗기게 되는 게 아닐까? 하는 생각도 드는데요. 

 

컴퓨터 / 인터넷의 등장으로 많은 직업이 사라졌지만 그만큼 새로운 직업들도 많이 생겨났듯이, 이번 격변기에도 수많은 기회가 창출될 것이라 믿어 의심치 않습니다.

 

개발자 입장에서 생각해 보면, 우리나라 개발자들은 유독 프런트 / 백엔드를 많이 나누는 경향이 있었는데요. GPT와 같은 AI의 등장으로 다른 영역에 진입 장벽이 낮아진 만큼, 풀 스택 개발자가 많아지지 않을까 하는 생각이 듭니다.

반응형