Module 7: Advanced Flows
Introduction: Real-World Complex Workflows
Overview
Your Research Agent v1 works. It takes a topic, breaks it into sub-queries, searches in parallel, synthesizes a report. On the happy path, it's flawless. But the happy path doesn't exist in production.
Run your agent against a real search API for a full day. Here's what you're going to find:
- At 2:47 PM, the API returns
429 Too Many Requests. Your agent crashes. - At 3:12 PM, one source takes 15 seconds to respond. Your agent sits there waiting.
- At 4:05 PM, an external service is down. Your agent returns an error with no partial results — even though the other two sources responded perfectly.
None of this is a bug in your code. It's the reality of systems that depend on external APIs, unstable networks, and third-party services. The agent you built in Module 6 does everything you asked of it. What it doesn't do is survive the real world.
This module closes that gap. You're going to add the patterns that turn a working prototype into a system a team can put in production without fear.
Where are we in the guide?
This is Module 7 of the guide LangChain & LangGraph: From Chains to Agents. It's the last module of Block 2 (LangGraph Fundamentals).
Block 1: LangChain Core (Modules 1-4) ✅ Completed
Block 2: LangGraph Fundamentals (Modules 5-7) ← YOU ARE HERE (Module 7)
Block 3: Advanced LangGraph (Modules 8-10)
Block 4: Production (Modules 11-12)
Your progress:
Block 1 — LangChain Core ✅ Completed
│
│ Module 1: Models and Providers ✅
│ Module 2: Tools and Tool Calling ✅
│ Module 3: Agents (create_agent) ✅
│ Module 4: Middleware and Customization ✅
│
▼
Block 2 — LangGraph Fundamentals
│
│ Module 5: Introduction to LangGraph ✅ Completed
│ Module 6: Functional API ✅ Completed
│ Module 7: Advanced Flows ← YOU ARE HERE
│
▼
Blocks 3-4 — Advanced + Production 🔒
In Module 5 you mastered the Graph API: StateGraph, nodes, edges, conditional edges, typed state. In Module 6 you learned the Functional API: @entrypoint, @task, native control flow, and you built the Research Agent v1. Now you add the patterns that make that agent fault-tolerant, fast, and modular.
The bridge: from "it works" to "it works in the real world"
What you already know how to do
After Modules 5 and 6, you can:
- ✅ Build graphs with StateGraph, typed nodes, conditional edges
- ✅ Create workflows with
@entrypointand@task - ✅ Use Python control flow (if/else, for, try/except) inside entrypoints
- ✅ Run tasks in parallel with Futures
- ✅ Combine the Graph API and the Functional API in the same project
- ✅ Build an end-to-end agent (Research Agent v1)
What's missing
You know how to build graphs and functional workflows. But production isn't clean — APIs fail, searches take too long, logic gets complicated, and errors propagate in ways you didn't anticipate. What's missing isn't LangGraph knowledge. It's systems engineering knowledge applied to LangGraph.
The real problems that motivate this module
Every pattern you'll learn here is born from a concrete problem. These aren't "nice-to-have features" — they're solutions to problems you'll hit the first week your agent is in production.
Problem 1: Your search API returns 429 Too Many Requests
You're making 50 searches per minute. The API has a rate limit of 30 per minute. Starting at search 31, you get 429 Too Many Requests. Without retry, your agent crashes and the user sees an error. With immediate retry, you fire 20 more requests in a second — you make the problem worse. What you need is retry with exponential backoff: wait 1 second, then 2, then 4, with random jitter so you don't sync up with other clients.
→ Solution: Cycles and retry patterns (Capsule 02)
Problem 2: Searching 3 sources sequentially takes 9 seconds
Your agent searches the web, academic papers, and news. Each source takes ~3 seconds. Sequentially: 3 + 3 + 3 = 9 seconds. Your user waits 9 seconds staring at an empty screen. But the three searches are independent — there's no reason to wait for one to finish before launching the next.
→ Solution: Branching and merge (Capsule 03) — you run all 3 in parallel: ~3 seconds total
Problem 3: The "search + summarize" pattern repeats for every source
For every source you do the same thing: call the API, parse the response, extract the key points, generate a partial summary. You repeat that 4-step sequence 3 times (once per source). If you copy and paste it, you end up with 12 nodes of duplicated logic. If the papers source needs a different parser, you have to find and modify the right nodes among those 12.
→ Solution: Subgraphs (Capsule 04) — you encapsulate "search + summarize" into a reusable subgraph
Problem 4: One source is completely down
The academic papers API has been returning 503 Service Unavailable for 3 hours. Your retries run out. Without a fallback, your agent returns a full error — even though the web and news sources responded perfectly with useful results. The user gets nothing when they could have gotten a partial report with 2 of 3 sources.
→ Solution: Error handling and graceful degradation (Capsules 05-06) — your agent returns partial results with a note explaining which source wasn't available
The patterns this module covers
| Pattern | Problem it solves | Impact |
|---|---|---|
| Cycles and retry | APIs that fail intermittently | The agent recovers automatically from transient errors |
| Branching and merge | Slow sequential searches | 3x faster execution with parallelism |
| Subgraphs | Duplicated logic across sources | Modular composition, one change affects every use |
| Map-reduce | Processing collections in parallel | Scales to N sources without changing the topology |
| Error handling | Failures that crash the whole system | Graceful degradation with partial results |
| Production patterns | Timeouts, rate limits, logging | An observable, controllable system |
Module map
| Capsule | Topic | What you'll learn |
|---|---|---|
| 01 | Introduction | Context, motivation, roadmap, connection to the project (this capsule) |
| 02 | Cycles and loops: retry patterns | Cycles in graphs, retry with exponential backoff + jitter, recursion_limit, StateGraph vs Functional API |
| 03 | Branching and merge: parallel execution | Fan-out/fan-in, Send API, parallel branches, merging results, deduplication |
| 04 | Subgraphs: modular composition | Graphs as nodes, shared state, encapsulating logic, reuse |
| 05 | Map-reduce: processing collections | Send API for dynamic collections, parallel map, reduce with aggregation |
| 06 | Error handling and fallback patterns | Try/except in graphs, fallback nodes, graceful degradation, circuit breaker |
| 07 | Production patterns | Per-node timeouts, internal rate limiting, structured logging, health checks |
| 08 | Project: Research Agent v2 | Integrating retry, parallel branching, and error handling into the Research Agent |
Learning flow: You start with the most immediate pattern — retry when an API fails (02). Then you speed things up with parallel execution (03). With parallelism under your belt, you encapsulate repeated logic in subgraphs (04) and scale to collections with map-reduce (05). Error handling (06) weaves into every previous pattern, it's not an isolated topic. Production patterns (07) add the final observability layer. In capsule 08, everything converges in the Research Agent v2.
The project evolves: Research Agent v1 → v2
This module doesn't rewrite the Research Agent. It improves it. Every change is a layer on top of the code you already wrote in Module 6.
Before (Module 6 — v1)
@entrypoint()
def research_agent(inputs: dict) -> dict:
query = inputs["query"]
# Break it down into sub-queries
sub_queries = decompose_query(query).result()
# Search sequentially — one source at a time
all_results = []
for sq in sub_queries:
result = search_source(sq).result() # Blocks until it completes
all_results.append(result)
# Synthesize the report
report = synthesize_report(all_results).result()
return report
Problems:
- ❌ Sequential search — if each source takes 3s, 3 sources = 9s
- ❌ No retry — if an API returns 429, the agent crashes
- ❌ No fallback — if one source is down, everything fails
- ❌ No timeouts — one slow source blocks the whole pipeline
- ❌ No logging — you don't know where it failed or how long each step took
After (Module 7 — v2)
@entrypoint()
def research_agent(inputs: dict) -> dict:
query = inputs["query"]
# Break it down into sub-queries
sub_queries = decompose_query(query).result()
# Search in parallel — every source simultaneously
search_futures = [search_with_retry(sq) for sq in sub_queries]
results = []
for future in search_futures:
try:
results.append(future.result())
except SourceUnavailableError:
results.append(partial_result(future.source, reason="unavailable"))
# Synthesize with partial results if needed
report = synthesize_report(results).result()
return report
Improvements:
- ✅ Parallel search — 3 sources in ~3s (3x faster)
- ✅ Retry with backoff — automatic retries on transient errors
- ✅ Graceful degradation — if one source fails, it returns partial results
- ✅ Per-source timeouts — no slow source blocks the entire pipeline
- ✅ Structured logging — every step records duration, errors, and results
The Module 6 code is still there. What changes is how you run the search (parallel instead of sequential), how you handle errors (retry + fallback instead of crash), and how you observe the system (logging instead of silence).
Connection to Block 3: what comes next
Module 7 closes Block 2 (LangGraph Fundamentals). After this module, your Research Agent is robust and fast. What it still doesn't have:
| Block 3 | Module | What it adds to the Research Agent |
|---|---|---|
| Advanced LangGraph | M8: Memory and Persistence | If the agent crashes mid-investigation, it resumes where it left off. It remembers previous investigations across sessions. |
| Advanced LangGraph | M9: Human-in-the-Loop | Before running expensive searches, it asks for human approval. The user can modify sub-queries before they run. |
| Advanced LangGraph | M10: Multi-Agent Systems | Instead of a single agent doing everything, a team: Researcher searches, Analyst analyzes, Writer drafts, Supervisor coordinates. |
Module 7 (you are here):
Research Agent v2 — robust, parallel, with error handling
│
▼
Module 8: + Persistence
The agent saves state. It can resume if it crashes.
│
▼
Module 9: + Human approval
The agent asks permission before expensive actions.
│
▼
Module 10: + Multi-agent
A team of specialized agents with a supervisor.
For M8's persistence to work, your agent needs to handle errors correctly (if it crashes with corrupt state, resuming is useless). For M9's human-in-the-loop to work, your agent needs to be designed with pauses and continuations (which subgraphs make easier). For M10's multi-agent to work, your agent needs to be modular (which subgraphs enable).
This module isn't just "adding robustness." It's building the technical foundation that makes the next three modules possible.
Technical setup
Prerequisites
Before continuing, check that you have:
- ✅ Modules 5 and 6 completed — you know how to build graphs with StateGraph and workflows with the Functional API
- ✅ A working Research Agent v1 — the Module 6 project runs end-to-end
- ✅ Python 3.11+ installed
- ✅ At least one API key from a provider (OpenAI recommended)
Installation
If you completed Modules 5-6, you already have everything installed. Verify:
pip install langgraph langchain-openai python-dotenv
There are no new packages in this module. The advanced patterns (retry, branching, subgraphs, map-reduce) are part of langgraph core.
New packages from the standard library
This module uses two packages from Python's standard library that you didn't use in previous modules:
| Package | What you use it for in this module |
|---|---|
time | time.sleep() to implement backoff between retries |
random | random.uniform() to add jitter to the backoff |
You don't need to install anything — they're part of Python. But you'll be using them constantly in the retry and branching capsules.
Verify everything works
import time
import random
from typing import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END
from langgraph.func import entrypoint, task
print(f"time: {time.__name__}")
print(f"random: {random.__name__}")
print(f"StateGraph: {StateGraph.__name__}")
print(f"entrypoint: {entrypoint.__name__}")
print(f"task: {task.__name__}")
print("Setup complete for Module 7")
# Expected output:
# time: time
# random: random
# StateGraph: StateGraph
# entrypoint: entrypoint
# task: task
# Setup complete for Module 7
Both APIs in play
In this module you'll use both the Graph API and the Functional API. Some patterns (cycles, branching with the Send API) express themselves naturally in StateGraph. Others (retry with a while loop) are better expressed with the Functional API. Part of the learning is developing judgment about when to use each one.
| Pattern | Recommended API | Why |
|---|---|---|
| Cycles/retry | Both | StateGraph if the retry is visible; Functional if it's internal |
| Branching (Send API) | StateGraph | The Send API is a Graph API feature |
| Subgraphs | StateGraph | Graph composition is a Graph API concept |
| Map-reduce | StateGraph | Send API for dynamic fan-out |
| Error handling | Both | Native try/except in Functional; fallback nodes in StateGraph |
| Timeouts, logging | Both | They're code patterns, not topology patterns |
What this module does NOT cover
- ❌ Advanced persistence and checkpointing — Covered in Module 8. Here you'll mention checkpoints as context, but you won't implement them in depth.
- ❌ Human-in-the-loop (interrupts, approvals) — Covered in Module 9. Here you design the agent to be "pausable," but you don't implement the interrupts.
- ❌ Multi-agent systems — Covered in Module 10. Here you build modular subgraphs that later become independent agents.
- ❌ LangSmith and full observability — Covered in Module 12. Here you add basic structured logging, but the full instrumentation comes later.
- ❌ Deployment — This module's production patterns are at the code level (timeouts, rate limiting, logging). Deployment to infrastructure is Module 12's topic.
Evidence of success
By the end of this module, you'll know you succeeded if:
- ✅ You can implement a retry cycle with exponential backoff and jitter — and explain why retry without backoff is an anti-pattern
- ✅ Your agent searches 3 sources in parallel and takes ~3s instead of ~9s
- ✅ If one source is down, your agent returns partial results with the sources that did respond
- ✅ You can encapsulate repeated logic in subgraphs and reuse it
- ✅ Your Research Agent v2 doesn't crash on transient errors — it retries, degrades gracefully, and logs what happened
- ✅ You know when to use StateGraph vs the Functional API for each pattern
This module's mindset: systems engineering
Modules 5 and 6 taught tools: StateGraph, Functional API, nodes, edges, tasks, entrypoints. This module teaches systems thinking.
The difference:
- Tools: "How do I do a retry in LangGraph?"
- Systems thinking: "My agent depends on 3 external APIs. What's my failure budget? What happens if two of three fail? How long can the user wait? What do I show them while they wait?"
Every pattern in this module answers an engineering question, not an API question. Retry isn't "a LangGraph feature" — it's a distributed-systems principle you implement using LangGraph. Parallel branching isn't "an elegant optimization" — it's the difference between 3 seconds and 9 seconds of latency that your users will notice.
Think like a systems engineer. The tools are the means, not the end.
The questions you're going to learn to answer
Before this module, your design process was: "What nodes do I need? How do I connect them?" After this module, your process will also include:
| Engineering question | Pattern that answers it |
|---|---|
| "What happens if API X doesn't respond?" | Retry with backoff → fallback |
| "What's the maximum the user can wait?" | Timeout budget per operation |
| "Can I do these operations simultaneously?" | Parallel branching (fan-out/fan-in) |
| "Does this logic repeat in several places?" | Reusable subgraph |
| "What do I show the user if I fail partially?" | Graceful degradation |
| "How do I know my agent is healthy in production?" | Structured logging + health checks |
These questions aren't specific to LangGraph. They're questions every systems engineer asks when designing a service. What changes is how you answer them — using graphs, nodes, and conditional edges instead of HTTP middleware or infrastructure-level circuit breakers.
The most common mistake: optimizing the happy path
Your Research Agent v1 has a perfect happy path: it decomposes, searches, synthesizes. The report comes out flawless. And the natural instinct is to polish that happy path — better prompts, better formatting, more sources.
Resist that instinct. A system that works perfectly 95% of the time but blows up in the remaining 5% isn't ready for production. That 5% is where users lose trust. It's where your system earns a reputation for being "unstable."
This module focuses on the 5%. On making your agent predictable, not just in the best case, but in the worst one too. The happy path already works. Now make the unhappy path acceptable.
Summary
- You're in Module 7 of 12, closing Block 2 (LangGraph Fundamentals). After Modules 5-6, you know how to build graphs and functional workflows. This module adds the production patterns
- Four real problems motivate this module: APIs returning 429 (→ retry), slow sequential searches (→ parallel branching), duplicated logic across sources (→ subgraphs), downed sources that crash everything (→ graceful degradation)
- The Research Agent evolves from v1 (sequential, no error handling, crashes on failures) to v2 (parallel, automatic retry, graceful degradation). The M6 code is still there — this module adds layers on top
- Six patterns: cycles/retry, branching/merge, subgraphs, map-reduce, error handling, production patterns. Each one is born from a concrete problem
- Connection to Block 3: this module's patterns are technical prerequisites for memory (M8), human-in-the-loop (M9), and multi-agent (M10)
- Both APIs in play: some patterns are better expressed in StateGraph (branching, subgraphs) and others in the Functional API (retry). Part of the learning is developing judgment about when to use each one
- The mindset: think like a systems engineer, not like an API user. The patterns are engineering principles implemented with LangGraph
Additional resources
- LangGraph — Concepts: Cycles — Official documentation on cycles and recursion_limit in LangGraph
- LangGraph — How to create branches for parallel node execution — Official guide to fan-out/fan-in with the Send API
- LangGraph — How to add and use subgraphs — Official guide to composing graphs inside graphs
- LangGraph — How to create map-reduce branches — Official guide to map-reduce with the Send API
- Exponential Backoff and Jitter (AWS Architecture Blog) — The reference article on backoff with jitter. Required reading
- Release It! — Michael Nygard — The book that popularized circuit breakers and stability patterns. Advanced context
Module 7 — LangChain & LangGraph: From Chains to Agents
Next capsule: Cycles and Loops: Retry Patterns — your search API fails 10% of the time. You'll implement retry with exponential backoff + jitter in StateGraph and the Functional API, and you'll understand why retry without backoff is an anti-pattern that amplifies problems instead of solving them.
Previous capsule: Evolving Project: Research Agent Base (v1) — the working end-to-end agent that you're now going to make robust and fast.