
대부분의 RAG 튜토리얼은 장난감 데모 수준에서 끝나거나 이미 프로덕션 환경에서 실행하는 방법을 안다고 가정합니다. 이 가이드는 그 간격을 메워줍니다. Python에서 RAG 애플리케이션을 처음부터 구축한 다음, 프로덕션 수준이 될 때까지 모든 컴포넌트를 점진적으로 업그레이드하게 됩니다.
RAG 한눈에 보기
한 줄의 코드도 작성하기 전에 컴포넌트를 선택하세요. 2026년에 RAG를 시작하는 대부분의 팀에 추천하는 스택은 다음과 같습니다:
2026년 RAG를 시작하는 대부분의 팀에 추천하는 스택입니다. 모든 컴포넌트는 교체 가능하며, 아래 섹션에서 언제 어떤 이유로 다르게 선택해야 하는지 설명합니다.
RAG란 무엇인가? (30초 버전)
Retrieval-Augmented Generation(RAG)은 LLM이 답변을 생성하기 전에 검색 단계를 추가합니다. 모델이 학습 중에 암기한 것에만 의존하는 대신, RAG는 자신의 데이터에서 관련 문서를 가져와 사용자의 질문과 함께 컨텍스트로 전달합니다.
왜 이것이 중요할까요? 세 가지 이유가 있습니다. 첫째, 모델이 학습 세트가 아닌 실제 데이터를 바탕으로 답변하기 때문에 환각이 극적으로 줄어듭니다. 둘째, 지식을 항상 최신 상태로 유지할 수 있습니다. 문서를 업데이트하면 다음 쿼리에 바로 반영되므로 재학습이 필요 없습니다. 셋째, RAG는 모델을 도메인 데이터로 미세조정하는 것보다 훨씬 저렴하고 빠르게 설정할 수 있습니다.
RAG와 미세조정의 차이점은 이것입니다: RAG는 모델에 쿼리 시점에 지식에 접근할 수 있게 해주는 반면, 미세조정은 지식을 모델의 가중치에 깊숙이 새깁니다. 데이터가 자주 변경될 때는 RAG를 사용하세요. 모델이 더 많이 알아야 할 뿐만 아니라 다르게 추론해야 할 때는 미세조정을 사용하세요.
RAG 아키텍처는 어떻게 작동하나요?
모든 RAG 시스템에는 두 개의 파이프라인이 있으며, 이 분리를 이해하는 것이 확장 가능한 시스템을 구축하는 핵심입니다.
인덱싱 파이프라인(오프라인)
이것은 배치로 실행되며, 시간 단위, 매일, 또는 데이터가 변경될 때마다 실행됩니다. 원본 문서를 네 단계를 통해 처리합니다:
- 문서 로딩 - PDF, 웹 페이지, 데이터베이스 레코드 또는 API 응답을 원본 텍스트로 수집
- 청킹 - 그 텍스트를 검색 가능한 조각으로 분할(청킹 섹션에서 자세히 설명)
- 임베딩 - 각 청크를 의미를 캡처하는 수치 벡터로 변환
- 저장 - 메타데이터와 함께 이 벡터를 벡터 데이터베이스에 저장
이 파이프라인은 문서당 한 번 실행됩니다. 문서가 업데이트되면 해당 문서만 다시 인덱싱합니다.
쿼리 파이프라인(런타임)
이것은 모든 사용자 질문에서 실행되며, 일반적으로 2초 이내에 완료됩니다:
- 쿼리 임베딩 - 사용자의 질문을 문서와 동일한 벡터 공간으로 변환
- 검색 - 벡터 데이터베이스에서 가장 유사한 청크를 검색(상위-k)
- 재순위 지정(선택사항) - 크로스 인코더로 검색된 청크를 다시 점수 매겨 더 높은 정밀도 달성
- 프롬프트 구성 - 프롬프트 조립: 시스템 지시사항 + 검색된 청크 + 사용자 질문
- LLM 생성 - 조립된 프롬프트를 LLM에 전달하고 응답 스트리밍
왜 이 두 파이프라인을 분리하는 것이 중요할까요? 프로덕션에서 인덱싱 파이프라인은 일정에 따라 수백만 개의 문서를 처리할 수 있지만, 쿼리 파이프라인은 실시간 트래픽을 처리합니다. 이들은 독립적으로 확장됩니다. 인덱싱 쪽을 건드리지 않고 쿼리 결과를 캐시할 수 있습니다. 쿼리 쪽에 어떤 다운타임도 없이 전체 말뭉치를 다시 인덱싱할 수 있습니다.
이 이원적 파이프라인 멘탈 모델이 이후 모든 내용의 틀이 될 것입니다. "검색 품질 개선"에 대해 이야기할 때, 우리는 쿼리 파이프라인을 최적화하고 있습니다. "청킹 전략"에 대해 이야기할 때, 우리는 인덱싱 파이프라인을 최적화하고 있습니다.
RAG 애플리케이션을 처음부터 어떻게 구축하나요?
Python과 OpenAI API만으로 작동하는 RAG 시스템을 구축해 봅시다. LangChain도, LlamaIndex도 없이, 단지 기초만 사용합니다. 내부에서 무슨 일이 일어나는지 이해하면, 프레임워크가 도움이 되는지 아니면 필요 없는 추상화만 추가하는지 결정할 수 있습니다.
전제 조건
OpenAI API 키가 필요합니다. 환경 변수로 설정하세요:
단계 1: 문서 로드
우리는 현실적인 예제로 작업합니다. 회사의 내부 문서를 쿼리합니다. 이 튜토리얼에서는 제품을 설명하는 마크다운 파일이 몇 개 있다고 가정하세요:
단계 2: 문서 청킹
각 문서를 겹치는 조각으로 분할합니다. 겹침은 청크 경계의 컨텍스트가 손실되지 않도록 보장합니다:
단계 3: 임베딩 생성
OpenAI의 임베딩 API를 사용하여 모든 청크를 벡터로 변환합니다:
프로토타입 제작을 위해 text-embedding-3-small을 사용하고 있습니다. 더 저렴하고 빠릅니다. 임베딩 모델 섹션에서 text-embedding-3-large로 업그레이드하는 방법을 논의하겠습니다.
단계 4: 관련 청크 검색
사용자의 질문을 동일한 벡터 공간에 임베딩한 다음, 코사인 유사도를 사용하여 가장 가까운 청크를 찾습니다:
단계 5: 컨텍스트로 답변 생성
검색된 청크를 사용자의 질문과 함께 컨텍스트로 LLM에 전달합니다:
이것이 Python 80줄 미만으로 작동하는 RAG 시스템입니다. 프레임워크가 필요하지 않습니다. 이 가이드의 나머지 부분은 각 컴포넌트를 프로덕션 품질로 업그레이드하는 방법을 보여줍니다. 더 나은 청킹, 강력한 임베딩, 실제 벡터 데이터베이스, 하이브리드 검색, 그리고 적절한 평가입니다.
참고로, LangChain의 RAG 튜토리얼은 이 모든 것을 몇 줄로 추상화합니다. 프레임워크는 무엇을 하는지 이해한 후에 훌륭합니다. 하지만 프로덕션에서 뭔가 깨지고 원본 검색 로직을 본 적 없다면, 디버깅은 매우 고통스러워집니다.
문서를 어떻게 청킹해야 하나요?
청킹은 검색 품질에 대해 당신이 가진 가장 큰 영향력입니다. 잘못하면 가장 좋은 임베딩 모델도 당신을 구하지 못합니다. 관련 정보가 청크 전체에 걸쳐 분할되거나 관련 없는 컨텍스트에 묻힐 것입니다.
고정 크기 청킹
가장 간단한 방법: 모든 N개 문자(또는 토큰)를 약간의 겹침으로 분할합니다. 위의 처음부터 작성한 코드가 정확히 이렇게 합니다. 작동하지만, 바보입니다. 문장을 반으로 자르거나 코드 블록을 함수 중간에서 자를 것입니다.
재귀적 문자 분할
의미 있는 업그레이드이면서도 여전히 간단합니다. 임의의 문자 경계에서 분할하는 대신, 분리자 계층을 시도합니다: 먼저 단락(\n\n), 그 다음 문장(\n), 그 다음 공백. LangChain의 RecursiveCharacterTextSplitter가 이 패턴을 잘 구현합니다. 대부분의 사용 사례에서 이것은 품질과 복잡성 사이의 달콤한 지점입니다.
의미론적 청킹
문자 수가 아니라 의미 경계에서 분할합니다. 문장을 임베딩한 다음, 임베딩 유사성이 급격히 떨어지는 지점을 찾습니다. 이것이 자연스러운 주제 경계입니다. 더 높은 품질이지만 계산 비용이 더 많이 들고 튜닝하기 어렵습니다. Weaviate의 청킹 분석에 따르면, 의미론적 청킹은 질문-답변 작업에서 고정 크기 방식을 지속적으로 능가합니다.
부모-자식 청킹
정밀한 검색을 위해 작은 청크를 저장하지만, 그들의 부모 청크(더 큰 주변 컨텍스트)를 LLM에 반환합니다. 둘 다 얻을 수 있습니다: 작은 청크에서의 검색 정밀도와 풍부한 컨텍스트에서의 답변 품질. 이것은 계약서, 연구 논문 또는 기술 사양과 같은 긴 문서에서 특히 잘 작동합니다.
결론: 50토큰 겹침으로 512토큰의 재귀적 문자 분할로 시작하세요. 사용 사례의 80%를 잘 처리합니다. RAGAS 평가 점수가 목표에 미치지 못할 때만 의미론적 청킹으로 전환하세요. 문제를 측정하기 전에 청킹을 과도하게 복잡하게 하지 마세요.
어떤 임베딩 모델을 사용해야 하나요?
임베딩은 검색을 가능하게 하는 수학적 표현입니다. 임베딩 모델은 문서 청크와 사용자 쿼리를 모두 동일한 공간의 벡터로 변환하므로 유사한 의미가 서로 가깝게 배치됩니다.
임베딩 모델의 선택은 검색 품질, 레이턴시, 비용 및 API가 필요한지 아니면 자체 호스팅할 수 있는지에 영향을 미칩니다. 주요 모델들이 MTEB(Massive Text Embedding Benchmark) 리더보드를 기준으로 어떻게 비교되는지는 다음과 같습니다:
...