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):

From tools node (_make_tools_to_model_edge, factory.py L1771):

From any middleware node:

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).