안녕하세요. 지난 시간에 이어 이력서 홈페이지 제작 프로젝트 과정 중 AI 챗봇 백엔드 코드를 구현한 내용을 포스팅 하도록 하겠습니다.
기초 지식
우선 AI ChatBot을 구축하기 전 OpenAI 에서 제공해주는 API들에 대해 가볍게 알아보도록 하겠습니다.
Assistant ?
Open AI 공식 문서에는 AI 모델을 호출하고 도구를 사용하여 작업을 수행할 수 있는 도우미라고 표기되어 있는 것으로 확인할 수 있습니다.
즉, Assistant 는 한마디로 자신만의 챗봇 모델이라고 볼 수 있고, OpenAI에서 제공해주는 Assistant API 가 있는데 이는 애플리케이션 내 자신만의 Assistant를 구축할 수 있도록 도와주는 도구(Toolkit)이라 생각하시면 될 것 같습니다.
자세한 내용은 공식 문서 참고
https://platform.openai.com/docs/api-reference/assistants/createAssistant
OpenAI Assistant 동작 흐름
앞서 OpenAI Assistant 가 무엇인지 살펴보았습니다. 이제 간단한 그림을 통해 OpenAI Assistant 가 어떻게 동작하는지 가볍게 알아보도록 하겠습니다.
- Assistant 생성
- Thread 생성
- Thread 내 Message 생성 : User Message로 OpenAI Assistant 모델 대상으로의 질문을 의미
- Thread 에서 Assistant 실행(Run)하여 응답 생성
- Thread 내 Message 생성 : Bot Message 로 OpenAI Assistant 모델이 생성한 응답을 의미
OpenAI Assistant 모델의 경우 크게 위와 같은 동작 흐름으로 동작합니다.
해당 백엔드 코드 작성 시 동작 흐름은 이력서 기반의 텍스트 파일을 기반으로 Assistant 를 생성하고, 메시지를 담을 Thread 를 생성한 후 사용자 입력값(질문)을 받아 Thread 내 User Message 를 생성하고, Thread_ID 기반으로 생성한 Assistant를 실행하여 응답을 Thread에 Poll 한 후 클라이언트에게 전달하는 방식으로 구현을 진행하였습니다.
Assistant 생성
OpenAI API 를 이용한 생성
from openai import OpenAI
client = OpenAI() # OpenAI API키를 이용하여 클라이언트 객체 생성
assistant = client.beta.assistants.create( # OpenAI API를 사용하여 Assistant를 생성
name="MyResume Assistant",
instructions=instructions, # 생성한 모델이 어떠한 방식으로 대답할 지 설정
tools=[{"type": "file_search"}],
tool_resources={
"file_search": {
"vector_store_ids": ["<vector_store_ids_ID>"] # vector_store_ids 설정 / 나의 정보를 기입한 txt 파일
}
},
model="gpt-3.5-turbo"
)
- name : Assistant 이름 설정
- intructions : Assistant 가 어떠한 방식으로 대답할 지 설정
- tools : Assistant 에서 활성화된 도구 목록으로 code_interpreter, file_search, function 를 사용할 수 있음
- model : ChatGPT 응답 모델 설정
자세한 내용은 공식 문서 참고
https://platform.openai.com/docs/api-reference/assistants/createAssistant
OpenAI 웹 콘솔을 이용한 Assistant 생성
만약 API 를 이용한 Assistant 생성이 불편하거나 어려울 경우 OpenAI 웹 콘솔을 이용한 Assistant 생성 진행하시면 됩니다.
마스킹 처리된 부분의 경우 Assistant_ID 이기에 이후 진행 시 참고하시길 바랍니다.
https://platform.openai.com/assistants
AI Chatbot 백엔드 코드 구현
import openai
from dotenv import load_dotenv # 윈도우 사용 시 주석 처리
import os
import time
import re
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
# Linux 환경에서 .env 파일을 사용하기 위해 아래 코드를 추가
# Load .env
load_dotenv()
API_KEY = os.environ.get('API_KEY') # .env 파일에 저장된 API_KEY를 가져옴
ASSISTANT_ID = os.environ.get('ASSISTANT_ID') # .env 파일에 저장된 Assistant ID를 가져옴
# # Windows 환경에서 .env 파일을 사용하기 위해 아래 코드를 추가
# API_KEY = os.getenv('API_KEY', '<API_KEY_VALUE>') # .env 파일에 저장된 API_KEY를 가져옴
# ASSISTANT_ID = os.getenv('ASSISTANT_ID', '<ASSISTANT_ID_VALUE>') # .env 파일에 저장된 Assistant ID를 가져옴
client = openai.OpenAI(api_key=API_KEY) # API_KEY를 사용하여 OpenAI 클라이언트 객체 생성
def poll_run(run, thread):
while run.status != "completed":
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id
)
time.sleep(0.5)
return run
def create_run(thread_id, assistant_id):
run = client.beta.threads.runs.create(
thread_id=thread_id,
assistant_id=assistant_id,
)
return run
@app.route('/chat', methods=['POST'])
def chat():
data = request.get_json() # 사용자 http 요청을 data 변수에 저장
user_message = data['question'] # 사용자 요청 json 데이터 중 question 키 값 추출
thread_id = data.get('thread_id', None) # 사용자 요청 json 데이터 중 thread_id 키 값 추출 thread_id 값이 없을 경우 None으로 초기화
if not user_message:
return jsonify({"error": "Not Message"}), 400
if thread_id != None:
thread = client.beta.threads.retrieve(thread_id) # thread_id 값이 있을 경우 해당 thread_id를 사용하여 쓰레드를 가져옴
else:
thread = client.beta.threads.create() # thread_id 값이 없을 경우 새로운 쓰레드를 생성
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content=user_message
)
run = create_run(thread.id, ASSISTANT_ID)
poll_run(run, thread)
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
messages_list = list(messages) # 가장 최근의 assistant 메시지를 찾기 위해 메시지를 리스트로 변환 인덱스 [0]이 항상 최신 메시지
last_message = None
for message in messages_list:
if message.role == "assistant":
last_message = message
break
if last_message:
response_content = re.sub(r'【\d+:\d+†source】', '', last_message.content[0].text.value) # Assistant의 답변에서 source를 제거
return jsonify({"response": response_content, "thread_id": thread.id})
else:
return jsonify({"error": "No response from assistant"}), 500
if __name__ == '__main__':
app.run(host='127.0.0.1', port='5050', debug=True)
Github
'Project > resume' 카테고리의 다른 글
MongoDB, Flask, Next.js 를 활용한 동적 블로그 컴포넌트 구현 (0) | 2024.08.17 |
---|---|
AI Chatbot 추천 질문 시스템 구현 / OpenAI Assistant Intruction 을 활용한 AI 응답 지정 (0) | 2024.08.17 |
[Github Actions] CI/CD 파이프라인 구축 / Github Actions 사용법 / Snyk Application CI 파이프라인 통합 / EC2 배포 자동화 (0) | 2024.08.13 |
MongoDB를 이용하여 Python에서 IP기반 요청 제한 구현 (0) | 2024.08.12 |
이력서 홈페이지 제작 프로젝트 (feat. 이력서 기반 AI챗봇) (0) | 2024.07.09 |