본문 바로가기

대외활동/OSSCA

[OSSCA] 오픈소스 컨트리뷰션 아카데미 5주차 후기 및 정리

오픈소스 컨트리뷰션 아카데미 5주차 정리

이번 5주차 모임에서는 Streamlit을 활용해 LLM 챗봇의 기본 틀을 만드는 수업을 진행했습니다. Streamlit의 직관적인 UI와 스크립트 기반으로 수업이 진행되어, 시각적으로 확인하고 직접 작성하는 부분이 많아 매우 흥미로운 시간이었습니다.

Streamlit이란?

Streamlit은 파이썬 기반의 오픈 소스 웹 애플리케이션 프레임워크로, 데이터 과학자나 개발자가 간단하게 대화형 웹 애플리케이션을 만들 수 있도록 도와줍니다. 복잡한 웹 개발 지식 없이도 몇 줄의 코드로 데이터를 시각화하고, 슬라이더, 버튼 등을 이용해 사용자와 상호작용할 수 있는 대시보드를 만들 수 있습니다. 주로 데이터 분석, 머신러닝 모델 시각화, 프로토타입 제작에 많이 활용됩니다.

VSCode 세팅

파이썬 버전 다운로드

최소 필요 버전은 Python 3.7 이상이며, 현 시점 가장 최신 버전은 Python 3.13.0 입니다. 높은 버전의 파이썬 버전을 다운로드 받으시는 것을 추천드립니다.

파이썬 인터프리터 설정

파이썬 버전을 다운받았다면, VSCode에서 Python 인터프리터를 설정해주어야 합니다. 먼저 VSCode의 명령어 팔레트를 열어줍니다. 명령어 팔레트는 Ctrl/cmd + Shift + P를 눌러 쉽게 열 수 있습니다.

python select interpreter

명령어 팔레트에 위와 같이 입력한 후 방금 다운로드받은 파이썬 최신 버전을 인터프리터로 선택해줍니다.

이후 Ctrl/cmd + ` 를 눌러 VSCode에서 터미널을 열고, 아래 명령어를 입력하여 필요한 패키지를 설치합니다. 주석 처리된 글은 제외하고 pip 명령어가 있는 줄을 실행하시면 됩니다.

// llama-index 패키지 설치
pip install streamlit llama-index

// llama-index-ollama 패키지 설치
pip install llama-index-llms-ollama

구현 스크립트 작성

스크립트 작성 및 실행

필요한 패키지를 모두 설치한 후, Streamlit을 활용하여 Local LLM을 구동하는 스크립트를 작성했습니다. 이 스크립트는 이전에 설치한 OLLAMA 3.2 모델을 기반으로 LLM을 사용합니다.

import streamlit as st
from llama_index.core.llms import ChatMessage
import logging
import time
from llama_index.llms.ollama import Ollama

logging.basicConfig(level=logging.INFO)

if 'messages' not in st.session_state:
        st.session_state.messages = []

def stream_chat(model, messages):
      try:
            llm = Ollama(model=model, request_timeout=120.0)
            resp = llm.stream_chat(messages)
            response = ""
            response_placeholder = st.empty()

            for r in resp:
                  response += r.delta
                  response_placeholder.write(response)
            logging.info(f"Model: {model}, Messages: {messages}, Response: {response}")
            return response
      except Exception as e:
            logging.error(f"스트리밍 에러 발생: {str(e)}")
            raise e

def main():
      st.title("LLM 모델과 채팅하기")
      logging.info("앱 시작")

      model = st.sidebar.selectbox("모델을 선택해주세요", ["llama3.2"])
      logging.info(f"선택한 모델: {model}")

      if prompt := st.chat_input("질문해주세요"):
            st.session_state.messages.append({"role": "user", "content": prompt})
            logging.info(f"유저 인풋: {prompt}")

            for message in st.session_state.messages:
                  with st.chat_message(message["role"]):
                        st.write(message["content"])

            if st.session_state.messages[-1]["role"] != "assistant":
                  with st.chat_message("assistant"):
                    start_time = time.time()
                    logging.info("응답 생성중")

            with st.spinner("응답 생성하는 중.."):
                try:
                      messages = [ChatMessage(role=msg["role"], content=msg["content"]) for msg in st.session_state.messages]
                      response_message = stream_chat(model, messages)
                      duration = time.time() - start_time
                      respone_message_with_duration = f"{response_message}\n\nDuration: {duration:.2f} seconds"
                      st.session_state.messages.append({"role": "assistant", "content": respone_message_with_duration})
                      st.write(f"응답 시간: {duration:.2f} 초")               
                
                except Exception as e:
                      st.session_state.messasges.append({"role": "assistant", "content": str(e)})
                      logging.error(f"에러 발생: {str(e)}")

if __name__ == "__main__":
    main()

위 코드를 파일로 저장한 후, 아래 명령어를 사용해 파일을 실행하면 로컬 브라우저에서 애플리케이션을 확인할 수 있습니다.

Streamlit run <fileName>.py

그러면 다음과 같은 브라우저 창이 나타납니다.

이제 필요한 작업은 모두 완료되었습니다. 이 상태로도 충분히 사용 가능하며, 현재는 LLama 3.2 모델만 있지만, 추가로 필요한 모델을 다운로드하여 원하는 작업에 활용할 수 있습니다. 다른 모델들은 Hugging Face라는 오픈소스 커뮤니티 사이트에서 확인할 수 있습니다.

그리고, 현재 사용 중인 llama3.2 모델의 경우 한국어 답변이 미흡한데, 서울과학기술대학교에서 한국어를 지원하는 모델을 만들었다고 하여 함께 소개드립니다.

여기서 제공하는 모델은 곧바로 우리가 사용 중인 스크립트에 적용할 수는 없는 것 같아서, 이를 적용하기 위한 방법을 차후 포스팅해볼까 합니다.

구현 스크립트 상세 분석

1. 라이브러리 및 초기 설정

import streamlit as st
from llama_index.core.llms import ChatMessage
import logging
import time
from llama_index.llms.ollama import Ollama

# 로깅 기본 설정
logging.basicConfig(level=logging.INFO)

# 세션 상태에 메시지 초기화
if 'messages' not in st.session_state:
        st.session_state.messages = []
  • Streamlit, Llama 모델, 로깅 기능을 가져옵니다.
  • 로깅을 통해 정보와 에러를 기록할 수 있도록 설정합니다.
  • 세션 상태에 messages라는 변수가 없다면 초기화하여 채팅 기록을 저장할 수 있도록 합니다.

2. 스트리밍 채팅 함수

def stream_chat(model, messages):
      try:
            # Ollama 모델 호출 및 타임아웃 설정
            llm = Ollama(model=model, request_timeout=120.0)
            # 모델에게 메시지 전달 후 응답 스트리밍
            resp = llm.stream_chat(messages)
            response = ""
            response_placeholder = st.empty()

            # 스트리밍되는 응답을 실시간으로 업데이트
            for r in resp:
                  response += r.delta
                  response_placeholder.write(response)
            logging.info(f"Model: {model}, Messages: {messages}, Response: {response}")
            return response
      except Exception as e:
            # 에러 발생 시 로깅
            logging.error(f"스트리밍 에러 발생: {str(e)}")
            raise e
  • Ollama 모델 객체를 생성하고, 주어진 메시지에 대한 답변을 실시간으로 스트리밍합니다.
  • 스트리밍된 데이터를 차곡차곡 합쳐 사용자에게 실시간으로 표시합니다.
  • 에러가 발생할 경우 로깅을 통해 에러 내용을 기록하고 예외를 발생시킵니다.

3. 메인 함수

def main():
      # 애플리케이션 제목 설정
      st.title("LLM 모델과 채팅하기")
      logging.info("앱 시작")

      # 사이드바에서 모델 선택
      model = st.sidebar.selectbox("모델을 선택해주세요", ["llama3.2"])
      logging.info(f"선택한 모델: {model}")

      # 유저 입력이 있으면 메시지 세션에 추가
      if prompt := st.chat_input("질문해주세요"):
            st.session_state.messages.append({"role": "user", "content": prompt})
            logging.info(f"유저 인풋: {prompt}")

            # 메시지를 화면에 출력
            for message in st.session_state.messages:
                  with st.chat_message(message["role"]):
                        st.write(message["content"])

            # 어시스턴트가 아직 답변하지 않았을 경우 답변 생성
            if st.session_state.messages[-1]["role"] != "assistant":
                  with st.chat_message("assistant"):
                    start_time = time.time()
                    logging.info("응답 생성중")

            # 스피너 표시 중 응답 생성
            with st.spinner("응답 생성하는 중.."):
                try:
                      # 유저와 어시스턴트의 메시지를 ChatMessage로 변환
                      messages = [ChatMessage(role=msg["role"], content=msg["content"]) for msg in st.session_state.messages]
                      response_message = stream_chat(model, messages)
                      duration = time.time() - start_time
                      respone_message_with_duration = f"{response_message}\n\nDuration: {duration:.2f} seconds"
                      st.session_state.messages.append({"role": "assistant", "content": respone_message_with_duration})
                      st.write(f"응답 시간: {duration:.2f} 초")               
                
                except Exception as e:
                      # 에러 발생 시 에러 메시지를 추가
                      st.session_state.messasges.append({"role": "assistant", "content": str(e)})
                      logging.error(f"에러 발생: {str(e)}")
  • st.title을 사용해 페이지 제목을 설정합니다.
  • selectbox를 통해 사이드바에서 사용할 모델을 선택할 수 있도록 합니다.
  • 사용자가 질문을 입력하면 **chat_input**을 통해 입력을 받고, 세션에 메시지를 저장한 후 메시지를 화면에 출력합니다.
  • st.spinner는 응답 생성 중임을 사용자에게 시각적으로 보여주며, 실제로 메시지가 처리되고 있을 때는 stream_chat 함수에서 스트리밍으로 실시간 응답을 받습니다.
  • 응답 시간과 함께 어시스턴트의 답변을 화면에 출력하고, 에러 발생 시 에러 메시지를 표시합니다.

4. 실행 코드

if __name__ == "__main__":
    main()
  • 스크립트가 직접 실행될 때 main 함수를 호출합니다.

후기

이번 모임에서는 다운로드와 환경설정을 해야 할 부분이 많아 다소 복잡하게 느껴졌습니다. 그래도 천천히 진행하면서 막히는 부분 없이 멘토님께서 수업을 잘 이끌어 주셨습니다. 저뿐만 아니라 다른 분들도 많이 막히셨는지 질문이 자주 나와서, 조금 여유롭게 수업을 따라갈 수 있었습니다. 이제 정말 제대로 된 Local LLM을 배우고 있다는 느낌이 들었고, 이를 실제로 활용할 수 있는 무언가를 만들면 재미있겠다는 생각이 들었습니다. 매우 유익한 시간이었습니다.