Skip to main content
LangChain 1.0’s create_agent is built on LangGraph — it returns a CompiledStateGraph you invoke like any other graph. So if you’re already using create_agent, the LangChain guide and rb.middleware() apply unchanged: the middleware hooks before_model / wrap_model_call on the underlying graph runtime. This page is for the second shape: hand-rolling your own StateGraph. There’s no AgentMiddleware slot to plug into when you’re defining nodes and edges yourself, so you wire a SteeringSession into the graph by hand. Same pipeline, same telemetry, same monitor + E-trace + routing behavior — just expressed as graph nodes instead of middleware hooks.

Path 1: create_agent (LangGraph under the hood)

If you build your agent with create_agent, you don’t need a LangGraph-specific integration. The middleware works as documented in the LangChain guide:
The returned graph is a langgraph.graph.state.CompiledStateGraph. Every step runs through the FSM scorer, monitor evaluator, E-trace pipeline, and live telemetry emitter — same as a non-LangGraph LangChain agent.

Path 2: hand-rolled StateGraph

When you build a graph from scratch with langgraph.graph.StateGraph, you call the model from your own node function. Wire SteeringSession around that node so the pipeline runs on every model call.
1

Install

2

Build the steering session

rb.claude_messages_session(...) builds a session wired against an Anthropic model identifier. rb.middleware(...).session does the same against any LangChain init_chat_model identifier — but for hand-rolled graphs without create_agent, the cleaner path is to construct SteeringSession directly so you can choose your own framework label.
Most users won’t build a session this manually — call rb.claude_messages_session(...) (for Claude) or wrap with rb.openai_model(...) (for OpenAI) and let those factories assemble the pieces. The hand-built form is shown here because pure-StateGraph users typically pick their own model adapter.
3

Define your graph nodes

Two node helpers — one before the LLM call, one after — keep the steering pipeline orthogonal to the rest of your graph.
4

Compose the graph + run

Looping (multi-step) graphs cycle through steering_prellmsteering_post → router → steering_pre again until the router decides the run is done. Each cycle produces one step_log entry on session.
5

Inspect the step log

Same shape as mw.step_log from the LangChain middleware: difficulty, FSM state, monitors fired, injection text, model id used, tokens, and latency.

What the LangGraph integration shares with LangChain

Identical: FSM scoring, server-side monitor evaluation, E1/E2/E3 retrieval, model routing, telemetry emission. Both ultimately drive the same SteeringSession. When LangChain 1.0 calls your middleware’s before_model hook, it’s running on the LangGraph runtime — that’s why our existing tests cover both paths.

What you don’t get on the hand-rolled path

Token-saving compression and the general-monitor middleware are LangChain AgentMiddleware implementations. They’re plumbed through create_agent but don’t have a hand-rolled StateGraph analog yet. Use the rb.middleware() + create_agent path if you need those.

Codebase memory tools

make_langchain_tools returns @tool-decorated functions that work in any LangGraph node, including hand-rolled graphs:
Bind them to your model (ChatAnthropic(...).bind_tools(graph_tools)) or attach to a ToolNode. The contract is unchanged from the LangChain integration.

LangChain guide

Full middleware walkthrough for create_agent-based agents.

SteeringSession reference

The shared core driving every framework integration.