Contents
see List개요
대형 언어모델(LLM)은 학습 데이터에 포함된 정보만 답변할 수 있다는 본질적 한계가 있습니다. 사내 문서, 최신 뉴스, 도메인 전문 지식 등 모델이 학습하지 않은 정보에 대해서는 할루시네이션(Hallucination)을 일으키기 쉽습니다.
RAG(Retrieval-Augmented Generation)는 이 문제를 해결하는 가장 실용적인 접근법입니다. 외부 지식 저장소에서 관련 문서를 검색(Retrieve)하고, 이를 LLM의 컨텍스트에 주입(Augment)하여 더 정확한 응답을 생성(Generate)합니다. 이 글에서는 벡터 데이터베이스를 활용한 RAG 파이프라인을 처음부터 구축하는 방법을 다룹니다.
핵심 개념: RAG 파이프라인 아키텍처
RAG 시스템은 크게 세 단계로 구성됩니다.
- 인덱싱(Indexing): 문서를 청크로 분할하고, 임베딩 벡터로 변환하여 벡터DB에 저장
- 검색(Retrieval): 사용자 질문을 임베딩하고, 벡터 유사도 검색으로 관련 문서 조각 추출
- 생성(Generation): 검색된 문서를 프롬프트에 포함시켜 LLM으로 답변 생성
벡터DB 선택지로는 Pinecone, Weaviate, Chroma, Qdrant, pgvector(PostgreSQL) 등이 있습니다. Supabase를 이미 사용 중이라면 pgvector 확장을 활용하는 것이 가장 효율적입니다.
실전 예제: Python으로 RAG 구축하기
LangChain과 OpenAI 임베딩, Chroma 벡터DB를 사용한 기본 RAG 파이프라인입니다.
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 1. 문서 로딩
loader = DirectoryLoader('./docs', glob="**/*.md", loader_cls=TextLoader)
documents = loader.load()
# 2. 텍스트 분할 (청킹)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ".", " "]
)
chunks = text_splitter.split_documents(documents)
# 3. 벡터DB 생성 (임베딩 + 저장)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 4. 검색 + 생성 체인
llm = ChatOpenAI(model="gpt-4o", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
return_source_documents=True
)
# 5. 질의
result = qa_chain.invoke({"query": "프로젝트의 인증 방식은 무엇인가요?"})
print(result["result"])
for doc in result["source_documents"]:
print(f" 출처: {doc.metadata['source']}")
Supabase pgvector를 사용하는 경우 다음과 같이 구성합니다.
-- Supabase에서 pgvector 활성화
create extension if not exists vector;
-- 문서 저장 테이블
create table documents (
id bigserial primary key,
content text,
metadata jsonb,
embedding vector(1536)
);
-- 유사도 검색 함수
create function match_documents(
query_embedding vector(1536),
match_count int default 5,
filter jsonb default '{}'
) returns table (id bigint, content text, metadata jsonb, similarity float)
language plpgsql as $$
begin
return query
select d.id, d.content, d.metadata,
1 - (d.embedding <=> query_embedding) as similarity
from documents d
where d.metadata @> filter
order by d.embedding <=> query_embedding
limit match_count;
end;
$$;
활용 팁
- 청크 크기 최적화: 500~1500자 사이에서 실험하세요. 너무 작으면 맥락이 부족하고, 너무 크면 노이즈가 증가합니다.
- 하이브리드 검색: 벡터 유사도 검색과 키워드 검색(BM25)을 결합하면 정확도가 크게 향상됩니다.
- 리랭킹(Re-ranking): Cohere Rerank나 Cross-Encoder를 사용하여 검색 결과의 순위를 재조정하세요.
- 메타데이터 필터링: 문서에 카테고리, 날짜 등 메타데이터를 태깅하여 검색 범위를 좁히세요.
- 평가 체계 구축: RAGAS 프레임워크로 Faithfulness, Relevancy 등을 정량적으로 측정하세요.
마무리
RAG는 LLM의 한계를 극복하는 가장 현실적인 방법이며, 기업 환경에서 AI를 도입할 때 첫 번째로 고려해야 할 아키텍처입니다. 벡터DB 선택, 청킹 전략, 검색 최적화 등 세부 요소를 잘 튜닝하면 파인튜닝 없이도 높은 품질의 도메인 특화 AI 시스템을 구축할 수 있습니다.