XBSTACK XBSTACK
Xiaobai

Xiaobai

Developer · Builder

Building AI engineering systems, developer tools and long-term digital assets at XBSTACK.

About Xiaobai & XBSTACK →
LangChain v1 agent architecture with create_agent, middleware, memory and human-in-the-loop

LangChain v1 Tutorial: Build Agents with create_agent, Middleware, Memory, and HITL

Build a LangChain v1 agent with create_agent, middleware, memory, runtime context and HITL, replacing legacy AgentExecutor-first patterns.

Published · 2026-04-254 min readXBSTACK
#AI Agent#LangChain#LangChain v1#LangGraph#Python#Middleware#Human-in-the-loop

Direct answer: for a new LangChain agent in 2026, start with create_agent, not a legacy AgentExecutor tutorial. LangChain v1 exposes create_agent as the standard high-level agent API. It runs on the LangGraph runtime and combines tools, middleware, checkpointers, runtime context, structured output and human-in-the-loop within one execution model.

AgentExecutor was not historically useless, but it is no longer the right mental model for a current introductory guide. Teaching handle_parsing_errors=True, max_iterations and a scratchpad-centric loop as the main production design sends new projects toward legacy APIs before they have even started.

Minimal current agent: create_agent plus a typed tool

from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.tools import tool


class WeatherInput(BaseModel):
    location: str = Field(description="City name")


@tool(args_schema=WeatherInput)
def get_weather(location: str) -> str:
    """Return weather data from the application's trusted weather service."""
    return f"weather:{location}"


agent = create_agent(
    model="your-model",
    tools=[get_weather],
    system_prompt=(
        "Use tools only when needed. Never invent tool results. "
        "Ask for clarification when required inputs are missing."
    ),
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "Is it a good day to hike?"}]
})

The hard part is not writing the loop. It is controlling tool schemas, authorization, failure classification and external side effects.

How create_agent relates to LangGraph

LangChain v1 does not use a separate execution foundation for its agents. Current LangChain agents run on LangGraph.

A useful split is:

  • LangChain — higher-level agent API, model/tool integrations, middleware and structured output;
  • LangGraph — runtime state, execution, persistence, interrupts, streaming and custom graph topology.

A simple agent therefore does not need a hand-written StateGraph. Drop down to LangGraph when the business workflow needs deterministic branches, fan-out/fan-in, custom state transitions, long-running recovery semantics or multiple orchestrated agents.

Middleware is the current production control plane

LangChain v1 consolidates many controls that older code spread across AgentExecutor parameters, callbacks and custom wrappers.

Current middleware patterns include:

  • dynamic system prompts;
  • conversation summarization and context trimming;
  • model-call and tool-call limits;
  • tool retry and model retry;
  • model fallback;
  • PII detection and redaction;
  • human-in-the-loop;
  • custom before-model, after-model and wrapped tool-call logic.

Production design should therefore ask: how many calls are allowed, which errors are retryable, which tools require approval, and which data must never enter the model context?

Memory: separate short-term thread state from long-term memory

Thread-level short-term memory lives in Agent state and can be persisted through a LangGraph checkpointer.

from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver()

agent = create_agent(
    model="your-model",
    tools=[get_weather],
    checkpointer=checkpointer,
)

config = {"configurable": {"thread_id": "user-42:trip-1"}}

agent.invoke(
    {"messages": [{"role": "user", "content": "I want to hike this weekend."}]},
    config=config,
)

InMemorySaver is appropriate for a local example. Production systems should use a database-backed checkpointer such as Postgres and define tenant isolation, thread lifecycle and cleanup rules.

Long-term user preferences or business memory that must survive across threads belongs in a Store or the application’s own database. Replaying an ever-growing message history is not a durable memory architecture.

Runtime Context: inject dependencies instead of hiding them in globals

LangChain Runtime can expose context, store access and execution information to tools and middleware. This is appropriate for user IDs, tenant configuration, database dependencies and other runtime values.

from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime


@dataclass
class Context:
    user_id: str


@tool
def load_profile(runtime: ToolRuntime[Context]) -> str:
    """Load approved profile fields for the current user."""
    return f"user:{runtime.context.user_id}"


agent = create_agent(
    model="your-model",
    tools=[load_profile],
    context_schema=Context,
)

This keeps identity and infrastructure dependencies testable instead of embedding them in prompts or global variables.

Human-in-the-loop: pause before the high-risk tool executes

Email sending, file writes, SQL mutations, publishing, deletion and payment should be approved after the model proposes a tool call and before the external side effect occurs.

HumanInTheLoopMiddleware can interrupt selected tools and allow approve, edit or reject decisions.

The framework interrupt is only the runtime pause. A production approval record should also contain:

  • approval ID;
  • approver identity;
  • approved tool arguments;
  • tool schema or business version;
  • expiration time;
  • idempotency key;
  • final execution result.

Resume capability by itself does not make an approval system safe.

Error handling: move beyond handle_parsing_errors

Modern tool calling relies heavily on structured schemas, so errors should be classified by layer:

FailureProduction response
Invalid tool argumentsReturn repairable field errors and cap retries
429 / transient network errorBounded backoff and respect Retry-After
Authorization deniedDo not retry; return an explicit policy failure
Ambiguous write timeoutCheck the idempotency ledger before replaying
Loop makes no progressModel/tool call limits plus progress checks
High-risk actionHuman approval, never model confidence alone

When create_agent is enough, and when to use LangGraph directly

Prefer create_agent for:

  • one agent with multiple tools;
  • normal tool loops;
  • common RAG;
  • retry, summarization, limits and HITL that middleware can express.

Use LangGraph directly for:

  • explicit multi-stage state machines;
  • parallel branches and fan-in;
  • multi-agent orchestration;
  • custom checkpoint/resume boundaries;
  • long-running business workflows and compensating paths.

The goal is not to choose the lowest-level framework. Add graph complexity only when the default agent loop cannot express the business topology clearly.

FAQ

Must every existing AgentExecutor application be rewritten immediately?

No. Do not rewrite stable production code for aesthetics. Evaluate LangChain v1 when adding new capabilities, especially where legacy parsers, memory, callbacks or approval/recovery limitations are already creating maintenance risk.

Does a LangChain agent guarantee a tool runs exactly once?

No. Network ambiguity, retries and worker redelivery can still repeat an external side effect. Payments, publishing, email and database writes need application-owned idempotency keys and uniqueness constraints.

More to Explore

Topic path / LangGraph

Continue through the production LangGraph learning path

The LangGraph hub organizes state isolation, checkpointing, human approval, retries, observability, supervisors, subgraphs and memory into one reviewable path.

More to Explore

Topic hub →
OpenAI Agents SDK Tool Approval Resume: RunState Across Processes and the v0.19.3 Streaming FixCompare OpenAI Agents SDK 0.18.3 and 0.19.3: reproduce the streamed-resume approved tool-output loss, verify the fix, and test cross-process RunState recovery.AutoGen Tutorial: AgentChat, Teams, Termination, and the v0.2 Migration BoundaryAutoGen AgentChat tutorial for AssistantAgent, Teams, termination, UserProxyAgent, state persistence, and migration from legacy v0.2 APIs.OpenAI Agents SDK Duplicate Tool Names: Why the Later Tool WinsOpenAI Agents SDK duplicate tool names can trigger a provider 400 or last-wins local dispatch. Reproduce 0.19.2 and add a preflight uniqueness gate.AI Agent Protocol and Framework Selection: How to Choose Between MCP, Function Calling, A2A, LangGraph, AutoGen, and CrewAI?AI Agent Protocol and Framework Selection: A systematic overview of protocol and framework selection for AI Agent development, covering Function Calling, MCP, A2A, LangGraph.

AI Engineering Weekly

Production changes, real failures, experiments and new XBSTACK assets.

Comments & evidence

DISCUSSION

Questions, verification and corrections

Sign in to comment. Every new comment is reviewed before publication; while pending, it is visible only to you and the administrator.

Sign-in required Reviewed before public
Loading the discussion…