본문 바로가기

파이썬으로 할 수 있는 일/AI&머신러닝

RAG(Retrieval-Augmented Generation) 구현하기: 파이썬으로 자체 지식 기반 AI 에이전트 구축

반응형

AI 에이전트에 관심있는 분들은 최근에 RAG라는 말을 많이 들어봤을 것이라고 생각한다. 그래서, 간단하게 구축할 수 있는 RAG에 대한 기본 개념을 잡아보면 어떨까 해서 글을 쓰게 되었다.
현재 PDF로 된 규정집을 갖고 있는데 관련 규정을 일일이 찾아보는 것은 업무를 잘알지 못하는 사람들은 시간이 꽤 소요되는 일이라고 생각했다. 그래서, 이 규정집 PDF 파일들의 내용을 AI 에이전트에서 검색을 통해 관련 읽어들일 수 있도록 하면 내가 원하는 정보를 아주 빠르고 정확하게 검색할 수 있지 않을까라는 생각이 들었다.

먼저 지금 사용중인 옵시디언에 저장된 내용들 전체를 쉽게 검색할 수 있도록 RAG를 사용하는 Smart Composer를 사용중으로 어떻게 사용을 할 수 있는지를 테스트해볼 수 있다. 
만약 옵시디언을 사용중이라면 커뮤니트 플러그인인 'Smart Composer'를 설치하고 사용해 보면 좋을 것 같다. 
설치가 되어 있다면, 오른쪽 창에 Chat을 할 수 있는 곳이 보이고, 거기에 검색하고 싶은 단어나 문장을 쓰고 Ctrl+Shift+Enter(맥은 Cmd+Shift+Enter)을 누르면 전체 글들을 인덱싱한 후 검색한 내용에 관련된 글들을 찾아서 보여준다.
옵시디언을 통해 자신만의 지식을 저장해서 사용하는 사람들에게는 매우 훌륭한 검색 도구라고 생각한다.

그럼 먼저 RAG의 정의와 장점부터 알아보자.

RAG란 무엇인가?

RAG는 'Retrieval-Augmented Generation'의 약자로, 대규모 언어 모델(LLM)의 생성 능력과 외부 지식 저장소에서 정보를 검색하는 기능을 결합한 기술이다. 이 접근법은 다음과 같은 두 가지 주요 과정으로 구성된다:

  1. 검색(Retrieval): 사용자 쿼리와 관련된 정보를 외부 데이터베이스나 지식 저장소에서 찾는다.
  2. 생성(Generation): 검색된 정보를 맥락으로 활용하여 LLM이 더 정확하고 최신 정보가 반영된 응답을 생성한다.

RAG의 가장 큰 장점은 LLM의 주요 한계점인 다음 문제들을 해결할 수 있다는 것이다:

  • 지식 차단(Knowledge Cutoff): LLM은 학습된 시점 이후의 정보를 알 수 없다.
  • 환각(Hallucination): LLM이 사실이 아닌 정보를 자신감 있게 제시하는 현상이다.
  • 블랙박스 추론: LLM의 응답이 어디서 비롯되었는지 추적하기 어렵다.

RAG 시스템의 핵심 구성 요소

효과적인 RAG 시스템을 구축하기 위해서는 다음과 같은 주요 구성 요소가 필요하다:

  1. 문서 처리 파이프라인: 다양한 형식의 문서를 로드하고 청크로 분할한다.
  2. 임베딩 모델: 텍스트를 벡터로 변환하여 의미적 검색을 가능하게 한다.
  3. 벡터 데이터베이스: 임베딩을 저장하고 효율적인 유사도 검색을 제공한다.
  4. LLM(대규모 언어 모델): 검색된 정보를 바탕으로 응답을 생성한다.
  5. 오케스트레이션 로직: 전체 프로세스를 조율하는 코드다.

그럼, 로컬 RAG를 간단하게 구축하는 순서에 대해 알아보도록 하자.

1. 문서 전처리(PDF, HWP 등의 문서들을 JSON 또는 Markdown의 문서들로 변환하도록 한다)
2. 임베딩 AI 모델로 문서 벡터화(상용은 Open AI의 임베딩 AI모델을 많이 사용하지만 비용이 들기 때문에, 로컬 PC에서 사용 가능한 모델을 사용해서 문서를 벡터화한다)
3. 벡터화된 문서들을 벡터 저장소(VectorDatabase)에 저장
4. 검색 및 재순위화 실행(LangChain 라이브러리를 사용한 벡터 검색기로 검색된 문서의 중요도에 따라 순서를 변경한다)

다음 내용을 클로드에게 부탁해서 만든 RAG 구축에 대한 기본적인 코드이다. 한번 읽어보고 참고하면 좋을 것 같다.


반응형

파이썬으로 RAG 구현하기

1. 필요한 라이브러리 설치

우선 필요한 라이브러리를 설치합니다:

pip install langchain langchain-openai sentence-transformers chromadb pypdf

2. 문서 로드 및 청크 분할

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# PDF 파일 로드
loader = PyPDFLoader("knowledge_base.pdf")
documents = loader.load()

# 청크 분할
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    length_function=len,
)
chunks = text_splitter.split_documents(documents)

print(f"문서를 {len(chunks)}개의 청크로 분할했습니다.")

3. 벡터 데이터베이스 생성

from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma

# 임베딩 모델 초기화
embedding_model = HuggingFaceEmbeddings(
    model_name="jhgan/ko-sroberta-multitask",  # 한국어 문서에 적합한 모델
    model_kwargs={'device': 'cpu'},
    encode_kwargs={'normalize_embeddings': True}
)

# 벡터 데이터베이스 생성
vectordb = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model,
    persist_directory="./chroma_db"
)

# 벡터 데이터베이스 저장
vectordb.persist()

4. RAG 파이프라인 구축

from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI

# LLM 초기화
llm = ChatOpenAI(
    model_name="gpt-4o",
    temperature=0.1,
    api_key="your_openai_api_key"  # 실제 키로 대체하세요
)

# 검색기 생성
retriever = vectordb.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 3}  # 상위 3개 문서 검색
)

# RAG 체인 생성
rag_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",  # 모든 컨텍스트를 하나의 프롬프트에 결합
    retriever=retriever,
    return_source_documents=True
)

5. 쿼리 실행 및 응답 생성

def query_rag(question):
    response = rag_chain({"query": question})
    answer = response['result']
    sources = [doc.metadata.get('source', 'Unknown') for doc in response['source_documents']]
    
    print(f"질문: {question}")
    print(f"답변: {answer}")
    print(f"참고 문서: {set(sources)}")
    
    return answer, sources

# RAG 시스템 테스트
query_rag("우리 회사의 2023년 4분기 매출액은 얼마인가요?")

 

고급 RAG 기법

기본적인 RAG 시스템을 구현했지만, 성능을 향상시키기 위한 고급 기법들이 있다:

1. 쿼리 확장 (Query Expansion)

사용자 쿼리를 LLM을 사용해 다양한 형태로 확장하여 검색 성능을 향상시킨다.

from langchain.retrievers.multi_query import MultiQueryRetriever

retriever_with_query_expansion = MultiQueryRetriever.from_llm(
    retriever=vectordb.as_retriever(),
    llm=llm
)

2. 재순위화 (Reranking)

검색된 문서를 더 정확한 순서로 재배열한다:

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

# 문서 압축기 생성
compressor = LLMChainExtractor.from_llm(llm)

# 압축 검색기 생성
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=retriever
)

3. 하이퍼파라미터 튜닝

청크 크기, 오버랩, 검색 개수 등의 하이퍼파라미터를 조정하여 성능을 최적화할 수 있다.

RAG 시스템 평가

RAG 시스템의 성능은 다음과 같은 측면에서 평가할 수 있다:

  1. 관련성 (Relevance): 검색된 문서가 질문과 얼마나 관련이 있는지
  2. 정확성 (Accuracy): 생성된 응답이 얼마나 정확한지
  3. 충실도 (Faithfulness): 응답이 검색된 문서에 충실한지, 환각이 없는지
  4. 정보성 (Informativeness): 응답이 얼마나 유용한 정보를 제공하는지

간단한 평가 코드를 구현해 보자:

from sklearn.metrics.pairwise import cosine_similarity

def evaluate_retrieval(question, retrieved_docs, ground_truth_docs, embedding_model):
    # 질문을 임베딩
    q_embedding = embedding_model.embed_query(question)
    
    # 검색된 문서 임베딩
    retrieved_embeddings = [embedding_model.embed_query(doc.page_content) for doc in retrieved_docs]
    
    # 정답 문서 임베딩
    ground_truth_embeddings = [embedding_model.embed_query(doc) for doc in ground_truth_docs]
    
    # 검색된 문서와 질문의 유사도 계산
    retrieved_similarities = [cosine_similarity([q_embedding], [emb])[0][0] for emb in retrieved_embeddings]
    
    # 정답 문서와 질문의 유사도 계산
    ground_truth_similarities = [cosine_similarity([q_embedding], [emb])[0][0] for emb in ground_truth_embeddings]
    
    # 평균 유사도 반환
    retrieved_avg_sim = sum(retrieved_similarities) / len(retrieved_similarities)
    ground_truth_avg_sim = sum(ground_truth_similarities) / len(ground_truth_similarities)
    
    return {
        "retrieved_avg_similarity": retrieved_avg_sim,
        "ground_truth_avg_similarity": ground_truth_avg_sim,
        "retrieval_effectiveness": retrieved_avg_sim / ground_truth_avg_sim if ground_truth_avg_sim > 0 else 0
    }

실제 비즈니스 적용 사례

RAG는 다음과 같은 비즈니스 시나리오에서 특히 유용하다:

  1. 고객 지원 챗봇: 회사 제품이나 서비스에 관한 최신 정보로 고객 문의에 응답
  2. 내부 지식 검색: 직원들이 회사 내부 문서에서 정보를 쉽게 찾을 수 있도록 지원
  3. 규제 준수: 금융이나 법률 분야에서 최신 규제 정보를 참조하여 정확한 조언 제공
  4. 의학 정보 시스템: 최신 의학 연구와 환자 기록을 결합하여 의사 결정 지원

결론 및 주의사항

RAG는 LLM 기반 애플리케이션의 정확성과 신뢰성을 크게 향상시킬 수 있는 강력한 기술이지만, 다음과 같은 점에 주의해야 한다:

  1. 데이터 품질: RAG 시스템은 지식 베이스의 품질에 크게 의존한다.
  2. 컴퓨팅 비용: 대규모 벡터 데이터베이스와 LLM 호출은 비용이 많이 소요될 수 있다.
  3. 프롬프트 엔지니어링: 검색된 정보를 효과적으로 활용하는 프롬프트 디자인이 중요하다.
  4. 평가 어려움: RAG 시스템의 성능을 체계적으로 평가하는 것은 복잡한 과제다.

 

참고 자료

  1. Lewis, P., et al. (2020). "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks". NeurIPS.
  2. LangChain 공식 문서: https://python.langchain.com/docs/use_cases/question_answering/
  3. Gao, J., et al. (2023). "Precise Zero-Shot Dense Retrieval without Relevance Labels". ACL.
  4. Chroma 공식 문서: https://docs.trychroma.com/

정말 잘하려고 하면 시간도 많이 걸리고 노력도 그만큼 많이 필요하다고 본다.
하지만, 겁내지 말고 내 주변에 있는 자료들을 가지고 한번 연습해 보면 어떨까?
그러면 자신감도 생기고 RAG에 대한 개념도 익숙해질 것이다.
성능이 좀 구리면 어떤가... 계속 테스트하고 고쳐나가면 되지 않겠나...
그럼 오늘도 도전하는 하루가 되길 바라며.

 

반응형