MODULE 4  ยท  RAG Architecture

Embedding Models: Which One to Pick and Why It Matters

'Just use OpenAI's embedding model.' That's like saying 'just use a Toyota' without knowing if you need a city car or a truck.

๐Ÿ“… Mar 2026
โฑ 10 min read
๐ŸŽฏ Episode 24 of 98
Embedding ModelsModel SelectionRAGComparison
In this episode

"Just use OpenAI's embedding model."

That's the advice you'll hear everywhere. And it's... fine. But it's like saying "just use a Toyota" without knowing if you need a city car or a truck. The embedding model you pick affects retrieval quality, latency, cost, and whether you can run things offline.

There are now dozens of embedding models โ€” some run locally on your laptop, some need GPU clusters, some are free, some charge per million tokens. The wrong choice costs you quality and money. The right choice can be free and better.

Let's open up the field and actually compare them.

What Makes an Embedding Model "Good"

An embedding model is evaluated on how well its vectors capture semantic relationships. The key metrics:

MTEB Benchmark (Massive Text Embedding Benchmark)

MTEB is the standard benchmark suite. It tests embedding models across multiple tasks:

Task What It Tests

RetrievalGiven a query, find relevant documents (most important for RAG)
Semantic Textual SimilarityScore how similar two sentences are
ClassificationUse embeddings for text classification
ClusteringGroup similar documents together
Pair ClassificationDetermine if two texts are paraphrases
RerankingReorder results by relevance

For RAG, the Retrieval score is what you care about most. A model that's great at clustering but mediocre at retrieval won't help your search pipeline.

Check the leaderboard: huggingface.co/spaces/mteb/leaderboard

โšก

The Big Players

OpenAI text-embedding-3-small

python

from openai import OpenAI

snippet
code
client = OpenAI()
response = client.embeddings.create(
model="text-embedding-3-small",
    input="Your text here"

)

snippet
code
vector = response.data[0].embedding  # 1536 dimensions

Spec Value

Dimensions1536
Max tokens8191
MTEB Retrieval~53
Cost$0.02 / 1M tokens
SpeedFast (API)

The workhorse. Good enough for most applications. The cost is negligible โ€” embedding your entire Wikipedia corpus costs under $10.

OpenAI text-embedding-3-large

python

snippet
code
response = client.embeddings.create(
model="text-embedding-3-large",
    input="Your text here",
    dimensions=3072  # Full dimensions (or reduce: 256, 512, 1024, 1536)

)

Spec Value

Dimensions3072 (configurable down to 256)
Max tokens8191
MTEB Retrieval~55
Cost$0.13 / 1M tokens

The upgrade. ~4% better retrieval than small, 6.5x more expensive. The dimension reduction feature is unique โ€” you can output 256-dim vectors (much less storage) with graceful quality degradation.

nomic-embed-text (Open Source, Local)

bash

Via Ollama (free, local)

ollama pull nomic-embed-text

curl http://localhost:11434/api/embeddings -d '{

"model": "nomic-embed-text",

"prompt": "search_document: Your text here"

}'

Spec Value

Dimensions768
Max tokens8192
MTEB Retrieval~53
CostFree (local)
Speed~50ms on M1 Mac

My recommendation for getting started. Free, runs locally, competitive with OpenAI small, no API dependency. Uses task prefixes โ€” prepend search_document: for documents and search_query: for queries.

BAAI/bge-large-en-v1.5

python

from sentence_transformers import SentenceTransformer

snippet
code
model = SentenceTransformer('BAAI/bge-large-en-v1.5')
embeddings = model.encode(
["Passage: Your text here"],  # Add "Passage: " prefix for documents
    normalize_embeddings=True

)

Spec Value

Dimensions1024
Max tokens512
MTEB Retrieval~54
CostFree (local)
Parameters335M

Strong open-source option. Part of the BGE (Beijing Academy of AI) series. The 512 token limit is restrictive โ€” your chunks must be small. Use the bge-m3 variant for longer contexts.

BAAI/bge-m3 (Multilingual Beast)

python

from FlagEmbedding import BGEM3FlagModel

snippet
code
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
embeddings = model.encode(
["Your text in any language"],
    max_length=8192,

)

Returns both dense AND sparse vectors!

snippet
code
dense = embeddings['dense_vecs']
sparse = embeddings['lexical_weights']

Spec Value

Dimensions1024
Max tokens8192
Languages100+
MTEB Retrieval~55
CostFree (local, needs GPU)

The multilingual champion. Supports 100+ languages, long context (8192 tokens), and outputs BOTH dense and sparse vectors โ€” perfect for hybrid search in a single model.

Cohere embed-v3

python

import cohere

snippet
code
co = cohere.Client("YOUR_KEY")
response = co.embed(
texts=["Your text here"],
    model="embed-english-v3.0",
    input_type="search_document",  # or "search_query"

)

Spec Value

Dimensions1024
Max tokens512
MTEB Retrieval~55
Cost$0.10 / 1M tokens

API option with input types. Cohere differentiates between document and query embeddings via input_type, which can improve retrieval quality.

๐Ÿ”ง

The Comparison Table

ModelDimsMax TokensRetrieval (MTEB)CostLocal?Best For
text-embedding-3-small15368191~53$0.02/1MโŒDefault choice, low cost
text-embedding-3-large30728191~55$0.13/1MโŒMax quality (OpenAI)
nomic-embed-text7688192~53Freeโœ…Privacy, local, free
bge-large-en-v1.51024512~54Freeโœ…Short chunks, quality
bge-m310248192~55Freeโœ… GPUMultilingual, hybrid
Cohere embed-v31024512~55$0.10/1MโŒCohere ecosystem
Jina embeddings-v310248192~56$0.02/1Mโœ…Long docs, multilingual

๐Ÿ“Š

Dimensions: Does Size Matter?

More dimensions = more storage + more compute. But do they improve quality?

Dimensions Storage per 1M vectors Retrieval Quality

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

256 ~1 GB ~90% of max

512 ~2 GB ~95% of max

768 ~3 GB ~97% of max

1024 ~4 GB ~99% of max

1536 ~6 GB ~99.5% of max

3072 ~12 GB 100% (baseline)

The sweet spot is 768-1024 dimensions. You get 97-99% of the quality at half the storage of 1536-dim models. The last 1% of quality rarely justifies doubling your storage and compute costs.

OpenAI's text-embedding-3-large lets you choose dimensions at query time โ€” try 512 or 1024 before defaulting to the full 3072.

Task Prefixes: The Hidden Feature

Several models use task prefixes to improve quality. The model behaves differently for queries vs documents:

Model Document Prefix Query Prefix

nomic-embed-textsearch_document:search_query:
bge-largePassage:Represent this sentence:
Cohereinput_type="search_document"input_type="search_query"
E5 modelspassage:query:

This actually matters. Tests show 2-5% retrieval improvement when using correct prefixes vs no prefixes.

python

WRONG โ€” same embedding for query and document

snippet
code
embed("How to deploy ML models")

RIGHT โ€” different treatment

snippet
code
embed("search_query: How to deploy ML models")        # For the query
embed("search_document: This guide covers ML deployment...")  # For the doc
๐Ÿ’ก

How to Choose: Decision Tree

Are you prototyping?

โ”œโ”€โ”€ Yes โ†’ nomic-embed-text (free, local, good enough)

โ””โ”€โ”€ No โ€” production use

example
code
โ”‚
    โ”œโ”€โ”€ Need to run locally / air-gapped?
    โ”‚   โ”œโ”€โ”€ English only โ†’ nomic-embed-text or bge-large
    โ”‚   โ”œโ”€โ”€ Multilingual โ†’ bge-m3 (needs GPU)
    โ”‚   โ””โ”€โ”€ Long documents โ†’ nomic-embed-text (8192 tokens)
    โ”‚
    โ”œโ”€โ”€ Budget is not a concern?
    โ”‚   โ”œโ”€โ”€ English โ†’ text-embedding-3-large (3072d)
    โ”‚   โ””โ”€โ”€ Multilingual โ†’ Jina embeddings-v3 or bge-m3
    โ”‚
    โ””โ”€โ”€ Want lowest cost?
        โ””โ”€โ”€ text-embedding-3-small ($0.02/1M tokens)

My recommendation ladder:

Start: nomic-embed-text (free, local, 768d)

Scale: text-embedding-3-small (cheap, reliable API)

Optimize: bge-m3 (if you need multilingual or hybrid search)

Max quality: text-embedding-3-large at 1024d (good balance)

๐Ÿ”ฌ

Migration Warning: You Can't Switch Models Easily

Once you embed your documents with Model A, you can't query them with Model B. The vector spaces are incompatible.

Model A's vector space Model B's vector space

example
code
โ€ข "kubernetes"                  โ€ข "kubernetes"
    โ€ข "docker"                              โ€ข "docker"

โ€ข "containers" โ€ข "containers"

example
code
(their arrangement)              (completely different!)

Switching models means re-embedding your entire corpus. For 10 million documents at $0.02/1M tokens, that's ~$20 and a few hours. Not expensive, but plan for it.

Best practice: Record which model and version you used in your vector DB metadata. When you upgrade models, you'll know what needs re-embedding.

๐Ÿ›ก๏ธ

Practical Takeaways

nomic-embed-text for local, text-embedding-3-small for API โ€” these cover 90% of use cases

768-1024 dimensions is the sweet spot โ€” don't pay for 3072 unless you've proven you need it

Use task prefixes โ€” 2-5% free quality improvement

Switching models = re-embedding everything โ€” choose carefully, document your choice

MTEB Retrieval score matters most for RAG โ€” ignore other task scores

Test with YOUR data โ€” benchmark scores are averages; your domain might prefer a different model

๐Ÿ“ฆ

What's Next?

Episode 25: Retrieval Strategies โ€” Naive RAG (embed โ†’ search โ†’ generate) is just the beginning. Multi-query retrieval, HyDE, parent-child chunks, contextual retrieval โ€” advanced techniques that push RAG quality from "good" to "production-ready."

โ† Previous

Ep 23: Document Parsing

Next โ†’

Ep 25: Retrieval Strategies

Next: Episode 25 โ€” Retrieval Strategies

The basic RAG pipeline works surprisingly well. Until it doesn't. Here's how to go beyond naive retrieval.

This is part of a 98-episode series covering AI engineering from tokens to production deployment.

โ† Previous Ep 23: Document Parsing: Getting Clean Text From Messy Formats