LangChain LangGraph Agent Loop (Modern)
LangChain LangGraph Agent Loop (Modern)
Defined in
langchain_v1/langchain/agents/factory.py. Entry point:create_agent().
The modern LangChain agent loop is an explicit LangGraph StateGraph compiled into a CompiledStateGraph. The loop is a DAG of nodes wired by conditional edges — not a while-loop.
Minimal graph
START → model ──(tool_calls?)──► tools
│ │
└◄───────────────────┘ (loop until exit condition)
└──(no tool calls)──────────────────────────────► END
Full graph (with middleware)
START
→ [before_agent] (once; zero or more middleware)
→ [before_model] (per iteration; zero or more middleware)
→ model
→ [after_model] (per iteration; zero or more middleware)
→ tools (parallel fan-out via Send)
→ [loop back]
→ [after_agent] (once; zero or more middleware)
→ END
Exit conditions
From model node (no tools path):
AIMessage.tool_calls == []→ END
From tools node (_make_tools_to_model_edge, factory.py L1771):
- All client-side tools have
return_direct=True→ END - A structured output tool executed → END
jump_to="end"in state → END
From any middleware node:
jump_toin state allows jumping to"model","tools", or"end"
Parallel tool dispatch
When the model calls multiple tools, _make_model_to_tools_edge returns a list of Send objects — one per pending tool call — enabling parallel fan-out across the tools node.
Middleware lifecycle
Each AgentMiddleware gets up to four hook nodes:
| Hook | Frequency | Use cases |
|---|---|---|
before_agent |
Once (pre-loop) | Auth, context injection |
before_model |
Per iteration | Token budgeting, prompt steering |
after_model |
Per iteration | Logging, structured output retry |
after_agent |
Once (post-loop) | Cleanup, result transformation |
Hooks can short-circuit by writing jump_to into state.
Structured output
When response_format is set, create_agent adds OutputToolBinding entries. The model is expected to call a special output tool; if it doesn't, _make_model_to_model_edge retries (loops model → model).
HITL (Human-in-the-Loop)
graph.compile(interrupt_before=..., interrupt_after=...) pauses execution at named nodes, allowing external state inspection and injection before resuming.
Key state fields
| Field | Purpose |
|---|---|
messages |
Full message history (AIMessage, ToolMessage, SystemMessage) |
jump_to |
Middleware directive: "model" | "tools" | "end" |
structured_response |
Set when structured output tool completes; triggers exit |
Comparison
See Source - LangChain Agent Loop for full legacy vs modern table.
Legacy predecessor: LangChain ReAct Agent Loop (Legacy).
Related
- Hermes Agent Loop — concurrent tool dispatch (similar fan-out idea), class-based
- Managed Agents Architecture — Anthropic's Brain/Hands/Session model vs this graph model
- Agent Loop (OpenClaw) — gateway-layer wrapping below model calls