본문 바로가기

Coding/TIL

TIL | #08 | ChatGPT API 활용 카카오톡 챗봇 | 23.12.06.(수)

23.12.06.(WED).TIL.

ChatGPT API 활용 카카오톡 챗봇

ChatGPT API 활용 카카오톡 챗봇

 
##### 기본 정보 설정 단계 #####
from fastapi import Request, FastAPI
from openai import OpenAI
import threading
import time
import queue as q
import os

# OpenAI API 키

client = OpenAI(
    api_key=os.environ['YOUR_API_KEY'], # this is also the default, it can be omitted
)

##### 기능 함수 구현 단계 #####
# 메시지 전송
def textResponseFormat(bot_response):
    response = {"version":"2.0", "template":{"outputs":[{"simpleText":{"text":bot_response}}], "quickReplies":[]}}
    return response


# 사진 전송
def imageResponseFormat(bot_response, prompt):
    output_text = prompt + "내용에 관한 이미지입니다"
    response = {"version":"2.0", "template":{"outputs":[{"simpleImage":{"imageUrl":bot_response, "altText":output_text}}], "quickReplies":[]}}
    return response

# 응답 초과 시 답변
def timeover():
    response = { "version":"2.0", "template":{
        "outputs":[
            {
                "simpleText":{
                    "text":"아직 제가 생각이 끝나지 않았어요 🙏🙏 \n잠시 후 아래 말풍선을 눌러주세요 👆"
                }
            }
        ],
        "quickReplies":[
            {
                "action":"message",
                "label":"생각 다 끝났나요? 🙋‍♂️",
                "messageText":"생각 다 끝났나요?"
            }
        ]
    }}
    return response

# ChatGPT에게 질문/답변받기
def getTextFromGPT(prompt):
    messages_prompt = [{"role": "system", "content": "You are a thoughtful assistant. Respond to all input in 25 words and answer in Korea"}]
    messages_prompt += [{"role": "system", "content": prompt}]
    response = client.chat.completions.create(model="gpt-3.5-turbo", messages=messages_prompt)
    message = response["choices"][0]["message"]["content"]
    return message

# DALL.E 2에게 질문/그림 URL 받기
def getImageURLFromDALLE(prompt):
    response = openai.Image.create(prompt=prompt, n=1, size="512x512")
    image_url = response["data"][0]["url"]
    return image_url

# 텍스트 파일 초기화
def dbReset(filename):
    with open(filename, 'w') as f:
        f.write("")

###### 서버 생성 단계 #####

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "kakaoTest"}

@app.post("/chat/")
async def chat(request: Request):
    kakaorequest = await request.json()
    return mainChat(kakaorequest)

##### 메인 함수 구현 단계 ######

# 메인 함수
def mainChat(kakaorequest):

    run_flag = False
    start_time = time.time()

    # 응답 결과를 저장하기 위한 텍스트 파일 생성
    cwd = os.getcwd()
    filename = cwd + "/botlog.txt"
    if not os.path.exists(filename):
        with open(filename, "w") as f:
            f.write("")
    else:
        print("File Exists")

    # 답변 생성 함수 실행
    response_queue = q.Queue()
    request_respond = threading.Thread(target=responseOpenAI, args = (kakaorequest, response_queue, filename))
    request_respond.start()

    # 답변 생성 시간 체크
    while (time.time() - start_time < 3.5):
        if not response_queue.empty():
            # 3.5초 안에 답변이 완성되면 바로 값을 반환
            response = response_queue.get()
            run_flag = True
            break
        # 안정적인 구동을 위한 딜레이 타임 설정
        time.sleep(0.01)

    # 3.5초 안에 답변이 생성되지 않을 경우
    if run_flag == False:
        response = timeover()

    return response

# 답변/사진 요청 및 응답 확인 함수
def responseOpenAI(request, response_queue, filename):
    # 사용자가 버튼을 클릭하여 답변 완성 여부를 다시 봤을 시
    if '생각 다 끝났나요?' in request["userRequest"]["utterance"]:
        # 텍스트 파일 열기
        with open(filename) as f:
            last_update = f.read()
        # 텍스트 파일 내 저장된 정보가 있을 경우
        if len(last_update.split()) > 1:
            kind, bot_res, prompt = last_update.split()[0], last_update.split()[1], last_update.split()[2]
            if kind == "img":
                response_queue.put(imageResponseFormat(bot_res, prompt))
            else:
                response_queue.put(textResponseFormat(bot_res))
            dbReset(filename)

    # 이미지 생성을 요청한 경우
    elif '/img' in request["userRequest"]["utterance"]:
        dbReset(filename)
        prompt = request["userRequest"]["utterance"].replace("/img", "")
        bot_res = getImageURLFromDALLE(prompt)
        response_queue.put(imageResponseFormat(bot_res, prompt))
        save_log = "img" + " " + str(bot_res) + " " + str(prompt)
        with open(filename, 'w') as f:
            f.write(save_log)

    # ChatGPT 답변을 요청한 경우
    elif '/ask' in request["userRequest"]["utterance"]:
        dbReset(filename)
        prompt = request["userRequest"]["utterance"].replace("/ask", "")
        bot_res = getTextFromGPT(prompt)
        response_queue.put(textResponseFormat(bot_res))

        save_log = "ask" + " " + str(bot_res) + " " + str(prompt)
        with open(filename, 'w') as f:
            f.write(save_log)

    # 아무 답변 요청이 없는 채팅일 경우
    else:
        # 기본 response 값
        base_response = {'version':'2.0', 'template': {'outputs': [], 'quickReplies': []}}
        response_queue.put(base_response)
 
Error #01

출력 : {"detail":"Not Found"}
오류 메시지 : GET http://127.0.0.1:8000/chat/ 404 chat/:1 (Not Found)
원인 : app = FastAPI()가 두 번 정의되어 있었기 때문
Error #02

출력 : {"detail":"Method Not Allowed"}
오류 메시지 : GET http://127.0.0.1:8000/chat/ 405 chat/:1 (Method Not Allowed)
해결 : 코드 수정

from fastapi import Request, FastAPI

app = FastAPI()

# `/` 경로에 대한 처리기 등록
@app.get("/")
async def root():
return {"message": "kakaoTest"}

@app.post("/chat/")
async def chat(request: Request):
    kakaorequest = await request.json()
    print(kakaorequest)
    return {"message": "kakaoTest"}
Error #03

출력 : {Internal Server ERror}
오류 메시지 : GET http://127.0.0.1:8000/chat/ 500 chat/:1 (Internal Server Error)
원인 :

    코드를 살펴보니 `@app.rout("/chat/", methods=["GET", "POST"])` 데코레이터와 함께 함수를 정의하고 있습니다. 그러나 FastAPI에서는 `@app.route` 대신에 `@app.get` 또는 `app.post`를 사용하여 각각의 HTTP 메서드에 대한 핸들러를 등록하는 것이 권장됩니다.

    또한, `@app.route`를 사용할 경우에는 `methods` 매개변수 대신에 각 HTTP 메서드에 대한 데코레이터를 별도로 사용해야 합니다.

    아래는 수정된 코드입니다.

    from fastapi import Request, FastAPI

    app = FastAPI()

    # `/` 경로에 대한 처리기 등록
    @app.get("/")
    async def root():
        return {"message": "kakaoTest"}

    # `/chat/` 경로에 대한 GET 요청 처리기 등록
    @app.get("/chat/")
    async def get_chat():
        return {"message": "kakaoTest"}

    # `/chat/` 경로에 대한 POST 요청 처리기 등록
    @app.post("/chat/")
    async def post_chat(request: Request):
        kakaorequest = await request.json()
        print(kakaorequest)
        return {"message": "kakaoTest"}