"RAG를 활용하여 LLM 서비스를 구성하는 과정은 다음과 같습니다: Load(문서 데이터 읽기), Split(문서를 청크 단위로 분할), Embedding(문서를 벡터로 변환), Vector Store(변환된 벡터를 DB에 저장), Retrieval(유사도 검색), Prompt(검색된 결과를 바탕으로 원하는 출력을 얻기 위한 프롬프트 작성), Model(LLM 모델 선택), Output(결과 출력).
주어진 파이썬 코드(Jupyter Notebook)를 위에 제시한 순서에 따라 재구성하고, 각 과정의 시작 부분에는 Markdown 형식으로 과정명을 삽입해 정리해 주세요.
코드에 포함된 주석은 원래대로 유지해 주세요."
지정된 디렉토리(./data)에서 문서를 불러와 텍스트로 추출합니다.
import os
from langchain.document_loaders import PyMuPDFLoader
# 디렉토리 내 모든 파일을 리스트로 변환하는 함수 정의
def list_files(directory):
file_list = []
for root, dirs, files in os.walk(directory):
for file in files:
file_list.append(os.path.join(root, file))
return file_list
# 지정된 디렉토리 내 모든 파일명을 리스트로 호출
file_names = list_files('./data')
print(file_names)
# PDF 파일 불러오기
loader = PyMuPDFLoader(file_names[0]) # 현재는 하나의 파일만 사용
documents = loader.load()
문서를 LLM이 처리 가능한 길이(예: 256 토큰)로 잘라 문맥 손실을 최소화합니다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 텍스트 분할기 설정
text_splitter = RecursiveCharacterTextSplitter(chunk_size=256, chunk_overlap=16)
docs = text_splitter.split_documents(documents)
각 텍스트 조각을 임베딩 모델로 벡터로 변환합니다. 여기서는 HuggingFace 다국어 임베딩 모델을 사용합니다.
from langchain.embeddings import HuggingFaceEmbeddings
# 임베딩 모델 설정
embeddings_model = HuggingFaceEmbeddings(
model_name='sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2',
model_kwargs={'device':'cpu'},
encode_kwargs={'normalize_embeddings': True},
)
임베딩된 벡터를 ChromaDB에 저장하여 나중에 검색할 수 있도록 합니다.
from langchain_chroma import Chroma
# ChromaDB 생성 (임베딩 결과 저장)
db_constitution = Chroma.from_documents(
documents=docs,
embedding=embeddings_model,
collection_name="db_constitution"
)
질문에 대해 가장 관련성 높은 텍스트 조각들을 벡터 유사도로 검색합니다.
# 검색 쿼리 정의
query = '대통령'
# 검색기 구성 및 실행
retriever = db_constitution.as_retriever(search_kwargs={'k': 20})
docs = retriever.get_relevant_documents(query)
검색된 텍스트들을 프롬프트에 삽입해 LLM에게 전달할 수 있는 형태로 만듭니다.
from langchain_core.prompts import ChatPromptTemplate
# Prompt 템플릿 정의
template = '''Answer the question based only on the following context:
{context} Please answer all the answers in Korean.:
Question: {question}
'''
prompt = ChatPromptTemplate.from_template(template)
# 검색된 문서를 문자열로 정리
def format_docs(docs):
return '\n\n'.join([d.page_content for d in docs])
LLM을 설정합니다. 이 예제에서는 로컬에서 실행 중인 Meta-Llama-3 모델을 HTTP API 형태로 연결합니다.
from langchain_openai import ChatOpenAI
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
# LLM 설정
llm = ChatOpenAI(
base_url="http://localhost:11434/v1",
api_key="lm-studio", # lm-studio와 같은 로컬 서버 사용 시 dummy 키
model="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF",
temperature=0.1,
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
프롬프트 + LLM + 출력 파서를 연결해 Chain을 구성하고 질문을 수행합니다.
from langchain_core.output_parsers import StrOutputParser
# RAG Chain 연결
rag_chain = prompt | llm | StrOutputParser()
# 예시 질문 수행
query = "대통령은 탄핵될 수 있나요?"
answer = rag_chain.invoke({
'context': format_docs(docs),
'question': query
})
print(answer)
# 다른 질문에 대해 실행
query = "국민의 의무에 관하여 남성과 여성의 차이가 있나요?"
answer = rag_chain.invoke({
'context': format_docs(docs),
'question': query
})
print(answer)
| 단계 | 설명 | 주요 모듈 |
|---|---|---|
| Load | 문서 읽기 | PyMuPDFLoader, os |
| Split | 텍스트 분할 | RecursiveCharacterTextSplitter |
| Embedding | 텍스트 → 벡터 | HuggingFaceEmbeddings |
| Vector Store | 벡터 저장소 생성 | Chroma |
| Retrieval | 유사도 검색 | .as_retriever() |
| Prompt | 프롬프트 구성 | ChatPromptTemplate |
| Model | LLM 호출 설정 | ChatOpenAI |
| Output | 결과 출력 | StrOutputParser |