Home / AI & GenAI / Vector Databases

AI & GenAIHow-toBeginner

Embeddings and vector search basics: what they are, how similarity works, and a small working example

Embeddings turn text into lists of numbers where similar meaning means nearby vectors. Learn cosine similarity, why approximate nearest-neighbour indexes exist, and build a tiny semantic search over your runbooks with Python before reaching for a vector database.

Deepak Bhardwaj4 min read

Applies toAny embedding model (Azure OpenAI, Bedrock Titan/Cohere, open-source sentence-transformers); NumPy; concepts apply to pgvector, Azure AI Search, Qdrant, Pinecone and others

Keyword search finds "disk full" only when the document says "disk full". Vector search can also find "no space left on device" and "volume at 100%", because it compares meaning. Embeddings are what make that possible.

What an embedding is

An embedding model turns a piece of text into a fixed-length list of numbers (a vector), for example 384, 768, 1,536 or 3,072 numbers depending on the model. The model is trained so that texts with similar meaning get vectors pointing in similar directions.

"disk is full"                -> [0.012, -0.341, 0.087, ...]
"no space left on device"     -> [0.015, -0.329, 0.091, ...]   (close)
"reset my password"           -> [-0.210, 0.044, -0.163, ...]  (far)

You cannot read meaning from individual numbers. Only distances between vectors matter.

Measuring similarity

Metric Idea Notes
Cosine similarity Angle between vectors (1 = same direction) Most common for text
Dot product Cosine times lengths Same as cosine when vectors are normalised
Euclidean (L2) Straight-line distance Lower = more similar

Many embedding models return normalised vectors, so cosine and dot product give the same ranking. Use the metric the model's documentation recommends.

A tiny semantic search in Python

pip install sentence-transformers numpy
from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer("all-MiniLM-L6-v2")      # small open model, 384 dimensions, runs on CPU

docs = [
    "Linux server root filesystem is 100% full; clean logs and grow the volume.",
    "User cannot sign in: account locked after too many attempts.",
    "Kubernetes pods evicted due to node disk pressure.",
    "SQL Server transaction log is full, error 9002.",
    "VPN tunnel down between on-premises and the cloud.",
]
doc_vecs = model.encode(docs, normalize_embeddings=True)

def search(query, k=3):
    q = model.encode([query], normalize_embeddings=True)[0]
    scores = doc_vecs @ q                         # cosine similarity (vectors are normalised)
    top = np.argsort(-scores)[:k]
    return [(round(float(scores[i]), 3), docs[i]) for i in top]

for score, doc in search("no space left on device"):
    print(score, doc)

The disk-full runbook and the disk-pressure note rank first, though neither contains the words "no space left on device".

With a hosted model (Azure OpenAI), only the embedding call changes:

resp = client.embeddings.create(model="text-embedding-3-small-prod", input=docs)   # deployment name
doc_vecs = np.array([d.embedding for d in resp.data])

Why vector databases and ANN indexes exist

Comparing a query with every vector (brute force) is fine for thousands of documents. With millions, it is too slow. Approximate nearest neighbour (ANN) indexes such as HNSW or IVF find almost the best matches much faster.

Term Meaning
Recall Share of the true top-k results the index returns (for example 0.95)
HNSW Graph-based index; fast and accurate, uses memory
IVF Clusters vectors and searches only nearby clusters
Quantization Compresses vectors to save memory, with some accuracy loss

You trade a little recall for a lot of speed, and tune settings to choose where you sit.

Rules that save beginners pain

Warning

Vectors from different embedding models (or different versions of one model) are not comparable. If you change the model, you must re-embed all documents. Store the model name and version with every vector.

  • Dimensions must match the index; a 1,536-dimension vector cannot go into a 768-dimension index.
  • Chunk long documents. Embedding a 40-page PDF as one vector blurs its meaning. Split into passages of a few hundred tokens.
  • Keep metadata (source, date, owner, permissions) next to each vector so you can filter results.
  • Vector search is not exact match. For IDs, error codes and product names, keyword search often wins. Hybrid search (keyword plus vector) is common in production.

Where embeddings are used

Use How
Semantic search Query vector vs document vectors
RAG (retrieval augmented generation) Retrieve relevant chunks, give them to an LLM to answer
Deduplication Very similar vectors = near-duplicate tickets or docs
Clustering / routing Group tickets by topic, route to the right team

Choosing a store

Scale / situation Start with
Experiments, under ~100k vectors In-memory NumPy, Chroma, or pgvector
Already on PostgreSQL pgvector
Azure search features (hybrid, semantic ranker) Azure AI Search
Large-scale, dedicated Qdrant, Milvus, Pinecone, Weaviate

Prevent it next time

  • Record embedding model, version and dimensions in your index metadata.
  • Test search quality with 20-50 real questions before and after any change.
  • Start simple; move to a vector database when brute force is too slow, not before.