iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…