danbibibi
article thumbnail
Published 2025. 5. 16. 14:20
VectorDB와 RAG AI

VectorDB

  • 데이터를 벡터 형식으로 저장하고 검색할 수 있는 데이터베이스
  • 벡터는 숫자의 배열로, 주로 이미지, 텍스트, 오디오 등의 비정형 데이터를 표현하는 데 사용
  • 두 벡터가 얼마나 비슷한지 비교하여 비슷한 데이터를 빠르게 찾을 수 있음. 예를 들어, 검색 엔 진이 질문에 대한 답변을 찾을 때 질문과 가장 유사한 문서를 벡터 DB에서 검색
  • 벡터 DB는 비정형 데이터를 저장하고, 유사한 데이터를 빠르게 찾아주는 데이터베이스
  • Chroma DB
    • 오픈소스 프로젝트로 자유롭게 사용, 수정, 배포할 수 있으며 다음과 같은 상황 에 주로 사용
    • LangChain과 같은 RAG(검색 증강 생성) 시스템을 구축할 때
    • 임베딩과 메타데이터를 함께 관리하면서 저장하고 검색해야 할 때
    • 간단한 Python API 기반으로 빠르게 구축하고 싶을 때
  • FAISS DB
    • FAISS(Facebook AI Similarity Search)는 Facebook 에 의해 개발된 라이 브러리로 오픈소스이며 다음과 같은 상황에 주로 사용
    • 수천만~수억 개의 벡터 데이터에서 빠른 검색이 필요할 때
    • GPU 가속을 활용하여 초고속 검색을 수행해야 할 때
    • 대용량 이미지/텍스트 벡터 검색 엔진을 구축할 때

 

 

RAG (Retrieval-Augmented Generation)

  • RAG(Retrieval-Augmented Generation)는 대규모 언어 모델(LLM)의 한계를 극복하기 위해 고안된 정보 검색 기반 생성 기법
  • 모델이 기존에 학습한 데이터만으로 답을 생성하는 대신, 외부 지식 소스에서 필요한 정보를 검색하여 활용한 후 응답을 생성하는 구조
한계 RAG의 이점
LLM은 최신 정보가 없음 외부 문서를 통해 정보 최신화 가능
허위 정보 생성 가능성 (Hallucination) 팩트 기반 응답 생성 가능
도메인 지식 부족 전문 문서를 포함해 정확도 향상
토큰 한계로 학습 데이터 부족 필요한 문서만 가져옴으로 컨텍스트 최적화

 

import bs4  # HTML 파싱을 위한 BeautifulSoup
from langchain import hub  # LangChain Hub에서 미리 만들어진 Prompt 가져오기
from langchain.chat_models import ChatOpenAI  # ChatGPT 계열 LLM 모델 사용
from langchain.document_loaders import WebBaseLoader  # 웹 페이지에서 문서 크롤링
from langchain.embeddings import OpenAIEmbeddings  # 문서를 벡터화하기 위한 임베딩 클래스
from langchain.schema import StrOutputParser  # LLM 출력값을 문자열로 파싱
from langchain.schema.runnable import RunnablePassthrough  # 입력값 그대로 전달하는 유틸리티
from langchain.text_splitter import RecursiveCharacterTextSplitter  # 문서를 청크로 나누기 위한 도구
from langchain.vectorstores import Chroma  # 벡터 DB (임베딩 저장소)

# 🔍 [1] 웹 페이지에서 특정 HTML 클래스만 크롤링하여 문서 로드
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),  # 가져올 웹 URL
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(  # BeautifulSoup 옵션: 아래 클래스들만 파싱
            class_=("post-content", "post-title", "post-header")
        )
    ),
)
docs = loader.load()  # → BeautifulSoup을 활용하여 웹 문서를 LangChain 문서 형식으로 로드

# ✂️ [2] 문서를 청크 단위로 분할 (토큰 길이 제약 대비)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,        # 한 청크당 최대 1000자
    chunk_overlap=200       # 청크 사이 중복 200자 (문맥 유지 목적)
)
splits = text_splitter.split_documents(docs)  # → 분할된 문서 리스트 반환

# 🧠 [3] 문서 청크를 벡터로 임베딩하고, Chroma 벡터 저장소에 저장
vectorstore = Chroma.from_documents(
    documents=splits,
    embedding=OpenAIEmbeddings()  # OpenAI API를 통한 임베딩 벡터 생성
)

# 🔎 [4] 벡터 저장소를 기반으로 관련 문서를 검색하는 검색기(retriever) 생성
retriever = vectorstore.as_retriever()

# 🧾 [5] LangChain Hub에서 가져온 RAG용 프롬프트 템플릿 로드
prompt = hub.pull("rlm/rag-prompt")  # "context"와 "question"을 받아 응답 생성하는 프롬프트

# 🤖 [6] 사용할 LLM 정의 (gpt-3.5-turbo)
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)  # temperature=0: 일관된 응답 유도

# 🧩 [7] 검색 결과 문서를 문자열로 변환하는 함수
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)  # 문서 내용만 이어붙이기

# 🔗 [8] RAG 체인 구성 (LCEL 방식)
rag_chain = (
    {
        # dict 입력: 'context' 키는 검색기 + format, 'question'은 그대로 전달
        "context": retriever | format_docs,     # 검색 결과 → 텍스트로 포맷
        "question": RunnablePassthrough()       # 사용자 질문은 그대로 넘김
    }
    | prompt    # context + question을 프롬프트에 전달
    | llm       # LLM에게 질문 및 문맥 포함 프롬프트 전달
    | StrOutputParser()  # 출력에서 텍스트만 추출
)

# 🧪 [9] 체인 실행: LLM에게 "What is LLM agent?" 질문을 던짐
rag_chain.invoke("What is LLM agent?")
🧠 참고: RAG 체인 구성도
User Question
      ↓
[Retriever] ← VectorDB ← Embedded Docs
      ↓
format_docs()
      ↓
{"context": ..., "question": ...}
      ↓
[Prompt Template]
      ↓
[Chat Model (LLM)]
      ↓
[Output Parser]
      ↓
🎯 Final Answer

 

 

활용 가능한 Embedding 종류

 

profile

danbibibi

@danbibibi

꿈을 꾸는 시간은 멈춰 있는 것이 아냐 두려워하지 마 멈추지 마 푸른 꿈속으로