YYYEJI

[MAC] Naive RAG 구현 코드 구현하기 본문

인공지능 챗봇

[MAC] Naive RAG 구현 코드 구현하기

YEJI ⍢ 2025. 8. 5. 14:26
728x90

간단한 형태의 RAG 모델로,

외부 문서(지식베이스)에서 정보를 검색하고,

그 정보를 기반으로 답변을 생성하는 방식으로 코드를 구현해보겠습니다!!

 

 

문서 임베딩 및 저장: 문서들을 임베딩 벡터로 변환해 저장

질문 임베딩: 질문을 임베딩벡터로 변환

검색(Retrieval): 질문 벡터와 문서 벡터 간 유사도를 계산해 가장 관련 있는 문서 선택

생성(Generation): 선택된 문서를 참고하여 답변 생성

 

 

약간의 Naive RAG 개념을 요약해봤고 진짜 코드를 구현해봅시다!


1) 벡터 저장소 로드

from langchain_chroma import Chroma
from langchain_openai import AzureOpenAIEmbeddings

# OpenAI 임베딩 모델 생성
embeddings_openai = AzureOpenAIEmbeddings(
    model="text-embedding-3-small",  # 사용할 모델 이름
    )

# 저장된 벡터 저장소를 가져오기
chroma_db = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings_openai
)

# 문서 개수 확인
print(f"백터 저장소의 문서 개수: {chroma_db._collection.count()}")

AzureOpenAIEnbeddings()에서 모델은 text-embedding-3-small을 사용합니다!

 

 

persist_directory="./chroma_db" 폴더에서 이미 저장된 벡터를 불러옵니다.

 

[MAC] 벡터 저장소 ChromaDB 사용한 코드 살펴보기

벡터 저장소(Vector Store)이란?벡터 저장소란!!비정형 데이터를 벡터(숫자 배열)형태로 저장하고,빠르게 검색할 수 있게 해주는 특수한 데이터베이스에요!단순한 키워드 검색이 아니라 의미 기반

yyyeji.tistory.com

 

 

 

2) 검색기(Retriever) 초기화

# mmr 검색기 생성
retriever = chroma_db.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 5,
                   "fetch_k": 10,
                   "lambda_mult": 0.3},
)

 

 

[MAC] 벡터 저장소 기반 RAG 검색기 사용하기

RAG란?검색(Retrieval)과 생성(Generation)을 결합해, LLM이 더 정확하고 최신 정보로 답변하게 하는 방식입니다! RAG 개념 알아보기RAG란 무엇일까요??Retrieval-Augmented Geteration의 줄임말이며,LLM의 한계를 보

yyyeji.tistory.com

mmr 검색을 사용하여 다양성을 높이는 설정을 했습니다!!

 

 

# 검색 테스트
query = "탄소 제로 정책에 대해 알려줘"

# 쿼리와 유사한 문서 검색
retrieved_docs = retriever.invoke(query)

print(f"쿼리: {query}")
print("검색 결과: ")
for i, doc in enumerate(retrieved_docs, 1):
    print(f"-{i}-\n{doc.page_content}\n[출처: {doc.metadata['source']}]")
    print("-" * 100)

retriever.invoke(query)로 검색기에 질문을 던져주고 관련 문서 리스트를 받아옵니다!

retrieved_docs는 문서 객체들의 리스트이고,

각 문서의 내용은 doc.page_content에

출처 정보는 doc.metadata['source']에 담겨 있으며 

문서별로 번호와 함께 내용, 출처를 출력하는 코드입니다!!

 

 

3) RAG 프롬프트 구성

LangChain의 ChatPromptTembpate 클래스를 사용해 {context}, {question} 변수를 포함하는 예제를 살펴보겠습니다!

# Prompt 템플릿 (커스텀 예시)
from langchain_core.prompts import ChatPromptTemplate

template = """주어진 컨텍스트를 기반으로 질문에 답변하시오.

[지침]
- 컨텍스트에 있는 정보만을 사용하여 답변할 것
- 외부 지식이나 정보를 사용하지 말 것
- 컨텍스트에서 답을 찾을 수 없는 경우 "주어진 정보만으로는 답변하기 어렵습니다."라고 응답할 것
- 불확실한 경우 명확히 그 불확실성을 표현할 것
- 답변은 논리적이고 구조화된 형태로 제공할 것
- 답변은 한국어를 사용할 것

[컨텍스트]
{context}

[질문]
{question}

[답변 형식]
1. 핵심 답변: (질문에 대한 직접적인 답변)
2. 근거: (컨텍스트에서 발견된 관련 정보)
3. 추가 설명: (필요한 경우 부연 설명 제공)

[답변]
"""

prompt = ChatPromptTemplate.from_template(template)

# 템플릿 출력
prompt.pretty_print()

프롬프트 구성요소

- 작업 지침

- 컨텍스트 영역

- 질문 영역

- 답변 형식 가이드

 

작업 지침

- 컨텍스트 기반 답변 원칙

- 외부 지식 사용 제한

- 불확실성 처리 방법

- 답변 불가능한 경우의 처리 방법

 

답변 형식

- 핵심 답변 섹션

- 근거 제시 섹션

- 추가 설명 섹션

 

제약사항 반영

- 답변은 사실에 기반해야 해야한다!

- 추측이나 가정을 최소화해야 한다!

- 명확한 근거 제시가 필요하다!

- 구조화된 형태로 작성되어야 한다!

 

 

템플릿을 왜 사용하는가?

- 일관성 유지

- 효율성 향상 (변수를 통해 재활용)

- 명확한 지침 제공

- 유연성 제공!! 합니당 

 

 

4) RAG 체인 구성 

from langchain_core.runnables import RunnablePassthrough, RunnableParallel  # RunnableParallel -> 한 번에 병렬 처리
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import AzureChatOpenAI

# LLM 설정
llm = AzureChatOpenAI(
    deployment_name="gpt-4o-mini",
    temperature=0.7)

# 문서 포맷팅
def format_docs(docs):
    return "\n\n".join([f"{doc.page_content}" for doc in docs])

# RAG 체인 생성
rag_chain = (
    RunnableParallel(
        {
            "context": retriever | format_docs,
            "question": RunnablePassthrough()  # 니가 뭘 하지 말고 받은 그대로 뒤로 넘겨라
        }
    )
    | prompt
    | llm
    | StrOutputParser()
)

# 체인 실행
query = "탄소 제로 정책에 대해서 알려줘 "
output = rag_chain.invoke(query)

print(f"쿼리: {query}")
print("답변:")
print(output)

retriever를 통해 질문에 맞는 문서들을 검색하고,

format_docs 함수로 문서 내용을 연결해 context로 만들고,

question은 그대로 LLM에게 변경없이 전달합니다!!

 

 

| prompt
| llm
| StrOutputParser()

prompt: 앞서 만든 프롬프트 텝플릿에 context와 question을 채우고

llm: GPT 모델에 프롬프트를 전달해 답변을 생성하고

StrOutputParser(): 결과를 문자열로 깔끔하게 파싱합니다!!

 

 

이제 체인을 실행해보면

output = rag_chain.invoke(query)

템플릿에 맞춰 답변을 받아보신 것을 확인할 수 있습니다!!

728x90