청크를 잘라 놨으니 이제 '검색이 되게' 만들 차례입니다. 문제는 컴퓨터가 '환불 규정'과 '반품 정책'이 비슷한 말이라는 걸 모른다는 것. 글자가 다르니까요. 임베딩(Embedding)이 이 문제를 풉니다.
임베딩: 의미를 지도 위의 좌표로
임베딩은 텍스트를 수백~수천 개의 숫자 배열(벡터)로 바꿉니다. 이 숫자들은 '의미 지도' 위의 좌표입니다. '강아지'와 '반려견'은 지도에서 가까운 곳에, '강아지'와 '자동차'는 먼 곳에 찍힙니다. 좌표 사이 거리를 재면 의미가 얼마나 비슷한지 계산할 수 있습니다.
임베딩 맛보기: 텍스트가 숫자가 되는 순간
# pip install langchain-openai
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model='text-embedding-3-small')
# 문장 하나를 벡터로 변환
vector = embeddings.embed_query('강아지가 공원에서 뛰어놀아요')
print(len(vector)) # 1536 (숫자 1536개짜리 좌표)
print(vector[:5]) # [0.012, -0.034, ...] 앞 5개만 미리보기
벡터스토어: 좌표 전용 검색 창고
벡터스토어는 청크와 그 좌표(벡터)를 함께 저장해 두는 데이터베이스입니다. 질문이 들어오면 질문도 좌표로 바꾼 뒤, 저장된 좌표들 중 가장 가까운 것들을 순식간에 찾아냅니다. 도서관 사서가 '주제가 비슷한 책'을 바로 뽑아 주는 것과 같습니다. 입문용으로는 설치가 간단한 Chroma를 추천합니다.
Chroma에 청크 저장하고 유사도 검색하기
# pip install langchain-chroma
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document
# 실습용 미니 문서들 (실전에서는 이전 레슨의 chunks를 넣습니다)
docs = [
Document(page_content='환불은 구매 후 7일 이내에 가능합니다.'),
Document(page_content='배송은 결제 후 평균 2~3일 걸립니다.'),
Document(page_content='회원 등급은 구매 금액에 따라 결정됩니다.'),
]
# 문서를 임베딩해서 Chroma에 저장 (persist_directory로 디스크 보존)
vectorstore = Chroma.from_documents(
documents=docs,
embedding=OpenAIEmbeddings(model='text-embedding-3-small'),
persist_directory='./chroma_db',
)
# 의미 기반 검색: '반품'이라는 단어가 문서에 없어도 찾아냅니다!
results = vectorstore.similarity_search('반품하고 싶어요', k=1)
print(results[0].page_content) # 환불은 구매 후 7일 이내에 가능합니다.
핵심을 보셨나요? '반품'으로 검색했는데 '환불' 문서가 나왔습니다. 키워드가 아니라 의미로 찾기 때문입니다. 이것이 RAG 검색이 일반 텍스트 검색보다 강력한 이유입니다.
점수와 함께 검색하기
유사도 점수 확인
# 점수가 낮을수록(거리가 가까울수록) 더 유사합니다
results = vectorstore.similarity_search_with_score('배송 기간 알려줘', k=2)
for doc, score in results:
print(f'[거리 {score:.3f}] {doc.page_content}')
| 벡터스토어 | 특징 | 추천 상황 |
|---|---|---|
| Chroma | 로컬 파일 기반, 설치 간단 | 학습, 프로토타입 |
| FAISS | 메타(구 페이스북)제, 매우 빠름 | 로컬 대용량 검색 |
| pgvector | PostgreSQL 확장 | 기존 DB에 통합 (Supabase 등) |
| Pinecone | 클라우드 관리형 | 운영 서비스, 스케일링 |
임베딩 모델은 저장할 때와 검색할 때 반드시 같은 것을 써야 합니다. 지도가 다르면 좌표를 비교할 수 없는 것과 같은 원리입니다. 중간에 임베딩 모델을 바꾸면 전체를 다시 저장해야 합니다.
임베딩 API 호출에도 비용이 듭니다. 큰 문서를 반복 실습할 때는 persist_directory로 저장해 두고 재사용하세요. Chroma(persist_directory='./chroma_db', embedding_function=...)으로 기존 저장소를 다시 열 수 있습니다.