Keyword search matches documents that contain the exact words in a query. Semantic search matches documents that carry the same meaning — even when the words differ entirely. A query for “delicious beans” can surface a review mentioning “fantastic instant refried beans” because their embeddings are close in vector space. This guide walks through building a complete semantic search pipeline: embedding a corpus, embedding a query, computing similarity scores, and returning the top results.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/openai/openai-cookbook/llms.txt
Use this file to discover all available pages before exploring further.
When to use semantic vs. keyword search
Semantic search excels when:- Queries and documents use different vocabulary (synonyms, paraphrases, cross-language)
- Users ask natural-language questions rather than typing keywords
- Documents are short enough that a single embedding captures the full meaning
- Exact term matching matters (product codes, identifiers, proper names)
- Query latency must be minimal and the corpus is small
- You need highly predictable, auditable retrieval behavior
Step 1: Embed your corpus
Start by computing an embedding for every document in your dataset. Store these vectors alongside your documents so you do not need to re-embed on every query.text-embedding-3-small accepts up to 8,191 tokens per input. Strip newlines before embedding — they can slightly degrade similarity quality.Step 2: Batch embedding for large datasets
Calling the API once per document is slow and consumes more rate-limit budget than batching. Pass a list of strings to embed multiple documents in a single request:Step 3: Compute cosine similarity
Cosine similarity measures the angle between two vectors. A value of 1.0 means the vectors point in the same direction; 0.0 means they are orthogonal (unrelated).Step 4: Search by query
Embed the query, then score every document and return the top-k results:Chunking strategies
A single embedding must fit within 8,191 tokens. For longer documents, split the text into chunks before embedding. The right chunking strategy depends on your content:Fixed-size chunks
Split by a fixed token count (e.g., 512 tokens) with a small overlap (e.g., 50 tokens) between adjacent chunks. Simple and predictable; works well for homogeneous text like articles.
Sentence or paragraph boundaries
Split at natural sentence or paragraph breaks. Preserves semantic coherence within each chunk and reduces mid-sentence cuts that confuse retrieval.
Recursive splitting
Try splitting at paragraphs, then sentences, then words until every chunk is under the token limit. Used by many vector-store libraries as a robust default.
Semantic chunking
Embed adjacent sentences and merge those with high similarity into one chunk. More expensive to compute but produces semantically tighter chunks.
Example: split by token count with overlap
Using a vector database
For corpora larger than a few thousand documents, computing cosine similarity against every stored vector becomes slow. Vector databases index embeddings for approximate nearest-neighbor (ANN) search, returning results in milliseconds regardless of corpus size. Popular options include:- Pinecone — fully managed, easy to get started
- Weaviate — open-source, supports hybrid search out of the box
- Qdrant — open-source, high-performance, self-hosted or cloud
- pgvector — PostgreSQL extension; keeps vectors in your existing database
Next steps
- RAG patterns — use retrieved chunks as context for a language model to generate grounded answers
- Embeddings overview — available models, dimensions, and token limits