✨Ai 프로젝트 만들기

5. OpenAI 를 활용하여 Aice 자격증 챗봇 만들기

@ENFJ 2025. 6. 26. 21:24

app.py 파일 코드

import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler 
from backend import aice_chain

# Initializes your app with your bot token and socket mode handler
# Slack API에 연결되는 봇 인스턴스 생성
# 여기에 리스너(이벤트 핸들러)를 등록해서 슬랙 메시지에 반응할 수 있게 함
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

# Listens to incoming messages that contain "hello"
# 슬랙 채팅방에서 누군가가 "hello"라고 메시지를 보내면 이 함수가 실행
# 이 함수는 메시지를 받고, 그 메시지에 대한 응답을 생성해서 다시 채널에 보내는 역할을 함
# visit https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html
@app.message("안녕")
def message_hello(message, say):
    # say(...) 함수는 같은 채널에 메시지를 다시 보내주는 함수야
    # <@{user}> 문법은 슬랙에서 멘션을 의미함 (즉, 사용자에게 직접 말하는 것처럼 보여)
    # breakpoint()
    message_text = message.get("text")
    response = aice_chain(message_text)
    say(response,thread_ts=message.get("ts"))
    # say(f"안녕하세요  <@{message['user']}> 님!")


# Listens to app_mention events
# 이 이벤트는 봇이 멘션되었을 때 발생
# 사용자가 봇을 멘션하면 이 함수가 실행되어, 봇이 응답을 생성하고 채널에 메시지를 보내는 역할을 함
@app.event("app_mention")
def aice_player(event, say):
    thread_ts = event["thread_ts"] or event["ts"] # 스레드 메시지의 경우, thread_ts가 없으면 ts를 사용
    text = event["text"]
    conversations = app.client.conversations_replies(channel=event['channel'] ,ts=thread_ts) # conversations_replies 메서드는 스레드 메시지의 경우, thread_ts를 사용하여 대화에 응답
    conversations.data['messages'][0]['text']
    breakpoint()
    response = aice_chain(text) # ✅ GPT 체인 실행
    say(text=response, thread_ts=thread_ts)# ✅ 응답 전송


# Start your app
# 이 핸들러는 슬랙 이벤트를 실시간으로 처리할 수 있게 해줌
if __name__ == "__main__":
    SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()

 

이전 글에 이어 이번에는 처음부터 지금까지의 대화 내용을 불러오는 작업을 하려고 합니다.

 

app.py 파일 에

 

우선 app.client 에 있는 conversation_replies 라는 함수를 통하여 작업을 할건데요, 필수 인자 값인  1)채널값, 2)스레드 ts 를 넘겨줄겁니다. 

 

 

# Listens to app_mention events
# 이 이벤트는 봇이 멘션되었을 때 발생
# 사용자가 봇을 멘션하면 이 함수가 실행되어, 봇이 응답을 생성하고 채널에 메시지를 보내는 역할을 함
@app.event("app_mention")
def aice_player(event, say):
    thread_ts = event["thread_ts"] or event["ts"] # 스레드 메시지의 경우, thread_ts가 없으면 ts를 사용
    text = event["text"]
    conversations = app.client.conversations_replies(channel=event['channel'] ,ts=thread_ts) # conversations_replies 메서드는 스레드 메시지의 경우, thread_ts를 사용하여 대화에 응답
    conversations.data['messages'][0]['text']
    breakpoint()
    response = aice_chain(text) # ✅ GPT 체인 실행
    say(text=response, thread_ts=thread_ts)# ✅ 응답 전송

 

그리고  아래 명령을 터미널에 입력하여 converation 에 있는 값들을 확인 해볼겁니다.

 

pp vars(conversations)

 

봐 보면, text 에 챗봇이 답변한 내용이 있는것을 확인 할 수 있습니다.

@app.event("app_mention")
def aice_player(event, say):
    thread_ts = event["thread_ts"] or event["ts"] # 스레드 메시지의 경우, thread_ts가 없으면 ts를 사용
    text = event["text"]
    conversations = app.client.conversations_replies(channel=event['channel'] ,ts=thread_ts) # conversations_replies 메서드는 스레드 메시지의 경우, thread_ts를 사용하여 대화에 응답
    context = [msg['text'] for msg in conversations.data['messages']]
    
    breakpoint()
    response = aice_chain(text) # ✅ GPT 체인 실행
    say(text=response, thread_ts=thread_ts)# ✅ 응답 전송

 

그래서 우리는 converstaions.data 에 messages 를 반복해서 가져오는데, 그 중에서 text 값을 가져올거다~! 

 

breakpoint 걸어서 아래 내용을 찍어보면, 

p [msg['text'] for msg in conversations.data['messages']]

 

이전 대화 내용들이 나오는것을 확인 할 수 있습니다.

제가 맨 처음에 안녕 이라고 쳤고, 챗봇이 안녕하세요! AICE 시험준비... 라고 대답한 내용이 터미널 창에 보입니다.

 

 

그럼 이때까지 대화 내용 데이터를 왜 들고 왔냐? 궁금할 수 있는데, 이 대화 내용을 backend.py 파일에 구현해놓은 aice chain 함수에 넣으면 된다는 거죠!

 

그런데 지금 현재 backend.py 파일 코드를 한번 보면

import os
import getpass

from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser


load_dotenv()
model = ChatOpenAI(model="gpt-4o", api_key=os.environ["OPENAI_API_KEY"])


# os.environ["OPENAI_API_KEY"] = getpass.getpass()

def aice_chain(text):
    system_prompt = '''
You are an expert tutor and test generator for the AICE (AI Certificate Exam).
You help students prepare for both the AICE Basic and Associate levels.
The student's goal is to pass the certification, and they are using you to review key concepts and solve practice questions.

Your tasks include:
- Summarizing key concepts in a simple, beginner-friendly way
- Generating 2 to 3 practice questions (multiple choice or short answer) based on real exam style
- Providing clear answers and explanations for each question
- Giving hints and feedback for common mistakes

Output your response in Markdown format with the following structure:
1. [Summary of Key Concepts]
2. [Practice Questions]
3. [Answers and Explanations]
4. [Tips for Further Study]

All explanations should be in simple English, and the tone should be encouraging and educational.
'''

  
    prompt_template = ChatPromptTemplate.from_messages(
        [("system", system_prompt), ("user", "{text}")]
    )


    # result = prompt_template.invoke({"language": "italian", "text": "hi"})


    chain = prompt_template | model | StrOutputParser()


    return chain.invoke({
        "text": text,
                })

지금 현재 aice_chain 함수에 받을 수 있는 파라미터 값이 하나 밖에 없다는 겁니다.

 

backend.py 부분을 수정해보도록 하겠습니다.

 

ChatPromptTemplate.from_messages

에 from_messages 함수를 들어가보면 

 

human 이랑 ai 가 있는데요, 이걸 보고 저희도 따라서 human ai 를 지정 할 수 있다는 것을 알 수 있습니다.

 

 

이때까지는 이전 대화 내용을 기억을 못했습니다.

 

 이제 app.py로 다시 돌아가서 챗봇의 id를 지정하고, 그 id 가 아닌 경우 human 으로 인식하게 하고, 이전 대화 내용 값들도  다 가져가서  이전 대화 내용을 기억하는! (메모리에 저장하는)   챗봇을 만들어 보도록 하겠습니다.

import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import ipdb # ipdb는 파이썬 디버거로, 코드 실행 중에 중단점을 설정하고 변수 상태를 확인할 수 있게 해줌
# ipdb는 개발 중에 코드의 동작을 디버깅하는 데 유용됨   
from backend import aice_chain

# Initializes your app with your bot token and socket mode handler
# Slack API에 연결되는 봇 인스턴스 생성
# 여기에 리스너(이벤트 핸들러)를 등록해서 슬랙 메시지에 반응할 수 있게 함
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

# Listens to incoming messages that contain "hello"
# 슬랙 채팅방에서 누군가가 "hello"라고 메시지를 보내면 이 함수가 실행
# 이 함수는 메시지를 받고, 그 메시지에 대한 응답을 생성해서 다시 채널에 보내는 역할을 함
# visit https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html
@app.message("안녕")
def message_hello(message, say):
    # say(...) 함수는 같은 채널에 메시지를 다시 보내주는 함수야
    # <@{user}> 문법은 슬랙에서 멘션을 의미함 (즉, 사용자에게 직접 말하는 것처럼 보여)
    # breakpoint()
    message_text = message.get("text")
    response = aice_chain(message_text)
    say(response,thread_ts=message.get("ts"))
    # say(f"안녕하세요  <@{message['user']}> 님!")


# Listens to app_mention events
# 이 이벤트는 봇이 멘션되었을 때 발생
# 사용자가 봇을 멘션하면 이 함수가 실행되어, 봇이 응답을 생성하고 채널에 메시지를 보내는 역할을 함
@app.event("app_mention")
def aice_player(event, say):
    thread_ts = event["thread_ts"] or event["ts"] # 스레드 메시지의 경우, thread_ts가 없으면 ts를 사용
    text = event["text"]
    conversations = app.client.conversations_replies(channel=event['channel'] ,ts=thread_ts) # conversations_replies 메서드는 스레드 메시지의 경우, thread_ts를 사용하여 대화에 응답
    context = [('ai'if msg['user'] == 'U091U2CE5EV' else 'human', msg['text']) for msg in conversations.data['messages']]
    response = aice_chain(context) # ✅ GPT 체인 실행
    say(text=response, thread_ts=thread_ts)# ✅ 응답 전송

    # conversations = app.client.conversations_replies(channel=event["channel"], ts=thread_ts) # 스레드 메시지의 경우, thread_ts를 사용하여 대화에 응답
    # context = [('ai' if msg['user'] =='U091E3JVCQ5'else 'human',msg['text']) 
            # for msg in conversations['messages'] if 'text' in msg and msg['text'] != ''] # 스레드 메시지의 경우, 대화 내용을 가져옴


# Start your app
# 이 핸들러는 슬랙 이벤트를 실시간으로 처리할 수 있게 해줌
if __name__ == "__main__":
    SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()

 

import os
import getpass

from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser


load_dotenv()
model = ChatOpenAI(model="gpt-4o", api_key=os.environ["OPENAI_API_KEY"])


# os.environ["OPENAI_API_KEY"] = getpass.getpass()

def aice_chain(context:list):
    system_prompt = '''
You are an expert tutor and test generator for the AICE (AI Certificate Exam).
You help students prepare for both the AICE Basic and Associate levels.
The student's goal is to pass the certification, and they are using you to review key concepts and solve practice questions.

Your tasks include:
- Summarizing key concepts in a simple, beginner-friendly way
- Generating 2 to 3 practice questions (multiple choice or short answer) based on real exam style
- Providing clear answers and explanations for each question
- Giving hints and feedback for common mistakes

Output your response in Markdown format with the following structure:
1. [Summary of Key Concepts]
2. [Practice Questions]
3. [Answers and Explanations]
4. [Tips for Further Study]

All explanations should be in simple English, and the tone should be encouraging and educational.
'''


    prompt_template = ChatPromptTemplate.from_messages([
            ("system", system_prompt), 
            *context
            
            ])


    # result = prompt_template.invoke({"language": "italian", "text": "hi"})


    chain = prompt_template | model | StrOutputParser()


    return chain.invoke({
                })

대화를 보면 내가 aice 자격증 associate 레벨을 준비해야겠다고 말을 했고,

 

이전 데이터를 바탕으로 대답하는 것을 볼 수 있습니다.

 

앞서 aice 자격증 associate 레벨을 준비하기로 하셨다고 하셨는데요.. 라고 하는게 보이시죠?