데이터 분석용 Chatbot 개발과정
개요
이번 주제는 OpenAI API (Application Programming Interface) 를 활용한 챗봇 개발입니다. 😃Wow~
아래 결과물을 먼저 살펴보겠습니다.
이 글은,
OpenAI API를 이용해 챗봇 GUI를 구성하고 데이터파일(*.xlsx)을 추가, 대화내용에 데이터를 포함한 분석 결과를 챗봇으로부터 얻는 과정을 설명합니다.
물론 웹브라우저로 OpenAI ChatGPT에 접속해서도 가능하지만 특정앱에 임베디드(Embedded) 하도록 API를 이용, 그 기능을 내장하는 것이 목적입니다.
개발환경
Windows 11 Pro, Python 3.11
openai 1.63.2, pandas 2.2.3
pyqt6 6.8.1
openAI 회원가입 후 결재
저는 US$ 10불을 작년에 결재해 두고 잊은게 있었습니다. 😅
(참고로 사용기간제한 (1년) 이 있으므로 테스트 시 너무 많은 금액을 결재하지
마세요, 테스트가 진행된 1~2일 동안 제가 사용한 토큰은 US 1$ 정도 소비)
결재 후 API Key, Billing Info, Usage 등을 모니터링 가능합니다.
![]() |
OpenAI 모니터링 페이지 |
분석용 샘플 엑셀파일
-
기상청 기상자료개방포털에서 가져온 엑셀파일
참고로, 샘플 엑셀파일은 기상청에서 받은 제주도의 2개월간 기온, 습도, 풍속 등의
단순데이터 입니다.
(기상청에서 받은 원본 엑셀파일 CP-949 에서 유니코드(UTF-8)로 변환처리 해둠)
![]() |
엑셀 샘플파일 |
단순 채팅을 넘어서 데이터를 챗봇에게 제공하고 그 분석 결과를 시험해보고 싶었습니다.
예를 들면,
위 엑셀 파일을 챗봇에게 제공한 후, "제주에서 체감온도가 가장 추운날은?" 이라고 질문하면 LLM이 주어진 데이터에서 온도, 습도, 바람을 감안해 정확한 답을 제시하는 것입니다.
그리고 챗봇에게 업로드 가능한 데이터의 허용량 (Token size)도 궁금했습니다.
GUI 화면 구성
아래는 PyQt6로 제작된 화면 구성입니다.
데이터를 추가하는 부분과 채팅을 진행하는 2개의 파트로 이루어져 있으며, 필요시 엑셀 파일을 추가해 챗봇과 상담이
가능합니다.
(다만 테스트에 사용한 'gpt-3.5-turbo' 모델의 토큰 허용량이 적어
데이터가 크면 오류나서 제한해 두었습니다.)
(현재 'gpt 4o mini' 모델을 써보니 파일 전체가 업로드 가능했습니다.)
![]() |
실행화면 |
참고로 gpt-3.5-turbo Model의 비용이 가성비가 좋아보여 테스트시 사용하고 있습니다.
아래는 1M(메가) token 당 비용입니다.
![]() |
출처 : openAI Pricing |
OpenAI 토큰이란?
토큰(token)은 일반적인 문자열의 한 글자가 아니라, 텍스트의 작은 단위를 의미합니다.
토큰은 일반적으로 단어, 부분 단어, 공백, 문장 부호 등으로 나뉘며, 언어와 문장 구조에 따라 다르게 계산됩니다.
Model별 차이는 있지만 예를 들면,
- "Hello, world!" → 2 tokens
- "안녕하세요" → 2~5 tokens
- 한글은 영어보다 토큰 개수가 더 많아질 수 있으며, 한글자가 2~3토큰
정도로 이해하면 좋을 것 같습니다.😉
세부적인 내용은
OpenAI Tokenizer 계산기를 사용해 보세요.
소스코드
소스코드 내용을 *.py 파일로 복사하고 같은 위치에 main.ui 파일(아래링크)을 위치한 뒤 실행시키면 예제코드가 동작합니다.
소스코드에 사용된 ui 파일 : main.ui (클릭후 다운로드, 소스코드와 같은 경로에 위치)
참고로 *.ui는 xml형태로 Qt Control 정보를 저장합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | from PyQt6.QtWidgets import QApplication, QWidget, QFileDialog from PyQt6.QtCore import QEvent, Qt from PyQt6.uic import loadUi import sys import pandas as pd from openai import OpenAI class Window(QWidget): OPENAI_KEY = 'input openAI API key here!' def __init__( self ): super ().__init__() loadUi( 'main.ui' , self ) self .setWindowTitle( 'Charbot_OpenAI - Ocean Coding School' ) self .te_resp.setReadOnly( True ) self .dfs = dict () self .client = OpenAI( api_key = Window.OPENAI_KEY, organization = 'your org ID' , project = 'your project name' ,) self .history = [] # model combobox models = ( 'gpt-4o' , 'gpt-4o-mini' , 'gpt-3.5-turbo' , 'gpt-4' , 'gpt-4-turbo' , 'text-embedding-3-small' ) self .cmb.addItems(models) #default 3.5-turbo self .cmb.setCurrentIndex( 2 ) #signal self .pb_add.clicked.connect( self .onAddFile) self .pb_del.clicked.connect( self .onDelFile) self .pb_send.clicked.connect( self .onSend) QApplication.instance().installEventFilter( self ) def eventFilter( self , obj, e): if obj = = self .te_query: if e. type () = = QEvent. Type .KeyPress and e.key() = = Qt.Key.Key_Return: self .onSend() return super ().eventFilter(obj, e) def getStringFromDF( self ): # get a string from dataframes all_data = '' for name, df in self .dfs.items(): all_data + = f 'File: {name}\n' all_data + = df.to_string(index = False ) # limit 5 rows #all_data += df.head(5).to_string(index=False) all_data + = '\n\n' return all_data def onAddFile( self ): path, ok = QFileDialog.getOpenFileNames( self , ' ', ' ', ' Excel Files( * .xlsx)') if ok: for file in path: # add listwidget name = file .split( '/' )[ - 1 ] if name not in self .dfs: self .lw_file.addItem(name) # add pandas, only single sheet with pd.ExcelFile( file ) as xlsx: df = pd.read_excel(xlsx, xlsx.sheet_names[ 0 ]) self .dfs[name] = df self .history.append({ 'user' : 'system' , 'response' : self .getStringFromDF()}) def onDelFile( self ): row = self .lw_file.currentRow() if row> = 0 : try : item = self .lw_file.takeItem(row) except Exception as e: print (e) else : name = item.text() if name in self .dfs: del self .dfs[name] #print( 'Number of df:', len(self.dfs) ) def onSend( self ): # get query query = self .te_query.toPlainText() if query: # add query on chatboard self .te_resp.append(f 'User: {query}' ) response = self .get_answer_from_chatbot(query, self .history) self .te_resp.append(f 'Chatbot: {response}\n' ) # clear query self .te_query.clear() def get_answer_from_chatbot( self , query, conversation_history): # prepare conversation = [{ 'role' : 'system' , 'content' : 'You are a helpful assistant.' }] for turn in conversation_history: conversation.append({ 'role' : 'user' , 'content' : turn[ 'user' ]}) conversation.append({ 'role' : 'assistant' , 'content' : turn[ 'response' ]}) # user query conversation.append({ 'role' : 'user' , 'content' : query}) try : sel_model = self .cmb.currentText() # Chat API 호출 response = self .client.chat.completions.create( #model='gpt-3.5-turbo', model = sel_model, messages = conversation, #max_tokens=150 ) except Exception as e: return e else : # 챗봇 응답 return response.choices[ 0 ].message.content.strip() if __name__ = = '__main__' : app = QApplication(sys.argv) w = Window() w.show() sys.exit(app. exec ()) |
그럼 코드를 함수별로 분석해 보겠습니다.
1. __init__(self):
- UI를 초기화하고, 창 제목을 설정
- OpenAI 클라이언트를 설정하고, 대화 기록을 초기화
- 버튼 클릭 이벤트(파일 추가, 삭제, 메시지 전송)에 연결된 슬롯을 설정
- eventFilter를 설치하여 te_query 텍스트 상자에서 엔터키 입력을 감지
2. eventFilter(self, obj, e):
- te_query 텍스트 상자에서 엔터키를 눌렀을 때 onSend() 함수를 호출
3. getStringFromDF(self):
- 현재 열린 Excel 파일들의 내용을 문자열로 반환
- 데이터프레임에서
5개의 행만전체 행을 가져와 문자열로 반환
4. onAddFile(self):
- 파일 선택 대화 상자를 통해 Excel 파일을 추가.
- 선택한 파일의 첫 번째 시트를 읽어 pandas DataFrame으로 저장, 파일 이름을 리스트에 추가
5. onDelFile(self):
- 선택한 파일을 리스트에서 삭제, 해당 DataFrame을 self.dfs에서 제거
6. onSend(self):
- 텍스트 상자에서 사용자의 입력을 가져와 챗봇에게 전송
- 챗봇의 응답을 받아 텍스트 상자에 표시
- 사용자 입력 후 입력란을 초기화
7. get_answer_from_chatbot(self, query, conversation_history):
- 대화 기록에 History를 포함, OpenAI API 로 보낼 대화형식 Query 생성
- 사용자 입력을 포함한 대화 형식을 OpenAI에 전달하여 답변
8. if __name__ == '__main__':
- 애플리케이션을 실행
- sys.exit(app.exec())로 실행하고 리턴시 앱 종료
느낀점
ChatGPT와 대화 시 이전 채팅내용을 현재 채팅내용에 계속 추가 포함시킨다는 사실.😰
그럼 채팅이 길어질수록 사용되는 데이터는 늘어날 수 밖에 없는 구조.🤑
따라서 conversation list에서 오래된 대화를 자르거나 요약해서 관리해야 할 필요성.🤔
그리고 챗봇에게 전송되는 데이터파일 또한 문자열로 채팅처럼 포함되어 전송된다는 사실.👌
이상으로 모든 설명을 마칩니다. 감사합니다.
2025.02.20 Updated
-
다양한 OpenAI LLM Model을 적용하기위해 콤보박스 추가.
토큰 허용량이 큰 모델에 파일을 업로드 후 분석 테스트
예제 코드의 첨부파일 제한 해제 (기존 5행으로 제한)
![]() |
엑셀파일 크기의 토큰 전송테스트 |
제주의 약 2개월치 기온, 풍속, 습도 등을 엑셀로 제공하고 분석을 의뢰.
챗봇이 답변한 1월 9일보다 기온이 낮은 날이 존재 (오류인가?)
추가적인 디테일은 뭐지?
흥미로운 점은 더 낮은 기온(제주 2월 7일)이 있었지만, 풍속, 습도를 고려해 체감온도로 대답한 점. 😮
Chat GPT가 이야기한 추가적인 디테일의 의미는 체감온도를 고려해 답변한 부분이었습니다.
와 👍 대단하십니다 컨셉으로 멋진 결과물을 도출해 내시는군요! AI chat 에 대해 더 공부 하고픈 마음이 듭니다.
답글삭제네, 아직까진 Chatbot LLM의 입,출력 토큰 허용량이 기업의 특정데이터를 분석할 만큼 크지 않지만 빠르게 개선되지 않을까 생각합니다.
삭제안녕하세요 선생님 위 코드 맥 환경에서 실행하는 방법을 알 수 있을까요
답글삭제안녕하세요. 맥에서도 별 다른 점은 없을 것 같습니다.
삭제게시물의 개발환경에 작성해둔 파이썬 설치, 외부 모듈들 중 openai, pyqt6, pandas 가 사용되었습니다. (외부모듈 pip 설치 필요)
그리고 openAI 에 회원가입하고 API에 대한 결재를 진행한 후, API Key만 따로 예제코드에 입력해 사용하시면 됩니다.
저는 Windows환경이지만 일단, 진행해 보시고 문제가 있는 부분을 알려주시면 자료를 찾아 도움드릴 수 있을 것 같습니다.