Turning Large PDFs into Vectors with LangChain: Split, Embed, and Retrieve
Because LLMs have limited context windows, the article shows how to feed a 50‑page PDF to an AI by loading the document, splitting it into manageable chunks, converting each chunk into embeddings, storing them in a vector database, and then retrieving the most relevant passages for answering questions.
Why Retrieval Is Needed
LLMs such as GPT‑4o have a maximum context window (128k tokens), which is insufficient for whole technical documents like a 50‑page PDF.
Solution Overview
The workflow avoids sending the entire document to the LLM and instead follows four steps:
Load the document and convert it to Document objects.
Split each document into small chunks.
Embed each chunk into a vector.
Store the vectors in a vector store and retrieve the most relevant chunks at query time.
Document Loaders
LangChain provides loaders for many formats. Example for PDF:
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("path/to/document.pdf")
pages = loader.load()
for page in pages:
print(f"Page {page.metadata['page']} length: {len(page.page_content)} characters")
print(page.page_content[:200])Other loaders include UnstructuredMarkdownLoader for Markdown, WebBaseLoader for web pages, and built‑in loaders such as CSVLoader, TextLoader, and DirectoryLoader.
Text Splitting
Three splitter options are demonstrated:
CharacterTextSplitter : simple character‑based splitting (e.g., 500‑char chunks with 50‑char overlap).
RecursiveCharacterTextSplitter : hierarchical splitting that prefers paragraphs, then sentences, then words.
Token‑aware splitting : uses tiktoken to count tokens for more stable chunk sizes.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
separators=["
", "
", "。", "!", "?", " ", ""]
)
texts = splitter.split_documents(docs)
print(f"Created {len(texts)} text chunks")Embedding
Two embedding providers are covered:
OpenAIEmbeddings (e.g., text-embedding-3-small, 1536‑dimensional, $0.02 per 1M tokens).
OllamaEmbeddings (e.g., nomic-embed-text, 768‑dimensional, free local model).
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings(model="text-embedding-3-small")
vector = embedding.embed_query("LangChain makes LLM app development easy")
print(f"Vector dimension: {len(vector)}")Vector Store Selection
Common vector databases and their trade‑offs:
Chroma : lightweight, Python‑native, ideal for prototypes; not suited for large‑scale production.
FAISS : fast, single‑machine, good for up to billions of vectors; lacks native distributed support.
Milvus : distributed, cloud‑native, for massive production workloads; deployment is complex.
Qdrant : strong performance with hybrid search; relatively new.
Pinecone : fully managed SaaS, pay‑as‑you‑go; incurs latency and cost.
Example with Chroma:
# pip install langchain-chroma==1.1.0 chromadb
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=texts,
embedding=embedding,
persist_directory="./chroma_db"
)
results = vectorstore.similarity_search(query="What are the core concepts of LangChain?", k=3)
for i, doc in enumerate(results):
print(f"Result {i+1}: {doc.page_content[:200]}...")Example with FAISS (including score):
# pip install faiss-cpu
from langchain_community.vectorstores import FAISS
vectorstore = FAISS.from_documents(documents=texts, embedding=embedding)
results, scores = vectorstore.similarity_search_with_score("How to use LangChain?", k=3)
for doc, score in zip(results, scores):
print(f"Score {score:.4f}, content: {doc.page_content[:100]}...")Full End‑to‑End Pipeline
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
loader = PyPDFLoader("tech-docs.pdf")
pages = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
texts = splitter.split_documents(pages)
vectorstore = Chroma.from_texts(
texts=[t.page_content for t in texts],
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./vector_db"
)
results = vectorstore.similarity_search("What technologies does this book cover?", k=2)
print(f"Retrieved {len(results)} relevant chunks")
for r in results:
print(f"- {r.page_content[:100]}...")Day 3 Recap
Key components and their primary APIs:
Document Loader – PyPDFLoader, WebBaseLoader, etc.
Text Splitter – RecursiveCharacterTextSplitter Embedding – OpenAIEmbeddings, OllamaEmbeddings Vector Store – Chroma,
FAISSSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tech Ocean
Focused on AI programming, sharing ready-to-use development efficiency solutions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
