Vector Search Basics
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This is the hands-on post, and the cover promise — vector search in 30 lines — sets a concrete, achievable expectation. The reassurance that the same shape applies whether you use FAISS, Pinecone, or pgvector tells the reader that the small example transfers directly to production tools.
The Code Example angle is about removing the activation energy to start. By keeping the example tiny and self-contained, the reader can copy it, run it, and feel the loop working, which builds the confidence to then point it at their own data.
The install slide is intentionally first and dead simple: three packages get you a working embedding model and a local vector index with no external services. sentence-transformers provides the model, faiss-cpu provides the index, and numpy handles the arrays.
Keeping setup to one line lowers the barrier to entry to nearly zero. A reader can paste this into a fresh environment and be ready to run every subsequent slide, which is exactly the friction-free on-ramp a code example should provide.
This slide embeds a tiny three-document corpus, deliberately chosen so the semantic query later has an obvious-but-non-keyword right answer. Loading all-MiniLM-L6-v2 and calling encode returns a matrix of vectors, one row per document.
Two details matter for correctness and carry through the rest of the post: normalize_embeddings=True so that inner product equals cosine similarity, and casting to float32 because FAISS requires it. Calling these out here prevents the subtle type and metric errors that trip up first-time users.
Building the index is just three lines. We read the dimensionality from the embeddings, create an IndexFlatIP — inner product, which on normalized vectors is exactly cosine similarity — and add the document vectors. Printing ntotal confirms the vectors landed.
IndexFlatIP is chosen deliberately over an ANN index here because the corpus is tiny and exact search is instant; it keeps the example honest and free of tuning parameters. The earlier post already covered HNSW for scale, so this slide can focus on the clean, minimal case.
The query slide closes the loop: embed the question with the same model and normalization, then call search for the top two matches. The loop prints each score next to its document so the reader sees the ranking directly rather than just raw arrays.
The query is phrased to have no keyword overlap with the winning document on purpose — 'which animal enjoys walks?' should surface the dogs sentence even though it does not contain the word 'walks' the same way. That payoff is what makes the semantic nature of the search visceral.
This trace shows the actual program output so the reader knows exactly what success looks like. The dogs document wins with a clearly higher score, the cats document trails, and the comment drives the lesson home: the match happened on meaning, not on a shared keyword.
Showing real output rather than describing it removes ambiguity. A reader running the code can compare their result line by line, and the visible score gap previews the next slide's discussion of how to interpret and threshold those numbers.
Reading scores correctly is where many beginners go wrong, so this slide gives concrete rules. With normalized vectors and inner product, higher means more similar. A healthy result has a top score that clearly beats the rest; flat or uniformly low scores signal that nothing in the index is a good match.
The practical advice to set a threshold and drop weak hits is the bridge to the mistakes post, where this becomes a guard against feeding irrelevant context to an LLM. Establishing the habit here, in the context of real output, makes it stick.
The pgvector slide shows that the exact same four-step pattern lives inside a relational database. With the vector extension and a vector(384) column, semantic search is just an ORDER BY on a distance operator, with LIMIT for top-k. The <=> operator computes cosine distance, and subtracting from 1 converts it to a similarity score.
Including the SQL equivalent matters because many teams already run Postgres and would rather add a column than stand up new infrastructure. Seeing FAISS and pgvector solve the identical problem reinforces that the concept is portable and the tool is an implementation detail.
This comparison frames the local-versus-managed decision the reader will face next. FAISS runs in-process with no server, which is perfect for prototypes and notebooks, but you are responsible for persistence and scaling. Managed options like Pinecone or pgvector handle scaling and bring metadata filtering and other production features out of the box.
Laying out the tradeoff prevents two common errors: prematurely adopting heavy infrastructure for a prototype, or trying to scale a bare FAISS index into production without the operational support it lacks. The right choice depends on where you are in the lifecycle.
These production-readiness tips bridge the gap between the toy script and a real deployment. Persisting the index with faiss.write_index means you do not re-embed on every restart. Storing document text and metadata alongside the ids is essential, because the index only returns ids — you need the mapping back to content.
Batch encoding improves throughput substantially over one-at-a-time calls, and metadata filtering is what lets you restrict results by date, tenant, or tags. Each tip addresses a concrete thing that breaks the first time a prototype meets real data and real users.
The summary slide names what the reader just built — a complete embed, index, query, rank pipeline — and explicitly tells them the path to production: swap the doc list for real chunks and FAISS for a managed DB. The four steps do not change; only the scale and tooling do.
This framing is empowering because it collapses the perceived distance between a 30-line demo and a production RAG retriever. The reader leaves understanding that they have already grasped the core, and the rest is engineering they now know how to reason about.
The closing card hands off to the mistakes post, which is the perfect follow-up to a working example: now that you can build it, here is how it silently breaks. That sequencing turns success into vigilance rather than overconfidence.
The save prompt is especially apt for a code post, since readers will want to return to copy the snippets when implementing their own search, making this one of the most save-worthy entries in the day.