Skip to content
SON BLOG
Go back

Qdrant 시맨틱 검색 튜토리얼 — 5분 만에 검색 엔진 만들기

Edit page

1. 5 분 만에 만드는 간단한 시맨틱 검색 엔진

1.1 준비 사항

pip install -U sentence-transformers qdrant-client>=1.7.1

1.2 코드 개요

from qdrant_client import QdrantClient, models
from sentence_transformers import SentenceTransformer

encoder = SentenceTransformer("all-MiniLM-L6-v2")
client = QdrantClient(":memory:")           # 인메모리 모드
docs = [...]                                # 책 메타데이터
client.create_collection(
    "my_books",
    vectors_config=models.VectorParams(
        size=encoder.get_sentence_embedding_dimension(),
        distance=models.Distance.COSINE,
    ),
)
client.upload_points(
    "my_books",
    [
        models.PointStruct(
            id=i,
            vector=encoder.encode(d["description"]).tolist(),
            payload=d,
        )
        for i, d in enumerate(docs)
    ],
)

1.3 질의 예시

hits = client.query_points(
    collection_name="my_books",
    query=encoder.encode("alien invasion").tolist(),
    limit=3,
).points

필터링 예시

hits = client.query_points(
    "my_books",
    query=encoder.encode("alien invasion").tolist(),
    query_filter=models.Filter(
        must=[models.FieldCondition(key="year",
               range=models.Range(gte=2000))]
    ),
    limit=1,
).points

2. Sentence Transformers + Qdrant로 뉴럴 검색 서비스 구축

2.1 데이터 준비

wget https://storage.googleapis.com/generall-shared-data/startups_demo.json
pip install sentence-transformers numpy pandas tqdm
from sentence_transformers import SentenceTransformer
import pandas as pd, numpy as np, json, tqdm

model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")   # GPU 없으면 cpu
df = pd.read_json("startups_demo.json", lines=True)
vectors = model.encode(
    [f"{r.alt}. {r.description}" for r in df.itertuples()],
    show_progress_bar=True,
)
np.save("startup_vectors.npy", vectors, allow_pickle=False)

2.2 Qdrant 실행

docker pull qdrant/qdrant
docker run -p 6333:6333 -v $(pwd)/qdrant_storage:/qdrant/storage qdrant/qdrant

2.3 벡터 업로드

from qdrant_client import QdrantClient, models

client = QdrantClient("http://localhost:6333")
client.create_collection(
    "startups",
    vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE),
)
payload_iter = map(json.loads, open("startups_demo.json"))
vectors = np.load("startup_vectors.npy")

client.upload_collection(
    "startups",
    vectors=vectors,
    payload=payload_iter,
    batch_size=256,
)

2.4 FastAPI 서비스

from fastapi import FastAPI
from neural_searcher import NeuralSearcher    # 자체 클래스

app = FastAPI()
searcher = NeuralSearcher("startups")

@app.get("/api/search")
def search(q: str):
    return {"result": searcher.search(q)}

# 실행: uvicorn service:app --reload

NeuralSearcher 클래스는 Sentence Transformer로 쿼리를 임베딩하고 client.query_points로 검색한다.

3. FastEmbed + Qdrant로 하이브리드 검색 서비스 구축

3.1 환경 설정

pip install "qdrant-client[fastembed]>=1.14.2"  # fastembed>=0.6.1 포함

3.2 컬렉션 구성

dense_vec_name  = "dense"
sparse_vec_name = "sparse"
dense_model     = "sentence-transformers/all-MiniLM-L6-v2"
sparse_model    = "prithivida/Splade_PP_en_v1"

client.create_collection(
    "startups",
    vectors_config={
        dense_vec_name: models.VectorParams(
            size=client.get_embedding_size(dense_model),
            distance=models.Distance.COSINE)
    },
    sparse_vectors_config={sparse_vec_name: models.SparseVectorParams()},
)

3.3 업로드 및 인퍼런스

client.upload_collectionmodels.Document(text, model=...)을 사용하면 FastEmbed가 자동으로 배치·병렬 인코딩 후 업로드한다.

3.4 하이브리드 검색 구현

search_result = client.query_points(
    collection_name="startups",
    query=models.FusionQuery(fusion=models.Fusion.RRF),
    prefetch=[
        models.Prefetch(
            query=models.Document(text=q, model=dense_model),
            using=dense_vec_name,
        ),
        models.Prefetch(
            query=models.Document(text=q, model=sparse_model),
            using=sparse_vec_name,
        ),
    ],
    limit=5,
).points

RRF(Reciprocal Rank Fusion) 또는 DBSF(Distribution-Based Score Fusion)로 두 결과를 합친다.

4. 검색 품질 측정 및 HNSW 튜닝

4.1 평가 지표

4.2 Exact 모드로 기준값 확보

ann_hits = client.query_points(..., limit=k).points
knn_hits = client.query_points(
    ..., limit=k,
    search_params=models.SearchParams(exact=True)
).points
precision = len({h.id for h in ann_hits} & {h.id for h in knn_hits}) / k

4.3 HNSW 파라미터 조정

기본값 m=16, ef_construct=100이다. 품질을 높이고자 한다면:

client.update_collection(
    "arxiv-titles",
    hnsw_config=models.HnswConfigDiff(m=32, ef_construct=200),
)

인덱싱 완료 후 precision@k를 다시 계산하여 향상 여부를 확인한다.

5. 종합 네트워킹·운영 팁

6. 결론 및 다음 단계

이제 간단한 데모부터 실서비스용 뉴럴·하이브리드 검색 API, 그리고 성능 평가·튜닝까지 일련의 흐름을 모두 익혔다. 실제 프로젝트에 맞추어 데이터 전처리, 필터 인덱스, 멀티테넌시, 배치 업로드, 재랭킹 등을 조합하면 보다 견고한 벡터 기반 검색 시스템을 구축할 수 있을 것이다.


Edit page
Share this post:

Previous Post
Qdrant로 대규모 PDF 검색 확장하기 — ColPali 멀티벡터 최적화
Next Post
Qdrant Similarity Search — 벡터 유사도 검색 API 완벽 가이드