[Part 1] 쏘카 팀이 만든 고객 서비스 챗봇 10분 안에 만들어보기
이번 개발글은 저번 Microsoft's "Generative AI Roadshow" 토크에서 발표한 쏘카팀에게 영감을 받은 프로젝트입니다. 쏘카 팀이 고객 서비스 챗봇을 만들었다고 했는데, 만든 방법은 시나리오 기반 LLM 봇 입니다.
조금 더 자세히 쏘카 팀의 개발 디자인에 대해 살펴봅시다.
먼저 유저의 텍스트 인풋에 따라 상담의 의도를 바로 LLM이 파악하기
RAG (Retrieval Augmented Generation), vector search를 같이 사용해서 LLM 에이전트가 상담 매뉴얼 DB에서 더욱 정확한 결과를 검색해서 뱉어내도록 하기
할루시네이션이 나오면 미리 후처리해서 LLM이 답하는 것보다 인간 상담원에게 바로 연결을 할 수 있도록 디자인 하기
이번 개발 프로젝트는 두 파트로 나눴습니다.
Part 1에서는 정말 간단한 ChatGPT와 같은 형식의 캐쥬얼한 대화를 할 수 있는 봇을 만들 것입니다. 아래 사진과 같이 유저 인터페이스도 바로 만들어줄거에요.
Part 2에서는 쏘카 팀이 사용한 RAG (Retrieval Augmented Generation) 기술을 사용해서 특정 다큐멘트 (예를 들어 고객 상담 서비스 매뉴얼)에 대해 대화를 나눌 수 있는 챗봇을 만들겠습니다. 마찬가지로 아래 이미지와 같게 유저 인터페이스를 만들어줄거에요.
백엔드 코드는 Langchain docs를 참고 했고, 프론트엔드 코드는 streamlit docs를 참고했습니다.
만약 전체 코드를 받아보고 싶다면 제 Github에 공개로 올려놨으니 참고해보세요:
VSCode 프로젝트 셋업
가장 먼저 VSCode를 열어서 새로운 폴더를 만들어주고, 안에 가상환경을 만들어주세요. (python3 -m venv venv)
가상 환경을 실행시켰다면 (source venv/bin/activate) 가상 환경 안에서 필요한 패키지들을 다운받아줍니다.
pip3 install langchain openai tiktoken python-dotenv faiss-cpu streamlit
모든 패키지들을 다운받았다면 폴더 내에 .env 파일을 만들고, 그 안에 OPENAI_API_KEY = "sk-...내 openai 비밀번호" 를 저장해주세요.
이 셋업에 대해 더 자세하게 다룬 글은 여기서 확인하거나 영상을 확인해주세요!
백엔드 코드
폴더 내에 chatbot.py라는 파이선 파일을 만들어주고, 다음 코드 블록을 복붙해주세요. 코드에 대한 더 자세한 설명은 인라인에 있습니다. # 주석을 읽어주세요!
# 필요한 모듈들 불러오기
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
#.env 파일에 저장돼있는 OPENAI_API_KEY 불러오기
import dotenv
dotenv.load_dotenv()
# LLM 셋업하기
llm = ChatOpenAI()
# LLM을 위한 커스텀 프롬프트 만들어주기
prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
# `variable_name`에 저장돼있는 건 'memory' 변수 안에 저장된것과 같아야함
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}"),
]
)
# memory는 대화 기록들을 저장할 수 있게 합니다. 유저가 새로운 질문을 했을때 전에 했던 대화 내용들을 바탕으로
# 대답할 수 있게 하는거에요.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation = LLMChain(llm=llm, prompt=prompt, verbose=True, memory=memory)
# LLM을 실행하기 위해 conversation({"question": "hi"})를 돌립니다.
# 유저의 인풋은 "hi"대신 다른걸로 대체해서 한번 실행해보세요!
result = conversation({"question": "hi"})
print(result['text'])이 블록을 복붙했다면 파일 저장을 하고 터미널에 python3 chatbot.py를 실행하면 다음과 같은 결과값이 나옵니다. 그러고 LLM의 답변도 함께 프린트가 됩니다.
이로써 백엔드 코드가 완성이 됐어요! 바로 이제 다음 단계인 프론트엔드 인터페이스를 만들어줄게요.
프론트엔드 코드
Streamlit docs를 보면 아래 이미지처럼 구현할 수 있는 코드가 마련돼있어요. 저희는 이 코드를 그대로 사용할 예정입니다.
위에서 만든 백엔드 코드를 프론트엔드 코드와 합쳐줄거에요. 방금 만들었던 코드를 삭제하고 대신 아래 코드 블록을 복붙해주세요.
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
import streamlit as st
import time
import dotenv
dotenv.load_dotenv()
#웹사이트 메인 헤더
st.title("ChatGPT")
# Set a default model
if "openai_model" not in st.session_state:
st.session_state["openai_model"] = "gpt-3.5-turbo"
if "messages" not in st.session_state:
st.session_state.messages = []
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 유저의 인풋이 있으면
if prompt := st.chat_input("What is up?"):
# 유저가 보낸 내용은 내용 그대로 사람 아이콘과 함께 화면에 출력하기
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# AI가 답변할 내용: 유저 인풋을 LLM에게 보내서 실행시키고, 받은 답변 실시간으로 출력하기
with st.chat_message("assistant"):
message_placeholder = st.empty()
full_response = ""
# 여기서부턴 위에서 만든 백엔드 코드가 들어갑니다!
# LLM
llm = ChatOpenAI()
# Prompt
custom_prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}"),
]
)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation = LLMChain(llm=llm, prompt=custom_prompt, verbose=True, memory=memory)
# 여기서 {"question": prompt}는 유저의 인풋 프롬프트를 LLM에게 보낸다는 뜻
result = conversation({"question": prompt})
# 실시간으로 텍스트가 로딩되는 효과를 보여주기 위해 결과값을 단어 단위로 쪼개주기
for chunk in result['text'].split():
full_response += chunk + " "
time.sleep(0.05)
message_placeholder.markdown(full_response + "▌")
message_placeholder.markdown(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})코드를 자세히 보면 message["role"]에 따라 웹사이트에 보여지는 결과가 달라집니다. 예를 들어 "role": "user"라면 인풋 박스에 저장된 유저의 인풋이 그대로 사람 아이콘과 함께 내용물이 출력됩니다. 반면 "assistant", 즉 AI라면 로봇 아이콘과 함께 유저 인풋을 LLM에게 보내고 나서 받은 결과물을 실시간으로 출력합니다.
코드에 대한 더 자세한 설명은 # 주석을 확인해주세요!
실행하기
이제 코드가 완료됐어요. 터미널에서 가상환경을 실행시켜줬다면 streamlit run chatbot.py를 돌려주세요.
그럼 새로운 윈도우가 팝업이 되고 내가 만든 LLM으로 대화할 수 있는 챗봇이 만들어집니다.
마무리
이로써 Part 1이 마무리 됐습니다! 진짜 쉬웠죠? 이제 다음 파트에서는 비슷한 구조의 코드로 RAG를 사용하여 특정 문서에 대해 대화할 수 있는 챗봇을 만들어볼게요.
하면서 어렵거나 잘 안되는 부분이 있으면 댓글 달아주세요! 감사합니다. :)
더 많은 개발 프로젝트들을 해보고 싶으시다면 10X AI University에서 현재 업무 자동화, 캐릭터 AI, 이미지 생성AI 등을 주제로 비슷한 컨텐츠들을 확인하실 수 있습니다!
댓글
로그인 후 댓글을 남길 수 있습니다.
🫶감사합니다!! ㅎㅎ
주말에 시간 내서 보고 만들어볼게용
10분 밖에 안걸리는데 지금 해보시죱.
혹시 주피터로 해도 되나여 vs code 이 노트북에 안깔려있어서요 ㅎ
될걸요...? 백엔드는 돌아갈텐데 프론트엔드가 streamlit을 사용하는거라 주피터로 될지 모르겠네용. 따로 streamlit run chatbot.py 라는 커맨드로 돌려야해서
주피터에서도 터미널 창 열어서 실행하시면 됩니다! ㅎㅎ
옹! 감사합니다!
우와..강의 너무 좋아요! 담에 봐야지
감사합니다 ㅎㅎ
코드 공유해주시는거 진짜 너무 좋아요!!!
RAG 완전 기대됩니다! 감사합니다!
1기 강의를 듣고 있는 회원입니다. 5분만에 설치 운영이 되었네요 ^^
1. 먼저 유저의 텍스트 인풋에 따라 상담의 의도를 바로 LLM이 파악하기 2. RAG (Retrieval Augmented Generation), vector search를 같이 사용해서 LLM 에이전트가 상담 매뉴얼 DB에서 더욱 정확한 결과를 검색해서 뱉어내도록 하기 3. 할루시네이션이 나오면 미리 후처리해서 LLM이 답하는 것보다 인간 상담원에게 바로 연결을 할 수 있도록 디자인 하기 위 3가지 프로세스를 확인하려면, 별도의 test 시나리오가 필요로 될 것 같아 보이네요 ^^
안녕하세요 봉근님! ㅎㅎ 맞아요 좀 더 고도화된 챗봇을 만들려면 따로 시나리오를 만들어야할 것 같아서 구상하고 있습니다 :)
저도 RAG관련으로 관심이 있어서 자료를 찾아보다가 하기내용이 있어서 test를 해 보고 있습니다. => test라고 해 봤자, Colab에서 실행test만 하고 있습니다. ㅎㅎ https://github.com/safevideo/autollm 어떤지 한번 봐 주세요 ^^
다양한 LLM들이랑 vector db들을 한번에 사용할 수 있어서 편리해보이네요!
감사합니다
안녕하세요! 올려주신 코드를 실행해 봤습니다. 기본적인 채팅이 무리없이 진행되는데 뭔가 이상해서 살펴보니 맥락에 맞는 대화가 이루어지지 않더라구요. 이를 위해서는 chat_history를 지정해 줘야 하는 것 같습니다. 올려주신 코드에 아래의 코드들을 추가하니 맥락에 맞는 대화가 잘 되더라구요. 좋은 코드 공유해 주셔서 감사드리고, 이 부분만 보강되면 완벽한 랭체인 기반 챗봇 구현 코드가 될 것 같습니다! ``` # 사용자와 챗봇 메시지를 분리하여 저장할 리스트 생성 user_inputs = [] ai_responses = [] # 메시지 리스트 순회 for msg in st.session_state.messages: if msg['role'] == 'user': user_inputs.append(msg['content']) elif msg['role'] == 'assistant': ai_responses.append(msg['content']) ....중간코드들... memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) for user_input, ai_response in zip(user_inputs, ai_responses): memory.chat_memory.add_user_message(user_input) memory.chat_memory.add_ai_message(ai_response) chat_history = memory.load_memory_variables({})['chat_history'] ```