본문 바로가기
AI/RAG

[#RAG] 01. RAG 프로세스 확인과 실습 진행

by dopal2 2026. 3. 1.
반응형

안녕하세요! 잡다한 공방입니다. 지난 포스팅에서 RAG의 이론을 다뤘다면, 오늘은 실제로 로컬 문서를 읽어와 AI에게 특정 분야의 전문 지식을 학습시키는 실습 환경 구축 과정을 정리했습니다.

 

1. RAG의 핵심 프로세스

RAG는 모델이 학습하지 않은 외부 데이터를 참조하여 답변의 정확도를 높이는 기술로, 총 5단계의 과정을 거칩니다.

  1. 문서 로드(Load): docx, pdf 등 다양한 포맷의 데이터를 읽어옵니다.
  2. 문서 분할(Splitting): 토큰 제한 및 처리 효율을 위해 문서를 적절한 크기(Chunk)로 쪼갭니다.
  3. 임베딩 및 벡터 저장: 텍스트를 벡터로 변환하여 벡터 DB(Chroma)에 저장합니다.
  4. 유사도 검색: 질문과 가장 관련성이 높은 문서 조각을 벡터 DB에서 찾아냅니다.
  5. 답변 생성: 검색된 결과와 질문을 결합하여 LLM이 최종 답변을 생성합니다.

 

2. 실습 환경 및 주제 선정

  • 학습 데이터: 정보보호_일반(docx), 정보시스템_네트워크보안(pdf)
  • LLM: Google Gemini (gemini-flash-latest)
  • 프레임워크: LangChain (v0.3 이상 권장)

 

3. 단계별 실습 진행 (Python)

Step 1. 필수 패키지 설치

문서 로딩, 텍스트 분할, 벡터 DB 활용을 위해 다음 패키지들을 설치합니다.

!pip install -U -q docx2txt pypdf langchain-community langchain-text-splitters langchain-chroma langchain-google-genai

Step 2. 문서 로더 생성 및 로드

#로더 생성
from langchain_community.document_loaders import Docx2txtLoader
from langchain_community.document_loaders import PyPDFLoader

# 괜히 문제생길 것 같아서 파일명 변경(정보보호일반>sercurity_info.docx, 정보보호네트워크보안>sercurity_network.pdf)
docx_loader =   Docx2txtLoader('./sercurity_info.docx')
pdf_loader  =   PyPDFLoader('./sercurity_network.pdf')

# 각 문서 로드
docx_document   =   docx_loader.load()
pdf_document    =   pdf_loader.load()

Step 3. 텍스트 분할 (Text Splitting)

문맥 유지를 위해 chunk_overlap을 적절히 설정하여 분할합니다.

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter   =   RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0) #chunk_size는 읽는 범위, chunk_overlap은 중복해서 읽는 범위

#   각 문서 Split 진행
docx_document_list  =   docx_loader.load_and_split(text_splitter=text_splitter)
pdf_document_list   =   pdf_loader.load_and_split(text_splitter=text_splitter)

Step 5. 벡터 DB(Chroma) 저장 및 배치 처리

Step 5. 벡터 DB(Chroma) 저장 및 배치 처리

무료 API 티어의 429 RESOURCE_EXHAUSTED 에러를 방지하기 위해 배치 처리 함수를 구현하는 것이 핵심입니다.

import time
from langchain_chroma import Chroma

# Chroma 생성
vector_database = Chroma(
    embedding_function=embeddings, 
    persist_directory="./chroma_langchain_db" #
)

# 문서 추가 함수
def add_documents(vector_db, document_list, size=5):
    for i in range(0, len(document_list), size):
        document = document_list[i : i + size]
        try:
            vector_db.add_documents(document) #
            print(f"[{i + len(document)} / {len(document_list)}] 저장 진행 중")
            time.sleep(15) # 15초 대기
        except Exception as e:
            print(f"에러 발생: {e}")
            time.sleep(60) # 에러 발생 시 1분 휴식
            vector_db.add_documents(document)

# 3. 저장 실행
add_documents(vector_database, docx_document_list)
add_documents(vector_database, pdf_document_list)

4. 답변 생성 테스트

구축된 지식 베이스에서 유사도 검색을 수행하고 모델에게 전문가 페르소나를 부여해 답변을 받아봅니다.

from langchain_core.prompts import PromptTemplate
from langchain_google_genai import ChatGoogleGenerativeAI

llm =   ChatGoogleGenerativeAI(model="gemini-flash-latest",api_key=“API-KEY는 비밀~”)

prompt  =   PromptTemplate(
    template="""[Identity] 넌 정보보안 전문가야
    [Context] {retrieved_docs}
    [Qustion] {query}""",
    input_variables={"retrieved_docs", "query"}
)

ai_message  =   llm.invoke(prompt.invoke({"retrieved_docs":retrieved_docs,"query":query}))
📢 생성 결과

[{'type': 'text',

  'text': '정보보안 전문가로서 제공해주신 문서를 바탕으로 **정보보호에서의 일반통제(General Control)** 대해 설명해 드리겠습니다.\n\n**일반통제** 정보시스템의 소프트웨어 생명주기 전반에 걸쳐 **모든 애플리케이션에 공통적으로 적용되는 통제** 의미합니다. 특정 개별 애플리케이션에만 국한되지 않고, 조직의 IT 환경 전반을 안전하게 관리하기 위한 기초적인 보안 체계라고 이해하시면 됩니다.\n\n주요 내용은 다음과 같습니다:\n\n1.  **IT 조직 직무 분리**: 권한 오남용을 방지하기 위해 업무 역할을 나누고 관리하는 \n2.  **시스템 개발**: 소프트웨어가 안전하게 개발되고 유지보수되도록 통제하는 \n3.  **논리적 물리적 보안**: 시스템 접근 권한 관리 실제 서버실과 같은 물리적 공간에 대한 보안\n4.  **하드웨어 통제**: 서버, 네트워크 장비 하드웨어 자산에 대한 관리\n5.  **백업 복구**: 데이터 손실에 대비한 정기적인 백업 수행\n6.  **비상계획 수립**: 재해나 장애 발생 서비스 연속성을 보장하기 위한 대응 계획 마련\n\n결과적으로 일반통제는 정보시스템이 안정적이고 신뢰할 있는 환경에서 운영될 있도록 뒷받침하는 **보안의 토대**라고 있습니다.',

  'extras': 

생략

=='}}]

 

5. RetrievalQA chain 생성

RetrievalQA Chain이란? '검색 -> 결합 -> 프롬프트 주입 -> 답변 생성' 복잡한 과정을 하나의 파이프라인으로 연결하는 것입니다.

 

우선 관련 패키지를 설치합니다. 랭체인이 v1로 되면서 그전에 자료들과 맞지 않는 상황이 발생해서 찾은 결과입니다.

# RetrievalQA Chain 활용을 위한 패키지 설치
!pip install -U -q langchain langchainhub langchain-community langchain-classic

 

그리고 프롬프트를 가져와하는데 여기서 LangSmith의 API-KEY가 필요합니다. 그래서 저는 LangSmith 가입하고 키를 발급받았습니다.

from langsmith import Client

client = Client(api_key="LangSmith-API-KEY")
prompt = client.pull_prompt("rlm/rag-prompt", include_model=True)

 

이제 RetrievalQA Chain을 생성을 진행했습니다만, rag-prompt와 retrieval_chain의 변수명이 일치하지 않아 오류가 발생했습니다. 그래서 강제로 변수명을 변경하여 작업했고, 이후 성공한 것을 확인했습니다.

# 현재 버전이 업데이트 됐나봅니다. 검색해도 내용과 다들달라서 langchain_classic을 사용해 진행하겠습니다.
from langchain_classic.chains import create_retrieval_chain
from langchain_classic.chains.combine_documents import create_stuff_documents_chain

# 문자열 치환
prompt.messages[0].prompt.template = prompt.messages[0].prompt.template.replace("{question}", "{input}")

# 요구 변수 목록도 'question'에서 'input'으로 직접 변경해 줍니다.
prompt.messages[0].prompt.input_variables = ["context", "input"]
prompt.input_variables = ["context", "input"]

# 리트리버 설정
retriever = vector_database.as_retriever(search_kwargs={"k": 3})

# 문서 결합 체인
combine_docs_chain = create_stuff_documents_chain(
    llm, 
    prompt, 
    document_variable_name="context"
)

# 최종 검색 체인 (리트리버와 문서 결합 체인을 연결)
rag_chain = create_retrieval_chain(
    retriever, 
    combine_docs_chain
)

response    =   rag_chain.invoke({"input": "일반통제가 뭐야?"})

 

마무리하며

로컬 문서 로드부터 최종 답변 생성까지 RAG의 핵심 과정을 직접 완주해 보았습니다. 특히 무료 API 환경에서 안정적으로 데이터를 주입하는 배치 처리 기법은 실무에서도 매우 유용하게 쓰이는 기술입니다.

다음에는 더 복잡한 에이전트 구조나 하이브리드 검색 방식을 통해 시스템을 고도화하는 과정을 다뤄보겠습니다.

🗝️ 참고: .env 설정

# Google API Key
GOOGLE_API_KEY=YOUR_GEMINI_KEY
GOOGLE_MODEL_NAME=gemini-flash-latest

# Ollama Model
OLLAMA_MODEL_NAME=deepseek-r1:1.5b
OLLAMA_BASE_URL=http://localhost:11434

 

아래와 같이 사용  

import os
import dotenv
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_ollama import ChatOllama

dotenv.load_dotenv()

# gemini
gemini_model    =   os.getenv("GOOGLE_MODEL_NAME")
gemini_llm  =   ChatGoogleGenerativeAI(model=gemini_model)

# ollama
ollama_model    =   os.getenv("OLLAMA_MODEL_NAME")
ollama_base_url =   os.getenv("OLLAMA_BASE_URL")

ollama_llm  =   ChatOllama(model=ollama_model, base_url=ollama_base_url)

 

반응형

'AI > RAG' 카테고리의 다른 글

[#RAG] 00. RAG란 무엇인가?  (0) 2026.02.27

댓글