이 글은 여러 형태의 RAG중 1개의 예제로 구성하는 형태에 대한 설명을 위한 글입니다.

2025년 2/4분기에 큰 인기를 얻는 Notebook-LM처럼 만드려고 했지만, 다 로컬(HP notebook)에서 실행해야하기에 가능한 부분만 구현하였습니다.
Ollama와 4Gbyte크기의 LLM으로만 구현하여, 인터넷과 무관하게 동작합니다.
생성 시, 참조하는 위치에 대한 정보는 오른쪽에 해당 서적, 해당 페이지를 표시하는 방법으로 변경하였습니다.

GUI는 ‘Streamlit’로 구현했습니다.
주요 기능
– 질문언어와 답변언어를 분리하였습니다.
– 원하는 서적에서 검색하도록 제한 할 수 있습니다.
– 생성을 위한 과정에서 어떤 서적의 어떤 페이지를 참조하는지 히스토리를 볼 수 있습니다.
– 여러 명이 동시에 사용할 수 있도록 작성 되었습니다.
– 관련 문서 찾는 기능과 질문에 대한 답변을 생성하는 기능을 LangChain에 권장 구조로 작성했고, 2차에 걸친 검토 후에 답변하도록 되어 있습니다.

(Operating Environment: Laptop from 2022, 8GB RAM, GTX 1650 VRAM 4GB)

(WEB 시스템의 사정으로 이미지가 분할됩니다.)



LLM을 이용하여, PDF내용을 검색하는 화면입니다 (LLM Search).
주요기능
– DB화 된 PDF를 선택적으로 검색할 수 있습니다.
– DB마다 검색할 페이지 수와 사용자에게 표시할 페이지 수를 선택할 수 있습니다.
– 검색된 페이지를 번역하여 내용을 알 수 있게 합니다.

이런 코드는 AI 서비스를 이용해서 만드시면 됩니다.
DeepSeek를 질문을 해서 코드를 만드는 것을 올립니다.

import streamlit as st
import os

# 세션 상태 초기화
if 'messages' not in st.session_state:
    st.session_state.messages = []
if 'answer' not in st.session_state:
    st.session_state.answer = ""
if 'selected_language' not in st.session_state:
    st.session_state.selected_language = "Korean"
if 'selected_book' not in st.session_state:
    st.session_state.selected_book = ""

# 페이지 설정
st.set_page_config(layout="wide")

# 책 목록 파일 읽기 함수
def load_booklist():
    try:
        with open('booklist.txt', 'r', encoding='utf-8') as f:
            return [line.strip() for line in f.readlines()]
    except FileNotFoundError:
        st.error("booklist.txt 파일을 찾을 수 없습니다.")
        return []
    except Exception as e:
        st.error(f"파일 읽기 오류: {str(e)}")
        return []

# 레이아웃 구성
col1, col2, col3 = st.columns([0.2, 0.5, 0.3])

# 좌측 컬럼 (설정 및 책 선택)
with col1:
    st.header("Settings")
    
    # 언어 선택
    language = st.radio(
        "Select Language",
        ["Korean", "English"],
        index=0 if st.session_state.selected_language == "Korean" else 1
    )
    st.session_state.selected_language = language
    
    # 책 목록 로드 및 선택
    books = load_booklist()
    if books:
        selected_book = st.selectbox(
            "Select a Book",
            books,
            index=books.index(st.session_state.selected_book) if st.session_state.selected_book in books else 0
        )
        st.session_state.selected_book = selected_book

# 중앙 컬럼 (질문 & 답변)
with col2:
    st.header("Q&A")
    
    # 질문 입력
    question = st.text_input("Enter your question:")
    
    # 답변 표시 영역
    answer_placeholder = st.empty()
    if st.session_state.answer:
        answer_placeholder.markdown(st.session_state.answer)
    
    # 처리 버튼 (실제 RAG 연결 부분)
    if st.button("Submit"):
        # 여기에 실제 RAG 처리 코드 추가
        mock_answer = f"Selected: {st.session_state.selected_book} ({language})\n\nQuestion: {question}\n\nAnswer: This is a sample response."
        st.session_state.answer = mock_answer
        answer_placeholder.markdown(mock_answer)
        
        # 메시지 패널에 로그 추가
        st.session_state.messages.append(f"Question processed: {question[:20]}...")

# 우측 컬럼 (메시지 패널)
with col3:
    st.header("Processing Messages")
    
    # 메시지 표시 영역
    messages_placeholder = st.container()
    
    # 메시지 표시
    with messages_placeholder:
        for msg in st.session_state.messages[-10:]:  # 최근 10개 메시지만 표시
            st.info(f"• {msg}")

    # 메시지 초기화 버튼
    if st.button("Clear Messages"):
        st.session_state.messages = []
        st.experimental_rerun()

윤영기 (尹泳祺, YOON, Young-Ki)
younggiyoon@hotmail.com
newton@eqboard.com

By neoy2g