RAG 시스템 실전 구축 (v47)
요약
LLM의 지식 범위를 확장하는 RAG 시스템의 핵심 구조와 실전 구축 방법을 다룹니다. 검색, 보강, 생성의 3단계 프로세스와 성능 최적화를 위한 의미적 Chunking 전략을 상세히 설명합니다.
핵심 포인트
- RAG의 3단계 핵심 구조: 검색, 보강, 생성
- 로컬 환경 중심의 성능 및 비용 효율적 구축 가이드
- 성능 향상을 위한 의미적 Chunking 전략 활용
RAG 시스템 실전 구축 (v47)
개요
RAG(Retrieval-Augmented Generation) 시스템은 LLM의 지식 범위를 확장하고, 특정 도메인 전문 지식을 통합하는 데 핵심적인 기술입니다. 이 가이드는 실제 개발 환경에서 RAG 시스템을 구축하는 실전 가이드를 제공하며, 특히 로컬 환경에서의 성능과 비용 효율성을 중심으로 다룹니다.
1. RAG 기본 구조
RAG는 세 가지 핵심 단계로 구성됩니다:
- 검색(Retrieval): 주어진 쿼리와 관련된 문서 조각을 찾습니다.
- 보강(Augmentation): 검색된 문서를 프롬프트에 추가합니다.
- 생성(Generation): LLM이 보강된 프롬프트를 기반으로 응답을 생성합니다.
# RAG 기본 구조 구현
class BasicRAG:
def __init__(self, embedding_model, vector_db):
self.embedding_model = embedding_model
self.vector_db = vector_db
def retrieve(self, query, k=5):
query_embedding = self.embedding_model.encode([query])
return self.vector_db.search(query_embedding, k)
def generate(self, query, retrieved_docs):
prompt = f"Query: {query}\n\nRelevant Docs:\n" + "\n\n".join(retrieved_docs)
return self.llm.generate(prompt)
def process(self, query):
docs = self.retrieve(query)
response = self.generate(query, docs)
return response
2. Chunking 전략
문서를 적절한 크기로 나누는 전략은 RAG 성능에 결정적 영향을 미칩니다.
2.1 의미적 Chunking
from sentence_transformers import SentenceTransformer
import numpy as np
def semantic_chunking(documents, model, threshold=0.7):
"""의미적 기반으로 문서를 chunking"""
embeddings = model.encode(documents)
chunks = []
current_chunk = []
current_embedding = np.zeros(embeddings[0].shape)
for i, (doc, embedding) in enumerate(zip(documents, embeddings)):
# 이전 chunk와의 유사도 계산
similarity = np.dot(current_embedding, embedding) / (
np.linalg.norm(current_embedding) * np.linalg.norm(embedding)
)
if similarity < threshold:
if current_chunk:
chunks.append(' '.join(current_chunk))
current_chunk = [doc]
current_embedding = embedding
else:
current_chunk.append(doc)
# 평균 임베딩 업데이트
current_embedding = (current_embedding + embedding) / 2
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
2.2 Recursive Chunking
def recursive_chunking(text, max_chunk_size=500):
"""재귀적 chunking으로 문장 단위로 분할"""
chunks = []
sentences = text.split('. ')
current_chunk = ""
for sentence in sentences:
if len(current_chunk) + len(sentence) < max_chunk_size:
current_chunk += sentence + ". "
else:
if current_chunk:
chunks.append(current_chunk.strip())
current_chunk = sentence + ". "
if current_chunk:
chunks.append(current_chunk.strip())
return chunks
3. 임베딩 모델 선택 및 비교
# 임베딩 모델 비교
from sentence_transformers import SentenceTransformer
from transformers import AutoTokenizer, AutoModel
import torch
class EmbeddingComparison:
def __init__(self):
self.models = {
'all-MiniLM-L6-v2': SentenceTransformer('all-MiniLM-L6-v2'),
'paraphrase-multilingual-MiniLM-v2': SentenceTransformer('paraphrase-multilingual-MiniLM-v2'),
'bge-small-en': SentenceTransformer('BAAI/bge-small-en'),
'fast-sentence-bert': SentenceTransformer('sentence-transformers/paraphrase-multilingual-mpnet-base-v2')
}
def benchmark_models(self, test_sentences):
results = {}
for name, model in self.models.items():
start_time = time.time()
embeddings = model.encode(test_sentences)
end_time = time.time()
results[name] = {
'time': end_time - start_time,
'dimension': embeddings.shape[1],
'memory': embeddings.nbytes
}
return results
# 사용 예시
comparison = EmbeddingComparison()
test_data = ["Hello world", "How are you?", "I am fine"]
results = comparison.benchmark_models(test_data)
4. Vector Database 비교
# Vector DB별 비교
class VectorDBComparison:
def __init__(self):
self.dbs = {
'chroma': ChromaDB(),
'qdrant': QdrantDB(),
'pgvector': PostgreSQLVector(),
'milvus': MilvusDB()
}
def test_insert_performance(self, documents, embeddings):
results = {}
for name, db in self.dbs.items():
start_time = time.time()
db.insert(documents, embeddings)
end_time = time.time()
results[name] = end_time - start_time
return results
# ChromaDB 구현 예시
class ChromaDB:
def __init__(self, path="./chroma_db"):
import chromadb
self.client = chromadb.Client(path)
self.collection = self.client.get_or_create_collection("docs")
def insert(self, documents, embeddings):
self.collection.add(
documents=documents,
embeddings=embeddings,
ids=[f"doc_{i}" for i in range(len(documents))]
)
def search(self, query_embedding, k=5):
results = self.collection.query(
query_embeddings=query_embedding,
n_results=k
)
return results['documents'][0]
5. 전체 RAG 파이프라인 코드
import numpy as np
from sentence_transformers import SentenceTransformer
import faiss
import time
import pickle
class LocalRAGPipeline:
def __init__(self, model_name='all-MiniLM-L6-v2'):
self.embedding_model = SentenceTransformer(model_name)
self.vector_index = None
self.documents = []
self.doc_ids = []
def create_vector_index(self, documents):
"""문서 벡터 인덱스 생성"""
# 문서 임베딩 생성
embeddings = self.embedding_model.encode(documents)
self.documents = documents
# FAISS 인덱스 생성
dimension = embeddings.shape[1]
self.vector_index = faiss.IndexFlatIP(dimension)
self.vector_index.add(np.array(embeddings, dtype=np.float32))
# 문서 ID 매핑
self.doc_ids = [f"doc_{i}" for i in range(len(documents))]
def search_relevant_docs(self, query, k=5):
"""관련 문서 검색"""
query_embedding = self.embedding_model.encode([query])
distances, indices = self.vector_index.search(
np.array(query_embedding, dtype=np.float32), k
)
relevant_docs = []
for idx, dist in zip(indices[0], distances[0]):
if idx < len(self.documents):
relevant_docs.append({
'content': self.documents[idx],
'similarity': float(dist)
})
return relevant_docs
def generate_response(self, query, relevant_docs):
"""LLM 응답 생성"""
# 간단한 프롬프트 생성
context = "\n\n".join([doc['content'] for doc in relevant_docs])
prompt = f"""
주어진 문서 내용을 바탕으로 질문에 답변해주세요.
문서 내용:
{context}
질문: {query}
"""
# 실제 LLM 호출은 여기서 수행 (예: local LLM)
# 예시로 간단한 응답 생성
return f"질문: {query}\n답변: 문서 내용을 바탕으로 답변을 생성했습니다."
def process_query(self, query):
"""전체 쿼리 처리 파이프라인"""
relevant_docs = self.search_relevant_docs(query)
response = self.generate_response(query, relevant_docs)
return response
# 사용 예시
pipeline = LocalRAGPipeline()
# 샘플 문서
documents = [
"Python은 고급 프로그래밍 언어로, 간결하고 가독성이 뛰어납니다.",
"장고(Django)는 웹 프레임워크로, 빠르고 안전한 웹 개발을 지원합니다.",
"FastAPI는 현대적인 웹 프레임워크로, 높은 성능과 자동 문서화 기능을 제공합니다."
]
# 벡터 인덱스 생성
pipeline.create_vector_index(documents)
# 쿼리 처리
response = pipeline.process_query("Python 프로그래밍 언어의 특징은?")
print(response)
6. 고급 기
📥 Get the full guide on Gumroad: https://gumroad.com/l/auto ($7)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기