ㅁ 개요
O 프로젝트 소개
- 이번 글은 이전글(나만의 AI챗봇만들기(API버전) - 5. AI챗봇 : 윈도우 버전 - 5.3 Groq API연동)에 이은 9번째 글로 AI챗봇의 윈도우버전 중 Groq API 비동기식 연동편에 대해 살펴보겠습니다.
O 완성된 프로그램 실행 화면
1. 5-4.py를 실행하면 아래와 같이 실행됩니다.
아래 실행화면을 자세히 살펴보면 빨간선을 기준으로
위에는 (1)사용자가 질의하면 (2)AI챗봇이 응답하고, 다시 (3)사용자가 질의하면 (4)AI챗봇이 응답하는 구조로 되어 있습니다. 이것이 동기식 즉, 순서대로 하는 것입니다.
그런데 빨간선을 기준으로
아래에는 (1)사용자가 질의 후 (3) 재빨리 사용자가 다시 질의한것을 볼 수 있고, 이후 (2)AI챗봇이 (1)에 응답하고, 다시 (3)에 응답한 것을 볼 수 있는데, 이렇듯 순서대로가 아닌 것 즉, 이것을 비동기식이라고 생각하시면 이해가 쉽습니다.
이전 프로그램(5-3.py)에서는 위에서 설명한 것처럼 사용자가 (1)(3)을 질문 먼저 한 후 AI챗봇이 (2)(4)로 대답할 수 없으며, 무조건 순차적으로 (1)사용자가 질문 후 (2)AI챗봇이 대답하고, 그 후 다시 (3)사용자가 질문 후 (4)AI챗봇이 대답하는 구조로만 될 수 있다는 것이 차이점입니다.<-- 이것이 동기식 방식입니다.
여기서 알 수 있는 것은 만일 AI챗봇이 바쁘다면(응답이 느리거나, 없다면) 우리는 채팅창에서 무한정 AI챗봇의 응답이 올때까지 아무것도 못하고 기다려야만 한다는 것입니다.
아래 코드에서 우리는 이 부분을 해결하고 어떻게 해결되었는지를 상세히 이해할 것입니다.
ㅁ 세부 내용
O 완성된 소스
소스파일 : 5-4.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QLineEdit, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QThread, pyqtSignal
from config import GROQ_API_KEY
from groq import Groq
# 4.py - 비동기 처리를 위한 QThread 추가:
# 아래 1~5과정에 의해 긴 시간이 걸리는 AI 응답 생성을 별도의 스레드에서 처리하면서,
# 그 결과를 GUI(메인 쓰레드)에 안전하게 표시할 수 있게 해줌
# AI 응답 생성은 백그라운드에서 이루어지고, 생성된 응답은 메인쓰레드인 GUI에 안전하게 표시
class AIThread(QThread):
# 0.시그널 생성
# QThread 클래스에서 사용되는 사용자 정의 시그널로
# pyqtSignal(str)은 문자열 타입의 데이터를 전송할 수 있는 시그널을 생성
# 이 시그널은 AI의 응답을 스레드에서 메인 GUI 스레드로 전달하는 데 사용됨
response_signal = pyqtSignal(str)
def __init__(self, client, content, prompt):
super().__init__()
self.client = client
self.content = content
self.prompt = prompt
def run(self):
chat_completion = self.client.chat.completions.create(
messages=[
{"role": "system", "content": "모든 응답은 한국어로 작성해 주세요."},
{"role": "user", "content": self.content},
{"role": "user", "content": self.prompt},
],
model="llama3-8b-8192",
)
response = chat_completion.choices[0].message.content
########################################
# 4.시그널 발생-> displayResponse() 메서드(슬롯)를 메인 쓰레드에서 호출
########################################
self.response_signal.emit(response)
class ChatWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.content = ""
self.client = Groq(api_key=GROQ_API_KEY)
def initUI(self):
self.setWindowTitle('AI 챗봇')
self.setGeometry(100, 100, 400, 800)
layout = QVBoxLayout()
self.chatDisplay = QTextEdit(self)
self.chatDisplay.setReadOnly(True)
layout.addWidget(self.chatDisplay)
self.inputField = QLineEdit(self)
layout.addWidget(self.inputField)
self.sendButton = QPushButton('전송', self)
layout.addWidget(self.sendButton)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
########################################
# 1.사용자가 메시지를 입력 후 엔터
########################################
self.inputField.returnPressed.connect(self.sendMessage)
self.sendButton.clicked.connect(self.sendMessage)
def sendMessage(self):
user_message = self.inputField.text()
self.chatDisplay.append(f"You: {user_message}")
self.chatDisplay.append(f"==================================")
self.inputField.clear()
self.thread = AIThread(self.client, self.content, user_message)
########################################
# 2.시그널과 슬롯을 연결
#AIThread의 response_signal(시그널)을 ChatWindow 클래스의 displayResponse 메서드(슬롯)와 연결
########################################
self.thread.response_signal.connect(self.displayResponse)
########################################
# 3.쓰레드 시작 -> AIThread의 run() 메서드가 별도의 스레드에서 자동 실행
########################################
self.thread.start()
########################################
# 5.displayResponse() 메서드가 메인 GUI 스레드에서 호출
########################################
def displayResponse(self, response):
self.chatDisplay.append(f"AI: {response}")
self.chatDisplay.append(f"==================================\n")
self.content += response
if __name__ == '__main__':
app = QApplication(sys.argv)
window = ChatWindow()
window.show()
sys.exit(app.exec_())
O 소스 실행 방법
O 주요 내용
아래 소스코드의 주석에 이미 자세한 설명을 달아 놓았으므로 여기서는 전체 주요 흐름에 대해서만 설명하겠습니다.
소스 파일 : 5-4.py
전체 소스에서 실행 순서를 아는 것이 중요하며, 실행 순서는 다음과 같습니다.
line 70~74 : 1.사용자가 AI챗봇에 질의하고 엔터를 누르면 sendMessage()메소드가 호출됩니다.
line 82 : AIThread 클래스의 객체(thread)를 생성 후
line 88 : 2.시그널과 슬롯을 연결합니다.
시그널과 슬롯은 비동기식 처리(쓰레드)를 위한 중요한 개념입니다.
여기서는 AIThread클래스에서 정의한 response_signal 시그널을 ChatWindow클래스의 displayResponse()메소드(슬롯)와 연결합니다.
line 93 : 3.쓰레드를 시작합니다. self.thread.start()라고 코딩하면 AIThread클래스의 run()메소드가 별도의 쓰레드에서 자동 실행됩니다.
line 41 : 4.AIThread클래스의 run()메소드가 실행되고, response_signal 시그널을 발생시킵니다. 이 시그널이 발생하면 메인쓰레드에서 displayResponse() 매소드(슬롯)가 호출 됩니다.
시그널을 발생시킨다는 의미는 run()메소드는 백그라운드로 ai챗봇의 응답을 별도의 쓰레드에서 받아 처리하고, 메인 쓰레드에서는 displayResponse() 매소드(슬롯)을 호출한다는 의미 입니다.
line 98~101 : 5.호출된 displayResponse()메서드가 메인 쓰레드에서 AI챗봇의 응답을 받아 이를 chatDisplay(윈도우 창 윗부분)에 뿌려줍니다.
위와 같이 각 쓰레드 별도 작업을 처리(AI응답처리는 AIThread클래스의 run()메소드에서, 화면 채팅 처리는 ChatWindow 클래스의 displayResponse() 메소드에서 처리)함으로써 AI챗봇의 응답이 늦어지더라도, 챗팅 화면상에서 계속 질의를 할 수 있게 되는 것입니다.
ㅁ 정리
O 우리가 배운 내용
오늘은 여기까지이며, 위의 내용이 유익하셨다면, 광고 한번씩만 클릭 부탁드립니다.
감사합니다.
'파이썬 AI 실습 > 나만의 AI챗봇 만들기(API버전)' 카테고리의 다른 글
나만의 AI챗봇만들기(API버전) - 5. AI챗봇 : 윈도우 버전 - 5.3 Groq API연동 (0) | 2024.08.04 |
---|---|
나만의 AI챗봇만들기(API버전) - 5. AI챗봇 : 윈도우 버전 - 5.2 메시지 송수신 기능 추가 (0) | 2024.08.04 |
나만의 AI챗봇만들기(API버전) - 5. AI챗봇 : 윈도우 버전 - 5.1 윈도우창 띄우기 (0) | 2024.08.04 |
나만의 AI챗봇만들기(API버전) - 4. AI챗봇 : 이전 질문 기억하고 답변하기 (0) | 2024.07.31 |
나만의 AI챗봇만들기(API버전) - 3. AI챗봇 : 계속 질문하기 (0) | 2024.07.31 |