Skip to main content

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:
  1. Create Document objects from a PDF.
  2. Generate embeddings.
  3. Load and split a PDF.
  4. Index chunks in a vector store and query by similarity.
  5. Wrap the store as a retriever.
The guide also includes a minimal RAG implementation on top of the search engine.

Concepts

This tutorial focuses on text retrieval and covers the following concepts:

Setup

Install dependencies

This tutorial reads a PDF using the pypdf package:
For more details, see the Installation guide.

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:
In a notebook, you can set them with:

Create documents

LangChain implements a Document 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:
Next, store embeddings in a vector store that supports efficient similarity search.

Select a vector store

LangChain VectorStore 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:

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.
A page is often too coarse for retrieval. Split pages further so relevant passages are not diluted by surrounding text. 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:
Most vector store integrations also support connecting to an existing store (for example with a client or index name). See the docs for a specific integration for details.

Query the vector store

After you have added the documents to the VectorStore, 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)
These methods generally return a list of 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:
Async query:

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:
Learn more:

Use retrievers

LangChain VectorStore 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:
Vector stores implement an 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: