각 단계별 모듈의 내용을 앞으로 상황에 맞게 변경하면서 문서에 적합한 구조를 찾아갈 수 있습니다.
(각 단계별로 다양한 옵션을 설정하거나 새로운 기법을 적용할 수 있습니다.)
#전체 코드
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# 단계 1: 문서 로드(Load Documents)
loader = PyMuPDFLoader("./data/extract_text/AI_Paradigm_Shift_Driven_by_DeepSeek.pdf")
docs = loader.load()
# 단계 2: 문서 분할(Split Documents)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
split_documents = text_splitter.split_documents(docs)
# 단계 3: 임베딩(Embedding) 생성
embeddings = AzureOpenAIEmbeddings(
model=AOAI_DEPLOY_EMBED_3_LARGE,
openai_api_version="2024-02-01",
api_key= AOAI_API_KEY,
azure_endpoint=AOAI_ENDPOINT
)
# 단계 4: DB 생성(Create DB) 및 저장
# 벡터스토어를 생성합니다.
vectorstore = FAISS.from_documents(documents=split_documents, embedding=embeddings)
# 단계 5: 검색기(Retriever) 생성
# 문서에 포함되어 있는 정보를 검색하고 생성합니다.
retriever = vectorstore.as_retriever()
# 단계 6: 프롬프트 생성(Create Prompt)
# 프롬프트를 생성합니다.
prompt = PromptTemplate.from_template(
"""You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Answer in Korean.
#Context:
{context}
#Question:
{question}
#Answer:"""
)
# 단계 7: 언어모델(LLM) 생성
# 모델(LLM) 을 생성합니다.
# ChatOpenAI 언어 모델을 초기화합니다. temperature는 0으로 설정합니다.
# ChatOpenAI 언어 모델을 초기화합니다. temperature는 0으로 설정합니다.
llm = AzureChatOpenAI(
openai_api_version="2024-02-01",
azure_deployment=AOAI_DEPLOY_GPT4O_MINI,
temperature=0.0,
api_key= AOAI_API_KEY,
azure_endpoint=AOAI_ENDPOINT
)
# 단계 8: 체인(Chain) 생성
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
prompt = PromptTemplate.from_template("""
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question...
""")
처음 RAG 구조를 보면서 가장 인상 깊었던 것은 "AI도 참고서가 필요하다"는 점이었습니다. 아무리 똑똑한 AI라도 모든 정보를 기억할 수 없고, 특히 최신 정보나 특정 문서의 세부 내용은 알 수 없다는 현실적 한계를 인정하고, 이를 검색과 생성을 결합하여 해결한 아이디어가 매우 실용적이라고 생각했습니다.
복잡해 보이는 AI 시스템이 실제로는 상당히 접근 가능한 도구들로 구성되어 있다는 점이 놀라웠습니다. LangChain 같은 프레임워크 덕분에 대학생 수준에서도 충분히 이해하고 구현할 수 있는 수준까지 기술의 진입장벽이 낮아졌다고 느꼈습니다.
이 RAG 파이프라인을 통해 AI 엔지니어링의 핵심은 알고리즘 개발보다는 시스템 설계와 데이터 처리에 있다는 것을 깨달았습니다. 앞으로는 다양한 임베딩 기법, 벡터 검색 최적화, 프롬프트 엔지니어링 등을 더 깊이 공부해보고 싶습니다.
무엇보다 이런 기술을 실제 문제 해결에 어떻게 적용할 수 있을지 고민하며 실습 프로젝트를 진행해보는 것이 중요하다고 생각합니다.
우리가 구글에서 검색어를 입력하고 0.2초 만에 수많은 결과를 받아볼 수 있는 건, 단순히 우연이 아닙니다. 그 뒤에는 수십억 개의 웹페이지를 미리 정리하고 색인화해둔 거대한 인덱싱 시스템이 있기 때문이죠.
AI 시대에 들어서면서 우리도 비슷한 도전에 직면하게 되었습니다. 방대한 문서 더미에서 사용자의 질문과 관련된 정보를 순식간에 찾아내는 것. 이것이 바로 RAG(Retrieval-Augmented Generation) 시스템의 핵심 과제입니다.
인덱스에서 답까지, 리트라이버의 역할
하지만 잘 정리된 인덱스만 있다고 끝이 아닙니다. 사용자의 질문이 들어왔을 때, 이 인덱스를 똑똑하게 활용해서 가장 적절한 문서들을 골라내는 역할이 필요한데, 이것이 바로 리트라이버(Retriever)가 하는 일입니다.
리트라이버는 단순히 비슷한 문서를 찾는 것을 넘어서, 다양한 전략을 통해 검색 품질을 높입니다. 때로는 유사도 점수로 관련성이 낮은 문서를 걸러내고, 때로는 MMR 알고리즘으로 중복을 피하면서 다양한 관점의 정보를 가져오기도 하죠. 심지어 여러 각도에서 질문을 재구성해서 놓칠 수 있는 정보까지 찾아내는 똑똑함을 보여줍니다.
우리가 풀어갈 이야기
이번 포스트에서는 이러한 인덱싱 시스템을 어떻게 구축하고, 효과적인 리트라이버를 어떻게 활용할 수 있는지 실제 코드와 함께 차근차근 알아보겠습니다.
1. VectorStoreRetriever 개요
VectorStoreRetriever는 "벡터 데이터베이스를 쉽게 검색할 수 있게 해주는 도구"입니다.
벡터 데이터베이스(FAISS, Chroma 등)는 강력하지만 사용법이 복잡해요. VectorStoreRetriever는 이것을 간단하게 만들어주는 "검색 전용 리모컨" 같은 존재라고 생각하시면 됩니다.
벡터 저장소의 유사도 검색, MMR 등 다양한 검색 방법 지원
as_retriever() 메서드로 VectorStore를 검색기로 변환
2. 기본 사용법
Retriever 초기화
# VectorStore를 검색기로 변환
retriever = db.as_retriever()
# 검색 실행
docs = retriever.invoke("검색 쿼리")
retriever = db.as_retriever(
search_type="mmr",
search_kwargs={
"k": 5, # 최종 반환 문서 수
"fetch_k": 20, # 1차 검색 문서 수
"lambda_mult": 0.5 # 다양성 조절 (0~1)
}
)
MMR 파라미터 설명:
fetch_k: 먼저 가져올 후보 문서 수
lambda_mult:
0에 가까움 → 다양성 중시
1에 가까움 → 유사성 중시
MMR DeepDive !! 🏄♀️
(Maximal Marginal Relevance)
MMR(Maximal Marginal Relevance)은 검색 결과의 중복을 피하는 똑똑한 알고리즘입니다.
일반 검색은 비슷한 문서만 반복해서 가져오지만, MMR은 관련성 높으면서도 서로 다른 문서들을 선별합니다.
lambda_mult 파라미터로 유사성과 다양성의 균형을 조절할 수 있어요.
1에 가까우면 → 유사성 중시
0에 가까우면 → 다양성 중시
결과: 풍부하고 다양한 정보 제공! 🌟
수학적 공식으로 이해하기
MMR 점수 = λ×(쿼리와의 유사도) + (1-λ)×(기존 선택과의 차별성)
유사도: 사용자 질문과 얼마나 관련있는가?
차별성: 이미 선택된 문서들과 얼마나 다른가?
핵심 원리: 임베딩 벡터의 거리 측정
비슷한 의미 = 벡터 거리가 가까움
다른 의미 = 벡터 거리가 멀음
MMR은 이 "거리"를 이용해:
쿼리와 가까운 것 (관련성)
이미 선택된 것과 먼 것 (다양성)
두 조건을 수학적으로 균형잡아 선택!
결론: MMR은 벡터 공간에서의 거리 계산을 통해 "관련있으면서도 다양한" 결과를 수학적으로 보장할 수 있습니다! 📊✨
가장 비슷한 걸 찾아줘야 하는데 왜 다양성을 고려하는거야?
사용자 질문: "파이썬 배우고 싶어"
사용자의 실제 의도:
파이썬 문법도 알고 싶고
실제 프로젝트 예시도 보고 싶고
어떤 분야에 활용되는지도 궁금하고
학습 로드맵도 알고 싶음
가장 비슷한 것만 찾으면:
"파이썬 기초 문법 - 변수와 자료형" (유사도: 0.95)
"파이썬 기본 문법 - 조건문과 반복문" (유사도: 0.94)
"파이썬 문법 완벽 가이드" (유사도: 0.93)
"파이썬 기초 - 함수 사용법" (유사도: 0.92)
결과: 전부 "기초 문법"만! 사용자가 원하는 다른 정보는 전혀 없음 😰
MMR의 진짜 목적
🎯 사용자 만족도 관점
🎪 "완벽한 정보 세트" 만들기
사용자 한 번의 검색으로:
✅ 가장 관련성 높은 정보 (1순위)
✅ 보완적 관점의 정보 (2순위)
✅ 실용적 활용법 (3순위)
✅ 심화 학습 방향 (4순위)
그럼 언제 "유사도만" 사용해야 할까?
✅ 유사도만 필요한 경우:
FAQ 검색 (정확한 답변 하나만 필요)
특정 오류 해결법 검색
정확한 수치나 공식 찾기
✅ MMR 필요한 경우:
학습 자료 검색 (다양한 관점 필요)
아이디어 발굴
종합적 이해가 필요한 주제
결론: "가장 비슷한 것"도 중요하지만, 실제 사용자는 도움이 되는 정보 세트를 원해요. MMR은 한 번의 검색으로 더 풍부한 답변을 제공하는 전략입니다! 🎯✨
4. 고급 검색 기법
4.1 MultiQueryRetriever
문제점: 단일 쿼리로는 다양한 관점의 정보 놓칠 수 있음
해결책: LLM이 자동으로 여러 관점의 쿼리 생성
from langchain.retrievers.multi_query import MultiQueryRetriever
# 하나의 질문 → 여러 관점의 질문들 자동 생성
multi_retriever = MultiQueryRetriever.from_llm(
retriever=retriever,
llm=llm
)
장점:
검색 결과의 다양성 증가
프롬프트 튜닝 자동화
더 풍부한 문서 집합 확보
4.2 LongContextReorder (긴 문맥 재정렬)
문제점: 문서가 10개 이상일 때 모델 성능 저하 발생
해결책: 문서 순서 재배열로 성능 향상
from langchain.document_transformers import LongContextReorder
# 문서 재정렬기 생성
reordering = LongContextReorder()
# 검색된 문서들 재정렬
docs = retriever.invoke("쿼리")
reordered_docs = reordering.transform_documents(docs)
문제: 문서를 10개 이상 모델에 넣으면, → 모델이 중간 부분을 대충 넘어가 버려서 중요한 내용을 무시하는 경우가 생김.
예시: 책을 읽을 때 맨 앞, 맨 끝은 잘 기억하는데, 중간 챕터는 흐릿하게 기억나는 경험과 비슷해요.
해결: 검색된 문서 순서를 다시 배열합니다.
가장 관련된 문서 → 시작과 끝 위치
덜 관련된 문서 → 중간 위치
모델이 중요한 정보에 더 잘 접근 가능
효과:
모델이 중요한 정보를 놓치지 않고 잘 활용
긴 컨텍스트에서도 성능 유지
5. LCEL Chain 활용
체인 구성 방식
LCEL 체인 흐름도 그림(입력 → 프롬프트 → 모델 → 파서 → 출력)
chain = prompt | model | output_parser
여러 개의 단계를 “파이프(|)”로 연결하는 방식이에요.
앞 단계의 결과가 → 다음 단계의 입력으로 자동 전달됩니다.
마치 파이프라인처럼순서대로 흐른다고 보면 돼요.
실제 적용 예시
prompt : 질문을 정리하거나 가이드라인을 붙여주는 단계
model : LLM이 실제로 답변을 생성하는 단계
output_parser : 모델이 낸 답변을 사람이 쓰기 좋은 형태로 가공
6. 실무 활용 팁
검색 성능 최적화
k값 조절: 너무 많으면 노이즈, 너무 적으면 정보 부족
임계값 설정: 데이터 특성에 맞게 0.7~0.9 사이에서 조정
MMR 활용: 중복 제거와 다양성이 중요한 경우
문서 재정렬: 긴 컨텍스트 처리 시 필수
주의사항
search_type과 search_kwargs 조합 확인
메타데이터 구조 정확히 파악 후 필터링
lambda_mult 값에 따른 결과 차이 테스트 필요
7. 🔎 Retriever 정리 & 느낀 점
RAG 파이프라인에서 Retriever는 질문(쿼리)을 받아서 벡터 저장소(예: FAISS, Chroma)에서 관련 문서들을 꺼내오는 역할을 합니다. Retriever를 잘 설계해야 LLM이 불필요한 정보를 덜 보고, 중요한 맥락을 더 정확히 반영할 수 있습니다.
Retriever를 공부하면서 단순히 “검색만 하는 도구”가 아니라, 검색 품질이 전체 RAG 성능을 좌우한다는 걸 깨달았습니다.
단순 유사도 검색만으로는 아쉬운 경우가 많고,
MultiQueryRetriever 같은 방법을 쓰면 더 다양한 각도의 정보를 얻을 수 있었어요.
또한 LongContextReorder는 “모델이 긴 문서를 어떻게 소화하는지”라는 실질적인 문제를 해결해준다는 점에서 인상 깊었습니다.
결국, Retriever는 RAG의 심장부라는 생각이 들었어요. 앞으로 프로젝트를 할 때는 단순히 LLM을 붙이는 것보다, 어떤 검색 전략을 쓰고 어떻게 문서를 다룰지를 더 신경 써야겠다고 느꼈습니다.
결론: 임베딩은 "의미를 수학으로 표현"해서, 컴퓨터가 "비슷한 의미"를 계산할 수 있게 해주는 핵심 기술입니다!
DB에는 이 "의미 숫자"들이 저장되어 있어요.
OpenAIEmbeddings
1. 문서 임베딩(Document Embedding)
정의: 문서 내용을 수치 벡터로 변환하는 과정.
효과: 문서의 의미를 벡터 공간에 매핑 → 검색, 분류, 유사도 계산에 활용.
대표 모델: BERT, GPT (문맥 정보 포착)
방법: 토큰화된 문서를 모델에 입력 → 토큰 임베딩 벡터 생성 → 평균 → 문서 임베딩
활용: 문서 분류, 감성 분석, 문서 간 유사도 계산 등
2. OpenAIEmbeddings 함수
embeddings.embed_query(text) - 단일 쿼리 임베딩
쿼리 문자열 → 임베딩 벡터 변환
유사한 텍스트 검색, 유사도 계산 등에 활용
활용 예시:
검색 쿼리 벡터화
실시간 유사도 계산
사용자 입력 처리
embeddings.embed_documents([text]) - 다중 문서 임베딩
문서 리스트 → 임베딩 벡터 리스트 반환
반환값을 변수(doc_result)에 저장해 활용
활용 예시:
문서 데이터베이스 구축
배치 처리로 효율성 향상
벡터 저장소 생성
차원 지정
text-embedding-3-small → 기본 1536차원
dimensions=1024 옵션으로 차원 축소 가능
# 차원 축소로 저장 공간 및 처리 속도 개선
embeddings_reduced = OpenAIEmbeddings(
model="text-embedding-3-small",
dimensions=1024 # 1536 → 1024로 축소
)
query_vector = embeddings_reduced.embed_query("테스트 쿼리")
print(f"축소된 차원: {len(query_vector)}") # 1024
3. 유사도 계산
scikit-learn 등으로 코사인 유사도 계산 가능
예시 문장:
sentence1 = "안녕하세요? 반갑습니다."
sentence2 = "안녕하세요? 반갑습니다!"
sentence3 = "안녕하세요? 만나서 반가워요."
sentence4 = "Hi, nice to meet you."
sentence5 = "I like to eat apples."
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from langchain.embeddings import OpenAIEmbeddings
import pandas as pd
# OpenAI Embeddings 초기화
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 문장들을 임베딩 벡터로 변환
sentence_vectors = embeddings.embed_documents(sentences)
# numpy 배열로 변환
vectors_array = np.array(sentence_vectors)
print(f"벡터 형태: {vectors_array.shape}") # (5, 1536)
# 코사인 유사도 계산
similarity_matrix = cosine_similarity(vectors_array)
# 최대 유사도 찾기
max_idx = np.unravel_index(np.argmax(similarity_no_diag), similarity_no_diag.shape)
max_similarity = similarity_no_diag[max_idx]
print(f"가장 유사한 쌍: {labels[max_idx[0]]} & {labels[max_idx[1]]}")
print(f"유사도 점수: {max_similarity:.3f}")
print(f"문장1: '{sentences[max_idx[0]]}'")
print(f"문장2: '{sentences[max_idx[1]]}'")
Chroma (벡터 저장소)
정의: 오픈소스 벡터 DB (Apache 2.0 라이선스)
특징: 개발자 친화적, AI 네이티브, RAG용 벡터 인덱스 관리
Chroma가 뭔가요?
간단히 말하면 "의미를 이해하는 데이터베이스"입니다. 실리콘밸리의 두 개발자가 "AI 시대에 맞는 새로운 데이터베이스가 필요하다"며 만든 오픈소스 프로젝트가 Chroma입니다. 지금은 전 세계 개발자들이 사용하는 인기 벡터 데이터베이스가 되었어요!
일반 데이터베이스는 정확히 일치하는 단어만 찾지만, Chroma는 비슷한 의미의 텍스트를 찾아줍니다.
예시:
일반 DB: "파이썬"으로 검색 → "파이썬"만 찾음
Chroma: "파이썬"으로 검색 → "Python", "프로그래밍 언어", "코딩" 등도 찾음
1. VectorStore 생성
Chroma.from_documents()
문서 리스트 → 벡터 저장소 생성
매개변수:
documents: 문서 리스트
embedding: 임베딩 함수
ids: 문서 ID 리스트 (기본 자동 생성)
collection_name: 컬렉션 이름
persist_directory: 저장 디렉토리 (없으면 메모리 임시 저장)
# 문서 객체에서 벡터 저장소 생성
vectorstore = Chroma.from_documents(
documents=doc_list, # Document 객체 리스트
embedding=embeddings, # 임베딩 함수
ids=["doc1", "doc2"], # 문서 ID (선택사항, 자동생성 가능)
collection_name="my_docs", # 컬렉션 이름
persist_directory="./chroma" # 저장 경로 (없으면 메모리 저장)
)
Chroma.from_texts()
텍스트 리스트 → 벡터 저장소 생성
메타데이터/ID 함께 지정 가능
저장 시 UUID 자동 생성
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
# 1단계: 임베딩 준비 (텍스트를 숫자로 바꿔주는 도구)
embeddings = OpenAIEmbeddings()
# 2단계: 저장할 텍스트들 준비
texts = [
"파이썬은 프로그래밍 언어입니다",
"자바스크립트는 웹 개발에 사용됩니다",
"데이터베이스는 정보를 저장합니다"
]
# 3단계: 벡터 저장소 만들기
db = Chroma.from_texts(
texts=texts,
embedding=embeddings,
persist_directory="./my_db" # 컴퓨터에 저장
)
# 4단계: 검색하기
results = db.similarity_search("코딩 언어", k=2)
print(results) # "파이썬은..." 이 가장 먼저 나올 것
# 그 외
# 과제 참고자료 정리
# 논문, 웹사이트 등을 저장
references = [
"딥러닝 논문: CNN의 구조와 특징",
"블로그 포스트: 머신러닝 입문 가이드",
"교수님 강의자료: 인공신경망 기초"
]
db = Chroma.from_texts(references, embeddings)
2. 저장 & 로드
저장 (persist_directory 지정) DB 파일로 저장해 재사용 가능
로드 컬렉션 이름이 다르면 데이터 조회 불가
중요한 주의사항
1. 컬렉션 이름이 달라야 데이터 조회 불가
2. 임베딩 함수도 동일해야 함
실용적인 활용법
한 번 만들고 계속 사용:
DB_PATH = "./my_database"
# 처음 실행 - 데이터 생성 & 저장
if not os.path.exists(DB_PATH):
db = Chroma.from_texts(texts, embeddings, persist_directory=DB_PATH)
else:
# 이후 실행 - 기존 데이터 로드
db = Chroma(persist_directory=DB_PATH, embedding_function=embeddings)
이렇게 하면 프로그램을 다시 실행해도 이전에 만든 벡터 데이터베이스를 그대로 사용할 수 있어요!
3. 주요 메서드
유사도 검색 (similarity_search)
입력 쿼리와 가장 비슷한 문서들을 찾아줍니다.
매개변수: query, k(결과 수), filter(메타데이터 조건)
반환: List[Document]
문서 추가 (add_documents)
새 문서 → 임베딩 후 DB 삽입/업데이트
반환: 추가된 문서의 ID 리스트
문서 삭제 (delete)
특정 ID 문서 삭제
반환: None
# 1. 초기 데이터베이스 생성
texts = ["Python은 프로그래밍 언어", "Java는 객체지향 언어"]
db = Chroma.from_texts(texts, embeddings, persist_directory="./db")
# 2. 검색해보기
results = db.similarity_search("코딩 언어", k=2)
print(f"검색 결과: {len(results)}개")
# 3. 새 문서 추가
new_doc = Document(page_content="JavaScript는 웹 개발 언어")
added_ids = db.add_documents([new_doc])
print(f"추가된 ID: {added_ids[0]}")
# 4. 다시 검색 (새 문서도 포함해서 검색됨)
results = db.similarity_search("프로그래밍", k=3)
# 5. 필요없는 문서 삭제
db.delete(ids=[added_ids[0]])
꿀팁
k값: 너무 크면 관련없는 문서까지 나옴 (보통 3-5개 적당)
filter: 날짜, 카테고리별로 검색 범위 제한 가능
메타데이터: 문서 출처, 날짜 등 추가 정보 저장해두면 유용
FAISS (Facebook AI Similarity Search)
1. 정의
FAISS는 Meta(구 Facebook) AI Research에서 만든 벡터 검색 라이브러리입니다.
역할: 수백만~수십억 개의 벡터 중에서, 주어진 쿼리 벡터와 가장 유사한 벡터(Nearest Neighbor)를 빠르게 찾아냄.
수백만 개의 문서에서도 0.1초 만에 비슷한 텍스트를 찾아줌
구글, 네이버 같은 대형 검색엔진에서 실제로 사용하는 기술
LangChain + FAISS = 간단해짐! 원래 FAISS는 복잡했지만, LangChain이 쉽게 만들어줬어요:
2. 특징
고속 근사 최근접 탐색(ANN, Approximate Nearest Neighbor) 지원
CPU/GPU 최적화 → 대규모 데이터셋에서도 빠른 검색 가능
다양한 인덱스 타입 제공: 상황에 맞는 검색 효율 선택 가능
오픈소스 (C++로 구현, Python 바인딩 제공)
3. LangChain에서의 FAISS
LangChain은 FAISS를 VectorStore로 감싸서 쉽게 쓸 수 있게 해줌.
FAISS.from_texts(texts, embedding) → 텍스트 리스트를 임베딩 후, FAISS 인덱스로 바로 저장소 생성
반환: FAISS VectorStore 인스턴스
4. 동작 방식
텍스트 → 임베딩 함수 → 벡터 변환
벡터와 메타데이터/ID를 묶어서 FAISS 인덱스에 저장
나중에 쿼리 벡터 입력 시, 최근접 이웃 검색(Nearest Neighbor Search) 수행
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
# 1단계: 임베딩 준비
embeddings = OpenAIEmbeddings()
# 2단계: 텍스트 준비
texts = [
"파이썬은 프로그래밍 언어입니다",
"자바스크립트는 웹 개발 언어입니다",
"HTML은 마크업 언어입니다"
]
# 3단계: 한 줄로 벡터 저장소 완성!
db = FAISS.from_texts(texts, embeddings)
# 4단계: 검색하기
results = db.similarity_search("코딩 언어", k=2)
5. 언제 FAISS를 쓸까요?
FAISS를 선택하세요:
Chroma를 선택하세요:
✅ 문서가 10만개 이상 ✅ 검색 속도가 중요함 ✅ 메모리 절약이 필요함 ✅ 실제 서비스에 배포할 때
✅ 개발/학습 단계 ✅ 문서가 1만개 이하 ✅ 데이터 저장이 중요함 ✅ 간편함을 원할 때
6. FAISS의 장단점
장점
단점
오픈소스 + 무료
로컬/온프레미스 환경에서 사용 가능
GPU 지원 → 대규모 데이터 처리에 적합
다양한 인덱스 구조 제공 (Flat, IVFFlat, HNSW 등)
메모리 기반→ 데이터가 커지면 메모리 부담 ↑
분산 환경 지원은 직접 구현해야 함 (Pinecone 같은 SaaS 대비 부족)
인덱스 선택/튜닝을 잘못하면 성능 저하 가능
알아두면 좋은 추가 개념
👉 지금 텍스트에는 안 나왔지만, 실무에서는 꼭 알아야 하는 부분이 있어요.
인덱스 타입
IndexFlatL2: 단순 유클리드 거리 기반, 정확하지만 속도 느림
IVFFlat: 코어세트(cluster) 기반 검색, 빠르지만 근사치 가능성 있음
HNSW: 그래프 기반, 대규모에서도 빠르고 효율적
유사도 측정 방식
L2 거리 (Euclidean)
Inner Product (Dot product)
Cosine Similarity
저장 & 로드
LangChain FAISS VectorStore → 디스크에 저장 가능 (save_local, load_local)
재시작 시 DB 다시 만들 필요 없이 로드 가능
✅ 최종 요약
OpenAIEmbeddings: 텍스트/문서를 벡터로 변환 → 의미 기반 검색·분류 가능
Chroma(VectorStore): 벡터를 저장/검색하는 DB → RAG에서 인덱싱과 검색의 핵심
FAISS = 오픈소스 벡터 검색 엔진, 로컬에서 대규모 벡터 검색 가능 / LangChain 연동 = FAISS.from_texts()로 쉽게 벡터 저장소 생성.
👉 즉, 임베딩으로 의미를 숫자로 바꾸고, RAG DB에 저장해 인덱스를 만들어두면, 나중에 질문이 들어왔을 때 의미적으로 비슷한 문서를 빠르게 찾아낼 수 있다는 게 핵심입니다.