Elasticsearch의 kNN 검색, Inference API, semantic_text 필드, ELSER, Elastic Rerank, 하이브리드 검색(RRF)을 실습과 함께 다룹니다.
Elasticsearch는 전통적인 전문 검색(full-text search) 엔진에서 AI 검색 플랫폼으로 빠르게 진화하고 있습니다. 버전 8.0에서 kNN 검색이 도입된 이후, 매 릴리스마다 AI 검색 기능이 강화되어 왔습니다.
| 버전 | 주요 AI 검색 기능 |
|---|---|
| 8.0 | kNN 검색 (dense vector) |
| 8.8 | RRF(Reciprocal Rank Fusion) |
| 8.11 | ELSER v2 (희소 벡터) |
| 8.13 | Inference API, semantic_text 필드 |
| 8.14 | Elastic Rerank (Cross-encoder) |
| 8.15+ | 통합 retriever API |
Elasticsearch의 kNN(k-Nearest Neighbors) 검색은 HNSW 알고리즘 기반의 벡터 검색을 제공합니다.
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "nori"
},
"content": {
"type": "text",
"analyzer": "nori"
},
"content_vector": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "cosine"
},
"published_date": {
"type": "date"
},
"category": {
"type": "keyword"
}
}
}
}한국어 문서에는 nori 분석기를 사용합니다. Elasticsearch에 analysis-nori 플러그인이 설치되어 있어야 합니다. nori 분석기는 한국어 형태소 분석을 수행하여 키워드 검색의 품질을 크게 향상시킵니다.
{
"knn": {
"field": "content_vector",
"query_vector": [0.12, -0.34, 0.56, "...1024개의 float 값..."],
"k": 10,
"num_candidates": 100
},
"_source": ["title", "content", "category"]
}num_candidates는 HNSW 탐색 시 고려하는 후보 수입니다. 값이 클수록 정확하지만 느려집니다. 일반적으로 k의 5-10배를 설정합니다.
from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer
es = Elasticsearch("http://localhost:9200")
model = SentenceTransformer("intfloat/multilingual-e5-large")
INDEX_NAME = "tech-articles"
def index_document(doc_id: str, title: str, content: str, category: str):
"""문서 인덱싱 (벡터 포함)"""
embedding = model.encode(
f"passage: {title} {content}",
normalize_embeddings=True,
)
es.index(
index=INDEX_NAME,
id=doc_id,
document={
"title": title,
"content": content,
"content_vector": embedding.tolist(),
"category": category,
},
)
def knn_search(query: str, k: int = 10, category_filter: str = None):
"""kNN 벡터 검색"""
query_vector = model.encode(
f"query: {query}", normalize_embeddings=True
)
knn_params = {
"field": "content_vector",
"query_vector": query_vector.tolist(),
"k": k,
"num_candidates": k * 10,
}
if category_filter:
knn_params["filter"] = {"term": {"category": category_filter}}
response = es.search(index=INDEX_NAME, knn=knn_params)
return response["hits"]["hits"]Elasticsearch 8.13부터 도입된 Inference API를 사용하면, 임베딩 생성을 Elasticsearch 내부에서 처리할 수 있습니다. 별도의 임베딩 서비스를 운영할 필요가 없어집니다.
{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".multilingual-e5-small"
}
}curl -X PUT "localhost:9200/_inference/text_embedding/my-e5-model" \
-H "Content-Type: application/json" \
-d '{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".multilingual-e5-small"
}
}'semantic_text는 Inference API와 결합되는 특별한 필드 타입입니다. 이 필드에 텍스트를 저장하면 Elasticsearch가 자동으로 임베딩을 생성하고 인덱싱합니다.
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "nori"
},
"content": {
"type": "semantic_text",
"inference_id": "my-e5-model"
},
"category": {
"type": "keyword"
}
}
}
}semantic_text 필드를 사용하면 문서 인덱싱 시 별도의 임베딩 코드가 필요 없습니다.
{
"title": "벡터 데이터베이스 비교 가이드",
"content": "Pinecone, Weaviate, Qdrant 등 주요 벡터 데이터베이스의 성능과 특징을 비교합니다.",
"category": "database"
}검색도 간단합니다.
{
"retriever": {
"standard": {
"query": {
"semantic": {
"field": "content",
"query": "벡터 저장소 성능 비교"
}
}
}
}
}semantic_text 필드는 임베딩 생성, 청킹, 인덱싱을 모두 자동화합니다. 빠른 프로토타이핑에 매우 유용하지만, 세밀한 청킹 제어가 필요한 경우에는 직접 임베딩을 관리하는 방식이 더 적합합니다.
**ELSER(Elastic Learned Sparse EncodeR)**는 Elasticsearch 전용의 희소 벡터 모델입니다. 밀집 벡터(dense vector)와 달리, 희소 벡터(sparse vector)는 대부분의 값이 0이고 일부 차원만 활성화됩니다.
밀집 벡터 (1024차원): [0.12, -0.34, 0.56, 0.01, -0.23, ...]
희소 벡터 (30000+차원): {"검색": 2.3, "시스템": 1.8, "벡터": 3.1, "데이터": 0.9, ...}
ELSER의 희소 벡터는 각 차원이 어휘(vocabulary)의 토큰에 대응합니다. 문서에 실제로 등장하지 않는 단어도 의미적으로 관련 있으면 활성화됩니다. 이는 BM25의 어휘 불일치 문제를 해결하면서도, 키워드 검색의 해석 가능성을 유지합니다.
{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".elser_model_2"
}
}{
"mappings": {
"properties": {
"content": {
"type": "semantic_text",
"inference_id": "my-elser-endpoint"
}
}
}
}ELSER는 Elasticsearch ML 노드에서 실행되므로 충분한 메모리(최소 4GB)가 필요합니다. 프로덕션 환경에서는 전용 ML 노드를 구성하는 것을 권장합니다.
Elasticsearch에서 BM25 키워드 검색과 벡터 검색을 결합하는 하이브리드 검색은 RRF retriever를 사용하여 구현합니다.
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"content": {
"query": "벡터 데이터베이스 성능 최적화",
"analyzer": "nori"
}
}
}
}
},
{
"knn": {
"field": "content_vector",
"query_vector": [0.12, -0.34, "..."],
"k": 50,
"num_candidates": 200
}
}
],
"rank_window_size": 100,
"rank_constant": 60
}
},
"size": 10
}semantic_text 필드를 사용하면 더 간결하게 하이브리드 검색을 구성할 수 있습니다.
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"title": "벡터 데이터베이스"
}
}
}
},
{
"standard": {
"query": {
"semantic": {
"field": "content",
"query": "벡터 데이터베이스 성능 비교"
}
}
}
}
],
"rank_window_size": 100
}
}
}5장에서 소개한 Elastic Rerank를 Elasticsearch 검색 파이프라인에 통합하면, 하이브리드 검색 결과를 Cross-encoder로 정밀 재평가할 수 있습니다.
{
"retriever": {
"text_similarity_reranker": {
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"content": "AI 검색 시스템 아키텍처"
}
}
}
},
{
"standard": {
"query": {
"semantic": {
"field": "content",
"query": "AI 검색 시스템 아키텍처"
}
}
}
}
],
"rank_window_size": 200
}
},
"field": "content",
"inference_id": "my-rerank-model",
"inference_text": "AI 검색 시스템 아키텍처",
"rank_window_size": 100
}
},
"size": 10
}이 쿼리는 다음과 같은 3단계 파이프라인을 수행합니다.
from elasticsearch import Elasticsearch
es = Elasticsearch("http://localhost:9200")
def full_hybrid_search(query: str, index: str, k: int = 10):
"""BM25 + 시맨틱 + Rerank 전체 파이프라인"""
response = es.search(
index=index,
body={
"retriever": {
"text_similarity_reranker": {
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"content": {
"query": query,
"analyzer": "nori",
}
}
}
}
},
{
"standard": {
"query": {
"semantic": {
"field": "content",
"query": query,
}
}
}
},
],
"rank_window_size": 200,
}
},
"field": "content",
"inference_id": "my-rerank-model",
"inference_text": query,
"rank_window_size": 100,
}
},
"size": k,
},
)
return [
{
"title": hit["_source"]["title"],
"score": hit["_score"],
"content": hit["_source"]["content"][:200],
}
for hit in response["hits"]["hits"]
]이번 장에서는 Elasticsearch의 AI 검색 기능을 실습과 함께 살펴보았습니다. kNN 검색으로 밀집 벡터 기반 시맨틱 검색을 구현하고, Inference API와 semantic_text 필드로 임베딩 생성을 자동화하며, ELSER로 희소 벡터 검색을 수행하는 방법을 학습했습니다. RRF를 활용한 하이브리드 검색과 Elastic Rerank를 통합한 3단계 파이프라인도 구현했습니다.
다음 장에서는 Elasticsearch 외의 검색 엔진들을 살펴봅니다. OpenSearch의 신경 검색 플러그인, Algolia NeuralSearch, Meilisearch, Typesense 등을 비교하고, 프로젝트에 맞는 검색 엔진 선택 가이드를 제시하겠습니다.
이 글이 도움이 되셨나요?
OpenSearch 신경 검색, 재랭킹 파이프라인과 Algolia, Meilisearch, Typesense 등 주요 검색 엔진의 AI 검색 기능을 비교합니다.
Bi-encoder와 Cross-encoder의 구조적 차이, Elastic Rerank의 DeBERTa v3 모델, 점수 퓨전(RRF), 학습 랭킹(LTR)을 심층적으로 다룹니다.
BM25와 시맨틱 검색의 결합 전략, RRF/선형 보간, 리랭킹 캐스케이드, 다단계 검색 파이프라인 설계와 성능-품질 트레이드오프를 다룹니다.