TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 Gen AI>

[Gen AI] 그라디오로 두 번째 챗봇 제작하기-2

by 기록자_Recordian 2024. 12. 21.
728x90
반응형
이전 내용
 

[Gen AI] 그라디오로 두 번째 챗봇 제작하기-1

이전 내용 [Gen AI] 문서 요약 프롬프트 제작이전 내용 [Gen AI] 음성 변환 기술 구현해보기(STT, TTS)이전 내용 [Gen AI] 그라디오 챗봇 업그레이드그라디오로 제작한 챗봇 시리즈 [Gen AI] 그라디오로

puppy-foot-it.tistory.com

 

그라디오와 랭체인을 활용해 음성 인식봇, 문서 요약봇, 일정 관리봇을 제작해 본다.


문서 요약봇 제작하기

 

◆ 문서 요약봇 레이아웃 구성하기

탭을 제외한 문서 요약봇의 레이아웃은 아래와 같이 세로로 4블록이 쌓여 있고 각각 하나의 row로 구성되어 있다. 나중을 위해 각각의 row로 컴포넌트를 설정한다.

	with.gr.Tab("음성 인식봇"):
    ....
	with gr.Tab("문서 요약봇"):
        with gr.Row():
            #1
            gr.Markdown()
        with gr.Row():
            #2
            gr.File()
        with gr.Row():
            #3
            gr.Button()
        with gr.Row():
            #4
            gr.Textbox()

 

- 문서 요약봇의 타이틀 및 설명

마크다운을 이용해 문서 요약봇의 헤드라인을 지정해 주고, 하나씩 컴포넌트를 추가하며 레이아웃을 완성시켜 나간다.

            gr.Markdown(
                value="""
                # <center>문서 요약봇</center>
                <center> AI 인공지능 비서 DUDE 입니다. PDF를 업로드하면 내용을 번역해 드립니다.</center>
                """
            )

 

- PDF 파일 업로드 컴포넌트 구현

해당 컴포넌트는 앞서 뼈대를 구성하는 코드에서 gr.File()을 통해 추가되었다.

 

- 버튼 및 요약 결과 출력 구현

PDF 요약된 텍스트 출력을 위한 텍스트 컴포넌트와 [제출] 버튼을 추가한다.

        with gr.Row():
            #3
            gr.Button(value="문서 요약하기")
        with gr.Row():
            #4
            gr.Textbox(
                label="PDF 요약",
                placeholder="PDF 요약 내용입니다.",

 

각 컴포넌트를 추가하고 나면 아래와 같이 된다.


◆ 문서 요약봇 레이아웃 구성하기 - 상세 설정

각각의 컴포넌트에 변수명과 구체적인 매개변수를 설정해 준다.

gr.Textbox 에는 lines=8 매개변수도 추가해 준다.

            summary=gr.Textbox(
                label="PDF 요약",
                lines=8,
                placeholder="PDF 요약 내용입니다.",
            )

◆ 문서 요약봇 기능 구현하기

 

- 챗봇 기능 추가

  • 문서 내용 읽어오기
  • 문서 내용 요약하기 - 랭체인 활용
  • 이벤트 리스너 추가하기

랭체인의 필요한 모듈을 import 한다.

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import PromptTemplate
from langchain.chains import MapReduceDocumentsChain

 

[문서 내용 읽어오기]

기본적인 PDF 문서를 불러오는 함수를 구현한다.

def pdf_loader(pdf_path):
    loader=PyPDFLoader(pdf_path)
    pdf_doc=loader.load()
    return pdf_doc
  • loader=PyPDFLoader(pdf_path): pdf_path를 통해 loader 설정
  • pdf_doc=loader.load(): load()를 통해 pdf 파일의 내용을 읽어온다.

[문서 내용 요약하기 - 랭체인 활용]

def pdf_bot_chatbot(pdf_path):
    pages_content=pdf_loader(pdf_path)
    text_splitter=RecursiveCharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=3000,
        chunk_overlap=0,
    )
    # pages_content 내용 분할
    split_docs=text_splitter.split_documents(pages_content)
    # 분할된 문서의 수
    # map template 설정, {pages_content} 분할된 내용이 입력
    map_template=""" 다음은 문서 중 일부 내용입니다.
    {pages_content}
    이 문서의 주요 내용을 요약해 주세요.
    """
    # map 기본 프롬프트
    map_prompt=PromptTemplate.from_template(map_template)
    # 문서 내용이 길 수 있기 때문에 model을 gpt-3.5-turbo-16k로 설정
    llm=ChatOpenAI(temperature=0,
                   model_name='gpt-3.5-turbo-16k')
    map_chain=LLMChain(llm=llm, prompt=map_prompt)

    # reduce 단계에서 처리할 프롬프트 정의
    reduce_template="""다음은 문서 요약의 집합입니다.
    {summaries}
    이 내용을 바탕으로 통합된 문서 요약을 작성해 주세요.
    """
    # reduce 프롬프트
    reduce_prompt=PromptTemplate.from_template(reduce_template)

    # reduce에서 수행할 LLMChain 정의
    reduce_chain=LLMChain(llm=llm, prompt=reduce_prompt)
    from langchain.chains.combine_documents.stuff import StuffDocumentsChain
    from langchain.chains import ReduceDocumentsChain

    # 문서 목록 통합 체인 설정
    combine_doc_chain=StuffDocumentsChain(
        llm_chain=reduce_chain,
        document_variable_name="summaries" # reduce 프롬프트에 대입되는 변수
    )

    # 분할된 문서 순차적으로 Reduce 처리
    reduce_doc_chain=ReduceDocumentsChain(
        combine_documents_chain=combine_doc_chain,
        collapse_documents_chain=combine_doc_chain,
        token_max=4000, # 토큰 최대 개수 설정
    )

    # 최종 체인 연결
    final_chain=MapReduceDocumentsChain(
        llm_chain=map_chain, # 각 문서 맵핑
        reduce_documents_chain=reduce_doc_chain,
        document_variable_name="pages_content",
        return_intermediate_steps=False,
    )
    # 최종 결과 실행
    result_summary=final_chain.run(split_docs)
    # 요약 결과 출력
    return result_summary

 

[이벤트 리스너 추가하기]

문서 요약봇의 이벤트 리스너는 '문서 요약 버튼 클릭' 하나이다.

작성 위치는 app.launch() 바로 위에 작성하면 된다.

            summary_btn.click(
                fn=pdf_bot_chatbot,
                inputs=[pdf_input],
                outputs=[summary]
            )

문서 요약봇 최종 레이아웃 및 전체 코드
(음성 인식봇 포함)

 

from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferWindowMemory
from langchain.prompts import(
    ChatPromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
import os
import openai

# 환경변수에서 OpenAI API 키를 읽어옴.
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

# OpenAI 클라이언트를 생성.
client = openai.OpenAI()

system_ai_ko = "당신은 인공지능 비서야, pdf 일기, 문서 요약하기, 일정 관리, 날씨, 최단 경로 검색, 웹 검색 등 다양한 내용에 답변할 수 있어야 해."
system_setting = SystemMessagePromptTemplate.from_template(system_ai_ko)

# 프롬프트 설정
voice_bot_prompt = ChatPromptTemplate.from_messages([
    system_setting,
    MessagesPlaceholder(variable_name="DUDE"),
    HumanMessagePromptTemplate.from_template("{master_user}")])

# 메모리 설정
voice_bot_memory = ConversationBufferWindowMemory(memory_key="DUDE",
                                                  ai_prefix="AI 비서 DUDE",
                                                  human_prefix="사용자:",
                                                  return_messages=True,
                                                  k=10)

# llm 모델 설정
chatgpt = ChatOpenAI(
    temperature=0,
    max_tokens=2048,
    model_name='gpt-3.5-turbo'
)

# llmchain 정의
conversation=LLMChain(
    prompt=voice_bot_prompt,
    memory=voice_bot_memory,
    llm=chatgpt,
    verbose=True
)

from pydub import AudioSegment
import numpy as np
def voice_bot_handle_audio(audio_record):
    save_file_path="voice.mp3"
    frame_rate=audio_record[0]
    audio_data=audio_record[1].tobytes()
    sample_width=audio_record[1].dtype.itemsize
    audio=AudioSegment(
        audio_data,
        frame_rate=frame_rate,
        sample_width=sample_width,
        channels=1,
    )
    audio.export(save_file_path, format="mp3")

def voice_bot_create_stt():
    file_path="voice.mp3"
    audio_file=open(file_path, "rb")
    transcript=client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file)
    return transcript.text

def voice_bot_create_audio(text):
    response=client.audio.speech.create(
        model="tts-1",
        voice="onyx",
        input=text)
    speech_file_path="output.mp3"
    with open(speech_file_path, "wb") as audio_file:
        audio_file.write(response.content)

def voice_bot_chat(message, cb_user_input_audio, chat_history):
    if cb_user_input_audio:
        message=voice_bot_create_stt()
    ai_answer=conversation({"master_user":message})['text']
    chat_history.append((message, ai_answer))
    audio_file=voice_bot_create_audio(ai_answer)
    return "", audio_file, chat_history

def voice_bot_undo(chat_history):
    if len(chat_history) > 1:
        chat_history.pop()
    return chat_history

def voice_bot_reset(chat_history):
    global voice_bot_memory
    chat_init=[[None, "안녕하세요, 인공지능 비서 DUDE 입니다. 무엇이든 시켜만 주세요."]]
    voice_bot_memory.clear() # 메모리 초기화
    return "", chat_init

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import PromptTemplate
from langchain.chains import MapReduceDocumentsChain

def pdf_loader(pdf_path):
    loader=PyPDFLoader(pdf_path)
    pdf_doc=loader.load()
    return pdf_doc

def pdf_bot_chatbot(pdf_path):
    pages_content=pdf_loader(pdf_path)
    text_splitter=RecursiveCharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=3000,
        chunk_overlap=0,
    )
    # pages_content 내용 분할
    split_docs=text_splitter.split_documents(pages_content)
    # 분할된 문서의 수
    # map template 설정, {pages_content} 분할된 내용이 입력
    map_template=""" 다음은 문서 중 일부 내용입니다.
    {pages_content}
    이 문서의 주요 내용을 요약해 주세요.
    """
    # map 기본 프롬프트
    map_prompt=PromptTemplate.from_template(map_template)
    # 문서 내용이 길 수 있기 때문에 model을 gpt-3.5-turbo-16k로 설정
    llm=ChatOpenAI(temperature=0,
                   model_name='gpt-3.5-turbo-16k')
    map_chain=LLMChain(llm=llm, prompt=map_prompt)

    # reduce 단계에서 처리할 프롬프트 정의
    reduce_template="""다음은 문서 요약의 집합입니다.
    {summaries}
    이 내용을 바탕으로 통합된 문서 요약을 작성해 주세요.
    """
    # reduce 프롬프트
    reduce_prompt=PromptTemplate.from_template(reduce_template)

    # reduce에서 수행할 LLMChain 정의
    reduce_chain=LLMChain(llm=llm, prompt=reduce_prompt)
    from langchain.chains.combine_documents.stuff import StuffDocumentsChain
    from langchain.chains import ReduceDocumentsChain

    # 문서 목록 통합 체인 설정
    combine_doc_chain=StuffDocumentsChain(
        llm_chain=reduce_chain,
        document_variable_name="summaries" # reduce 프롬프트에 대입되는 변수
    )

    # 분할된 문서 순차적으로 Reduce 처리
    reduce_doc_chain=ReduceDocumentsChain(
        combine_documents_chain=combine_doc_chain,
        collapse_documents_chain=combine_doc_chain,
        token_max=4000, # 토큰 최대 개수 설정
    )

    # 최종 체인 연결
    final_chain=MapReduceDocumentsChain(
        llm_chain=map_chain, # 각 문서 맵핑
        reduce_documents_chain=reduce_doc_chain,
        document_variable_name="pages_content",
        return_intermediate_steps=False,
    )
    # 최종 결과 실행
    result_summary=final_chain.run(split_docs)
    # 요약 결과 출력
    return result_summary


import gradio as gr
with gr.Blocks() as app:
    with gr.Tab("음성 인식봇"):
        with gr.Column():
            #1
            gr.Markdown(
                value="""
                # <center>음성 인식봇</center>
                <center>AI 인공지능 비서 DUDE 입니다.
                음성으로 묻거나, 문서 요약, 일정 관리를 할 수 있습니다.</center>
                """
            )
            #2
            cb_chatbot=gr.Chatbot(
                value=[[None, "안녕하세요, 인공지능 비서 DUDE 입니다. 무엇이든 시켜만 주세요."]],
                show_label=False
            )
        with gr.Row():
            #3
            cb_user_input=gr.Textbox(
                lines=1,
                placeholder="입력 창",
                container=False,
                scale=7
            )
            #4
            cb_audio_record=gr.Audio(
                sources=["microphone"],
                format="mp3",
                scale=1,
                min_width=200,
                label="음성을 입력해 주세요."
            )
            # 음성 출력
            cb_audio_chatbot=gr.Audio(autoplay=True,
                                      visible=False)
            #5
            cb_submit_btn=gr.Button(
                value="보내기",
                scale=1,
                visible="primary",
                icon="https://cdn-icons-png.flaticon.com/128/12439/12439334.png"
            )
    # 음성 녹음 완료
        cb_audio_record.stop_recording(
            fn=voice_bot_handle_audio,
            inputs=[cb_audio_record]
        )
    # [보내기] 버튼 클릭
        cb_submit_btn.click(
            fn=voice_bot_chat,
            inputs=[cb_user_input, cb_audio_record, cb_chatbot],
            outputs=[cb_user_input, cb_audio_record, cb_chatbot]
        )
    # ENTER 제출 입력
        cb_user_input.submit(
            fn=voice_bot_chat,
            inputs=[cb_user_input, cb_audio_record, cb_chatbot],
            outputs=[cb_user_input, cb_audio_record, cb_chatbot]
        )
        with gr.Row():
            #6
            gr.Button(value="↪️되돌리기").click(
                fn=voice_bot_undo,
                inputs=[cb_chatbot],
                outputs=[cb_chatbot]
            )
            #7
            gr.Button(value="🔄️초기화").click(
                fn=voice_bot_reset,
                inputs=[cb_chatbot],
                outputs=[cb_chatbot]
            )
    with gr.Tab("문서 요약봇"):
        with gr.Row():
            #1
            gr.Markdown(
                value="""
                # <center>문서 요약봇</center>
                <center> AI 인공지능 비서 DUDE 입니다. PDF를 업로드하면 내용을 번역해 드립니다.</center>
                """
            )
        with gr.Row():
            #2
            pdf_input=gr.File()
        with gr.Row():
            #3
            summary_btn=gr.Button(value="문서 요약하기")
        with gr.Row():
            #4
            summary=gr.Textbox(
                label="PDF 요약",
                lines=8,
                placeholder="PDF 요약 내용입니다.",
            )
            summary_btn.click(
                fn=pdf_bot_chatbot,
                inputs=[pdf_input],
                outputs=[summary]
            )
            
app.launch()

 

다음에는 일정 관리봇을 제작해 본다.

 


다음 내용

 

[Gen AI] 그라디오로 두 번째 챗봇 제작하기-3

이전 내용 [Gen AI] 그라디오로 두 번째 챗봇 제작하기-1이전 내용 [Gen AI] 문서 요약 프롬프트 제작이전 내용 [Gen AI] 음성 변환 기술 구현해보기(STT, TTS)이전 내용 [Gen AI] 그라디오 챗봇 업그레이

puppy-foot-it.tistory.com


[출처]

Hey, 파이썬! 생성형 AI 활용 앱 만들어줘

728x90
반응형