RAG와 Knowledge Bases 실전

검색 증강 생성(RAG) 아키텍처를 이해하고 Amazon Bedrock Knowledge Bases로 기업 문서를 활용한 AI Q&A 시스템을 구축합니다. 임베딩, 벡터 DB, 청킹 전략까지 실전 RAG 개발의 모든 것을 배웁니다.

AWS,AI,RAG,Bedrock,Knowledge Bases중급
14시간
14개 항목
학습 진행률0 / 14 (0%)

학습 항목

1. AWS
초급
RAG 아키텍처 완벽 이해 가이드
2. AWS
초급
텍스트 임베딩 개념 완벽 가이드
3. AWS
초급
Amazon Titan Embeddings 완벽 가이드
4. AWS
초급
벡터 데이터베이스 완벽 가이드
5. AWS
초급
AWS Knowledge Bases 완벽 가이드
6. AWS
초급
AWS S3 문서 연동 완벽 가이드
7. AWS
초급
청킹 전략 완벽 가이드
8. AWS
초급
AWS Bedrock 검색과 응답 생성 완벽 가이드
9. AWS
초급
AWS Bedrock 인용과 출처 표시 완벽 가이드
10. AWS
초급
AWS Bedrock 하이브리드 검색 완벽 가이드
11. AWS
초급
Knowledge Base 최적화 완벽 가이드
12. AWS
초급
Streamlit으로 RAG 챗봇 UI 완벽 가이드
13. AWS
초급
사내 문서 Q&A 시스템 실전 구축 가이드
14. AWS
초급
RAG 평가와 개선 완벽 가이드
1 / 14
🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

이미지 로딩 중...

RAG 아키텍처 완벽 이해 가이드 - 슬라이드 1/7
상세 보기

RAG 아키텍처 완벽 이해 가이드

LLM의 한계를 극복하는 RAG 아키텍처를 처음부터 끝까지 배웁니다. 검색-증강-생성의 전체 흐름과 실제 구현 방법을 실무 중심으로 설명합니다.


목차

  1. RAG란_무엇인가
  2. LLM의_한계와_RAG의_필요성
  3. RAG_아키텍처_구성_요소
  4. 검색_증강_생성_흐름
  5. RAG_vs_Fine-tuning_비교
  6. RAG_활용_사례

1. RAG란 무엇인가

김개발 씨는 회사에서 사내 문서 검색 챗봇을 만들라는 업무를 받았습니다. "ChatGPT처럼 자연스럽게 답변하면서, 우리 회사 문서 내용도 정확하게 알려줘야 해요." 팀장님의 요구사항을 듣고 막막해진 김개발 씨는 선배에게 조언을 구했습니다.

RAG는 Retrieval-Augmented Generation의 약자로, 검색 기능으로 증강된 생성 모델을 의미합니다. 마치 시험 볼 때 교과서를 펼쳐놓고 답안을 작성하는 것처럼, AI가 답변하기 전에 관련 문서를 먼저 찾아보는 방식입니다.

LLM이 학습하지 못한 최신 정보나 회사 내부 데이터도 정확하게 답변할 수 있게 해줍니다.

다음 코드를 살펴봅시다.

# RAG의 기본 흐름을 보여주는 간단한 예제
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.llms import OpenAI

# 1. 문서를 벡터로 변환하여 저장 (검색 준비)
documents = ["회사 휴가 규정은 연 15일입니다.", "점심시간은 12시부터 1시까지입니다."]
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_texts(documents, embeddings)

# 2. 사용자 질문과 유사한 문서 검색
query = "휴가는 며칠인가요?"
relevant_docs = vectorstore.similarity_search(query, k=2)

# 3. 검색된 문서와 질문을 함께 LLM에 전달
llm = OpenAI()
context = "\n".join([doc.page_content for doc in relevant_docs])
answer = llm(f"다음 정보를 바탕으로 답변하세요.\n{context}\n\n질문: {query}")

"RAG요? 그게 뭔데요?" 김개발 씨가 물었습니다.

선배 박시니어 씨가 커피를 한 모금 마시고 설명을 시작했습니다. "간단해요.

우리가 시험 볼 때를 생각해봐요. 암기한 내용만으로 답을 쓰는 게 일반 LLM이라면, RAG는 교과서를 보면서 답을 쓰는 거예요." 김개발 씨의 눈이 반짝였습니다.

"아, 그러니까 AI한테 우리 회사 문서를 참고하게 만드는 거군요!" RAG의 핵심 개념을 이해하려면 먼저 일반 LLM의 한계를 알아야 합니다. ChatGPT 같은 LLM은 학습 시점까지의 데이터만 알고 있습니다.

2023년에 학습을 마쳤다면, 2024년의 뉴스는 모릅니다. 더 큰 문제는 회사 내부 문서나 개인 데이터처럼 학습하지 않은 정보는 아예 답변할 수 없다는 점입니다.

"그럼 우리 회사 인사 규정을 학습시키면 되지 않나요?" 김개발 씨가 물었습니다. 박시니어 씨가 고개를 저었습니다.

"파인튜닝은 비용이 너무 많이 들어요. 게다가 규정이 바뀔 때마다 다시 학습해야 하죠.

그래서 나온 게 RAG예요." RAG는 세 단계로 작동합니다. 첫째, Retrieval(검색) 단계입니다.

사용자가 질문하면, 시스템은 관련된 문서나 정보를 먼저 찾습니다. 마치 도서관 사서가 질문에 맞는 책을 찾아주는 것처럼 말이죠.

둘째, Augmented(증강) 단계입니다. 찾아낸 문서를 사용자의 질문과 함께 묶습니다.

"이 정보를 참고해서 답변해줘"라고 LLM에게 지시하는 거죠. 셋째, Generation(생성) 단계입니다.

LLM이 검색된 문서를 바탕으로 자연스러운 답변을 만들어냅니다. 단순히 문서 내용을 복사하는 게 아니라, 사용자 질문에 맞게 재구성하는 겁니다.

"아하!" 김개발 씨가 무릎을 쳤습니다. "그러면 문서가 업데이트되면 자동으로 최신 정보로 답변하겠네요?" 박시니어 씨가 미소를 지었습니다.

"정확해요. 모델을 다시 학습할 필요가 없죠." 위의 코드를 자세히 살펴봅시다.

먼저 7번째 줄에서 회사 규정 문서들을 정의합니다. 실제로는 수백, 수천 개의 문서가 될 수 있습니다.

9번째 줄에서는 이 문서들을 벡터 형태로 변환하여 저장합니다. 벡터란 문서의 의미를 숫자 배열로 표현한 것으로, 의미가 비슷한 문서끼리는 비슷한 벡터값을 갖게 됩니다.

12번째 줄에서 사용자 질문이 들어오면, 13번째 줄에서 유사도 검색을 수행합니다. "휴가는 며칠인가요?"라는 질문과 의미가 가장 비슷한 문서 2개를 찾는 거죠.

16번째 줄부터가 핵심입니다. 검색된 문서들을 하나의 문자열로 합친 뒤, 17번째 줄에서 원래 질문과 함께 LLM에 전달합니다.

"다음 정보를 바탕으로 답변하세요"라는 프롬프트와 함께 말이죠. 실제 기업에서는 이런 방식으로 활용합니다.

법률 회사는 수만 건의 판례를 RAG로 구축하여, 변호사들이 유사 판례를 빠르게 찾아 법률 자문을 제공합니다. 고객센터는 제품 매뉴얼과 FAQ를 RAG로 만들어, 상담사가 정확한 답변을 즉시 제공할 수 있게 합니다.

연구소에서는 논문 데이터베이스를 RAG로 구축하여, 연구자들이 최신 연구 동향을 파악합니다. 하지만 주의할 점도 있습니다.

초보자들이 자주 하는 실수는 검색 품질을 무시하는 것입니다. 아무리 LLM이 좋아도, 관련 없는 문서를 검색하면 엉뚱한 답변이 나옵니다.

따라서 임베딩 모델 선택청킹 전략(문서를 어떻게 나눌지)이 매우 중요합니다. 또 다른 실수는 검색된 문서를 너무 많이 넣는 것입니다.

LLM에는 입력 토큰 제한이 있기 때문에, 적절한 개수(보통 3-5개)만 선택해야 합니다. 김개발 씨는 이제 자신감이 생겼습니다.

"그럼 저는 회사 문서들을 벡터 DB에 넣고, 질문이 오면 검색해서 LLM에 넘기면 되는 거네요!" 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

RAG의 핵심은 바로 그거예요. 복잡해 보이지만, 결국 검색 + LLM의 조합이죠." RAG를 이해하면 LLM의 한계를 극복하고, 실시간으로 업데이트되는 정보를 정확하게 제공하는 AI 시스템을 구축할 수 있습니다.

여러분도 오늘 배운 개념을 활용해 나만의 지식 베이스 챗봇을 만들어보세요.

실전 팁

💡 - 문서는 의미 단위로 적절히 나눠야 검색 정확도가 높아집니다 (보통 500-1000자 단위)

  • 벡터 DB는 Pinecone, Weaviate, FAISS 등 다양한 선택지가 있으니 프로젝트 규모에 맞게 선택하세요
  • 검색 결과는 항상 출처와 함께 사용자에게 보여주면 신뢰도가 높아집니다

2. LLM의 한계와 RAG의 필요성

며칠 뒤, 김개발 씨는 프로토타입을 만들어 팀장님께 시연했습니다. 하지만 팀장님이 "작년에 바뀐 복지 제도는 뭐야?"라고 묻자, 챗봇이 엉뚱한 답변을 내놓았습니다.

"왜 이렇게 나오는 거죠?" 김개발 씨는 당황했습니다.

일반 LLM은 학습 시점 이후의 데이터를 알지 못하며, 학습하지 않은 도메인 지식도 제공할 수 없습니다. 마치 2023년에 출판된 백과사전으로 2024년 뉴스를 찾으려는 것과 같습니다.

이런 한계 때문에 환각(Hallucination) 현상이 발생하고, 기업 내부 정보에 대해서는 답변을 못하거나 틀린 정보를 만들어냅니다.

다음 코드를 살펴봅시다.

# LLM의 한계를 보여주는 예제
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)

# 학습하지 않은 회사 내부 정보 질문
question = "우리 회사 2024년 신규 복지 제도는 무엇인가요?"
answer = llm(question)
print(answer)  # 결과: 모호하거나 잘못된 답변 (환각 현상)

# RAG를 사용한 올바른 접근
from langchain.vectorstores import Chroma
documents = ["2024년 신규 복지: 재택근무 주 3회, 자녀 학자금 지원"]
vectorstore = Chroma.from_texts(documents, OpenAIEmbeddings())
relevant = vectorstore.similarity_search(question)
answer_with_rag = llm(f"정보: {relevant[0].page_content}\n질문: {question}")
print(answer_with_rag)  # 결과: 정확한 답변

박시니어 씨가 김개발 씨의 화면을 보더니 한숨을 쉬었습니다. "그럴 줄 알았어요.

이게 바로 LLM의 근본적인 한계예요." "하지만 GPT-4는 똑똑하잖아요?" 김개발 씨가 반문했습니다. 선배가 고개를 저었습니다.

"똑똑한 건 맞아요. 하지만 타임머신이 아니에요.

학습 이후의 세상은 모르죠." LLM의 첫 번째 한계는 시간적 제약입니다. GPT-4가 2023년 4월에 학습을 마쳤다면, 2023년 5월의 뉴스는 전혀 모릅니다.

오늘이 2024년이라면, 최근 1년간의 모든 정보가 공백입니다. 기업 입장에서는 치명적입니다.

어제 발표한 신제품 정보를 AI가 모른다면, 고객 문의에 제대로 답변할 수 없으니까요. 두 번째 한계는 지식의 범위입니다.

LLM은 인터넷에 공개된 정보로 학습됩니다. 하지만 기업 내부 문서, 개인 데이터, 유료 데이터베이스는 학습 데이터에 포함되지 않습니다.

아무리 GPT-4가 똑똑해도, 우리 회사 인사 규정은 절대 모릅니다. "그럼 파인튜닝으로 학습시키면 되지 않나요?" 김개발 씨가 다시 물었습니다.

박시니어 씨가 계산기를 꺼냈습니다. "GPT-3.5 파인튜닝 비용이 시간당 약 8달러예요.

회사 문서 10만 건을 학습시키면 수백만 원이 들어가죠. 게다가 문서가 업데이트될 때마다 다시 학습해야 해요.

한 달에 한 번만 업데이트해도 연간 수천만 원이 들어갑니다." 김개발 씨의 얼굴이 굳었습니다. "그럼 방법이 없는 건가요?" 세 번째 한계는 환각(Hallucination) 현상입니다.

LLM은 모르는 것을 솔직히 "모른다"고 말하지 않습니다. 대신 그럴듯한 거짓말을 만들어냅니다.

마치 시험에서 모르는 문제를 빈칸으로 남기지 않고 아무 답이나 쓰는 학생처럼 말이죠. 실제 사례를 들어봅시다.

한 변호사가 ChatGPT에게 판례를 물었고, ChatGPT는 그럴듯한 판례 번호와 내용을 제시했습니다. 하지만 법원에서 확인해보니 존재하지 않는 판례였습니다.

완전히 지어낸 정보였던 거죠. "무섭네요..." 김개발 씨가 중얼거렸습니다.

박시니어 씨가 고개를 끄덕였습니다. "그래서 RAG가 필수예요.

RAG는 이 세 가지 한계를 모두 해결합니다." 위의 코드를 분석해봅시다. 6번째 줄에서 LLM에게 회사 내부 정보를 질문합니다.

하지만 LLM은 이 정보를 학습한 적이 없기 때문에, 8번째 줄의 결과는 엉뚱하거나 그럴듯하게 꾸며낸 답변입니다. 반면 11번째 줄부터는 RAG 방식을 사용합니다.

12번째 줄에서 실제 회사 문서를 벡터 DB에 저장하고, 13번째 줄에서 관련 문서를 검색한 뒤, 15번째 줄에서 검색된 정보와 함께 LLM에 질문합니다. 결과는 정확합니다.

네 번째 한계는 비용과 속도입니다. LLM을 파인튜닝하려면 막대한 비용이 듭니다.

하지만 RAG는 문서를 벡터 DB에 저장만 하면 됩니다. 벡터 임베딩 비용은 파인튜닝의 1/100 수준입니다.

게다가 문서 업데이트도 즉시 반영됩니다. "그럼 RAG가 만능인가요?" 김개발 씨가 물었습니다.

박시니어 씨가 미소를 지었습니다. "완벽하진 않아요.

검색 품질이 나쁘면 답변도 나빠지죠. 하지만 대부분의 기업 활용 사례에서는 RAG가 파인튜닝보다 훨씬 효율적입니다." 실제 기업들의 선택을 봐도 알 수 있습니다.

마이크로소프트의 Copilot은 RAG를 사용하여 회사 내부 문서를 검색합니다. Notion AI도 RAG로 사용자의 노트를 참조합니다.

수십억 원을 들여 파인튜닝하는 대신, RAG로 빠르고 저렴하게 문제를 해결한 겁니다. 김개발 씨는 이제 확신이 생겼습니다.

"그럼 저도 RAG로 다시 만들어볼게요!" 박시니어 씨가 등을 두드려줬습니다. "좋아요.

이제 RAG의 구체적인 구성 요소를 배워봅시다." LLM의 한계를 이해하면, 왜 RAG가 필수인지 명확해집니다. 시간적 제약, 지식 범위, 환각 현상, 비용 문제를 모두 해결하는 RAG는 현대 AI 시스템의 핵심 아키텍처입니다.

실전 팁

💡 - 파인튜닝은 언어 스타일이나 특정 형식을 학습시킬 때 유용하고, 사실 정보는 RAG로 제공하는 것이 효율적입니다

  • 환각 현상을 줄이려면 프롬프트에 "제공된 정보에만 기반하여 답변하세요"라고 명시하세요
  • 답변에 출처를 함께 표시하면 환각 현상을 사용자가 직접 검증할 수 있습니다

3. RAG 아키텍처 구성 요소

김개발 씨는 RAG를 구현하기로 마음먹었지만, 막상 어디서부터 시작해야 할지 막막했습니다. "벡터 DB?

임베딩? 청킹?" 생소한 용어들이 머릿속을 가득 채웠습니다.

RAG 아키텍처는 크게 다섯 가지 핵심 구성 요소로 이뤄집니다. 문서 처리 계층(Document Processing), 임베딩 모델(Embedding Model), 벡터 데이터베이스(Vector Database), 검색 엔진(Retriever), 그리고 생성 모델(LLM)입니다.

각 구성 요소는 레고 블록처럼 조립되어 완전한 RAG 시스템을 만들어냅니다.

다음 코드를 살펴봅시다.

# RAG 아키텍처의 전체 구성 요소를 보여주는 예제
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Pinecone
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI

# 1. 문서 로딩 (Document Processing)
loader = TextLoader("company_docs.txt")
documents = loader.load()

# 2. 청킹 (문서를 적절한 크기로 분할)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = text_splitter.split_documents(documents)

# 3. 임베딩 모델 (텍스트를 벡터로 변환)
embeddings = OpenAIEmbeddings()

# 4. 벡터 DB에 저장
vectorstore = Pinecone.from_documents(chunks, embeddings, index_name="company-kb")

# 5. 검색기 설정
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 6. LLM과 연결
llm = OpenAI(temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)

# 7. 질의응답 실행
answer = qa_chain.run("재택근무 정책은?")

박시니어 씨가 화이트보드에 다섯 개의 박스를 그렸습니다. "RAG는 이 다섯 개의 퍼즐 조각으로 만들어져요." 첫 번째 조각은 문서 처리 계층입니다.

"원본 문서는 보통 PDF, Word, 웹페이지 같은 형태죠." 박시니어 씨가 설명했습니다. "이걸 AI가 이해할 수 있는 텍스트로 변환하고, 적절한 크기로 나눠야 해요." 여기서 중요한 개념이 **청킹(Chunking)**입니다.

100페이지짜리 매뉴얼을 통째로 LLM에 넣을 수는 없습니다. 토큰 제한도 있고, 관련 없는 내용까지 포함되면 답변 품질이 떨어지니까요.

그래서 문서를 500-1000자 정도의 작은 조각으로 나눕니다. "마치 큰 피자를 한입 크기로 자르는 거예요." 박시니어 씨가 비유했습니다.

위 코드의 12-13번째 줄을 보면, RecursiveCharacterTextSplitter가 문서를 1000자씩 나누되, 200자는 겹치게 만듭니다. 왜 겹칠까요?

문장이 중간에 잘리는 걸 방지하기 위해서입니다. 두 번째 조각은 임베딩 모델입니다.

"텍스트를 그대로 저장하면 검색이 안 돼요." 박시니어 씨가 계속했습니다. "예를 들어 '자동차'와 '차량'은 다른 단어지만 의미는 비슷하죠.

컴퓨터가 이걸 어떻게 알까요?" 답은 벡터 임베딩입니다. 텍스트를 1536개의 숫자로 이뤄진 배열로 변환하는 거죠.

의미가 비슷한 단어들은 비슷한 숫자 패턴을 갖게 됩니다. 마치 단어의 DNA처럼 말이죠.

16번째 줄의 OpenAIEmbeddings()가 바로 이 역할을 합니다. "재택근무"라는 단어를 [0.23, -0.45, 0.67, ...]처럼 1536개의 숫자로 바꿔줍니다.

세 번째 조각은 벡터 데이터베이스입니다. "일반 데이터베이스는 정확히 일치하는 것만 찾아요." 박시니어 씨가 설명했습니다.

"하지만 벡터 DB는 의미가 유사한 것을 찾습니다." 벡터 DB는 Pinecone, Weaviate, Chroma, FAISS 같은 것들이 있습니다. 19번째 줄에서 문서 청크들을 벡터로 변환하여 Pinecone에 저장합니다.

이제 수백만 개의 문서 중에서도 의미적으로 관련된 것을 빠르게 찾을 수 있습니다. "도서관의 사서가 책 내용까지 다 기억하는 거죠." 김개발 씨가 비유했습니다.

박시니어 씨가 손가락을 튕겼습니다. "정확해요!" 네 번째 조각은 **검색기(Retriever)**입니다.

벡터 DB에 저장만 해서는 안 됩니다. 사용자 질문이 들어왔을 때 어떻게 검색할지 전략을 정해야 합니다.

22번째 줄의 as_retriever(search_kwargs={"k": 3})는 "가장 유사한 문서 3개를 찾아라"는 설정입니다. 검색 전략은 다양합니다.

단순히 유사도 높은 순서로 찾을 수도 있고, 최신 문서에 가중치를 줄 수도 있습니다. 또는 특정 카테고리 안에서만 검색하게 할 수도 있죠.

다섯 번째 조각은 **생성 모델(LLM)**입니다. 25번째 줄에서 LLM을 초기화하고, 26번째 줄에서 검색기와 연결합니다.

이제 사용자가 질문하면, 검색기가 관련 문서를 찾아 LLM에게 전달하고, LLM은 그 정보를 바탕으로 답변을 생성합니다. 29번째 줄을 실행하면 마법이 일어납니다.

"재택근무 정책은?"이라는 질문이 들어오면, 시스템은 자동으로 관련 문서를 검색하고, LLM이 자연스러운 답변을 만들어냅니다. "와, 생각보다 간단하네요!" 김개발 씨가 감탄했습니다.

박시니어 씨가 고개를 저었습니다. "코드는 간단해 보이지만, 각 구성 요소를 제대로 설정하는 게 핵심이에요.

청크 크기, 임베딩 모델 선택, 검색 개수 등 수많은 하이퍼파라미터가 있거든요." 실제 운영 환경에서는 더 복잡합니다. 대규모 문서를 처리하려면 메타데이터 필터링이 필요합니다.

예를 들어 HR 부서 문서와 개발팀 문서를 구분해서 저장하고, 질문 의도에 따라 필터링합니다. 또한 하이브리드 검색(키워드 검색 + 벡터 검색)을 결합하면 정확도가 더 높아집니다.

"생각보다 할 게 많네요..." 김개발 씨가 한숨을 쉬었습니다. 박시니어 씨가 웃었습니다.

"하나씩 해보면 금방 익숙해져요. 일단 기본 구조부터 만들어보세요." RAG 아키텍처의 다섯 가지 구성 요소를 이해하면, 각 단계에서 무엇을 최적화해야 할지 명확해집니다.

다음 단계는 이 구성 요소들이 어떻게 흘러가는지 살펴보는 것입니다.

실전 팁

💡 - 청크 크기는 문서 특성에 따라 다릅니다. 기술 문서는 500자, FAQ는 200자가 적합합니다

  • 임베딩 모델은 OpenAI(성능 좋음, 유료)와 Sentence-Transformers(무료, 빠름) 중 선택하세요
  • 프로덕션 환경에서는 Pinecone이나 Weaviate 같은 관리형 벡터 DB를 추천합니다

4. 검색 증강 생성 흐름

김개발 씨는 RAG의 구성 요소는 이해했지만, 실제로 요청이 들어왔을 때 어떤 순서로 처리되는지 궁금했습니다. "사용자가 질문하면 정확히 어떤 일이 일어나는 거죠?" 박시니어 씨에게 물었습니다.

RAG의 실행 흐름은 크게 인덱싱 단계(Indexing Phase)와 쿼리 단계(Query Phase)로 나뉩니다. 인덱싱은 문서를 미리 처리하여 벡터 DB에 저장하는 준비 과정이고, 쿼리는 사용자 질문이 들어왔을 때 검색하고 답변을 생성하는 실시간 과정입니다.

마치 도서관에서 책을 분류해두는 것과 실제 책을 찾는 것의 차이와 같습니다.

다음 코드를 살펴봅시다.

# RAG의 전체 실행 흐름을 단계별로 보여주는 예제
import time
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.llms import OpenAI

# === 인덱싱 단계 (사전 준비) ===
print("1. 문서 로딩 및 청킹...")
documents = ["재택근무는 주 3회 가능합니다.", "점심시간은 12-1시입니다."]
chunks = [doc for doc in documents]  # 실제로는 복잡한 청킹 로직

print("2. 임베딩 변환...")
embeddings_model = OpenAIEmbeddings()
# "재택근무는..." -> [0.23, -0.45, 0.67, ..., 0.12] (1536차원 벡터)

print("3. 벡터 DB 저장...")
vectorstore = Chroma.from_texts(chunks, embeddings_model)
print("인덱싱 완료!\n")

# === 쿼리 단계 (실시간 처리) ===
query = "재택은 며칠 가능해?"
print(f"사용자 질문: {query}")

print("4. 질문 임베딩 변환...")
query_vector = embeddings_model.embed_query(query)  # 질문도 벡터로

print("5. 유사도 검색...")
relevant_docs = vectorstore.similarity_search(query, k=2)
print(f"   검색 결과: {relevant_docs[0].page_content}")

print("6. 프롬프트 구성 및 LLM 호출...")
llm = OpenAI(temperature=0)
prompt = f"다음 정보로 답변하세요:\n{relevant_docs[0].page_content}\n\n질문: {query}"
answer = llm(prompt)
print(f"   최종 답변: {answer}")

박시니어 씨가 화이트보드에 두 개의 큰 박스를 그렸습니다. "RAG는 크게 준비 단계실행 단계로 나뉘어요." "영화 촬영 전 리허설과 본 촬영 같은 건가요?" 김개발 씨가 물었습니다.

박시니어 씨가 손가락을 튕겼습니다. "정확해요!" 인덱싱 단계는 시스템을 준비하는 과정입니다.

이 단계는 사용자가 질문하기 전에 미리 해둡니다. 회사 문서가 업데이트될 때마다 한 번씩 실행하면 됩니다.

매번 할 필요는 없죠. 첫째, 문서 로딩 및 청킹입니다.

8-9번째 줄처럼 원본 문서를 불러와서 적절한 크기로 나눕니다. 실제로는 PDF 파싱, HTML 정제, 표 추출 등 복잡한 전처리가 들어갑니다.

"이 단계에서 문서 품질이 결정돼요." 박시니어 씨가 강조했습니다. "쓰레기를 넣으면 쓰레기가 나오죠." 둘째, 임베딩 변환입니다.

12-13번째 줄에서 각 청크를 1536차원의 벡터로 변환합니다. "재택근무는 주 3회 가능합니다"라는 문장이 [0.23, -0.45, 0.67, ..., 0.12] 같은 숫자 배열이 되는 거죠.

이 과정은 시간이 좀 걸립니다. 문서 1만 개를 처리하면 5-10분 정도 소요됩니다.

하지만 한 번만 하면 되니까 괜찮습니다. 셋째, 벡터 DB 저장입니다.

16번째 줄에서 변환된 벡터들을 데이터베이스에 저장합니다. 이제 빠른 검색이 가능한 상태가 됐습니다.

"자, 이제 준비 끝!" 박시니어 씨가 손을 털었습니다. 쿼리 단계는 사용자가 질문할 때마다 실시간으로 실행됩니다.

21번째 줄에서 사용자 질문이 들어옵니다. "재택은 며칠 가능해?" 이제부터가 진짜 시작입니다.

넷째, 질문 임베딩 변환입니다. 24번째 줄에서 사용자 질문도 똑같이 벡터로 변환합니다.

문서와 같은 방식으로 변환해야 의미 비교가 가능하니까요. "마치 같은 언어로 번역하는 거예요." 김개발 씨가 끄덕였습니다.

다섯째, 유사도 검색입니다. 27번째 줄이 가장 중요한 부분입니다.

벡터 DB는 질문 벡터와 가장 가까운(유사한) 문서 벡터를 찾습니다. 수학적으로는 코사인 유사도유클리드 거리를 계산합니다.

"재택은 며칠 가능해?"라는 질문 벡터가 "재택근무는 주 3회 가능합니다"라는 문서 벡터와 가장 가깝다면, 그게 검색 결과입니다. 28번째 줄에서 검색 결과를 출력하면, "재택근무는 주 3회 가능합니다"가 나옵니다.

완벽하게 관련된 문서를 찾았습니다! "오, 신기해요!" 김개발 씨가 감탄했습니다.

여섯째, 프롬프트 구성 및 LLM 호출입니다. 31-33번째 줄이 마지막 단계입니다.

검색된 문서를 프롬프트에 포함시켜 LLM에게 전달합니다. "다음 정보로 답변하세요: 재택근무는 주 3회 가능합니다.

질문: 재택은 며칠 가능해?" LLM은 이 프롬프트를 받아서 자연스러운 답변을 생성합니다. "재택근무는 주 3회까지 가능합니다." 검색된 정보를 기반으로 정확하면서도 자연스러운 답변이죠.

34번째 줄에서 최종 답변을 사용자에게 보여주면 끝입니다! "와, 이렇게 순식간에 일어나는 거였구나..." 김개발 씨가 놀라워했습니다.

박시니어 씨가 웃었습니다. "맞아요.

사용자 입장에서는 1-2초 만에 답변이 나오지만, 뒤에서는 이렇게 여섯 단계가 돌아가는 거죠." 성능 최적화도 중요합니다. 실제 서비스에서는 인덱싱 단계를 백그라운드에서 배치 작업으로 돌립니다.

매일 새벽 3시에 업데이트된 문서들을 자동으로 인덱싱하는 식이죠. 쿼리 단계는 캐싱을 활용합니다.

같은 질문이 반복되면 벡터 검색을 건너뛰고 캐시된 결과를 바로 반환합니다. "검색 결과가 잘못되면 어떻게 되나요?" 김개발 씨가 걱정스럽게 물었습니다.

박시니어 씨가 고개를 끄덕였습니다. "좋은 질문이에요.

그래서 **재순위화(Re-ranking)**라는 기법을 사용합니다. 일단 10개를 검색한 뒤, 더 정교한 모델로 재평가해서 상위 3개만 LLM에 전달하는 거죠." 김개발 씨는 이제 RAG의 전체 흐름이 머릿속에 그려졌습니다.

"이제 직접 구현해볼 수 있을 것 같아요!" RAG의 실행 흐름을 이해하면, 병목 지점을 파악하고 최적화할 수 있습니다. 인덱싱은 느려도 괜찮지만, 쿼리는 빨라야 합니다.

이 균형을 잘 맞추는 것이 성공적인 RAG 시스템의 핵심입니다.

실전 팁

💡 - 인덱싱은 증분 업데이트(Incremental Update)를 지원하도록 설계하세요. 전체 재인덱싱은 비효율적입니다

  • 쿼리 응답 시간을 줄이려면 벡터 검색 결과를 Redis 같은 캐시에 저장하세요
  • 검색 품질을 높이려면 하이브리드 검색(키워드 + 벡터)을 사용하고 재순위화를 적용하세요

5. RAG vs Fine-tuning 비교

김개발 씨가 RAG 프로토타입을 만들자, 다른 팀 동료가 물었습니다. "그냥 GPT를 파인튜닝하면 안 돼요?

더 정확하지 않나요?" 김개발 씨는 대답을 못 했습니다. 박시니어 씨에게 다시 물어봐야겠다고 생각했습니다.

파인튜닝은 LLM을 특정 도메인 데이터로 재학습시켜 모델 자체를 변경하는 방식이고, RAG는 외부 지식을 검색하여 제공하는 방식입니다. 파인튜닝은 특정 스타일이나 형식을 학습하는 데 효과적이지만, 사실 정보는 빠르게 구식이 되고 업데이트 비용이 큽니다.

RAG는 최신 정보를 즉시 반영하고 비용이 저렴하지만, 검색 품질에 따라 성능이 좌우됩니다.

다음 코드를 살펴봅시다.

# Fine-tuning vs RAG 비교 예제
from openai import OpenAI

client = OpenAI()

# === Fine-tuning 방식 ===
# 1. 학습 데이터 준비 (수천 개 필요)
training_data = [
    {"messages": [{"role": "user", "content": "재택은?"},
                  {"role": "assistant", "content": "주 3회 가능합니다."}]},
    # ... 수천 개의 예제
]
# 2. 파인튜닝 실행 (시간과 비용 소요)
# job = client.fine_tuning.jobs.create(training_file="file-abc", model="gpt-3.5-turbo")
# 3. 파인튜닝된 모델 사용
# answer = client.chat.completions.create(model="ft:gpt-3.5:org:id", messages=[...])

# === RAG 방식 ===
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

# 1. 문서 저장 (즉시 반영)
docs = ["재택근무는 주 3회 가능합니다."]
vectorstore = Chroma.from_texts(docs, OpenAIEmbeddings())

# 2. 검색 및 답변 (실시간)
query = "재택은?"
context = vectorstore.similarity_search(query)[0].page_content
llm_response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": f"정보: {context}\n질문: {query}"}]
)
answer = llm_response.choices[0].message.content
print(answer)  # "주 3회 가능합니다."

박시니어 씨가 화이트보드에 표를 그리기 시작했습니다. "이 질문은 모든 개발자가 하는 질문이에요." "둘 다 좋아 보이는데, 뭐가 다른 거예요?" 김개발 씨가 물었습니다.

선배가 설명을 시작했습니다. "간단히 비유하자면, 파인튜닝은 학생을 재교육하는 거고, RAG는 학생한테 교과서를 주는 거예요." 파인튜닝의 작동 방식을 먼저 살펴봅시다.

파인튜닝은 기존 LLM을 특정 도메인 데이터로 추가 학습시킵니다. 예를 들어 의료 상담 AI를 만들고 싶다면, 수천 건의 의료 상담 데이터로 GPT를 재학습시킵니다.

모델의 가중치(weight)가 업데이트되면서, 의료 용어와 상담 스타일을 내재화합니다. 12-14번째 줄 주석에서 보듯, 학습 데이터를 준비하고 OpenAI에 파인튜닝 작업을 요청합니다.

보통 수 시간에서 하루 정도 걸립니다. "그럼 완벽하게 우리 회사 전문가가 되는 거 아닌가요?" 김개발 씨가 물었습니다.

박시니어 씨가 고개를 저었습니다. "장점도 있지만, 치명적인 단점도 있어요." 파인튜닝의 장점은 명확합니다.

첫째, 스타일과 톤을 완벽하게 학습합니다. 고객 응대 말투, 기술 문서 작성 스타일, 특정 형식의 보고서 등을 일관되게 생성합니다.

둘째, 외부 검색 없이 즉시 답변합니다. 지연 시간(latency)이 짧습니다.

"좋은데요?" 김개발 씨가 고개를 끄덕였습니다. 하지만 파인튜닝의 단점이 더 큽니다.

첫째, 비용이 막대합니다. GPT-3.5 파인튜닝은 1000 토큰당 $0.008입니다.

회사 문서 1GB를 학습시키면 수백만 원이 들어갑니다. GPT-4 파인튜닝은 더 비쌉니다.

둘째, 업데이트가 어렵습니다. 회사 규정이 바뀌면?

전체를 다시 파인튜닝해야 합니다. 최신 뉴스를 반영하려면?

매일 재학습해야 합니다. 현실적으로 불가능합니다.

셋째, 환각 현상이 여전히 발생합니다. 파인튜닝해도 LLM은 틀린 정보를 만들어낼 수 있습니다.

학습 데이터에 없던 질문이 들어오면 여전히 추측합니다. "앗, 그럼 파인튜닝은 별로네요?" 김개발 씨가 실망했습니다.

박시니어 씨가 손을 들었습니다. "아니에요.

목적에 따라 달라요." RAG의 작동 방식을 보겠습니다. 21-22번째 줄에서 문서를 벡터 DB에 저장합니다.

파인튜닝과 달리 모델을 재학습하지 않습니다. 25-30번째 줄에서 질문이 들어오면, 관련 문서를 검색하여 일반 LLM에 제공합니다.

"모델은 그대로인데, 교과서만 바꾸는 거네요!" 김개발 씨가 이해했습니다. RAG의 장점은 압도적입니다.

첫째, 비용이 저렴합니다. 임베딩 비용은 1000 토큰당 $0.0001로, 파인튜닝의 1/80 수준입니다.

둘째, 즉시 업데이트 가능합니다. 문서만 추가하면 5분 안에 반영됩니다.

셋째, 출처 추적이 가능합니다. 답변의 근거가 된 문서를 사용자에게 보여줄 수 있습니다.

"그럼 RAG가 무조건 좋은 거 아닌가요?" 김개발 씨가 물었습니다. RAG의 단점도 있습니다.

첫째, 검색 품질에 의존합니다. 관련 문서를 못 찾으면 답변도 나빠집니다.

둘째, 지연 시간이 길어집니다. 벡터 검색 → LLM 호출의 두 단계를 거치니까요.

셋째, 스타일 일관성은 보장 못 합니다. 매번 다른 문서를 참조하면 답변 톤이 달라질 수 있습니다.

박시니어 씨가 표를 완성했습니다. "그래서 실무에서는 둘을 함께 사용해요." "어떻게요?" 김개발 씨가 궁금해했습니다.

"파인튜닝으로 스타일을, RAG로 사실 정보를 제공하는 거죠." 박시니어 씨가 설명했습니다. "예를 들어 고객 응대 챗봇이라면, 파인튜닝으로 친절한 말투를 학습시키고, RAG로 제품 정보를 제공합니다." 실제 사례를 봅시다.

OpenAI의 Assistants API는 파인튜닝된 모델에 RAG를 결합합니다. Microsoft Copilot도 마찬가지입니다.

기본 모델은 파인튜닝으로 마이크로소프트 스타일로 만들고, 회사 문서는 RAG로 제공합니다. "아하, 장점만 취하는 거네요!" 김개발 씨가 이해했습니다.

박시니어 씨가 고개를 끄덕였습니다. "정확해요.

하지만 대부분의 경우, RAG만으로도 충분합니다. 파인튜닝은 정말 스타일이 중요한 경우에만 하세요." 선택 기준을 정리하면 이렇습니다.

파인튜닝을 선택해야 하는 경우: (1) 특정 형식이나 스타일이 중요할 때, (2) 극도로 짧은 지연 시간이 필요할 때, (3) 도메인 특화 언어를 사용할 때(예: 의료, 법률). RAG를 선택해야 하는 경우: (1) 자주 업데이트되는 정보일 때, (2) 정확한 사실 정보가 중요할 때, (3) 출처 추적이 필요할 때, (4) 예산이 제한적일 때.

"우리 프로젝트는 RAG가 맞겠네요!" 김개발 씨가 확신했습니다. 파인튜닝과 RAG를 이해하면, 프로젝트 요구사항에 맞는 최적의 아키텍처를 선택할 수 있습니다.

대부분의 기업 활용 사례에서는 RAG가 더 실용적이고 경제적입니다.

실전 팁

💡 - 시작은 RAG로 하세요. 나중에 필요하면 파인튜닝을 추가할 수 있습니다

  • 파인튜닝 비용을 계산할 땐 재학습 주기도 고려하세요. 한 번이 아니라 지속적인 비용입니다
  • 하이브리드 접근이 최선입니다. 스타일은 파인튜닝, 사실은 RAG로 제공하세요

6. RAG 활용 사례

김개발 씨가 RAG 시스템을 완성하고 팀장님께 보고했습니다. "잘했어요!

근데 다른 곳에서도 이거 쓸 수 있을까요?" 팀장님이 물었습니다. 김개발 씨는 RAG가 실제로 어떻게 활용되는지 더 알아보기로 했습니다.

RAG는 기업 내부 검색, 고객 지원, 의료·법률 자문, 연구 보조, 개인화 추천 등 다양한 분야에서 활용됩니다. 공통점은 정확한 사실 정보최신 데이터가 중요한 도메인이라는 점입니다.

각 활용 사례는 문서 특성에 맞게 청킹 전략과 검색 방법을 최적화합니다.

다음 코드를 살펴봅시다.

# 실제 RAG 활용 사례: 코드 문서 검색 시스템
from langchain.document_loaders import GitHubLoader
from langchain.text_splitter import Language, RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Pinecone
from langchain.chains import ConversationalRetrievalChain
from langchain.chat_models import ChatOpenAI

# 1. GitHub 레포지토리 로딩 (기술 문서 활용 사례)
loader = GitHubLoader(repo="user/repo", branch="main", file_filter=lambda x: x.endswith(".py"))
documents = loader.load()

# 2. 코드 전용 청킹 전략
code_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=500, chunk_overlap=50
)
chunks = code_splitter.split_documents(documents)

# 3. 벡터 DB에 저장
embeddings = OpenAIEmbeddings()
vectorstore = Pinecone.from_documents(chunks, embeddings, index_name="codebase-search")

# 4. 대화형 검색 체인 (채팅 이력 유지)
llm = ChatOpenAI(model="gpt-4", temperature=0)
qa_chain = ConversationalRetrievalChain.from_llm(
    llm=llm, retriever=vectorstore.as_retriever(search_kwargs={"k": 5})
)

# 5. 사용자 질문 처리
chat_history = []
question = "인증은 어떻게 구현되어 있나요?"
result = qa_chain({"question": question, "chat_history": chat_history})
print(result["answer"])
chat_history.append((question, result["answer"]))

박시니어 씨가 자신의 노트북을 열었습니다. "실제 기업들이 RAG를 어떻게 쓰는지 보여줄게요." 첫 번째 활용 사례: 기업 내부 검색 "가장 흔한 사례예요." 박시니어 씨가 말했습니다.

"직원이 수천 개의 내부 문서에서 정보를 찾아야 할 때 사용하죠." 예를 들어 Notion AI는 사용자의 모든 노트와 문서를 RAG로 검색합니다. "Q2 마케팅 전략 회의록 찾아줘"라고 물으면, 관련 페이지를 찾아 요약해줍니다.

Slack의 AI 검색도 마찬가지입니다. 수년간 쌓인 대화에서 관련 내용을 즉시 찾아냅니다.

"우리 회사에도 딱 필요한 기능이네요!" 김개발 씨가 감탄했습니다. 핵심은 메타데이터 필터링입니다.

부서별, 프로젝트별, 날짜별로 문서를 분류하여 검색 정확도를 높입니다. HR 질문에는 HR 문서만, 개발 질문에는 기술 문서만 검색하는 식이죠.

두 번째 활용 사례: 고객 지원 챗봇 "고객센터 상담사를 도와주는 AI예요." 박시니어 씨가 설명했습니다. Zendesk, Intercom 같은 고객 지원 플랫폼은 RAG로 제품 매뉴얼, FAQ, 과거 티켓을 검색합니다.

고객이 "비밀번호 재설정이 안 돼요"라고 문의하면, 관련 매뉴얼을 즉시 찾아 상담사에게 제시합니다. 상담사는 복사-붙여넣기만 하면 됩니다.

더 나아가 완전 자동 응답도 가능합니다. 단순 질문은 AI가 직접 답변하고, 복잡한 질문만 사람에게 전달합니다.

이렇게 하면 상담사 업무량이 50% 줄어듭니다. "고객 만족도는 떨어지지 않나요?" 김개발 씨가 걱정했습니다.

박시니어 씨가 고개를 저었습니다. "오히려 올라가요.

답변이 더 빠르고 정확하거든요. 24시간 응대도 가능하고요." 세 번째 활용 사례: 의료 및 법률 자문 "전문 지식이 필요한 분야에서 활용도가 높아요." 박시니어 씨가 말했습니다.

의료 분야에서는 수백만 건의 논문과 임상 데이터를 RAG로 구축합니다. 의사가 희귀 질환 환자를 진료할 때, AI가 유사 사례와 최신 치료법을 즉시 제시합니다.

환자 안전이 중요하기 때문에, AI는 반드시 출처(논문 링크)를 함께 제공합니다. 법률 분야도 마찬가지입니다.

변호사가 판례를 검색할 때, RAG가 수만 건의 판례 중 유사 사례를 찾아줍니다. "부당해고 + 정신적 피해" 같은 복합 조건도 의미 기반으로 검색합니다.

"출처가 중요하다는 게 핵심이네요." 김개발 씨가 메모했습니다. 네 번째 활용 사례: 코드베이스 검색 위의 코드가 바로 이 사례입니다.

9번째 줄에서 GitHub 레포지토리를 로딩합니다. 신입 개발자가 대규모 코드베이스를 이해하려면 몇 주가 걸립니다.

하지만 RAG를 사용하면 "인증은 어떻게 구현되어 있나요?"라고 물어볼 수 있습니다. 13-15번째 줄이 핵심입니다.

일반 텍스트와 달리, 코드는 언어별 파서로 청킹해야 합니다. Python 함수 단위, 클래스 단위로 나눠야 검색이 정확합니다.

24-26번째 줄에서 대화형 체인을 사용합니다. "인증은 어떻게 구현되어 있나요?" → "JWT를 사용합니다" → "JWT는 어디서 검증하나요?" 처럼 이어지는 대화가 가능합니다.

GitHub Copilot도 내부적으로 유사한 방식을 사용합니다. 사용자가 작성 중인 코드와 유사한 패턴을 레포지토리에서 찾아 제안합니다.

"이거 완전 편리하겠네요!" 김개발 씨가 눈을 반짝였습니다. 다섯 번째 활용 사례: 연구 및 교육 "연구자들이 논문 홍수에서 허우적대는 걸 도와줘요." 박시니어 씨가 설명했습니다.

Semantic Scholar, Connected Papers 같은 서비스는 수천만 건의 논문을 RAG로 검색합니다. "Transformer 아키텍처의 최신 개선 방법"이라고 물으면, 최근 6개월간의 관련 논문을 찾아줍니다.

단순 키워드 매칭이 아니라 의미 기반 검색이기 때문에, "BERT 성능 향상" 같은 유사 논문도 함께 찾아냅니다. 교육 분야에서는 학생들의 질문에 맞춤형 답변을 제공합니다.

"양자역학의 불확정성 원리를 쉽게 설명해줘"라고 물으면, 교과서와 강의 노트에서 관련 부분을 찾아 초보자 수준으로 재구성합니다. 여섯 번째 활용 사례: 개인화 추천 "Netflix, Amazon 같은 곳에서 쓰기 시작했어요." 박시니어 씨가 말했습니다.

사용자의 시청 이력, 리뷰, 선호도를 RAG로 분석합니다. "SF 영화 추천해줘"라고 물으면, 사용자가 좋아했던 영화들과 유사한 작품을 찾아냅니다.

기존 협업 필터링보다 설명 가능성이 높습니다. "블레이드 러너를 좋아하셨으니 이 영화를 추천합니다" 같은 이유를 제시할 수 있거든요.

"와, RAG가 이렇게 다양하게 쓰이는 줄 몰랐어요!" 김개발 씨가 놀라워했습니다. 박시니어 씨가 웃었습니다.

"이제 시작일 뿐이에요. 앞으로 더 많은 분야에서 RAG가 활용될 겁니다." 성공 사례의 공통점을 정리하면 이렇습니다.

첫째, 도메인에 맞는 청킹 전략을 사용합니다. 의료 논문은 단락 단위, 법률 문서는 조항 단위, 코드는 함수 단위로 나눕니다.

둘째, 메타데이터를 적극 활용합니다. 날짜, 카테고리, 작성자 등으로 필터링하여 검색 정확도를 높입니다.

셋째, 출처를 명시합니다. 사용자가 AI 답변을 신뢰할 수 있도록 근거를 보여줍니다.

"우리 프로젝트도 이렇게 확장할 수 있겠네요!" 김개발 씨가 자신감 있게 말했습니다. 박시니어 씨가 어깨를 두드렸습니다.

"그럼요. RAG를 제대로 이해했으니, 이제 뭐든 만들 수 있어요." RAG의 다양한 활용 사례를 보면, 이 아키텍처가 얼마나 강력하고 범용적인지 알 수 있습니다.

여러분의 프로젝트에서도 RAG를 활용하여 사용자에게 정확하고 최신의 정보를 제공하세요.

실전 팁

💡 - 코드베이스 검색에는 ast(추상 구문 트리) 기반 청킹을 사용하면 정확도가 높아집니다

  • 의료/법률처럼 신뢰가 중요한 분야에서는 반드시 출처와 함께 답변을 제공하세요
  • 대화형 RAG를 구현할 때는 chat_history를 벡터화하여 문맥을 유지하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#RAG#LLM#VectorDB#Embedding#AWS
RAG와 Knowledge Bases 실전 | CodeDeck | CodeDeck