Embeddings
Embeddings are dense vectors that capture semantic meaning. Use them for similarity search, clustering, RAG retrieval, recommendations. LangChain wraps every major provider behind a single interface.
Embed, similarity, vector store, hybrid
EXAMPLE
from langchain_openai import OpenAIEmbeddings
from langchain_anthropic import ChatAnthropic
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma, FAISS
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
import numpy as np
# 1) Embed strings
embeddings = OpenAIEmbeddings(model='text-embedding-3-small', dimensions=512)
vecs = embeddings.embed_documents([
'How do I reset my password?',
'Where can I see my invoices?',
'What is your refund policy?',
])
query = embeddings.embed_query('I forgot my login')
# 2) Cosine similarity
def cosine(a, b):
a, b = np.array(a), np.array(b)
return (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
for v, text in zip(vecs, ['reset password', 'invoices', 'refund']):
print(f'{cosine(query, v):.3f} {text}')
# Highest score should be 'reset password'
# 3) Local embedding model (no API call, free, slower per call)
local = HuggingFaceEmbeddings(
model_name='sentence-transformers/all-MiniLM-L6-v2',
model_kwargs={'device': 'cuda'}, # or 'cpu'
encode_kwargs={'normalize_embeddings': True},
)
# 4) Chunk + index a corpus
docs = [
Document(page_content='Long doc 1…', metadata={'src': 'faq.md'}),
Document(page_content='Long doc 2…', metadata={'src': 'tos.md'}),
]
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100)
chunks = splitter.split_documents(docs)
# 5) Vector store — Chroma (local), FAISS (in-mem), Pinecone (cloud)
vs = Chroma.from_documents(chunks, embeddings, persist_directory='./chroma')
vs.persist()
# Or FAISS for in-memory
faiss = FAISS.from_documents(chunks, embeddings)
faiss.save_local('./faiss')
# 6) Search
results = vs.similarity_search('how do I cancel?', k=4)
for r in results:
print(r.metadata['src'], r.page_content[:100])
results_with_scores = vs.similarity_search_with_relevance_scores('cancel order', k=4)
# 7) Maximum marginal relevance — diversify results
results = vs.max_marginal_relevance_search('product safety', k=5, fetch_k=20, lambda_mult=0.5)
# 8) Hybrid — vector + BM25 keyword
bm25 = BM25Retriever.from_documents(chunks)
bm25.k = 4
vec = vs.as_retriever(search_kwargs={'k': 4})
ensemble = EnsembleRetriever(
retrievers=[bm25, vec],
weights=[0.4, 0.6], # tune for your data
)
results = ensemble.invoke('refund policy')
# 9) Metadata filters at search time
results = vs.similarity_search(
'how to install',
k=4,
filter={'src': {'$in': ['install.md', 'getting-started.md']}},
)
# 10) Reranking — cross-encoder for higher quality
# pip install sentence-transformers
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.retrievers import ContextualCompressionRetriever
reranker = HuggingFaceCrossEncoder(model_name='BAAI/bge-reranker-base')
compressor = CrossEncoderReranker(model=reranker, top_n=3)
compressed = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=vec)
# 11) Costs to track
# OpenAI text-embedding-3-small: ~$0.02 per 1M tokens
# Local MiniLM-L6: free, but ~3x lower quality on hard queries
# Most apps: embed once at ingest, search many times — embedding cost is dominated by ingest
# 12) When to choose what
# Quick / small data → FAISS in-memory
# Persistent / single host → Chroma
# Distributed / scale → Pinecone / Qdrant / Weaviate / pgvector
# Postgres shop → pgvector (no new infra)
# 13) Best practices
# - Pick ONE embedding model and stick with it — switching means re-embedding everything
# - Normalise vectors (most modern models prefer it) — cosine becomes a dot product
# - Chunk size 500-1000 tokens with 10-20% overlap works for most prose
# - Add hybrid + reranker for production RAG — vector-only loses on exact-match queries
Why it matters
Hybrid retrieval (vector + BM25) + a cross-encoder reranker is the modern RAG gold standard. Vector finds semantic neighbours; BM25 catches exact-match keywords; reranker decides what wins.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
from langchain_openai import OpenAIEmbeddings emb = OpenAIEmbeddings(model='text-embedding-3-small') vecs = emb.embed_documents([d.page_content for d in chunks])Try it Yourself »
Discussion
Loading…