AI & ML / Agents & orchestration / LangGraph / 04_create_agent_and_middleware.md

create_agent and middleware

Updated 5 interview angles 4 min read source
On this page6
  1. The front door
  2. The arguments worth knowing
  3. Middleware: the actual 1.0 idea
  4. When to drop to StateGraph
  5. Related
  6. Interview angle

create_agent and middleware

The prebuilt tool-calling loop, and the 1.0 mechanism for customising it without subclassing anything.

The front door

python
from langchain.agents import create_agent

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search, lookup_order],
)

agent.invoke({"messages": [
    {"role": "user", "content": "Where is order 123?"}
]})

That is a working agent. The model string is provider:model, so switching providers is a string change rather than an import change.

create_agent returns a compiled graph. Everything from Nodes, edges and routing applies — you can stream it, checkpoint it, and inspect its state, because it is the same runtime underneath.

Naming: create_react_agent is the pre-1.0 name for this. Using it in conversation dates your knowledge to before October 2025.

The arguments worth knowing

Argument Does
model "provider:model" or an instance
tools callables or LangChain tools
system_prompt shapes behaviour
response_format a Pydantic model for typed output
checkpointer persistence across turns
middleware hooks into the loop
state_schema extra state keys beyond messages
context_schema per-run configuration

response_format is the one people miss. Pass a Pydantic model and the final answer is validated against it, which turns “parse the model’s prose” into a typed object — see Function Calling, Tool Use, and Structured Output.

python
from pydantic import BaseModel

class Answer(BaseModel):
    order_id: str
    status: str
    eta_days: int

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[lookup_order],
    response_format=Answer,
)

Middleware: the actual 1.0 idea

Before 1.0, customising the loop meant subclassing and overriding. That made every customisation a fork. Middleware makes them composable units that hook the loop at defined points.

The formula the docs use: agent = model + harness. Middleware is how you change the harness without rewriting it.

What people build with it:

Concern Middleware does
Summarisation compact history before the call
Guardrails inspect or block a tool call
Approval pause before a risky action
Retry catch a failure, try again
Steering inject context per turn

The value is that each is independent. Summarisation does not know about approval; both compose onto the same agent. Contrast the pre-1.0 world where adding the second meant editing the class that implemented the first.

python
agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[refund_order],
    middleware=[summarisation, approval_gate],
)

The interview point: middleware is where policy lives. Approval gates, PII redaction and budget checks belong in a hook that runs on every tool call, not in prompt text asking the model to behave. Policy in code at the boundary is enforceable; policy in a prompt is a suggestion. See Guardrails and output validation.

When to drop to StateGraph

create_agent is a good default and it is one shape: a loop that calls tools until the model stops asking. Move to an explicit graph when:

  • The flow has stages that are not a loop — ingest, then verify, then report, with different tools available at each.
  • You need parallel branches with a merge.
  • The routing is mostly deterministic and only one decision needs a model.
  • You need state the agent loop does not model.

The migration is not a rewrite. create_agent produces a graph, so you can start with it, hit its edges, and rebuild the parts you need explicitly while keeping the same state and checkpointer.

Gotcha: reaching for StateGraph first is the more common error. A hand-built graph that reimplements the tool loop is code you now maintain, and it will drift behind the prebuilt one. Start prebuilt.

Interview angle 5

  • “How do you build an agent in LangGraph?” - create_agent from langchain.agents with a model string and tools. It returns a compiled graph, so streaming, checkpointing and state inspection all work on it — it is not a separate abstraction.
  • “What is middleware?” - composable hooks into the agent loop, and the 1.0 replacement for subclassing. Summarisation, guardrails, approval and retry each become an independent unit instead of a fork of the loop class.
  • “Where should approval logic live?” - in middleware, running on every tool call, not in prompt text. Policy in code at the boundary is enforceable; policy in a prompt is a suggestion the model can be talked out of.
  • “When would you use StateGraph instead?” - when the flow has stages rather than a loop, needs parallel branches, or is mostly deterministic with one model decision. Start prebuilt though — a hand-built tool loop is maintenance you took on for nothing.
  • “How do you get typed output?” - response_format with a Pydantic model, which validates the final answer and turns prose parsing into an object.