RAG 시스템 구현 A to Z: 임베딩부터 리트리버까지의 실무 가이드
RAG를 처음 만들 때 막히는 지점들을 중심으로 정리했습니다. 임베딩 모델 선택부터 청크 전략, 하이브리드 검색, 재랭킹까지 실제로 부딪혀본 문제들 위주로 씁니다.
왜 RAG인가
LLM의 가장 큰 한계는 학습 데이터 이후의 정보를 모른다는 것입니다. 내부 문서나 사내 데이터는 당연히 모르고요. 그렇다고 파인튜닝은 비용도 높고 업데이트할 때마다 다시 해야 합니다.
RAG(Retrieval-Augmented Generation)는 이 문제를 다르게 풉니다. 모델 가중치를 바꾸는 대신, 외부 지식 저장소에서 관련 문서를 실시간으로 끌어와 프롬프트에 붙여줍니다. LLM은 그 컨텍스트를 보고 답변합니다. 단순해 보이지만 실제로 만들어보면 챙겨야 할 부분이 꽤 많습니다.
구조 먼저
RAG를 구성하는 요소는 크게 네 가지입니다.
지식 저장소(Knowledge Base): 시스템이 참조할 문서들을 전처리하고 저장하는 곳입니다. 원본 문서를 일정 크기로 쪼개고(청킹), 임베딩 모델로 벡터화해서 벡터 DB에 저장합니다.
임베딩 모델: 텍스트를 수치 벡터로 변환하는 핵심입니다. 의미가 비슷한 텍스트끼리는 벡터 공간에서 가까이 위치하게 됩니다. 이게 잘 돼야 검색이 잘 됩니다.
검색기(Retriever): 사용자의 질의를 벡터로 만들고, 저장소에서 가장 유사한 청크 k개를 뽑습니다.
생성기(Generator): 검색된 청크들과 원본 질의를 합쳐 프롬프트를 만들고 LLM에 넣습니다. 최종 답변이 나오는 단계입니다.
실제로 어려운 부분들
임베딩 모델 선택
text-embedding-ada-002 같은 범용 모델로 시작하는 건 나쁘지 않습니다. 다만 한국어 문서가 많거나, 금융·법률처럼 전문 용어가 많은 도메인이라면 다국어 또는 도메인 특화 모델을 써야 성능이 올라옵니다.
모델 교체 비용도 감안해야 합니다. 임베딩 모델을 바꾸면 저장소 전체를 다시 임베딩해야 합니다.
청크 전략
청크 크기는 생각보다 결과에 많은 영향을 미칩니다. 너무 작으면 문맥이 잘려서 LLM이 답변을 만들기 어렵고, 너무 크면 검색 정밀도가 떨어지고 LLM의 컨텍스트 창도 빨리 찹니다.
보통 시작점으로 512 토큰 내외에 100 토큰 정도 오버랩을 씁니다. 문단 경계나 섹션 단위로 자르는 게 고정 크기보다 나은 경우도 많습니다.
재귀적 분할도 고려할 만합니다. 먼저 큰 단위로 나누고, 큰 청크를 임베딩해서 검색하고, 히트된 큰 청크 안에서 작은 청크를 다시 검색하는 방식입니다.
재랭킹(Reranking)
벡터 유사도만으로 검색하면 가끔 엉뚱한 문서가 상위에 올라옵니다. 해결책 중 하나는 1차 검색에서 상위 N개(예: 50~100개)를 뽑고, 크로스인코더 모델로 2차 정렬해 최종 k개를 고르는 것입니다.
크로스인코더는 질의와 문서를 한 번에 넣어서 관련성을 판단하기 때문에 바이인코더 기반 유사도보다 정확합니다. 속도는 느리지만 1차 필터링 후에만 쓰니 실용적입니다.
하이브리드 검색
벡터 검색과 BM25 같은 키워드 검색을 섞는 방법입니다. 벡터 검색은 의미 기반이라 “비슷한 내용”을 잘 잡지만, 고유명사나 코드 이름 같은 정확한 키워드는 키워드 검색이 낫습니다.
두 결과를 합치는 방법으로 RRF(Reciprocal Rank Fusion)를 많이 씁니다. 가중치를 도메인에 맞게 조정하는 과정이 필요합니다.
자주 막히는 지점
검색이 틀리면 답변도 틀립니다. LLM이 아무리 좋아도 검색기가 엉뚱한 문서를 가져오면 아무 소용이 없습니다. 파이프라인을 평가할 때 검색 품질을 먼저 보는 게 맞습니다.
다중 홉 질의는 기본 RAG로는 한계가 있습니다. “작년 정책과 올해 정책의 차이”처럼 여러 문서를 연결해야 하는 질문은 단순 검색-생성으로는 어렵습니다. LangGraph 같은 에이전트 프레임워크를 얹어야 합니다.
지식 저장소 최신성 관리도 생각보다 손이 많이 갑니다. 문서가 자주 바뀌는 환경이라면 업데이트 파이프라인을 처음부터 설계해야 합니다.
자주 받는 질문들
임베딩 파인튜닝은 꼭 해야 하나요? 꼭은 아닙니다. 도메인 특화 용어가 많거나 일반 모델이 특정 언어(한국어 등)에서 성능이 낮을 때 고려합니다. 먼저 범용 모델로 기준선을 잡고, 거기서 성능이 부족할 때 시도하는 게 낫습니다.
벡터 DB가 꼭 필요한가요? 프로토타입 단계에선 FAISS 라이브러리와 파일 저장으로도 됩니다. 수백만 개 이상의 벡터를 프로덕션에서 운영하려면 Qdrant, Weaviate, Pinecone 같은 전용 솔루션을 써야 합니다.
생성된 답변이 검색 내용을 실제로 반영하는지 어떻게 확인하나요? 답변과 소스 문서의 일치도(Groundedness)를 별도 모델로 검증하는 단계를 붙입니다. 파이프라인 끝에 LLM-as-judge 형태로 넣는 게 흔한 방법입니다.