Module 1: Why Vector Databases for AI Engineers
When you DON'T need a vector database
Capsule overview
Not every project needs a vector database. Sometimes numpy is enough. Sometimes SQL with pgvector is better. Sometimes you don't need embeddings at all.
This capsule saves you from over-engineering. Seeing developers spend 2 weeks learning Pinecone when they had 5K vectors (numpy would take 8ms) is common. This capsule prevents that mistake.
You'll learn clear signs that you do NOT need a vector DB and which alternatives to use instead.
Sign #1: You have <10K vectors
The inflection point
numpy performance at different scales:
1K vectors: 2-3ms ✅ Excellent (better than vector DB overhead)
5K vectors: 8-10ms ✅ Excellent
10K vectors: 15-20ms ✅ Acceptable
50K vectors: 75-100ms ⚠️ Gray zone
100K vectors: 150-200ms ❌ Slow
Decision rule:
def should_use_vector_db(n_vectors):
if n_vectors < 10_000:
return False, "numpy is simpler and fast enough"
elif n_vectors < 100_000:
return "maybe", "evaluate latency requirements"
else:
return True, "vector DB needed for performance"
Case: MVP prototype with 2K documents
Setup:
- Startup building an internal documentation chatbot
- 2,000 docs (6,000 chunks)
- 10 employees using it
- Prototype to validate the concept
With numpy:
import numpy as np
from openai import OpenAI
# Load embeddings (6K vectors)
embeddings = np.load('embeddings.npy') # Shape: (6000, 1536)
# Query
client = OpenAI()
query_emb = client.embeddings.create(
input="how to deploy?",
model="text-embedding-3-small"
).data[0].embedding
# Search (brute force)
similarities = np.dot(embeddings, query_emb)
top_5 = np.argsort(similarities)[-5:][::-1]
# Latency: ~10ms ✅ Excellent
Why is numpy enough?
- Scale: 6K vectors → 10ms latency (excellent)
- Simplicity: pip install numpy (vs learning the ChromaDB API)
- Cost: $0 (vs Pinecone $70/mo)
- Prototype: If the concept doesn't work, you didn't waste time on vector DB setup
Decision: ✅ numpy is the better option (don't over-engineer)
Sign #2: There's no latency requirement
Batch processing vs real-time
If your use case is:
- Offline analysis (no users waiting)
- ETL pipelines (process overnight)
- Research (unhurried exploration)
- Clustering (once, not repeated)
→ Latency doesn't matter. numpy is enough.
Case: Clustering 500K scientific papers
Setup:
- Research: Group 500K papers into thematic clusters
- No users (offline analysis)
- Can take hours (no rush)
With numpy (brute force):
import numpy as np
from sklearn.cluster import KMeans
# Load embeddings (500K vectors)
embeddings = np.load('papers_embeddings.npy') # Shape: (500000, 1536)
# Clustering (will take ~30-60 minutes, but it's done only once)
kmeans = KMeans(n_clusters=100, random_state=42)
clusters = kmeans.fit_predict(embeddings)
# Analyze clusters
for cluster_id in range(100):
papers_in_cluster = np.where(clusters == cluster_id)[0]
print(f"Cluster {cluster_id}: {len(papers_in_cluster)} papers")
Why is numpy enough?
- No users waiting (offline)
- Latency doesn't matter (it can take 1 hour)
- It runs once (you don't need to optimize for repetition)
Decision: ✅ numpy is the better option (a vector DB would be unnecessary overhead)
Sign #3: Local development/experimentation
A single developer iterating
If you're:
- Prototyping different chunking strategies
- Experimenting with embedding models
- Iterating on prompt engineering
- Developing on a local laptop
→ numpy is faster to iterate with
Case: A developer experimenting with RAG
Setup:
- 1 developer testing different strategies
- Dataset: 8K documents (24K chunks)
- Fast iteration (change, re-run, evaluate)
With numpy:
# Experimentation cycle:
# 1. Generate embeddings → save .npy (5 minutes)
# 2. Test retrieval → numpy search (20ms)
# 3. Evaluate results → adjust strategy
# 4. Repeat
# Total iteration time: 10-15 minutes
# With a vector DB:
# 1. Generate embeddings
# 2. Setup ChromaDB/Pinecone (first time: 30 min)
# 3. Insert embeddings (10 minutes)
# 4. Test retrieval
# 5. Evaluate
# 6. Change strategy → re-insert embeddings (10 min)
# 7. Repeat
# Total iteration time: 25-30 minutes (2x slower)
Why is numpy better for experimentation?
- No setup overhead (just pip install)
- Fast changes (reload .npy < 1 second)
- You don't need to learn a new API (focus on experimentation)
Decision: ✅ numpy for development, migrate to a vector DB only when the strategy is validated and you're going to production
Sign #4: You don't need embeddings at all
Keyword search can be enough
Cases where keyword search >> semantic search:
-
Exact search of technical terms:
- "ERROR-404" → keyword search is perfect
- Semantic search: "error not found" (not exact)
-
Proper nouns (people, places, products):
- "John Smith" → keyword searches exactly that
- Embeddings: "John" and "Smith" are poorly semanticized
-
Codes / IDs:
- "ORDER-12345" → keyword search finds a unique match
- Embeddings: vectorizes "order twelve thousand..." (useless)
-
SQL queries / code:
- "SELECT * FROM users WHERE age > 30" → keyword searches the exact syntax
- Semantic search: understands the concept but loses the syntax
Case: Searching a support knowledge base
Setup:
- Troubleshooting articles with error codes
- Users search for "ERROR-404", "CONNECTION-TIMEOUT", etc.
- They need an exact match, not a semantic one
With keyword search (Elasticsearch):
POST /articles/_search
{
"query": {
"match": {
"content": {
"query": "ERROR-404",
"operator": "and"
}
}
}
}
Latency: 10-50ms (excellent)
Accuracy: 100% (exact match)
With semantic search (embeddings + vector DB):
# Query: "ERROR-404"
# Embedding: [0.12, -0.34, 0.56, ...]
# Results: Docs about "errors", "404", but also "403", "500"
# Accuracy: 60-70% (not exact)
Decision: ✅ Keyword search is better (you do NOT need embeddings or a vector DB)
When to combine: Hybrid search (keyword + semantic) in Module 3
Sign #5: Static dataset (doesn't grow)
Read-only datasets
If your dataset:
- Doesn't change (historical, archive)
- Is generated once (no updates)
- Doesn't get new docs added
→ precomputed numpy can be optimal
Case: Wikipedia archive (2023 snapshot)
Setup:
- Wikipedia dump from 2023 (6M articles)
- Static dataset (doesn't change)
- Read-only (search, not writing)
Optimal strategy:
# 1. Generate embeddings (once, offline)
# Takes: 10-20 hours (6M docs × 3 chunks = 18M vectors)
# 2. Save in an optimized format
np.save('wikipedia_embeddings.npy', embeddings) # 110GB
# 3. For queries:
# Option A: Load EVERYTHING into RAM (if you have 128GB+ RAM)
embeddings = np.load('wikipedia_embeddings.npy') # Takes 5-10 min, but only once
# Then: queries at 50-100ms (ultra-optimized brute force in RAM)
# Option B: FAISS with PQ (compression)
import faiss
index = faiss.read_index('wikipedia_faiss.index')
# Queries: 200-300ms, memory: 30GB (vs 110GB)
Vector DB or numpy?
- Vector DB: Unnecessary overhead (the dataset doesn't grow, you don't need writes)
- numpy/FAISS: Optimal (one-time setup, fast queries)
Decision: ✅ numpy/FAISS (specialized), NOT a vector DB (over-engineering)
Sign #6: Zero budget (student/hobby)
Learning at no cost
If:
- Student learning RAG
- Hobby project (no revenue)
- You can't spend $70/mo (Pinecone)
- Local laptop (no server)
→ numpy or ChromaDB local (free)
Case: A student building a RAG portfolio
Setup:
- Student building a chatbot over their class notes
- 3,000 notes (9,000 chunks)
- Only they use it (1 user)
- Budget: $0
Options:
- numpy: Free, 15ms retrieval, enough for 9K vectors ✅
- ChromaDB local: Free, 5ms retrieval, more features ✅
- Pinecone: $70/mo, 3ms retrieval, unnecessary ❌
Decision: ✅ numpy or ChromaDB local (zero cost, enough for a portfolio)
When to migrate to Pinecone: If the project becomes a startup with funding (then $70/mo is reasonable)
Decision tree: Do you need a vector DB?
Flowchart
How many vectors do you have?
├─ <10K → numpy enough ✅
├─ 10K-100K → Do you need <100ms latency?
│ ├─ NO → numpy enough ✅
│ └─ YES → Vector DB recommended ⚠️
└─ >100K → Vector DB needed ✅
Do you have a latency requirement?
├─ NO (batch/offline) → numpy enough ✅
└─ YES (<500ms) → How many vectors?
├─ <10K → numpy enough ✅
└─ >10K → Vector DB needed ✅
Multiple concurrent users?
├─ NO (1 user) → numpy enough ✅
└─ YES (>10 users) → How many vectors?
├─ <10K → numpy with locks ⚠️
└─ >10K → Vector DB needed ✅
Does the dataset grow dynamically?
├─ NO (static) → numpy/FAISS enough ✅
└─ YES (docs added) → How often?
├─ Rarely (1x/month) → reloadable numpy ✅
└─ Frequently (daily) → Vector DB needed ✅
Budget available?
├─ $0 → numpy or ChromaDB local ✅
├─ <$100/mo → ChromaDB self-hosted ✅
└─ >$100/mo → Pinecone managed ✅
Summary
When you DON'T need a vector DB:
- ✅ <10K vectors → numpy enough (15ms latency)
- ✅ No latency requirement → numpy batch processing
- ✅ 1 user (development) → numpy for fast iteration
- ✅ Keyword search is enough → Elasticsearch, not embeddings
- ✅ Static dataset → precomputed numpy/FAISS
- ✅ $0 budget → numpy or ChromaDB local
Alternatives to a vector DB:
- numpy: <10K vectors, development, prototype
- SQL + pgvector: You already use Postgres, <100K vectors
- Elasticsearch: Hybrid search (keyword + semantic)
- FAISS: Static dataset, offline processing
Why it matters:
- Don't over-engineer: If numpy meets the requirements, it saves time and complexity
- Don't under-engineer: If you need a vector DB, don't waste time with inadequate numpy
- Start simple (numpy) → Scale when necessary (vector DB)
Next capsule: Full trade-offs: simplicity vs performance vs cost (quantitative comparison).
Additional resources
- When NOT to Use Vector Databases - Anti-patterns
- NumPy for Semantic Search - Simple approach
- FAISS for Static Datasets - Offline optimization
- Keyword vs Semantic Search - When to use each one
- pgvector Guide - SQL alternative
- Cost Optimization for AI - Budget considerations
Reading time: 6-8 minutes
Next: 07-trade-offs-simplicity-vs-performance.md