본문 바로가기
생성형 AI 활용법

LLM Search Engine: Elasticsearch, Pinecone, FAISS, Milvus 비교 및 샘플 코드

by 진희코딩 2025. 1. 31.

📢 LLM 서비스에서 검색이 중요한 이유

LLM(대형 언어 모델)은 강력한 생성 능력을 갖추고 있지만, 정확한 정보 검색 없이 사용하면 가짜 정보(Hallucination) 가 발생할 수 있습니다. 이를 해결하는 방식이 RAG(Retrieval-Augmented Generation) 이며, 외부 데이터를 검색하여 신뢰할 수 있는 답변을 생성하는 것이 핵심입니다.

RAG 구현의 핵심은 효율적인 검색 엔진 구축이며, 주요 검색 방식은 두 가지입니다.

✅ LLM 검색을 위한 두 가지 핵심 방법

1️⃣ 키워드 검색 (전통적인 검색 방식)

  • 텍스트 매칭(Elasticsearch 등)
  • 키워드 기반 필터링
  • 문서 검색, DB 검색 등

2️⃣ 벡터 검색 (유사도 기반 검색 방식)

  • 문서 임베딩을 벡터로 변환 후, 유사도 검색 (FAISS, Pinecone 등)
  • LLM 질문과 가장 유사한 문서를 찾음

🔍 LLM 서비스에서 검색 엔진 비교

아래 표는 가장 많이 사용되는 LLM 검색 엔진을 비교한 것입니다.

검색 엔진 방식 장점 단점 추천 사용 사례
Elasticsearch 키워드 + 벡터 검색 (HNSW 지원) 하이브리드 검색 가능, 대규모 데이터 처리 벡터 검색 최적화 필요 텍스트 검색 + 벡터 검색을 같이 쓰는 경우
Pinecone 벡터 검색 전용 빠른 벡터 검색, 관리형 서비스 비용이 높음, 키워드 검색 불가 LLM 기반 RAG 서비스
FAISS 벡터 검색 (고속 GPU 지원) 빠른 검색 속도, 로컬 실행 가능 분산 처리 기능 부족 단일 서버에서 벡터 검색
Milvus 벡터 검색 (클러스터 지원) 대규모 데이터 처리 가능, 확장성 우수 설정이 복잡함 대량 데이터 벡터 검색
Weaviate 벡터 + 그래프 검색 LLM 연동 최적화, GraphQL API 지원 리소스 사용량 높음 LLM과 직접 통합 검색

 

🚀 언제 어떤 검색 엔진을 써야 할까?   

상황추천 검색 엔진
일반적인 키워드 검색 + 벡터 검색이 필요한 경우 Elasticsearch
OpenAI 기반 RAG 시스템 구축 Pinecone, FAISS
초고속 벡터 검색이 필요한 경우 FAISS (GPU 지원)
대규모 클러스터 벡터 검색 Milvus, Weaviate
LLM과 직접 연결되는 검색 Weaviate

 

 

💡 샘플 코드: OpenAI + FAISS를 이용한 벡터 검색

아래 코드는 OpenAI 임베딩 API를 사용해 문장을 벡터화하고, FAISS를 이용해 유사한 문서를 검색하는 예제입니다.

1️⃣ Elasticsearch 샘플 코드 (키워드 + 벡터 검색)

Elasticsearch는 기본적으로 키워드 검색을 제공하며, 벡터 검색도 가능합니다.

아래 코드는 OpenAI 임베딩을 이용해 문서를 인덱싱하고, 코사인 유사도로 검색하는 예제입니다.


from elasticsearch import Elasticsearch
import openai
import numpy as np

# OpenAI API 키 설정
OPENAI_API_KEY = "your_api_key"

def get_embedding(text):
    response = openai.Embedding.create(
        model="text-embedding-3-small",
        input=text
    )
    return response["data"][0]["embedding"]

# Elasticsearch 설정
es = Elasticsearch("<http://localhost:9200>")

# 문서 인덱싱 (벡터 포함)
documents = ["엘라스틱서치는 검색 엔진입니다.", "FAISS는 벡터 검색 라이브러리입니다.", "Pinecone은 벡터 데이터베이스입니다."]
for i, doc in enumerate(documents):
    embedding = get_embedding(doc)
    es.index(index="documents", id=i, body={"text": doc, "embedding": embedding})

# 검색
query = "벡터 검색 엔진은?"
query_embedding = get_embedding(query)

script_query = {
    "script_score": {
        "query": {"match_all": {}},
        "script": {
            "source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0",
            "params": {"query_vector": query_embedding}
        }
    }
}

res = es.search(index="documents", body={"query": script_query, "size": 1})
print("Elasticsearch 검색 결과:", res["hits"]["hits"][0]["_source"]["text"])

설명:

  • Elasticsearch에 문서를 저장할 때 임베딩 벡터도 함께 저장
  • 코사인 유사도를 이용해 가장 유사한 문서 검색

2️⃣ Pinecone 샘플 코드 (벡터 검색 최적화)

Pinecone은 벡터 데이터베이스로, 대규모 벡터 검색을 효율적으로 수행합니다.


import pinecone
import openai
import numpy as np

# API 키 설정
OPENAI_API_KEY = "your_api_key"
PINECONE_API_KEY = "your_pinecone_api_key"

# Pinecone 연결
pinecone.init(api_key=PINECONE_API_KEY, environment="us-west1-gcp")
index = pinecone.Index("llm-search")

def get_embedding(text):
    response = openai.Embedding.create(
        model="text-embedding-3-small",
        input=text
    )
    return response["data"][0]["embedding"]

# 문서 삽입
documents = ["엘라스틱서치는 검색 엔진입니다.", "FAISS는 벡터 검색 라이브러리입니다.", "Pinecone은 벡터 데이터베이스입니다."]
vectors = [(str(i), get_embedding(doc)) for i, doc in enumerate(documents)]
index.upsert(vectors)

# 검색
query = "벡터 검색 엔진은?"
query_embedding = get_embedding(query)

res = index.query(queries=[query_embedding], top_k=1, include_metadata=True)
print("Pinecone 검색 결과:", documents[int(res["matches"][0]["id"])])

설명:

  • Pinecone에 벡터 데이터 저장
  • top_k=1 설정으로 가장 유사한 문서 검색

3️⃣ FAISS 샘플 코드 (초고속 벡터 검색)

FAISS는 GPU를 활용할 수 있어, 빠른 벡터 검색이 가능합니다.


import faiss
import openai
import numpy as np

# API 키 설정
OPENAI_API_KEY = "your_api_key"

def get_embedding(text):
    response = openai.Embedding.create(
        model="text-embedding-3-small",
        input=text
    )
    return np.array(response["data"][0]["embedding"])

# 문서 임베딩 변환
documents = ["엘라스틱서치는 검색 엔진입니다.", "FAISS는 벡터 검색 라이브러리입니다.", "Pinecone은 벡터 데이터베이스입니다."]
doc_embeddings = np.array([get_embedding(doc) for doc in documents])

# FAISS 인덱스 생성
dimension = len(doc_embeddings[0])
index = faiss.IndexFlatL2(dimension)
index.add(doc_embeddings)

# 검색
query = "벡터 검색 엔진은?"
query_embedding = get_embedding(query).reshape(1, -1)

D, I = index.search(query_embedding, k=1)  # 가장 유사한 문서 1개 찾기
print("FAISS 검색 결과:", documents[I[0][0]])

설명:

  • FAISS는 로컬에서 빠른 벡터 검색이 가능
  • L2 거리(유클리드 거리)를 이용해 유사한 문서 찾기

4️⃣ Milvus 샘플 코드 (대규모 벡터 검색)

Milvus는 분산 환경에서 대규모 벡터 검색을 지원합니다.


from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
import openai
import numpy as np

# API 키 설정
OPENAI_API_KEY = "your_api_key"

def get_embedding(text):
    response = openai.Embedding.create(
        model="text-embedding-3-small",
        input=text
    )
    return np.array(response["data"][0]["embedding"])

# Milvus 연결
connections.connect(host="localhost", port="19530")

# 컬렉션 생성
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536)
]
schema = CollectionSchema(fields)
collection = Collection("llm_search", schema)

# 문서 삽입
documents = ["엘라스틱서치는 검색 엔진입니다.", "FAISS는 벡터 검색 라이브러리입니다.", "Pinecone은 벡터 데이터베이스입니다."]
vectors = [[i, get_embedding(doc).tolist()] for i, doc in enumerate(documents)]
collection.insert(vectors)

# 검색
query = "벡터 검색 엔진은?"
query_embedding = get_embedding(query).tolist()
res = collection.search([query_embedding], "embedding", top_k=1, output_fields=["id"])
print("Milvus 검색 결과:", documents[res[0][0].id])

설명:

  • Milvus는 대규모 분산 벡터 검색을 지원
  • top_k=1 설정으로 가장 유사한 문서 검색

5️⃣ Weaviate 샘플 코드 (LLM 통합 최적화)

Weaviate는 GraphQL API를 활용하여 LLM과 쉽게 통합할 수 있습니다.


import weaviate
import openai

# OpenAI API 설정
OPENAI_API_KEY = "your_api_key"

def get_embedding(text):
    response = openai.Embedding.create(
        model="text-embedding-3-small",
        input=text
    )
    return response["data"][0]["embedding"]

# Weaviate 연결
client = weaviate.Client("<http://localhost:8080>")

# 데이터 삽입
documents = ["엘라스틱서치는 검색 엔진입니다.", "FAISS는 벡터 검색 라이브러리입니다.", "Pinecone은 벡터 데이터베이스입니다."]
for doc in documents:
    client.data_object.create({"text": doc, "embedding": get_embedding(doc)}, "Document")

# 검색
query = "벡터 검색 엔진은?"
query_embedding = get_embedding(query)

res = client.query.get("Document", ["text"]).with_near_vector(query_embedding).with_limit(1).do()
print("Weaviate 검색 결과:", res["data"]["Get"]["Document"][0]["text"])

설명:

  • Weaviate는 LLM과 GraphQL API를 통해 쉽게 통합 가능

📌 참고 자료

🔹 Elasticsearch 공식 문서: https://www.elastic.co/guide/
🔹 Pinecone 공식 문서: https://www.pinecone.io/
🔹 FAISS GitHub: https://github.com/facebookresearch/faiss
🔹 Milvus 공식 문서: https://milvus.io/docs/
🔹 Weaviate 공식 문서: https://weaviate.io/