Vector embeddings are the foundation of modern semantic search and RAG systems. An embedding model takes a piece of text -- a sentence, a paragraph, a document chunk -- and compresses its meaning into a fixed-length array of floating-point numbers. Two texts with similar meanings produce vectors that are close together in this high-dimensional space, even if they share no words in common.
This is what makes embeddings transformative: they capture semantic similarity, not just lexical overlap. The query 'how to fix a broken pipe' and the document 'repairing plumbing leaks' have zero words in common, but their embeddings will be near each other because the meaning overlaps. Traditional keyword search would miss this match entirely.
Choosing the right embedding model, understanding similarity metrics, and knowing how dimensionality affects performance are the decisions that determine whether your retrieval pipeline returns relevant results or noise. This guide covers the mechanics, the models, and the practical code to embed text and measure similarity.
sentence-transformers is the go-to open-source library for text embeddings. The normalize_embeddings=True flag unit-normalizes each vector, which means dot product equals cosine similarity -- this avoids a separate normalization step during search. The similarity scores confirm that semantically related texts cluster together.
Cosine similarity is direction-only and ignores magnitude, making it robust for texts of different lengths. Dot product is faster but sensitive to vector magnitude -- normalize first. Euclidean distance is intuitive but inverted (lower = more similar). Most vector databases use cosine or dot product because they are faster to compute and work well with HNSW indexes.
Not all embedding models are equal. all-MiniLM-L6-v2 is the standard starting point -- fast and good enough for most use cases. nomic-embed-text-v1.5 offers better retrieval quality at the cost of speed and memory. Run this benchmark on your own hardware to make an informed trade-off between quality and throughput.
pgvector lets you use PostgreSQL as your vector database -- no separate service to manage. The HNSW index makes similarity search fast even at millions of vectors. The <=> operator computes cosine distance (1 - cosine_similarity), so we subtract from 1 to get similarity. This pattern is ideal when your application already uses PostgreSQL.
When embedding large corpora, batch processing prevents memory issues and gives you progress visibility. The npz format compresses the numpy array for efficient storage. normalize_embeddings=True ensures all vectors are unit-length, which makes dot product equivalent to cosine similarity during search.
Using different embedding models for indexing documents and querying. The vectors live in incompatible spaces, so similarity scores are meaningless.
Always use the exact same model and version for both document embedding and query embedding. Store the model name alongside your vector index so you can verify consistency.
Embedding entire documents as a single vector, which forces the model to compress pages of content into one fixed-size representation, losing most of the detail.
Chunk documents into 200-1000 token passages before embedding. Each chunk becomes its own vector, allowing retrieval to pinpoint the relevant section rather than returning a vague document-level match.
Assuming higher-dimensional embeddings are always better. Using 3072-dimensional vectors when 384 would suffice quadruples storage and slows down search with minimal quality gain.
Benchmark on your actual retrieval task. For most RAG applications, 384 to 768 dimensions provide the best quality-to-cost ratio. Only move to higher dimensions if your evaluation metrics show a meaningful improvement.
Not normalizing embeddings before using dot product similarity, which causes vectors with larger magnitudes to dominate regardless of semantic relevance.
Either normalize all vectors to unit length (L2 norm = 1) and use dot product, or use cosine similarity which normalizes implicitly. Most embedding libraries have a normalize flag -- use it.
Caching embeddings without tracking the model version, then switching to a new embedding model without re-embedding the corpus. Old and new embeddings are incompatible.
Treat model changes as a full re-indexing event. Store the model name and version as metadata on your vector index. When you upgrade the model, re-embed all documents before serving queries.
Vector embeddings transform text into dense numerical representations that capture semantic meaning. Similar texts produce similar vectors, enabling search by meaning rather than keyword matching. Choose an embedding model based on your quality, speed, and cost requirements -- sentence-transformers models run locally for free while API-based models offer higher quality at a price. Normalize your vectors, use cosine similarity or dot product for comparison, and always embed documents and queries with the same model. The embedding step is the first and most critical stage of any RAG pipeline.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.