Skip to main content
We’re going to build a semantic search engine step-by-step. The story begins with TechDocs, Inc., where users search through documentation 10,000 times a day. More than half of those searches fail because traditional keyword search can’t connect related phrases like “reset password” and “password recovery.” Our mission is to fix that by building a search system that understands meaning, not just words.
A screenshot of a presentation or tutorial page titled "Mission: Build TechDocs Semantic Search Engine" that explains a documentation search problem (high failure rate due to keyword mismatches) and outlines a mission to build a semantic search engine to improve results. The page shows Before/After examples and a note about using embeddings rather than AI generation.

Approach overview

We’ll build a production-grade semantic search pipeline by following these core steps:
  • Convert text (documents and queries) into vector embeddings using an embedding model (sentence-transformers / Hugging Face).
  • Store embeddings in a fast vector database (ChromaDB) for nearest-neighbor search.
  • For each query, find nearby document embeddings (semantic similarity) and retrieve the top-K chunks.
  • Rank and return the most relevant document chunks to the user.
This approach enables queries like “forgot my password” to match documents titled “Password recovery” or “Login help” even when keywords differ.

Environment setup

Install the packages used for embeddings, orchestration, and vector storage:
  • sentence-transformers — embedding models (e.g. all-MiniLM-L6-v2)
  • LangChain — orchestration utilities & text splitters
  • langchain-community & langchain-huggingface — community integrations for LangChain
  • ChromaDB — vector database
  • numpy, tempfile, and other utilities
A screenshot of an "Environment Setup" panel showing "Installing Vector Search Libraries" with a checklist of packages (sentence-transformers, langchain, langchain-community, langchain-huggingface, chromadb, numpy) and model names to auto-download. On the right is a code file list including README.md and several task_*.py files.
Example environment setup (bash):
After installing dependencies, run the provided verification script to confirm everything is working:
A successful verification prints messages confirming LangChain ↔ ChromaDB integration and basic vector similarity checks, for example:

Understanding embeddings

Embeddings are the backbone of semantic search. Rather than working with individual keywords, embeddings convert text into dense numerical vectors where semantically similar texts are close in vector space. That enables the search engine to connect queries and documents that use different words but share meaning.
A screenshot of a dark-themed slide or document titled "Understanding Embeddings — The Foundation of Semantic Search" with bullet points explaining embeddings and how models learn meaning. A file/sidebar with Python filenames is visible on the right and a colorful cursor points near the heading.

Quick embedding example (Task 1)

This concise example demonstrates loading a sentence-transformers model, encoding a query and several documents, computing cosine similarity, and printing results. Normalizing embeddings (normalize_embeddings=True) can improve cosine-similarity stability.
Example output (abridged):

Document chunking

Large documents should be split into smaller chunks for embedding for two reasons:
  • Embedding models have context limits; extremely long texts can be truncated or produce noisy embeddings.
  • Smaller, focused chunks preserve local context and improve retrieval accuracy.
However, naive splitting may cut sentences and lose meaning. Use overlapping chunks to preserve sentence continuity at boundaries. A common starting point is ~500 characters per chunk with ~100-character overlap; tune this for your documents and model.
A screenshot of a "Smart Document Chunking" guide that explains the overlap strategy and optimal settings (e.g., chunk size 500 chars, overlap 100 chars). A dark sidebar on the right lists Python files like task_1_understanding_embeddings.py.
Example using LangChain’s RecursiveCharacterTextSplitter:
• Preserves sentence boundaries
• Maintains context with overlap
• Optimizes chunks for embedding models
• Can improve retrieval accuracy significantly

Vector stores (ChromaDB)

Embeddings are vectors; we need a vector store to index and search them efficiently. ChromaDB is a production-ready vector database that supports fast similarity search and metadata filtering. LangChain integrates with ChromaDB to simplify storing and querying embeddings. How vector search works (high-level):
  1. Document → embed → store in DB
  2. Query → embed → find similar embeddings
  3. Return top-K results ranked by cosine similarity

Create a Chroma vector store and index documents (Task 3)

This example shows how to initialize HuggingFace embeddings via LangChain, create Document objects, and build a Chroma vector store in a persistent temporary directory.

Semantic search — Bringing it all together

Now implement the search pipeline: convert the user query to an embedding, query the ChromaDB vector store for the top-K similar chunks, optionally filter by a score threshold, and return the best results.
A dark-themed screenshot of a slide or docs page titled "Semantic Search - Bringing It All Together," explaining semantic vs. traditional search. It shows a pipeline of steps (embedding, vector search, retrieve chunks, rank & return) and a file/sidebar on the right.

Full search example (Task 4)

This example assumes you have a built vectorstore (as in Task 3). It shows how to run a similarity search, obtain scores, apply a threshold, and print filtered results.
Simulated example run summary:

Recap & next steps

In this lab we:
  • Set up an environment for embeddings and vector search.
  • Learned how embeddings capture semantic similarity beyond keywords.
  • Implemented smart, overlapping document chunking.
  • Built a ChromaDB-backed vector store and indexed document chunks.
  • Implemented a semantic search pipeline that converts queries to embeddings, performs similarity search, and returns ranked, filtered results.
Next experiments to improve relevance and production-readiness:
  • Try different embedding models (speed vs. accuracy tradeoffs).
  • Tune chunk sizes and overlap parameters based on document structure.
  • Persist vector stores to a stable location and design a scalable deployment.
  • Add metadata filtering (document type, last-updated) and combine with a ranker or reranker for hybrid retrieval.
Next steps: experiment with model variants, tune chunking/thresholds, and add metadata filters (e.g., document type, last-updated) to further improve relevance.

Tools & resources

Further reading: Happy building — with embeddings, you can transform keyword-limited search into meaning-aware discovery.

Watch Video

Practice Lab