LangGraph Tutorial (Python): handling long documents for beginners
This tutorial shows you how to build a LangGraph workflow that takes a long document, splits it into chunks, summarizes each chunk, and combines the results into one final answer. You need this when a single prompt is too large for your model context window or when you want more reliable processing over long PDFs, reports, policies, or contracts.
What You'll Need
- •Python 3.10+
- •
langgraph - •
langchain-core - •
langchain-openai - •An OpenAI API key set as
OPENAI_API_KEY - •A long text file to test with, or any large string you want to process
Install the packages:
pip install langgraph langchain-core langchain-openai
Step-by-Step
- •Start by loading your document and splitting it into manageable chunks. For beginners, fixed-size character chunks are good enough to understand the pattern before moving to token-based splitting.
from pathlib import Path
def load_document(path: str) -> str:
return Path(path).read_text(encoding="utf-8")
def chunk_text(text: str, chunk_size: int = 2000) -> list[str]:
return [text[i : i + chunk_size] for i in range(0, len(text), chunk_size)]
text = load_document("long_document.txt")
chunks = chunk_text(text)
print(f"Loaded {len(text)} characters")
print(f"Created {len(chunks)} chunks")
- •Define the graph state and the functions that will process each chunk. The state holds the original chunks, per-chunk summaries, and the final summary.
from typing import TypedDict
class DocumentState(TypedDict):
chunks: list[str]
summaries: list[str]
final_summary: str
def initialize_state(chunks: list[str]) -> DocumentState:
return {
"chunks": chunks,
"summaries": [],
"final_summary": "",
}
- •Create a summarizer node using a real LangChain chat model. This node will summarize one chunk at a time, which keeps each model call within context limits.
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])
def summarize_chunk(chunk: str) -> str:
prompt = (
"Summarize this document chunk in 5 bullet points. "
"Focus on facts, decisions, risks, and actions.\n\n"
f"{chunk}"
)
response = llm.invoke([HumanMessage(content=prompt)])
return response.content
- •Build the LangGraph workflow with two nodes: one for map-style chunk summarization and one for reducing those summaries into a final result. This is the core pattern for long-document handling.
from langgraph.graph import StateGraph, START, END
def map_summaries(state: DocumentState) -> dict:
summaries = [summarize_chunk(chunk) for chunk in state["chunks"]]
return {"summaries": summaries}
def reduce_summaries(state: DocumentState) -> dict:
combined = "\n\n".join(state["summaries"])
prompt = (
"Combine these chunk summaries into one concise document summary. "
"Preserve important details and remove duplicates.\n\n"
f"{combined}"
)
response = llm.invoke([HumanMessage(content=prompt)])
return {"final_summary": response.content}
graph = StateGraph(DocumentState)
graph.add_node("map_summaries", map_summaries)
graph.add_node("reduce_summaries", reduce_summaries)
graph.add_edge(START, "map_summaries")
graph.add_edge("map_summaries", "reduce_summaries")
graph.add_edge("reduce_summaries", END)
app = graph.compile()
- •Run the graph on your document and print the final output. If your source text is large enough, you now have a repeatable pipeline that works around context limits without manual prompt trimming.
if __name__ == "__main__":
sample_chunks = chunks[:5] if len(chunks) > 5 else chunks
result = app.invoke(initialize_state(sample_chunks))
print("\n=== FINAL SUMMARY ===\n")
print(result["final_summary"])
Testing It
Use a real long text file so you can see multiple chunks being created. A good test is a policy document, annual report, or product spec with at least several thousand characters.
Run the script and confirm that it prints both the number of chunks and a final summary. If the model call fails, check that OPENAI_API_KEY is set correctly and that langchain-openai is installed.
If the output feels too verbose or too short, adjust the summarization prompt in summarize_chunk. For better quality on very large documents, reduce chunk_size or switch from character splitting to token-aware splitting later.
Next Steps
- •Replace fixed-size chunking with
RecursiveCharacterTextSplitterfrom LangChain - •Add parallel chunk processing with LangGraph fan-out/fan-in patterns
- •Store intermediate summaries in a database or vector store for auditability
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit