🧭 13. 벡터 검색 (Vector Search) 실습

🎯 학습 목표

이 실습에서는 LangChain + Pinecone + OpenAI 임베딩 모델을 이용해
한국어 영화 데이터셋에서 벡터 기반 검색을 구현합니다.

학습 포인트설명
🔹 텍스트를 벡터 임베딩으로 변환OpenAI text-embedding-3-small 사용
🔹 벡터 DB 구축Pinecone 인덱스 생성 및 데이터 업서트
🔹 검색 수행KNN, 메타데이터 필터, 하이브리드 검색 실습

🧱 1. 임베딩(Embedding) 생성

텍스트를 고차원 숫자 벡터로 표현하여 의미적 유사도를 비교할 수 있게 만듭니다.

# 예시 한글 영화 데이터셋 정의
movies = [
    {
        "id": "movie1",
        "title": "7번방의 선물",
        "year": 2013,
        "genre": "드라마",
        "description": "억울한 누명을 쓰고 교도소에 수감된 아빠와 그의 어린 딸의 감동적인 스토리"
    },
    {
        "id": "movie2",
        "title": "미나리",
        "year": 2020,
        "genre": "드라마",
        "description": "한국계 미국인 가족의 따뜻하고 감성적인 성장 이야기"
    },
    {
        "id": "movie3",
        "title": "기생충",
        "year": 2019,
        "genre": "드라마",
        "description": "가난한 가족과 부자 가족 사이의 빈부격차를 그린 사회 풍자 드라마"
    },
    {
        "id": "movie4",
        "title": "범죄도시",
        "year": 2017,
        "genre": "범죄",
        "description": "형사가 범죄 조직을 소탕하는 범죄 액션 영화"
    },
    {
        "id": "movie5",
        "title": "범죄도시 2",
        "year": 2022,
        "genre": "범죄",
        "description": "형사와 범죄 조직의 대결을 그린 범죄 액션 영화의 속편"
    },
    {
        "id": "movie6",
        "title": "헤어질 결심",
        "year": 2022,
        "genre": "범죄",
        "description": "산에서 발생한 의문의 죽음(살인 사건)을 수사하던 형사가 피의자에게 이끌리며 벌어지는 미스터리 멜로 영화"
    },
    {
        "id": "movie7",
        "title": "다만 악에서 구하소서",
        "year": 2020,
        "genre": "범죄",
        "description": "청부 살인업자와 범죄 조직의 마지막 거래를 그린 범죄 액션 영화"
    }
]
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings


# 환경변수 로드
load_dotenv()

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
movie_vectors = embeddings.embed_documents([m["description"] for m in movies])

print(len(movie_vectors[0]))  # 1536차원 출력

💡 text-embedding-3-small 모델은 1536차원 벡터를 생성합니다.


🗃️ 2. Pinecone 인덱스 구성 및 업서트

import os
from pinecone import Pinecone,ServerlessSpec

pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))

index_name = "movie-vector-index"


if pc.has_index(index_name):
    pc.delete_index(index_name)

pc.create_index(
    name=index_name,
    dimension=1536,
    metric="cosine",  
    spec=ServerlessSpec(
        cloud="aws",       
        region="us-east-1" 
    )
)
index = pc.Index(index_name)

이후 영화 데이터를 임베딩과 함께 업서트합니다.

vector_data = [
    (m["id"], v, {"title": m["title"], "genre": m["genre"], "year": m["year"], "description": m["description"]})
    for m, v in zip(movies, movie_vectors)
]
index.upsert(vectors=vector_data)

🔍 3. 벡터 검색

쿼리와 의미적으로 유사한 항목을 찾습니다.

# 검색 쿼리 예시
query = "감성적인 드라마 영화 추천해줘"
# 쿼리 문장을 임베딩 벡터로 변환
query_vector = embeddings.embed_query(query)

# Pinecone에서 벡터 유사도 검색 수행 (코사인 유사도 기반)
# 상위 3개의 가장 가까운 벡터를 찾고, 메타데이터를 포함하여 반환
result = index.query(vector=query_vector, top_k=3, include_metadata=True)

# 결과 출력: 각 결과의 제목, 연도, 장르를 표시
for match in result["matches"]:
    print(match["metadata"]["title"], match["metadata"]["year"],match["metadata"]["genre"])

📊 예시 결과

기생충 2019.0 드라마
7번방의 선물 2013.0 드라마
범죄도시 2017.0 범죄

➡️ 의미적으로 “감성적인 드라마”에 가까운 영화들이 추천됨.


🎯 4. 메타데이터 필터 검색

특정 조건(예: 연도, 장르 등)을 기반으로 검색 범위를 제한할 수 있습니다.

# 메타데이터 필터를 활용한 검색: 2020년 이후 개봉한 영화들 중 상위 3개 반환
filter_condition = {"year": {"$gte": 2020}}
query_vector = embeddings.embed_query("영화")

result = index.query(vector=query_vector, top_k=3, filter=filter_condition, include_metadata=True)
# 결과 출력: 각 결과의 제목, 연도, 장르를 표시
for match in result["matches"]:
    print(match["metadata"]["title"], match["metadata"]["year"],match["metadata"]["genre"])

📊 예시 결과

범죄도시 2 2022.0 범죄
헤어질 결심 2022.0 범죄
다만 악에서 구하소서 2020.0 범죄

➡️ 필터 조건 year >= 2020을 만족하는 결과만 반환.


⚡ 5. 하이브리드 검색 (쿼리 + 필터)

자연어 쿼리와 구조화된 필터를 동시에 적용합니다.

hybrid_query = "2020년 이후의 범죄 영화 보여줘"
hybrid_vector = embeddings.embed_query(hybrid_query)
hybrid_filter = {"genre": {"$eq": "범죄"}, "year": {"$gte": 2020}}

result = index.query(vector=hybrid_vector, top_k=3, filter=hybrid_filter, include_metadata=True)
# 결과 출력: 각 결과의 제목, 연도, 장르를 표시
for match in result["matches"]:
    print(match["metadata"]["title"], match["metadata"]["year"],match["metadata"]["genre"])

📊 예시 결과

다만 악에서 구하소서 2020.0 범죄
범죄도시 2 2022.0 범죄
헤어질 결심 2022.0 범죄

➡️ “쿼리 의미 + 필터 조건”을 모두 충족하는 검색 결과를 제공.


🧩 6. 실습 요약

검색 방식설명예시
KNN 검색의미적으로 가장 유사한 항목 검색“감성적인 드라마 영화”
메타데이터 필터특정 조건으로 제한year >= 2020
하이브리드 검색쿼리 + 필터 결합“2020년 이후의 범죄 영화”

💡 핵심 포인트

  • Pinecone은 필터 조건을 만족하는 벡터 subset만 대상으로 유사도 계산을 수행.
  • 실제 응용에서는 RAG, 추천 시스템, AI 검색 서비스 등에 필수로 사용됩니다.