Skip to content

Split View: Embedding 모델 선택 완전 가이드: OpenAI부터 오픈소스까지 2025년 기준

|

Embedding 모델 선택 완전 가이드: OpenAI부터 오픈소스까지 2025년 기준

들어가며

RAG 시스템을 구축할 때 가장 먼저 결정해야 하는 것 중 하나가 임베딩 모델이다. 잘못 선택하면 검색 품질이 나빠지고, 비용이 10배 이상 차이 날 수 있다. 그런데 놀랍도록 많은 팀이 "OpenAI 쓰면 되겠지"로 결정을 끝낸다.

이 글에서는 2025년 기준 주요 임베딩 모델을 실제 성능, 비용, 다국어 지원, 인프라 요구사항 측면에서 비교하고, 상황별 최고의 선택을 제시한다.


Embedding이란 무엇인가?

임베딩은 텍스트를 의미를 담은 숫자 벡터로 변환하는 기술이다.

from openai import OpenAI
import numpy as np

client = OpenAI()

# 텍스트 → 벡터 변환
text = "인공지능이 세상을 바꾸고 있다"
response = client.embeddings.create(
    input=text,
    model="text-embedding-3-small"
)
embedding = response.data[0].embedding
print(f"벡터 차원: {len(embedding)}")  # 1536

# 의미적 유사도 계산
def cosine_similarity(a: list, b: list) -> float:
    a_np = np.array(a)
    b_np = np.array(b)
    return float(np.dot(a_np, b_np) / (np.linalg.norm(a_np) * np.linalg.norm(b_np)))

# 유사한 문장끼리는 높은 유사도
text1 = "머신러닝은 AI의 한 분야다"
text2 = "딥러닝은 인공지능 기술이다"
text3 = "오늘 날씨가 맑다"

emb1 = client.embeddings.create(input=text1, model="text-embedding-3-small").data[0].embedding
emb2 = client.embeddings.create(input=text2, model="text-embedding-3-small").data[0].embedding
emb3 = client.embeddings.create(input=text3, model="text-embedding-3-small").data[0].embedding

print(f"AI 관련 두 문장 유사도: {cosine_similarity(emb1, emb2):.3f}")  # ~0.85
print(f"AI vs 날씨 유사도: {cosine_similarity(emb1, emb3):.3f}")      # ~0.30

임베딩 공간에서 비슷한 의미를 가진 텍스트는 가까이, 다른 의미는 멀리 위치한다. RAG 시스템은 이 특성을 활용해 "의미적으로 가장 관련 있는 문서"를 검색한다.


주요 임베딩 모델 완전 비교 (2025 기준)

OpenAI Embeddings

가장 널리 사용되는 상용 임베딩 모델이다.

from openai import OpenAI
client = OpenAI()

# text-embedding-3-small: 빠르고 저렴, 대부분의 경우에 충분
small_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-small"
).data[0].embedding
# 차원: 1536, 비용: $0.020/1M tokens

# text-embedding-3-large: 최고 품질, 5배 비쌈
large_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-large"
).data[0].embedding
# 차원: 3072, 비용: $0.130/1M tokens

# 차원 축소 지원 (Matryoshka Representation Learning)
reduced_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-small",
    dimensions=256  # 1536에서 256으로 축소, 저장 비용 6배 절감
).data[0].embedding

장점:

  • 신뢰할 수 있는 일관된 성능
  • 별도 인프라 불필요, 즉시 사용 가능
  • OpenAI 생태계와 완벽한 통합

단점:

  • 규모가 커지면 비용 급증
  • 데이터가 OpenAI 서버로 전송됨 (프라이버시 우려)
  • 한국어/일본어 성능이 전문 다국어 모델 대비 떨어짐

Cohere Embed

다국어 지원이 탁월한 상용 모델이다.

import cohere

co = cohere.Client(api_key="your-key")

# 한국어 포함 100개+ 언어 지원
texts = [
    "인공지능이 세상을 바꾸고 있다",
    "AI is changing the world",
    "AIが世界を変えている"
]

response = co.embed(
    texts=texts,
    model="embed-multilingual-v3.0",
    input_type="search_document"  # 문서 인덱싱용
    # input_type="search_query"   # 쿼리 임베딩용
)

# 쿼리는 다른 input_type 사용 (중요!)
query_response = co.embed(
    texts=["AI 발전에 대해 알려줘"],
    model="embed-multilingual-v3.0",
    input_type="search_query"  # 쿼리 전용
)

# 압축 지원: 저장 비용 대폭 절감
compressed_response = co.embed(
    texts=texts,
    model="embed-multilingual-v3.0",
    input_type="search_document",
    embedding_types=["float", "int8", "binary"]  # 다양한 정밀도
)
# binary: 원본 대비 32배 저장 공간 절감!

장점:

  • 한국어/일본어 포함 100개+ 언어에서 최고 수준 성능
  • int8/binary 압축으로 저장 비용 4~32배 절감
  • search_document/search_query 분리로 검색 품질 향상

단점:

  • float32 기준으로는 OpenAI보다 약간 비쌈
  • 데이터가 Cohere 서버로 전송됨

오픈소스 모델 (자체 호스팅)

데이터 프라이버시가 중요하거나 규모가 크다면 자체 호스팅이 답이다.

from sentence_transformers import SentenceTransformer
import torch

# BGE-M3: 2025년 최고의 오픈소스 다국어 임베딩 모델
model = SentenceTransformer("BAAI/bge-m3")

texts = ["인공지능이 세상을 바꾸고 있다", "AI is changing the world"]
embeddings = model.encode(texts, normalize_embeddings=True)
print(f"차원: {embeddings.shape}")  # (2, 1024)

# E5-mistral-7b: 7B LLM을 인코더로 사용, 최고 품질
# (GPU 메모리 최소 16GB 필요)
e5_model = SentenceTransformer("intfloat/e5-mistral-7b-instruct")
# 사용 시 쿼리에 "Instruct: ... \nQuery: " 접두어 필요
query = "Instruct: 관련 문서를 검색하시오\nQuery: 인공지능 기술 동향"
doc = "최근 AI 기술이 빠르게 발전하고 있다"
query_emb = e5_model.encode(query, normalize_embeddings=True)
doc_emb = e5_model.encode(doc, normalize_embeddings=True)

# nomic-embed-text-v1.5: 균형잡힌 성능/크기
nomic_model = SentenceTransformer("nomic-ai/nomic-embed-text-v1.5", trust_remote_code=True)
# 768차원, 좋은 성능 대비 가벼운 모델

# multilingual-e5-large: 안정적인 다국어 선택
ml_e5 = SentenceTransformer("intfloat/multilingual-e5-large")
# 일본어 포함 다국어에서 강력한 성능

MTEB 벤치마크 결과 해석

MTEB(Massive Text Embedding Benchmark)는 56개의 데이터셋에서 임베딩 모델을 평가하는 표준 벤치마크다. leaderboard.txt에서 확인할 수 있다.

주요 태스크별 해석

MTEB 태스크 유형:
- Retrieval (검색): RAG에서 가장 중요. nDCG@10 지표
- STS (Semantic Textual Similarity): 문장 간 유사도. Spearman 상관계수
- Classification: 텍스트 분류. Accuracy
- Clustering: 군집화. V-measure
- Reranking: 재순위 매기기. MAP

RAG 시스템 구축 시 → Retrieval 점수 최우선
텍스트 분류 시 → Classification 점수 우선
의미 유사도 계산 시 → STS 점수 우선

2025년 주요 모델 MTEB 점수 비교

모델평균RetrievalSTS차원비용
text-embedding-3-large64.655.481.73072$0.13/1M
Cohere embed-v364.555.082.11024$0.10/1M
E5-mistral-7b66.656.984.74096자체 호스팅
BGE-M363.254.979.81024자체 호스팅
text-embedding-3-small62.353.280.41536$0.02/1M

주의: 영어 기반 MTEB 점수다. 한국어/일본어 성능은 별도 평가 필요.


언어별 최고 모델 선택

언어1순위2순위비고
한국어BGE-M3 (OSS)Cohere multilingualBGE-M3이 한국어에서 특히 강함
일본어multilingual-e5-largeBGE-M3두 모델 모두 우수
영어E5-mistral-7btext-embedding-3-large오픈소스가 상용 능가
다국어 혼합Cohere multilingualBGE-M3100개+ 언어 균일 지원
코드text-embedding-3-largevoyage-code-2코드 특화 모델 유리

실용적인 선택 가이드

다음 함수로 상황에 맞는 모델을 빠르게 결정할 수 있다:

def choose_embedding_model(
    languages: list,        # 예: ['ko', 'en'], ['ja', 'en', 'zh']
    budget: str,            # 'low', 'medium', 'high'
    privacy_required: bool, # 데이터를 외부로 보내면 안 되는 경우
    scale: str,             # 'small'(<100k req/day), 'medium', 'large'(>1M req/day)
    use_case: str = 'rag'   # 'rag', 'classification', 'similarity'
) -> dict:
    """
    상황별 최적 임베딩 모델 추천

    Returns:
        dict: {'model': str, 'reason': str, 'estimated_monthly_cost': str}
    """

    # 프라이버시 또는 대규모 → 자체 호스팅
    if privacy_required or scale == 'large':
        if 'ko' in languages or 'ja' in languages:
            return {
                "model": "BAAI/bge-m3",
                "reason": "최고의 오픈소스 한국어/일본어 지원 + 프라이버시 보장",
                "estimated_monthly_cost": "GPU 서버 비용만 (~$200-500/월)"
            }
        if use_case == 'rag' and budget == 'high':
            return {
                "model": "intfloat/e5-mistral-7b-instruct",
                "reason": "최고 품질 오픈소스, 영어 Retrieval 최강",
                "estimated_monthly_cost": "GPU 서버 비용만 (A100 기준 ~$1000/월)"
            }
        return {
            "model": "nomic-ai/nomic-embed-text-v1.5",
            "reason": "가벼우면서 좋은 성능, 자체 호스팅 용이",
            "estimated_monthly_cost": "GPU 서버 비용만 (~$100-200/월)"
        }

    # 다국어 (3개 이상) → Cohere
    if len(languages) > 2:
        return {
            "model": "cohere/embed-multilingual-v3.0",
            "reason": "100개+ 언어 균일한 고품질 지원",
            "estimated_monthly_cost": "요청량에 따라 $100-1000/월"
        }

    # 비용 민감 + 영어 위주
    if budget == 'low' and 'en' in languages:
        return {
            "model": "text-embedding-3-small",
            "reason": "가장 저렴하고 대부분의 영어 태스크에 충분",
            "estimated_monthly_cost": "1M req 기준 ~$20/월"
        }

    # 기본: 최고 품질 상용
    return {
        "model": "text-embedding-3-large",
        "reason": "최고의 상용 임베딩 품질, 운영 부담 없음",
        "estimated_monthly_cost": "1M req 기준 ~$130/월"
    }

# 사용 예시
result = choose_embedding_model(
    languages=['ko', 'en'],
    budget='medium',
    privacy_required=True,
    scale='medium',
    use_case='rag'
)
print(f"추천 모델: {result['model']}")
print(f"이유: {result['reason']}")

차원 축소와 비용 절감: Matryoshka Representation Learning

OpenAI의 text-embedding-3 모델과 일부 오픈소스 모델은 MRL을 지원한다. 전체 차원의 앞부분만 잘라내도 품질이 거의 유지된다.

from openai import OpenAI
import numpy as np

client = OpenAI()

def compare_dimension_tradeoffs(text: str):
    """차원별 성능/비용 트레이드오프 분석"""

    dimensions_to_test = [256, 512, 1024, 1536]  # 1536이 기본
    results = {}

    for dim in dimensions_to_test:
        response = client.embeddings.create(
            input=text,
            model="text-embedding-3-small",
            dimensions=dim
        )
        emb = response.data[0].embedding
        results[dim] = {
            "embedding": emb,
            "storage_bytes": len(emb) * 4,  # float32 = 4 bytes
            "relative_storage": f"{dim/1536:.1%} of full"
        }
        print(f"차원 {dim}: 저장 {results[dim]['storage_bytes']} bytes "
              f"({results[dim]['relative_storage']})")

    return results

# 실제 성능 측정 (MTEB Retrieval 점수 기준)
# 모델: text-embedding-3-small
# 1536차원: 53.2 (기준)
# 1024차원: 52.8 (-0.4)
# 512차원:  52.1 (-1.1)
# 256차원:  50.9 (-2.3)
# → 256차원으로 줄여도 품질 4% 미만 감소, 저장 비용 6배 절감!

# pgvector 사용 시 실질적인 저장/검색 비용 비교:
# 문서 100만 건 기준:
# 1536차원: 100만 × 1536 × 4 bytes = 6.14 GB
# 256차원:  100만 × 256 × 4 bytes = 1.02 GB
# → 저장 비용 6배 절감 + 검색 속도 약 3배 향상

실전 임베딩 파이프라인 구축

from sentence_transformers import SentenceTransformer
import numpy as np
from typing import Union
import time

class EmbeddingPipeline:
    """프로덕션 임베딩 파이프라인"""

    def __init__(self, model_name: str = "BAAI/bge-m3"):
        self.model = SentenceTransformer(model_name)
        self.model_name = model_name

    def embed_documents(self, texts: list, batch_size: int = 32) -> np.ndarray:
        """문서 배치 임베딩 (인덱싱용)"""
        all_embeddings = []
        for i in range(0, len(texts), batch_size):
            batch = texts[i:i + batch_size]
            # BGE 계열은 "passage: " 접두어 사용
            if "bge" in self.model_name.lower():
                batch = [f"passage: {t}" for t in batch]
            embeddings = self.model.encode(
                batch,
                normalize_embeddings=True,
                show_progress_bar=False
            )
            all_embeddings.append(embeddings)
        return np.vstack(all_embeddings)

    def embed_query(self, query: str) -> np.ndarray:
        """쿼리 임베딩 (검색용)"""
        if "bge" in self.model_name.lower():
            query = f"query: {query}"  # BGE 계열 쿼리 접두어
        return self.model.encode(
            query,
            normalize_embeddings=True
        )

    def benchmark(self, texts: list) -> dict:
        """임베딩 속도 벤치마크"""
        start = time.time()
        embeddings = self.embed_documents(texts)
        elapsed = time.time() - start
        return {
            "total_texts": len(texts),
            "total_time_sec": elapsed,
            "texts_per_second": len(texts) / elapsed,
            "embedding_shape": embeddings.shape
        }

# 사용 예시
pipeline = EmbeddingPipeline("BAAI/bge-m3")

# 문서 임베딩
documents = [
    "RAG는 검색 증강 생성 기술입니다",
    "임베딩은 텍스트를 벡터로 변환합니다",
    "벡터 데이터베이스는 유사도 검색에 최적화되어 있습니다"
]
doc_embeddings = pipeline.embed_documents(documents)

# 쿼리 임베딩
query = "텍스트 검색 기술에 대해 알려주세요"
query_embedding = pipeline.embed_query(query)

# 유사도 계산
similarities = np.dot(doc_embeddings, query_embedding)
best_match_idx = np.argmax(similarities)
print(f"가장 관련 있는 문서: {documents[best_match_idx]}")
print(f"유사도 점수: {similarities[best_match_idx]:.3f}")

빠른 결정 요약

당장 시작해야 한다면: text-embedding-3-small (OpenAI) — 가장 쉽고, 대부분의 경우 충분하다.

한국어/일본어가 중요하다면: BGE-M3 (자체 호스팅) 또는 Cohere embed-multilingual-v3.0

비용이 최우선이라면: BGE-M3이나 nomic-embed-text-v1.5를 자체 호스팅 — 규모가 커질수록 압도적으로 유리하다.

최고 품질이 필요하다면: E5-mistral-7b-instruct — 오픈소스 중 최강이지만 GPU 16GB+ 필요.

데이터 프라이버시가 필수라면: 상용 API 전부 제외, 자체 호스팅만 고려.

임베딩 모델 교체는 RAG 시스템 전체를 재인덱싱해야 하므로, 초기에 충분히 검토하는 것이 중요하다.

Embedding Model Selection Guide 2025: From OpenAI to Open-Source Options

Introduction

When building a RAG system, one of the first decisions you make is which embedding model to use. Choose poorly and your retrieval quality suffers, your costs might be 10x higher than necessary, or your Korean/Japanese text ends up with mediocre representation. Yet a surprising number of teams make this decision with "let's just use OpenAI" and call it done.

This guide compares the major embedding models available in 2025 on real performance, cost, multilingual support, and infrastructure requirements — and gives you a clear decision framework for picking the right one.


What Is an Embedding?

An embedding converts text into a dense vector — a list of numbers that captures semantic meaning. Text with similar meaning maps to nearby points in this vector space.

from openai import OpenAI
import numpy as np

client = OpenAI()

# Text → vector
text = "machine learning is transforming the world"
response = client.embeddings.create(
    input=text,
    model="text-embedding-3-small"
)
embedding = response.data[0].embedding
print(f"Vector dimension: {len(embedding)}")  # 1536

# Semantic similarity via cosine similarity
def cosine_similarity(a: list, b: list) -> float:
    a_np, b_np = np.array(a), np.array(b)
    return float(np.dot(a_np, b_np) / (np.linalg.norm(a_np) * np.linalg.norm(b_np)))

text1 = "machine learning is a subfield of AI"
text2 = "deep learning is an artificial intelligence technique"
text3 = "the weather is sunny today"

emb1 = client.embeddings.create(input=text1, model="text-embedding-3-small").data[0].embedding
emb2 = client.embeddings.create(input=text2, model="text-embedding-3-small").data[0].embedding
emb3 = client.embeddings.create(input=text3, model="text-embedding-3-small").data[0].embedding

print(f"AI sentences similarity: {cosine_similarity(emb1, emb2):.3f}")  # ~0.85
print(f"AI vs weather similarity: {cosine_similarity(emb1, emb3):.3f}")  # ~0.30

RAG systems exploit this property to find "semantically most relevant documents" to a query — not exact keyword matches, but meaning-level matches.


Major Embedding Models: 2025 Comparison

OpenAI Embeddings

The most widely used commercial embeddings.

from openai import OpenAI
client = OpenAI()

# text-embedding-3-small: fast, cheap, good for most use cases
small_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-small"
).data[0].embedding
# Dimensions: 1536 | Cost: $0.020/1M tokens

# text-embedding-3-large: highest quality, 6.5x more expensive
large_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-large"
).data[0].embedding
# Dimensions: 3072 | Cost: $0.130/1M tokens

# Dimension reduction via Matryoshka Representation Learning
reduced_emb = client.embeddings.create(
    input="your text here",
    model="text-embedding-3-small",
    dimensions=256  # Reduced from 1536 — 6x storage savings
).data[0].embedding

Pros: Zero ops, consistent quality, tight integration with OpenAI ecosystem.

Cons: Cost spikes at scale, data leaves your infrastructure, weaker multilingual performance than dedicated models.

Cohere Embed

The strongest commercial option for multilingual workloads.

import cohere
co = cohere.Client(api_key="your-key")

# 100+ languages including Korean and Japanese
texts = [
    "machine learning is transforming the world",
    "AI is changing how we work",
    "the future of technology is bright"
]

# Note: use different input_type for documents vs queries!
doc_response = co.embed(
    texts=texts,
    model="embed-multilingual-v3.0",
    input_type="search_document"  # For indexing
)

query_response = co.embed(
    texts=["what is machine learning?"],
    model="embed-multilingual-v3.0",
    input_type="search_query"  # For querying — matters for quality!
)

# Compression support: dramatically reduce storage costs
compressed = co.embed(
    texts=texts,
    model="embed-multilingual-v3.0",
    input_type="search_document",
    embedding_types=["float", "int8", "binary"]
)
# binary: 32x storage reduction vs float32!
# Quality drop for binary: minimal for most use cases

Pros: Excellent multilingual (100+ languages), int8/binary compression for 4-32x storage savings, separated document/query encoding improves retrieval quality.

Cons: Slightly more expensive than OpenAI at float32, data goes to Cohere servers.

Open-Source Models (Self-Hosted)

When data privacy matters or you're operating at scale, self-hosting wins.

from sentence_transformers import SentenceTransformer
import numpy as np

# BGE-M3: Best open-source multilingual embedding model (2025)
# Strong for Korean, Japanese, Chinese + English
bge_model = SentenceTransformer("BAAI/bge-m3")

texts = ["RAG is retrieval-augmented generation", "embeddings convert text to vectors"]
# BGE models use "passage:" prefix for documents, "query:" for queries
doc_texts = [f"passage: {t}" for t in texts]
embeddings = bge_model.encode(doc_texts, normalize_embeddings=True)
print(f"Shape: {embeddings.shape}")  # (2, 1024)

# E5-mistral-7b: Uses a 7B LLM as encoder — highest quality open-source
# Requires 16GB+ GPU RAM
e5_model = SentenceTransformer("intfloat/e5-mistral-7b-instruct")
# Requires "Instruct: ...\nQuery: " prefix for queries
query = "Instruct: Retrieve relevant documents\nQuery: how does RAG work?"
doc = "RAG combines retrieval with language model generation"
q_emb = e5_model.encode(query, normalize_embeddings=True)
d_emb = e5_model.encode(doc, normalize_embeddings=True)

# nomic-embed-text-v1.5: Great performance/size ratio
nomic_model = SentenceTransformer(
    "nomic-ai/nomic-embed-text-v1.5",
    trust_remote_code=True
)
# 768 dimensions, excellent performance, easy to self-host

# multilingual-e5-large: Solid multilingual for CJK languages
ml_e5 = SentenceTransformer("intfloat/multilingual-e5-large")
# Strong for Japanese, Korean, Chinese

Reading MTEB Benchmark Results

MTEB (Massive Text Embedding Benchmark) is the standard for evaluating embedding models — 56 datasets across retrieval, classification, clustering, STS, and reranking tasks.

MTEB task types:
- Retrieval:    Most important for RAG. Metric: nDCG@10
- STS:          Sentence similarity. Metric: Spearman correlation
- Classification: Text classification. Metric: Accuracy
- Clustering:   Document grouping. Metric: V-measure
- Reranking:    Result reordering. Metric: MAP

For RAG systems → prioritize Retrieval score
For text classification → prioritize Classification score
For semantic similarity → prioritize STS score

2025 MTEB Scores (English-focused)

ModelAvgRetrievalSTSDimCost
E5-mistral-7b66.656.984.74096Self-hosted
text-embedding-3-large64.655.481.73072$0.13/1M
Cohere embed-v364.555.082.11024$0.10/1M
BGE-M363.254.979.81024Self-hosted
text-embedding-3-small62.353.280.41536$0.02/1M
nomic-embed-text-v1.562.053.579.3768Self-hosted

Note: These are English-focused scores. Korean/Japanese performance requires separate evaluation.


Language-Specific Recommendations

LanguageTop ChoiceRunner-upNotes
KoreanBGE-M3 (OSS)Cohere multilingualBGE-M3 particularly strong for Korean
Japanesemultilingual-e5-largeBGE-M3Both excellent
EnglishE5-mistral-7btext-embedding-3-largeOpen-source beats commercial
Mixed multilingualCohere multilingualBGE-M3100+ languages uniformly
Codetext-embedding-3-largevoyage-code-2Specialized code models help

Practical Decision Framework

def choose_embedding_model(
    languages: list,         # e.g., ['en'], ['ko', 'en'], ['ja', 'en', 'zh']
    budget: str,             # 'low', 'medium', 'high'
    privacy_required: bool,  # Can data leave your infrastructure?
    scale: str,              # 'small'(<100k/day), 'medium', 'large'(>1M/day)
    use_case: str = 'rag'    # 'rag', 'classification', 'similarity'
) -> dict:
    """
    Returns the optimal embedding model for your situation.
    """

    # Privacy or large scale → self-hosting
    if privacy_required or scale == 'large':
        if 'ko' in languages or 'ja' in languages:
            return {
                "model": "BAAI/bge-m3",
                "reason": "Best OSS multilingual + privacy guaranteed",
                "est_monthly_cost": "GPU server only (~$200-500/mo)"
            }
        if use_case == 'rag' and budget == 'high':
            return {
                "model": "intfloat/e5-mistral-7b-instruct",
                "reason": "Best OSS quality overall, especially English retrieval",
                "est_monthly_cost": "GPU server only (A100 ~$1000/mo)"
            }
        return {
            "model": "nomic-ai/nomic-embed-text-v1.5",
            "reason": "Lightweight, strong performance, easy to self-host",
            "est_monthly_cost": "GPU server only (~$100-200/mo)"
        }

    # 3+ languages → Cohere
    if len(languages) > 2:
        return {
            "model": "cohere/embed-multilingual-v3.0",
            "reason": "Uniformly high quality across 100+ languages",
            "est_monthly_cost": "Usage-based, $100-1000/mo typical"
        }

    # Cost-sensitive + English-primary
    if budget == 'low' and languages == ['en']:
        return {
            "model": "text-embedding-3-small",
            "reason": "Cheapest commercial option, sufficient for most English tasks",
            "est_monthly_cost": "~$20/mo at 1M requests"
        }

    # Default: best commercial quality, no ops
    return {
        "model": "text-embedding-3-large",
        "reason": "Best commercial quality, zero infrastructure burden",
        "est_monthly_cost": "~$130/mo at 1M requests"
    }

# Example usage
recommendation = choose_embedding_model(
    languages=['en'],
    budget='low',
    privacy_required=False,
    scale='small',
    use_case='rag'
)
print(f"Model: {recommendation['model']}")
print(f"Reason: {recommendation['reason']}")

Dimension Reduction: Cut Storage Costs Without Sacrificing Quality

OpenAI's text-embedding-3 models and several open-source models support Matryoshka Representation Learning (MRL). You can truncate to fewer dimensions while retaining most quality.

from openai import OpenAI
client = OpenAI()

# Quality vs cost tradeoff for text-embedding-3-small
# (MTEB Retrieval scores, approximate)
# 1536 dims: 53.2 (baseline)
# 1024 dims: 52.8 (-0.4)
#  512 dims: 52.1 (-1.1)
#  256 dims: 50.9 (-2.3)
# → 256 dims = <5% quality drop, 6x storage savings!

# Storage impact at 1M documents:
# 1536 dims: 1M × 1536 × 4 bytes = 6.14 GB
#  256 dims: 1M × 256 × 4 bytes = 1.02 GB
# Plus faster vector search proportional to dimension count

def get_reduced_embedding(text: str, dimensions: int = 256) -> list:
    response = client.embeddings.create(
        input=text,
        model="text-embedding-3-small",
        dimensions=dimensions
    )
    return response.data[0].embedding

# For most RAG applications, 256-512 dimensions is the sweet spot:
# - Storage cost 3-6x lower
# - Search speed 2-3x faster
# - Quality loss typically <3%

Production Embedding Pipeline

from sentence_transformers import SentenceTransformer
import numpy as np
import time

class EmbeddingPipeline:
    """Production-ready embedding pipeline"""

    def __init__(self, model_name: str = "BAAI/bge-m3"):
        self.model = SentenceTransformer(model_name)
        self.model_name = model_name
        self.is_bge = "bge" in model_name.lower()
        self.is_e5 = "e5" in model_name.lower()

    def embed_documents(self, texts: list, batch_size: int = 32) -> np.ndarray:
        """Batch embed documents for indexing"""
        all_embeddings = []

        for i in range(0, len(texts), batch_size):
            batch = texts[i:i + batch_size]

            # Model-specific prefixes matter for quality
            if self.is_bge:
                batch = [f"passage: {t}" for t in batch]

            embeddings = self.model.encode(
                batch,
                normalize_embeddings=True,
                show_progress_bar=False
            )
            all_embeddings.append(embeddings)

        return np.vstack(all_embeddings)

    def embed_query(self, query: str) -> np.ndarray:
        """Embed a single query for search"""
        if self.is_bge:
            query = f"query: {query}"
        elif self.is_e5:
            query = f"Instruct: Retrieve relevant passages\nQuery: {query}"

        return self.model.encode(query, normalize_embeddings=True)

    def search(self, query: str, doc_embeddings: np.ndarray,
               documents: list, top_k: int = 5) -> list:
        """Semantic search over pre-computed document embeddings"""
        query_emb = self.embed_query(query)
        scores = np.dot(doc_embeddings, query_emb)
        top_indices = np.argsort(scores)[::-1][:top_k]

        return [
            {"document": documents[i], "score": float(scores[i])}
            for i in top_indices
        ]

# Usage
pipeline = EmbeddingPipeline("BAAI/bge-m3")

docs = [
    "RAG combines retrieval with language model generation",
    "Embeddings map text to dense vector representations",
    "Vector databases support approximate nearest neighbor search",
    "Prompt engineering optimizes LLM outputs"
]

# Index documents
doc_embeddings = pipeline.embed_documents(docs)

# Search
results = pipeline.search(
    "how do vector databases work?",
    doc_embeddings,
    docs,
    top_k=2
)
for r in results:
    print(f"Score: {r['score']:.3f} | {r['document']}")

Quick Decision Summary

Starting immediately: text-embedding-3-small — easiest, sufficient for most cases, $0.02/1M tokens.

Korean or Japanese is important: BGE-M3 (self-hosted) or Cohere embed-multilingual-v3.0.

Cost is the top constraint: Self-hosted BGE-M3 or nomic-embed-text-v1.5 — dramatically cheaper at scale.

Maximum quality needed: E5-mistral-7b-instruct — best open-source quality, but requires 16GB+ GPU.

Data privacy is non-negotiable: No commercial APIs. Self-hosted only.

One critical note: switching embedding models requires re-indexing your entire document store. Evaluate thoroughly before committing — changing models mid-project is expensive.