Subgraphs
Subgraphs compose graphs from smaller graphs with isolated state. Before you use one, understand the checkpoint multiplication cost, failure propagation behavior, and state boundary rules — because a flat graph is almost always simpler.
Quick Reference
- →Subgraph: a compiled graph added as a node via add_node() — runs START→END as a single parent step
- →Shared keys: same name + same type = automatic pass-through between parent and child schemas
- →Different schemas need a wrapper function — explicit translation prevents silent key collisions
- →compile(checkpointer=None): per-invocation mode — inherits parent checkpointer, starts fresh each call
- →compile(checkpointer=True): per-thread mode — subgraph accumulates state across calls on the same thread
- →compile(checkpointer=False): stateless mode — no subgraph checkpoints, reduces storage cost
- →get_state(config, subgraphs=True) drills into subgraph internals — without it, the subgraph is a black box
- →GraphRecursionError from a subgraph propagates to the parent — catch it in a wrapper or the parent graph dies
Should You Use a Subgraph?
Start flat — extract a subgraph only when schema isolation or cross-parent reuse forces it
| Alternative | When it beats a subgraph | Why it's simpler |
|---|---|---|
| Flat nodes in the parent | Same state schema, no reuse needed | Zero boundary overhead, one checkpoint namespace, trivial to debug |
| add_sequence([step_1, step_2, step_3]) | Linear steps with no branching | No state mapping, no checkpoint multiplication, less boilerplate |
| Wrapper function (no subgraph) | Schema translation needed but not encapsulation | Full control over what crosses the boundary without checkpoint overhead |
Subgraphs solve three specific problems: schema isolation (child needs different state keys than parent), encapsulation (parent should not see child internals), and cross-parent reuse (same compiled subgraph used by multiple parent graphs). If none of those apply, keep nodes flat.
Premature decomposition — extracting a subgraph for 'cleanliness' when flat nodes would be simpler, cheaper, and easier to debug. Every subgraph boundary adds: state serialization overhead, checkpoint multiplication, a new failure propagation surface, and another layer of get_state() to navigate when something breaks.