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.

Tech Ocean
Tech Ocean
Tech Ocean
Turning Large PDFs into Vectors with LangChain: Split, Embed, and Retrieve

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,

FAISS
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonLLMLangChainembeddingRetrievalVectorStore
Tech Ocean
Written by

Tech Ocean

Focused on AI programming, sharing ready-to-use development efficiency solutions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.