Overview
Build a semantic search engine over a PDF with LangChain embeddings and vector stores. Use it to retrieve passages similar to a query, then plug the retriever into retrieval-augmented generation (RAG) or other LLM workflows. This tutorial covers:- Create
Documentobjects from a PDF. - Generate embeddings.
- Load and split a PDF.
- Index chunks in a vector store and query by similarity.
- Wrap the store as a retriever.
Concepts
This tutorial focuses on text retrieval and covers the following concepts:Setup
Install dependencies
This tutorial reads a PDF using thepypdf package:
Configure LangSmith
Many of the applications you build with LangChain will contain multiple steps with multiple invocations of LLM calls. As these applications get more and more complex, it becomes crucial to be able to inspect what exactly is going on inside your chain or agent. The best way to do this is with LangSmith. After you sign up at the link above, make sure to set your environment variables to start logging traces:Create documents
LangChain implements aDocument abstraction for a unit of text and associated metadata. It has three attributes:
page_content: a string representing the content.metadata: a dict containing arbitrary metadata.id: (optional) a string identifier for the document.
metadata can capture the source of the document, its relationship to other documents, and other information. An individual Document often represents a chunk of a larger document.
The following code creates sample documents:
Generate embeddings
Vector search stores numeric vectors associated with text. Embed a query as a vector of the same dimension, then use similarity metrics (such as cosine similarity) to find related text. LangChain supports embeddings from many providers. Select a model to specify how text should be converted into a numeric vector:- OpenAI
- Azure
- Google Gemini
- Google Vertex
- AWS
- HuggingFace
- Ollama
- Cohere
- MistralAI
- Nomic
- NVIDIA
- Voyage AI
- IBM watsonx
- Fake
- Isaacus
Select a vector store
LangChainVectorStore objects add text and Document objects to a store and query them with similarity metrics. They are often initialized with embedding models that translate text into numeric vectors.
LangChain includes integrations with many vector store technologies. Some are hosted and need credentials, some run in separate infrastructure (local or third-party), and others run in-memory for lightweight workloads. Select a vector store:
- In-memory
- Amazon OpenSearch
- AstraDB
- Chroma
- Milvus
- MongoDB
- PGVector
- PGVectorStore
- Pinecone
- Qdrant
Load and split a PDF
Load content from a PDF, then split it into smaller chunks before indexing. This example uses a sample Nike 10-K filing from 2023.RecursiveCharacterTextSplitter recursively splits on common separators (such as newlines) until each chunk is the target size. This is the recommended text splitter for generic text use cases.
Set add_start_index=True so each split keeps a start_index metadata field for its character offset in the original document.
Index documents
Index the chunks into the vector store:Query the vector store
After you have added the documents to theVectorStore, you can query it:
- Synchronously and asynchronously
- By string query and by vector
- With and without similarity scores
- By similarity and maximum marginal relevance (to balance similarity with diversity)
Document objects.
Search by string
Embeddings map text to dense vectors so similar meanings are geometrically close. This means you can Retrieve relevant passages by passing a natural-language question:Return scores
You can return similarity scores with the documents. Score meaning varies by provider. In this case, the score is a distance metric that varies inversely with similarity:Search by vector
Embed the query yourself, then search with the resulting vector:Use retrievers
LangChainVectorStore objects do not subclass Runnable. Retrievers are Runnables, so they support standard methods such as sync and async invoke and batch.
You can also build retrievers from vector stores, and retrievers can also wrap non-vector sources (such as external APIs).
In this case, create a simple retriever without subclassing Retriever by wrapping similarity_search:
as_retriever method that returns a VectorStoreRetriever. These retrievers expose search_type and search_kwargs to select and parameterize the underlying store methods. Replicate the example above with:
VectorStoreRetriever supports search types of "similarity" (default), "mmr" (maximum marginal relevance), and "similarity_score_threshold". Use the last option to filter documents by similarity score.
You can use retrievers in more complex apps such as retrieval-augmented generation (RAG), which combine a question with retrieved context in a prompt for an LLM. To learn more about building such an application, check out the RAG tutorial tutorial.
Next steps
You’ve now seen how to build a semantic search engine over a PDF document. For more information see: For more on RAG:Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

