그라디오로 제작한 챗봇 시리즈
이전 내용
수행내용
이번에는 앞서 그라디오로 제작했던 챗봇 모델(상담봇, 번역봇, 소설봇) 중 상담봇과 번역봇을 랭체인의 기능을 추가하여 성능을 업그레이드 시켜보려고 한다.
상담봇 업그레이드
이전에 만들었던 상담봇은 이전의 대화 내용을 기억하지 못한다는 치명적인 단점이 있으므로, 이를 보완하기 위해 상담봇에 랭체인의 ConversationBufferMemory를 추가하여 대화 내용을 기억하도록 업그레이드 한다.
◆ 모듈 임포트
ConversationBufferMemory를 사용하기 위한 모듈을 불러오는 코드를 기존 챗봇 최상단에 작성한다.
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.schema import AIMessage, HumanMessage, SystemMessage
- OpenAI API를 사용할 수 있는 ChatOpenAI, 메모리를 저장해 줄 ConverstationBufferMemory,
- LLM 데이터와 메모리를 연결해 줄 ConversationChain,
- 채팅 메시지를 구현해 줄 AIMessage, HumanMessage, SystemMessage를 불러온다.
◆ ConversationChain 초기화
ConversationChain을 사용하기 위한 값을 불러오는데, 챗봇을 처음 실행할 때 한 번만 초기화하면 되므로, 앞서 모듈 임포트 코드 밑에 작성해 주면 된다.
# LLM과 메모리 초기화
llm = ChatOpenAI(temperature=0.0, model="gpt-3.5-turbo")
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm=llm, memory=memory)
◆ 채팅 및 답변 수정
기존에 작성했던 채팅 및 답변 함수의 내용을 수정한다. 기존의 코드 중 OpenAI API를 호출하는 코드를 삭제하고 랭체인을 활용한 코드를 작성해 준다.
# 상담봇 - 채팅 및 답변
def counseling_bot_chat(message, chat_history):
if message == "":
return "", chat_history
else:
result_message=""
if len(chat_history) <= 1:
messages=[
SystemMessage(content="당신은 DUDE의 상담원입니다. 판매 물품과 관련되지 않은 질문에는 정중히 거절하세요."),
AIMessage(content="안녕하세요, Dude 입니다. 상담을 도와드리겠습니다."),
HumanMessage(content=message)
]
result_message=conversation.invoke(input=messages)
else:
result_message=conversation.invoke(input=message)
chat_history.append([message, result_message])
return "", chat_history
- 처음 OpenAI API를 호출할 때는 반드시 SystemMessage를 추가해 주어야 상담봇의 역할이 부여되므로, chat_history 길이가 1 이하인지 구분하여 코드를 실행한다.
- SystemMessage, AIMessage, HumanMessage를 추가한 messages를 생성한다.
- messages를 conversation.invoke에 입력하여 생성된 값을 선언한 result_message에 바인딩한다.
- chat_history의 길이가 1보다 클 경우, 이미 SystemMessage가 추가되어 있는 상태이기 때문에 곧바로 conversation.invoke를 실행한다. input 값에는 해당 함수의 매개변수인 message를 입력한다.
- chat_history에 사용자의 질문과 랭체인으로 얻은 챗GPT의 값을 입력해 준다.
코드를 실행 후 질문을 하면 아웃풋에 에러가 발생하여 코드를 일부 수정했다.
# 상담봇 - 채팅 및 답변
def counseling_bot_chat(message, chat_history):
if not message:
return "", chat_history
# 대화 초기 설정
if not chat_history or chat_history == [[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]]:
chat_history = [[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]]
# OpenAI와의 대화 설정
messages = [
SystemMessage(content="당신은 DUDE의 상담원입니다. 판매 물품과 관련되지 않은 질문에는 정중히 거절하세요."),
]
for human_message, ai_message in chat_history:
if human_message is not None:
messages.append(HumanMessage(content=human_message))
if ai_message is not None:
messages.append(AIMessage(content=ai_message))
messages.append(HumanMessage(content=message))
# 메모리 업데이트 및 응답 생성
memory.chat_memory.clear() # 먼저 이전 메모리를 초기화
for msg in messages:
memory.chat_memory.add_message(msg)
result_message = conversation.run(input=message)
chat_history.append([message, result_message])
return "", chat_history
[코드 설명]
1. 함수 정의 및 입력값 확인:
def counseling_bot_chat(message, chat_history) ~ return "", chat_history
- 함수 counseling_bot_chat는 message (사용자가 입력하는 메시지)와 chat_history (이전에 주고받은 대화의 기록)를 입력으로 받는다.
- 만약 사용자가 메시지를 입력하지 않았다면, 빈 문자열과 현재의 chat_history를 반환한다. 이는 유효하지 않은 입력을 처리하기 위한 부분이다.
2. 대화 초기 설정:
if not chat_history or chat_history == ~ 상담을 도와드리겠습니다."]]
- chat_history가 비어있거나 초기값이면, 챗봇의 인사말을 포함하는 기본값으로 설정한다.
3. OpenAI와의 대화 설정:
messages = ~ messages.append(HumanMessage(content=message))
- 초기 SystemMessage는 챗봇의 역할과 행동 지침을 설정한다.
- 기존의 chat_history에서 각 사용자 메시지(human_message)와 챗봇의 응답(ai_message)을 번갈아 가며 messages 리스트에 추가한다.
- 마지막으로 사용자가 입력한 현재 메시지를 messages 리스트에 추가한다.
4. 메모리 업데이트 및 응답 생성:
memory.chat_memory.clear() ~ return "", chat_history
- memory.chat_memory.clear()를 통해 예전의 대화 기록을 초기화힌다.
- messages 리스트의 각 메시지를 memory.chat_memory에 저장한다.
- conversation.run(input=message)를 호출해 현재 메시지에 대한 챗봇의 응답을 생성한다.
- 생성된 응답을 chat_history에 추가하고, 빈 문자열(텍스트 박스를 초기화하기 위함)과 업데이트된 chat_history를 반환한다.
이렇게 하면 대화 기억 기능이 추가된 챗봇으로 업그레이드 되며, 이전의 대화 내용을 기억하는 것을 확인할 수 있다.
▶ 근데 좀 까칠해졌다.
번역봇 업그레이드
txt 형식이나 pdf 형식의 파일에 있는 글을 번역하는 기능을 추가하기 위해 랭체인의 Document Loader와 그라디오의 UploadButton을 추가해 본다.
◆ 파일 업로드 버튼 추가하기 (txt, pdf 총 2개)
코드의 레이아웃 부분에 아래 코드를 추가한다. (위치는 하단 확인)
with gr.Row():
tb_TXTupload=gr.UploadButton(label="📜 TXT 업로드")
tb_PDFupload=gr.UploadButton(label="📂 PDF 업로드")
◆ 모듈 임포트
챗봇 코드의 상단에 TextLoadeer와 PyPDFLoader를 사용하기 위한 모듈을 불러온다.
from langchain.document_loaders import TextLoader
from langchain.document_loaders import PyPDFLoader
◆ 텍스트 업로드 기능 추가
# 번역봇 함수 밑에 텍스트 파일을 번역하는 기능을 가진 함수를 추가한다.
# 번역봇 - Text 업로드
def translate_bot_Text_upload(files):
loader=TextLoader(files)
document=loader.load()
return document[0].page_content
- translate_bot_Text_upload 함수를 생성한 후 인자값에 files 추가. files는 UploadButton 컴포넌트를 통해 텍스트 파일을 받게 된다.
- TextLoader(files) 로 파일을 파싱한 값을 loader에 바인딩한 후, loader.load()로 텍스트를 불러와 document 변수에 바인딩한다.
- document의 0번 인덱스에서 page_content 속성을 불러와 return 해 준다.
※ Text Loader의 경우 encoding을 지정해 줘야 한다.
따라서, loader 부분에 encoding 매개변수를 아래와 같이 추가한다.
# 번역봇 - Text 업로드
def translate_bot_Text_upload(files):
loader=TextLoader(files, encoding='UTF-8')
document=loader.load()
return document[0].page_content
◆ PDF 업로드 기능 추가
PDF 파일을 번역하는 기능을 가진 translate_bot_PDF_upload() 함수를 생성하여 # 번역봇 - Text 업로드 코드 밑에 코드를 작성한다.
# 번역봇 - PDF 업로드
def translate_bot_PDF_upload(files):
loader=PyPDFLoader(files)
pages=loader.load_and_split()
return pages[0].page_content
- translate_bot_PDF_upload 함수를 생성한 후 인자값에 files를 추가. files는 UploadButton 컴포넌트를 통해 PDF 파일을 받게 된다.
- PyPDFLoader(files)로 파일을 파싱한 값을 loader에 바인딩한 후 loader.load_and_split 함수로 텍스트를 불러와 pages 변수에 바인딩한다.
- pages의 0번 인덱스에서 page_content 속성을 불러와 return 해 준다.
◆ 이벤트 리스너 추가
함수를 불러올 이벤트 리스너를 레이아웃의 #소설봇 위에 추가한다. (하단 이미지 참고)
# Text 파일 업로드
tb_TXTupload.upload(
fn=translate_bot_Text_upload,
inputs=tb_TXTupload,
outputs=tb_input_text
)
# PDF 파일 업로드
tb_PDFupload.upload(
fn=translate_bot_PDF_upload,
inputs=tb_PDFupload,
outputs=tb_input_text
)
이제 파일이 정상적으로 업로드되는지 확인해 본다.
txt, PDF 모두 업로드가 잘 되며, 번역도 잘 된다.
업그레이드 최종 코드
전체 코드는 다음과 같다.
# 챗봇 업그레이드
import gradio as gr
import os
import openai
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain.document_loaders import TextLoader, PyPDFLoader
# OpenAI 클라이언트 설정
openai_api_key = os.getenv('OPENAI_API_KEY')
# API 키가 환경변수에 설정되어 있는지 확인.
if not openai_api_key:
raise ValueError("환경변수 'OPENAI_API_KEY'가 설정되지 않았습니다.")
# OpenAI API 키를 설정.
openai.api_key = openai_api_key
# LLM과 메모리 초기화
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm=llm,
memory=memory)
# 상담봇 - 채팅 및 답변
def counseling_bot_chat(message, chat_history):
if not message:
return "", chat_history
# 대화 초기 설정
if not chat_history or chat_history == [[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]]:
chat_history = [[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]]
# OpenAI와의 대화 설정
messages = [
SystemMessage(content="당신은 DUDE의 상담원입니다. 판매 물품과 관련되지 않은 질문에는 정중히 거절하세요."),
]
for human_message, ai_message in chat_history:
if human_message is not None:
messages.append(HumanMessage(content=human_message))
if ai_message is not None:
messages.append(AIMessage(content=ai_message))
messages.append(HumanMessage(content=message))
# 메모리 업데이트 및 응답 생성
memory.chat_memory.clear() # 먼저 이전 메모리를 초기화
for msg in messages:
memory.chat_memory.add_message(msg)
result_message = conversation.run(input=message)
chat_history.append([message, result_message])
return "", chat_history
# 상담봇 - 되돌리기
def counseling_bot_undo(chat_history):
if len(chat_history) > 1:
chat_history.pop()
return chat_history
# 상담봇 - 초기화
def counseling_bot_reset(chat_hisotry):
chat_history=[[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]]
return chat_history
# 번역봇
def translate_bot(output_conditions, output_language, input_text):
if input_text == "":
return ""
else:
if output_conditions == "":
output_conditions=""
else:
output_conditions="번역할 때의 조건은 다음과 같습니다." + output_conditions
completion = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "당신은 번역가입니다. 입력한 언어를 설명 없이 곧바로 {0}로 번역해서 알려 주세요.\
번역이 불가능한 언어라면 번역이 불가하다고 말한 후 그 이유를 설명해 주세요. {1}".format(output_language, output_conditions)},
{"role": "user", "content":input_text}
])
return completion.choices[0].message.content
# 번역봇 - Text 업로드
def translate_bot_Text_upload(files):
loader=TextLoader(files, encoding='UTF-8')
document=loader.load()
return document[0].page_content
# 번역봇 - PDF 업로드
def translate_bot_PDF_upload(files):
loader=PyPDFLoader(files)
pages=loader.load_and_split()
return pages[0].page_content
# 소설봇
def novel_bot(model, temperature, detail):
completion = openai.chat.completions.create(
model=model,
temperature=temperature,
messages=[
{"role": "system", "content": "당신은 소설가입니다. 요청하는 조건에 맞춰 소설을 작성해 주세요."},
{"role": "user", "content": detail}
])
return completion.choices[0].message.content
# 레이아웃
with gr.Blocks(theme=gr.themes.Default()) as app:
with gr.Tab("상담봇"):
gr.Markdown(
value="""
# <center>상담봇</center>
<center> DUDE 상담봇입니다. 판매하는 상품과 관련된 질문에 답변 드립니다.</center>
""")
cb_chatbot=gr.Chatbot(
value=[[None, "안녕하세요, DUDE 입니다. 상담을 도와드리겠습니다."]],
show_label=False
)
with gr.Row():
cb_user_input=gr.Text(
lines=1,
placeholder="입력 창",
container=False,
scale=9
)
cb_send_btn=gr.Button(
value="보내기",
scale=1,
variant="primary",
icon="https://cdn-icons-png.flaticon.com/128/12439/12439334.png"
)
with gr.Row():
gr.Button(value="↪️되돌리기").click(fn=counseling_bot_undo, inputs=cb_chatbot, outputs=cb_chatbot)
gr.Button(value="🔄️초기화").click(fn=counseling_bot_reset, inputs=cb_chatbot, outputs=cb_chatbot)
# 보내기1
cb_send_btn.click(fn=counseling_bot_chat, inputs=[cb_user_input, cb_chatbot], outputs=[cb_user_input, cb_chatbot])
# 보내기2
cb_user_input.submit(fn=counseling_bot_chat, inputs=[cb_user_input, cb_chatbot], outputs=[cb_user_input, cb_chatbot])
with gr.Tab("번역봇"):
gr.Markdown(
value="""
# <center>번역봇</center>
<center>다국어 번역봇입니다.</center>
""")
with gr.Row():
tb_output_conditions=gr.Text(
label="번역 조건",
placeholder="예시: 자연스럽게",
lines=1,
max_lines=3
)
tb_output_language=gr.Dropdown(
label="출력 언어",
choices=["한국어", "영어", "일본어", "중국어"],
value="한국어",
allow_custom_value=True,
interactive=True
)
with gr.Row():
tb_TXTupload=gr.UploadButton(label="📜 TXT 업로드")
tb_PDFupload=gr.UploadButton(label="📂 PDF 업로드")
tb_submit=gr.Button(
value="번역 하기",
variant="primary"
)
with gr.Row():
tb_input_text=gr.Text(
placeholder="번역할 내용을 적어 주세요.",
lines=10,
max_lines=20,
show_copy_button=True,
label=""
)
tb_output_text=gr.Text(
lines=10,
max_lines=20,
show_copy_button=True,
label="",
interactive=False
)
tb_submit.click(
fn=translate_bot,
inputs=[tb_output_conditions,
tb_output_language,
tb_input_text],
outputs=tb_output_text
)
# Text 파일 업로드
tb_TXTupload.upload(
fn=translate_bot_Text_upload,
inputs=tb_TXTupload,
outputs=tb_input_text
)
# PDF 파일 업로드
tb_PDFupload.upload(
fn=translate_bot_PDF_upload,
inputs=tb_PDFupload,
outputs=tb_input_text
)
with gr.Tab("소설봇"):
gr.Markdown(
value="""
# <center>소설봇</center>
<center>소설을 생성해 주는 봇입니다.</center>
""")
with gr.Accordion(label="사용자 설정"):
with gr.Row():
with gr.Column(scale=1):
nb_model=gr.Dropdown(
label="모델 선택",
choices=["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4",
"gpt-4-32k", "gpt-4-1106-preview"],
value="gpt-4-1106-preview",
interactive=True
)
nb_temperature=gr.Slider(
label="창의성",
info="숫자가 높을수록 창의적",
minimum=0,
maximum=2,
step=0.1,
value=1,
interactive=True
)
nb_detail=gr.Text(
container=False,
placeholder="소설의 세부적인 설정을 작성합니다.",
lines=8,
scale=4
)
nb_submit=gr.Button(
value="생성하기",
variant="primary"
)
nb_output=gr.Text(
label="",
placeholder="이곳에 소설의 내용이 출력됩니다.",
lines=10,
max_lines=200,
show_copy_button=True
)
# 보내기
nb_submit.click(
fn=novel_bot,
inputs=[nb_model, nb_temperature, nb_detail],
outputs=nb_output
)
app.launch()
다음 내용
[출처]
Hey, 파이썬! 생성형 AI 활용 앱 만들어줘
StackOverFlow
Gradio 홈페이지
Langchain 홈페이지
OpenAI API 홈페이지
'[파이썬 Projects] > <파이썬 Gen AI>' 카테고리의 다른 글
[Gen AI] 문서 요약 프롬프트 제작 (0) | 2024.12.20 |
---|---|
[Gen AI] 음성 변환 기술 구현해보기(STT, TTS) (1) | 2024.12.19 |
[Gen AI] 랭체인을 활용한 챗봇 업그레이드 - 3 (2) | 2024.12.13 |
[Gen AI] 랭체인을 활용한 챗봇 업그레이드 - 2 (4) | 2024.12.13 |
[Gen AI] 랭체인을 활용한 챗봇 업그레이드 - 1 (0) | 2024.12.13 |