본문 바로가기
AI

Retrieval-Augmented Generation(RAG) 설명

by markbyun 2025. 5. 8.

Retrieval-Augmented Generation(RAG): 아키텍처, 변형, 실전 적용 전략

From Microsoft

Retrieval-Augmented Generation(RAG)은 대규모 언어 모델(LLM)과 외부 지식 검색 시스템을 결합한 하이브리드 접근 방식입니다. 기존의 LLM이 내부 파라미터에 내재된 지식에 의존하는 반면, RAG는 외부 소스를 동적으로 참조하여 보다 정확하고 사실 기반의 응답을 생성할 수 있게 합니다. 이 과정에서 가장 핵심적인 역할을 수행하는 기술이 벡터 데이터베이스 기반의 문서 검색입니다.

가장 간단한 형태의 RAG는 생성형 AI를 사용하는 사용자가 특정한 도메인 지식을 참고하기 위해 URL 정보나 PDF 파일과 같은 외부 자료를 직접 프롬프트에 첨부하여 질문하는 방식이라고 볼 수 있습니다. 이러한 방식은 도메인 특화 정보를 직접 수동으로 제공함으로써, AI가 보다 정확하고 맥락에 맞는 응답을 생성할 수 있도록 돕습니다.

하지만 이 과정을 매번 수동으로 반복하는 것은 비효율적이며, 확장성이 떨어집니다. 이를 자동화한 것이 바로 RAG 시스템입니다.
RAG 시스템은 다양한 도메인 특화 정보를 벡터 데이터베이스(예: FAISS 등)에 저장해두고, 사용자가 질문을 할 때마다 해당 질문과 관련된 정보를 자동으로 검색하여, 생성형 AI의 프롬프트에 첨부합니다. 이 과정을 통해 생성형 AI는 보다 정확하고 사실에 기반한 응답을 생성할 수 있게 됩니다.

이제 보다 체계적으로 RAG가 어떤 것이고 어떤 경우에 사용될 수 있는지 살펴보도록 하고 간단한 RAG 시스템을 Gemini, FAISS, LangChain을 이용해서 구현해보도록 하겠습니다.

RAG의 기본 아키텍처

일반적인 RAG 파이프라인은 두 개의 주요 컴포넌트로 구성됩니다:

  • 검색기(Retriever): 사용자 쿼리에 따라 관련 문서를 외부에서 검색해오는 역할을 합니다. 기존의 BM25와 같은 기호 기반 검색 방식이 아닌, Dense Embedding을 사용한 근사 최근접 이웃(ANN) 검색이 활용되며, 대표적인 벡터 데이터베이스로는 FAISS, Pinecone, Weaviate, Chroma 등이 있습니다.
  • 생성기(Generator): 검색된 문서와 사용자 쿼리를 입력으로 받아 최종 응답을 생성하는 대규모 언어 모델입니다. (예: GPT, T5, FLAN, Gemini)

RAG의 기본 구조는 다음과 같습니다:

사용자 쿼리 → Dense Retriever → 상위 k개 문서 → LLM Generator → 최종 응답

컴포넌트 상세 설명

1. 벡터 저장소 / 벡터 데이터베이스

벡터 데이터베이스는 문서의 임베딩을 저장하고 인덱싱하는 역할을 수행합니다. 주요 솔루션은 다음과 같습니다:

  • FAISS: Facebook AI에서 개발한 고속 ANN 검색 엔진.
  • Pinecone: 클라우드 기반의 완전 관리형 벡터 DB 서비스.
  • Weaviate: 스키마 기반 필터링과 하이브리드 검색을 지원하는 벡터 DB.
  • Chroma: 오픈소스 기반의 경량 벡터 DB로 LangChain과 잘 통합됩니다.

이러한 시스템은 문서 단위 텍스트를 임베딩하여 저장하고, 쿼리 임베딩과의 코사인 유사도 또는 내적을 기반으로 문서를 검색합니다.

2. 임베딩 모델

문서 및 쿼리를 Dense Vector로 변환하는 모델입니다. 주요 모델은 다음과 같습니다:

  • Sentence-BERT (SBERT)
  • OpenAI text-embedding-ada
  • Instructor-XL 및 기타 크로스도메인 모델

3. 검색 알고리즘

벡터 DB에서 사용하는 근사 최근접 이웃 검색 방식은 다음과 같습니다:

  • HNSW (Hierarchical Navigable Small World)
  • IVF (Inverted File Index)
  • ScaNN, Annoy, Pinecone 및 Milvus의 독자적 알고리즘

4. RAG 생성기 모델

RAG에서 사용되는 생성 모델은 다음과 같습니다:

  • BART: Facebook AI의 원래 RAG 논문에서 사용됨.
  • GPT 계열: GPT-3.5, GPT-4 등.
  • T5: 인코더-디코더 구조를 가지며 FiD(Fusion-in-Decoder) 방식에 주로 사용됩니다.

RAG 구현 방식의 종류

  • Vanilla RAG: 상위 k개 문서를 LLM 입력에 단순 연결하여 응답 생성.
  • RAG-Token: 각 토큰 생성 시 특정 문서에 기반한 생성.
  • FiD (Fusion-in-Decoder): 문서들을 별도로 인코딩하고 디코더에서 결합.
  • Multi-hop RAG: 다단계 문서 검색 및 추론 수행.
  • LangChain 기반 RAG: 체인 방식으로 RAG를 구성하고 조건 분기, 메모리 등을 제어.
  • Haystack, LlamaIndex: 파이프라인 전체를 관리하는 오픈소스 기반 RAG 프레임워크.

RAG의 장점

  • 사실성 향상: 최신 외부 데이터를 참조함으로써 환각(hallucination) 감소.
  • 비용 효율성: 소형 LLM과 정밀한 검색기를 조합하여 대형 모델보다 뛰어난 결과 도출 가능.
  • 도메인 적응성: LLM 재학습 없이도 새로운 지식 기반에 빠르게 적응 가능.
  • 투명성: 검색된 문서를 함께 제공하여 생성 근거 제공 가능.

RAG의 단점

  • 검색 품질 의존: 검색이 부정확하면 생성 결과도 저하됨.
  • 지연 시간: 검색 → 생성의 2단계 파이프라인은 실시간 서비스에 부적합할 수 있음.
  • 토큰 한계: 많은 문서를 연결하면 입력 한도를 초과할 수 있음.
  • 엔지니어링 복잡성: 검색기, 생성기, 벡터 DB, 파이프라인 등 관리 요소 증가.

RAG 적용 시 권장 전략

  • 도메인에 특화된 임베딩 모델 사용.
  • R@k 등의 지표로 검색 성능 지속적 평가.
  • 문서 Chunking 기법 (예: 슬라이딩 윈도우) 적용.
  • Prompt 설계 시 검색된 문서 기반 응답을 유도.
  • 벡터 DB의 검색 파라미터(HNSW의 M, efConstruction 등) 튜닝.

간단한 RAG 시스템 구현 예시

구현할 예시 RAG 시스템은 크게 세 부분으로 나눠질 수 있습니다.

첫번째 파트는 GenAI와 주어진 데이터들을 임베딩 벡터로 변환하여 벡터 DB(FAISS)에 저장하기 위한 임베딩 모델에 대한 인스턴스들을 생성하는 부분입니다. 본 예제에서는 Gemini-2.5-pro 모델을 ChatGoogleGenerativeAI() 함수를 호출해서 LLM 인스턴스를 생성하고, 구글의 text-embedding-004 모델을 임베딩 모델로 사용하기 위해서 GoogleGenerativeAIEmbeddings() 함수를 호출합니다.

import os
from langchain_community.document_loaders import TextLoader, UnstructuredExcelLoader, PyPDFLoader
from langchain_community.vectorstores import FAISS
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain.schema import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.messages import SystemMessage
from langchain_core.tools import tool
from langgraph.graph import END, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver

from dotenv import load_dotenv

load_dotenv(".env")

llm = ChatGoogleGenerativeAI(model='gemini-2.5-pro-exp-03-25', 
                             temperature=0.9)

# Setup embedding model as GoogleGenerativeAIEmbeddings
# 'google_api_key' parameter will be assigned by 'GOOGLE_API_KEY' environment variable
embeddings = GoogleGenerativeAIEmbeddings(model = "models/text-embedding-004")

db_path = "./faiss-doc-db"

두번째는 특정 도메인에 해당하는 PDF 문서(본 예제에서는 메타의 약관 파일을 사용했습니다.)를 FAISS 데이터베이스에 저장하는 부분 입니다. 한국 메타의 2025년 약관 정보를 사용한 이유는 최신 정보이고 인터넷에 충분히 확산된 정보가 아니라 (본 예제에서는 Gemini)가 학습에 사용했을 가능성이 낮기 때문입니다. 실제 아래 코드에서 llm.bind_tools()라는 함수에 벡터 DB에 있는 내용을 검색하는 함수를 파라미터로 넣어주는데, 이 함수가 하는 역활은 사용자가 입력한 질문에 해당하는 답을 GenAI가 이전에 학습한 내용으로 충분히 만들 수 있다면 별도로 DB 검색을 하지 않고 바로 답을 생성하고(즉, RAG를 사용하지 않고) 아니면 DB에서 검색한 자료를 기반으로 답을 제공하도록 합니다. 따라서 가능한 GenAI가 사전에 학습하지 않을 정보와 관련된 질문을 해서 RAG 시스템이 어떻게 동작하는지 보여주기 위함 입니다.

벡터 DB 생성 및 저장에 대한 상세한 설명은 'LangChain, FAISS, Gemini 임베딩을 활용한 벡터 DB 저장 및 검색 방법'에서 보실 수 있습니다.

# Create new vector DB if there is the database but if there is previous db then add new information
def create_vector_database(db_path, txt_path, type="text"):
    if os.path.exists(db_path):
        db = FAISS.load_local(db_path, embeddings=embeddings, allow_dangerous_deserialization=True)
    else:
        documents = [Document(page_content='RAG자료')]
        db = FAISS.from_documents(documents, embeddings)

    separators = ['\n\n', '\n', ' ', '\t']
    chunk_size = 1000
    chunk_overlap = 100
    if type == "excel":
        loader = UnstructuredExcelLoader(txt_path)
    elif type == "pdf":
        loader = PyPDFLoader(txt_path)
    else:
        loader = TextLoader(txt_path)
    docs = loader.load()   
    
    documents = RecursiveCharacterTextSplitter(
        separators=separators,
        chunk_size=chunk_size, 
        is_separator_regex=False, 
        chunk_overlap=chunk_overlap
    ).split_documents(docs)

    MAX_BUFFER_SIZE = 100000
    IDX_DELTA = MAX_BUFFER_SIZE//chunk_size        
    doc_size = len(documents)
    remainder = doc_size % IDX_DELTA
    last_idx = doc_size - remainder
    print(f"Total documents: {doc_size}")
    print(f"Last index: {last_idx}")
    print(f"Remainder: {remainder}")
    for idx in range(0, last_idx, IDX_DELTA):
        db.add_documents(documents=documents[idx:idx+IDX_DELTA])
    if last_idx < doc_size:
        db.add_documents(documents=documents[last_idx:])

    db.save_local(db_path)

# Save custom PDF document as vector database
create_vector_database(db_path, "./Meta 서비스 약관.pdf", type="pdf")

세번째 코드 파트는 저장된 벡터 DB를 불러오고 랭체인 그래프를 이용해서 기본적인 FAQ 처리를 위한 파이프라인 구성 및 LLM이 미리 학습된 내용으로 부터 질문에 대한 충분한 답을 찾을 수 없을 경우 미리 지정한 벡터 DB를 검색해서 관련 정보를 가져온 후 답을 제공하는 파이프라인을 구축합니다. 그리고 메타의 2025년 약관에 대한 질문을 던져서 답을 얻는 코드로 구성되어 있습니다.

vectorstore_faiss = FAISS.load_local("./faiss-doc-db", embeddings, allow_dangerous_deserialization=True)
faiss_retriever = vectorstore_faiss.as_retriever(search_type="similarity", search_kwargs={"k": 2})

@tool(response_format="content_and_artifact")
def retrieve(query: str):
    """Retrieve information related to a query."""
    retrieved_docs = faiss_retriever.get_relevant_documents(query)
    print('-----Retrieved document: ', retrieved_docs)
    serialized = "\n\n".join(
        (f"Source: {doc.metadata}\n" f"Content: {doc.page_content}")
        for doc in retrieved_docs
    )
    return serialized, retrieved_docs

tools = ToolNode([retrieve])

def query_or_respond(state: MessagesState):
    llm_with_tools = llm.bind_tools([retrieve])
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}

def generate(state: MessagesState):
    recent_tool_messages = []
    for message in reversed(state["messages"]):
        if message.type == "tool":
            recent_tool_messages.append(message)
        else:
            break
    tool_messages = recent_tool_messages[::-1]

    docs_content = "\n\n".join(doc.content for doc in tool_messages)
    system_message_content = (
        "You are a Meta employee. Answer the given question by referrencing the attached document."
        "\n\n"
        f"{docs_content}"
    )
    conversation_messages = [
        message for message in state["messages"] 
        if message.type in ('human', 'system')
        or (message.type == 'ai' and not message.tool_calls)
    ]
    prompt = [SystemMessage(system_message_content)] + conversation_messages

    response = llm.invoke(prompt)
    return {"messages": [response]}
    

# Build graph
graph_builder = StateGraph(MessagesState)
graph_builder.add_node("query_or_respond", query_or_respond)
graph_builder.add_node("tools", tools)
graph_builder.add_node("generate", generate)

graph_builder.set_entry_point("query_or_respond")
graph_builder.add_conditional_edges("query_or_respond", tools_condition, {END: END, "tools": "tools"})
graph_builder.add_edge("tools", "generate")
graph_builder.add_edge("generate", END)

memory = MemorySaver()
graph = graph_builder.compile(checkpointer=memory)


# Specify an ID for the thread
config = {"configurable": {"thread_id": "abc123"}}

input_message = "메타의 2025년 약관에 대해서 알려줘."

for step in graph.stream(
    {"messages": [{"role": "user", "content": input_message}]},
    stream_mode="values",
    config=config,
):
    step["messages"][-1].pretty_print()

GenAI가 생성한 답변:

================================ Human Message =================================

메타의 2025년 약관에 대해서 알려줘.
================================== Ai Message ==================================

메타의 2025년 약관에 대한 정보를 찾아보겠습니다.
Tool Calls:
  retrieve (aaf25f9b-89a2-4d61-a668-5a6a9a4fb5ab)
 Call ID: aaf25f9b-89a2-4d61-a668-5a6a9a4fb5ab
  Args:
    query: 메타 2025년 약관
-----Retrieved document:  [Document(id='e1d6ff33-504a-4686-bbd1-0a518e5adb0c', metadata={'producer': 'Skia/PDF m135', 'creator': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', 'creationdate': '2025-05-07T23:56:14+00:00', 'title': 'Meta 서비스 약관', 'moddate': '2025-05-07T23:56:14+00:00', 'source': './Meta 서비스 약관.pdf', 'total_pages': 11, 'page': 0, 'page_label': '1'}, page_content="서비스  약관\n방침  둘러보기\n개요\n1. 저희가  제공하는  서비스\n2. 저희  서비스의  자금  조달  방식\n3. Facebook 및  커뮤니티에  대한  약속\n4. 추가  조항\n5. 회원님에게  적용될  수  있는  기타  약관  및  정책\n개요\n2025년 1월 1일부터 적용\nMeta는 사람들이 서로 교류하고 커뮤니티를 만들며 비즈니스를 성장시킬 수 있는 기술과 서비스\n를 개발합니다. (본 약관이 아닌) 별도의 약관이 적용된다고 명시되어 있지 않은 한, 본 서비스 약\n관('약관')은 회원님의 Facebook, Messenger 및 기타 저희가 제공하는 제품, 웹사이트, 기능, 앱,\n서비스, 기술, 소프트웨어(Meta 제품 또는 제품) 액세스 및 이용에 적용됩니다. 예를 들어 회원님\n의 Instagram 이용에는 Instagram 이용 약관이 적용됩니다. 해당 제품은 Meta Platforms, Inc.\n가 회원님에게 제공합니다. 따라서 본 약관은 회원님과 Meta Platforms, Inc. 사이의 계약에 해당\n됩니다. 본 약관에 동의하지 않는 경우 Facebook 또는 본 약관이 적용되는 다른 제품 및 서비스에\n액세스하거나 이용하지 마세요.\n본 약관(과거 명칭: 권리 및 책임에 관한 정책)은 회원님의 저희 제품 이용에 관한 회원님과 Meta\nPlatforms, Inc. 간의 완전한 합의를 구성합니다. 본 약관은 이전의 모든 계약을 대체합니다.\n달리 명시된 경우를 제외하고 저희는 회원님에게 Facebook 또는 기타 본 약관이 적용되는 제품\n및 서비스의 사용료를 청구하지 않습니다. 그 대신, 비즈니스와 단체 및 다른 사람이 저희에게 그\n들의 제품 및 서비스 광고를 회원님에게 보여드리도록 비용을 지불합니다. 저희 제품을 이용함으\n로써 회원님은 저희가 회원님 및 회원님의 관심사와 관련 있을 수 있다고 생각하는 광고를 보여주\n는 것에 동의하게 됩니다. 저희는 회원님의 개인정보를 이용하여 회원님에게 보여드릴 맞춤형 광\n고를 결정합니다."), Document(id='a565831a-06f5-48ff-a8eb-f32bc81a0aa8', metadata={'producer': 'Skia/PDF m135', 'creator': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', 'creationdate': '2025-05-07T23:56:14+00:00', 'title': 'Meta 서비스 약관', 'moddate': '2025-05-07T23:56:14+00:00', 'source': './Meta 서비스 약관.pdf', 'total_pages': 11, 'page': 5, 'page_label': '6'}, page_content='수정, 실행, 복사, 공개적 이용 또는 표시, 번역, 2차적 저작물 제작이 가능한 비독점적이고 양도\n25. 5. 8. 오전  8:56 Meta 서비스  약관\nhttps://mbasic.facebook.com/legal/terms/plain_text_terms/ 6/11')]
================================= Tool Message =================================
Name: retrieve

Source: {'producer': 'Skia/PDF m135', 'creator': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', 'creationdate': '2025-05-07T23:56:14+00:00', 'title': 'Meta 서비스 약관', 'moddate': '2025-05-07T23:56:14+00:00', 'source': './Meta 서비스 약관.pdf', 'total_pages': 11, 'page': 0, 'page_label': '1'}
Content: 서비스  약관
방침  둘러보기
개요
1. 저희가  제공하는  서비스
2. 저희  서비스의  자금  조달  방식
3. Facebook 및  커뮤니티에  대한  약속
4. 추가  조항
5. 회원님에게  적용될  수  있는  기타  약관  및  정책
개요
...
    *   Meta 제품을 이용함으로써 사용자는 Meta가 사용자의 관심사와 관련 있을 수 있다고 생각하는 광고를 보여주는 것에 동의하게 됩니다.
    *   Meta는 사용자의 개인정보를 이용하여 사용자에게 보여줄 맞춤형 광고를 결정합니다.

요약하자면, 2025년 1월 1일부터 적용되는 이 약관은 Meta Platforms, Inc.와 사용자 간의 계약으로, Facebook, Messenger 등 대부분의 Meta 서비스에 적용되며, 서비스는 주로 광고를 통해 자금을 조달하고 사용자는 맞춤형 광고 표시에 동의하는 것을 기본으로 합니다.

결과에서 확인할 수 있듯이 생성형 AI가 이전에 학습된 자료에서는 질문에 대한 충분한 답을 찾을 수 없어 벡터 DB를 검색하고 가장 유사한 내용을 검색해 와서 최종 답을 사용자에게 제공하고 있습니다.

결론

Retrieval-Augmented Generation(RAG)은 검색 기반 지식 접근과 생성형 모델을 결합한 최신 패러다임으로, 사실성, 유연성, 비용 효율성을 모두 만족시킬 수 있는 강력한 접근 방식입니다. 특히 도메인 지식이 중요한 엔터프라이즈 AI, 과학 연구, 문서 분석 등에서 효과적으로 활용되고 있으며, 앞으로도 더욱 정교한 RAG 아키텍처가 등장할 것으로 기대됩니다.

참고문헌