Skip to main content
Middleware provides a way to more tightly control what happens inside the agent. The core agent loop involves calling a model, letting it choose tools to execute, and then finishing when it calls no more tools:
Core agent loop diagram
Middleware exposes hooks before and after each of those steps:
Middleware flow diagram

What can middleware do?

Monitor

Track agent behavior with logging, analytics, and debugging

Modify

Transform prompts, tool selection, and output formatting

Control

Add retries, fallbacks, and early termination logic

Enforce

Apply rate limits, guardrails, and PII detection
Add middleware by passing it to create_agent:

Built-in middleware

LangChain provides prebuilt middleware for common use cases:

Summarization

Automatically summarize conversation history when approaching token limits.
Perfect for:
  • Long-running conversations that exceed context windows
  • Multi-turn dialogues with extensive history
  • Applications where preserving full conversation context matters
string
required
Model for generating summaries
number
Token threshold for triggering summarization
number
default:"20"
Recent messages to preserve
function
Custom token counting function. Defaults to character-based counting.
string
Custom prompt template. Uses built-in template if not specified.
string
default:"## Previous conversation summary:"
Prefix for summary messages

Human-in-the-loop

Pause agent execution for human approval, editing, or rejection of tool calls before they execute.
Perfect for:
  • High-stakes operations requiring human approval (database writes, financial transactions)
  • Compliance workflows where human oversight is mandatory
  • Long running conversations where human feedback is used to guide the agent
dict
required
Mapping of tool names to approval configs. Values can be True (interrupt with default config), False (auto-approve), or an InterruptOnConfig object.
string
default:"Tool execution requires approval"
Prefix for action request descriptions
InterruptOnConfig options:
list[string]
List of allowed decisions: "approve", "edit", or "reject"
string | callable
Static string or callable function for custom description
Important: Human-in-the-loop middleware requires a checkpointer to maintain state across interruptions.See the human-in-the-loop documentation for complete examples and integration patterns.

Anthropic prompt caching

Reduce costs by caching repetitive prompt prefixes with Anthropic models.
Perfect for:
  • Applications with long, repeated system prompts
  • Agents that reuse the same context across invocations
  • Reducing API costs for high-volume deployments
Learn more about Anthropic Prompt Caching strategies and limitations.
string
default:"ephemeral"
Cache type. Only "ephemeral" is currently supported.
string
default:"5m"
Time to live for cached content. Valid values: "5m" or "1h"
number
default:"0"
Minimum number of messages before caching starts
string
default:"warn"
Behavior when using non-Anthropic models. Options: "ignore", "warn", or "raise"

Model call limit

Limit the number of model calls to prevent infinite loops or excessive costs.
Perfect for:
  • Preventing runaway agents from making too many API calls
  • Enforcing cost controls on production deployments
  • Testing agent behavior within specific call budgets
number
Maximum model calls across all runs in a thread. Defaults to no limit.
number
Maximum model calls per single invocation. Defaults to no limit.
string
default:"end"
Behavior when limit is reached. Options: "end" (graceful termination) or "error" (raise exception)

Tool call limit

Control agent execution by limiting the number of tool calls, either globally across all tools or for specific tools.
Perfect for:
  • Preventing excessive calls to expensive external APIs
  • Limiting web searches or database queries
  • Enforcing rate limits on specific tool usage
  • Protecting against runaway agent loops
To limit tool calls globally across all tools or for specific tools, set tool_name. For each limit, specify one or both of:
  • Thread limit (thread_limit) - Max calls across all runs in a conversation. Persists across invocations. Requires a checkpointer.
  • Run limit (run_limit) - Max calls per single invocation. Resets each turn.
Exit behaviors:
string
Name of specific tool to limit. If not provided, limits apply to all tools globally.
number
Maximum tool calls across all runs in a thread (conversation). Persists across multiple invocations with the same thread ID. Requires a checkpointer to maintain state. None means no thread limit.
number
Maximum tool calls per single invocation (one user message → response cycle). Resets with each new user message. None means no run limit.Note: At least one of thread_limit or run_limit must be specified.
string
default:"continue"
Behavior when limit is reached:
  • "continue" (default) - Block exceeded tool calls with error messages, let other tools and the model continue. The model decides when to end based on the error messages.
  • "error" - Raise a ToolCallLimitExceededError exception, stopping execution immediately
  • "end" - Stop execution immediately with a ToolMessage and AI message for the exceeded tool call. Only works when limiting a single tool; raises NotImplementedError if other tools have pending calls.

Model fallback

Automatically fallback to alternative models when the primary model fails.
Perfect for:
  • Building resilient agents that handle model outages
  • Cost optimization by falling back to cheaper models
  • Provider redundancy across OpenAI, Anthropic, etc.
string | BaseChatModel
required
First fallback model to try when the primary model fails. Can be a model string (e.g., "openai:gpt-4o-mini") or a BaseChatModel instance.
string | BaseChatModel
Additional fallback models to try in order if previous models fail

PII detection

Detect and handle Personally Identifiable Information in conversations.
Perfect for:
  • Healthcare and financial applications with compliance requirements
  • Customer service agents that need to sanitize logs
  • Any application handling sensitive user data
string
required
Type of PII to detect. Can be a built-in type (email, credit_card, ip, mac_address, url) or a custom type name.
string
default:"redact"
How to handle detected PII. Options:
  • "block" - Raise exception when detected
  • "redact" - Replace with [REDACTED_TYPE]
  • "mask" - Partially mask (e.g., ****-****-****-1234)
  • "hash" - Replace with deterministic hash
function | regex
Custom detector function or regex pattern. If not provided, uses built-in detector for the PII type.
boolean
default:"True"
Check user messages before model call
boolean
default:"False"
Check AI messages after model call
boolean
default:"False"
Check tool result messages after execution

To-do list

Equip agents with task planning and tracking capabilities for complex multi-step tasks.
Perfect for:
  • Complex multi-step tasks requiring coordination across multiple tools
  • Long-running operations where progress visibility is important
Just as humans are more effective when they write down and track tasks, agents benefit from structured task management to break down complex problems, adapt plans as new information emerges, and provide transparency into their workflow. You may have noticed patterns like this in Claude Code, which writes out a to-do list before tackling complex, multi-part tasks.
This middleware automatically provides agents with a write_todos tool and system prompts to guide effective task planning.
string
Custom system prompt for guiding todo usage. Uses built-in prompt if not specified.
string
Custom description for the write_todos tool. Uses built-in description if not specified.

LLM tool selector

Use an LLM to intelligently select relevant tools before calling the main model.
Perfect for:
  • Agents with many tools (10+) where most aren’t relevant per query
  • Reducing token usage by filtering irrelevant tools
  • Improving model focus and accuracy
string | BaseChatModel
Model for tool selection. Can be a model string or BaseChatModel instance. Defaults to the agent’s main model.
string
Instructions for the selection model. Uses built-in prompt if not specified.
number
Maximum number of tools to select. Defaults to no limit.
list[string]
List of tool names to always include in the selection

Tool retry

Automatically retry failed tool calls with configurable exponential backoff.
Perfect for:
  • Handling transient failures in external API calls
  • Improving reliability of network-dependent tools
  • Building resilient agents that gracefully handle temporary errors
number
default:"2"
Maximum number of retry attempts after the initial call (3 total attempts with default)
list[BaseTool | str]
Optional list of tools or tool names to apply retry logic to. If None, applies to all tools.
tuple[type[Exception], ...] | callable
default:"(Exception,)"
Either a tuple of exception types to retry on, or a callable that takes an exception and returns True if it should be retried.
string | callable
default:"return_message"
Behavior when all retries are exhausted. Options:
  • "return_message" - Return a ToolMessage with error details (allows LLM to handle failure)
  • "raise" - Re-raise the exception (stops agent execution)
  • Custom callable - Function that takes the exception and returns a string for the ToolMessage content
number
default:"2.0"
Multiplier for exponential backoff. Each retry waits initial_delay * (backoff_factor ** retry_number) seconds. Set to 0.0 for constant delay.
number
default:"1.0"
Initial delay in seconds before first retry
number
default:"60.0"
Maximum delay in seconds between retries (caps exponential backoff growth)
boolean
default:"true"
Whether to add random jitter (±25%) to delay to avoid thundering herd

LLM tool emulator

Emulate tool execution using an LLM for testing purposes, replacing actual tool calls with AI-generated responses.
Perfect for:
  • Testing agent behavior without executing real tools
  • Developing agents when external tools are unavailable or expensive
  • Prototyping agent workflows before implementing actual tools
list[str | BaseTool]
List of tool names (str) or BaseTool instances to emulate. If None (default), ALL tools will be emulated. If empty list, no tools will be emulated.
string | BaseChatModel
default:"anthropic:claude-3-5-sonnet-latest"
Model to use for generating emulated tool responses. Can be a model identifier string or BaseChatModel instance.

Context editing

Manage conversation context by trimming, summarizing, or clearing tool uses.
Perfect for:
  • Long conversations that need periodic context cleanup
  • Removing failed tool attempts from context
  • Custom context management strategies
list[ContextEdit]
default:"[ClearToolUsesEdit()]"
List of ContextEdit strategies to apply
string
default:"approximate"
Token counting method. Options: "approximate" or "model"
ClearToolUsesEdit options:
number
default:"100000"
Token count that triggers the edit
number
default:"0"
Minimum tokens to reclaim
number
default:"3"
Number of recent tool results to preserve
boolean
default:"False"
Whether to clear tool call parameters
list[string]
default:"()"
List of tool names to exclude from clearing
string
default:"[cleared]"
Placeholder text for cleared outputs

Custom middleware

Build custom middleware by implementing hooks that run at specific points in the agent execution flow. You can create middleware in two ways:
  1. Decorator-based - Quick and simple for single-hook middleware
  2. Class-based - More powerful for complex middleware with multiple hooks

Decorator-based middleware

For simple middleware that only needs a single hook, decorators provide the quickest way to add functionality:

Available decorators

Node-style (run at specific execution points):
  • @before_agent - Before agent starts (once per invocation)
  • @before_model - Before each model call
  • @after_model - After each model response
  • @after_agent - After agent completes (once per invocation)
Wrap-style (intercept and control execution): Convenience decorators:

When to use decorators

Use decorators when

• You need a single hook
• No complex configuration

Use classes when

• Multiple hooks needed
• Complex configuration
• Reuse across projects (config on init)

Class-based middleware

Two hook styles

Node-style hooks

Run sequentially at specific execution points. Use for logging, validation, and state updates.

Wrap-style hooks

Intercept execution with full control over handler calls. Use for retries, caching, and transformation.

Node-style hooks

Run at specific points in the execution flow:
  • before_agent - Before agent starts (once per invocation)
  • before_model - Before each model call
  • after_model - After each model response
  • after_agent - After agent completes (up to once per invocation)
Example: Logging middleware
Example: Conversation length limit

Wrap-style hooks

Intercept execution and control when the handler is called:
  • wrap_model_call - Around each model call
  • wrap_tool_call - Around each tool call
You decide if the handler is called zero times (short-circuit), once (normal flow), or multiple times (retry logic). Example: Model retry middleware
Example: Dynamic model selection
Example: Tool call monitoring

Custom state schema

Middleware can extend the agent’s state with custom properties. Define a custom state type and set it as the state_schema:

Execution order

When using multiple middleware, understanding execution order is important:
Before hooks run in order:
  1. middleware1.before_agent()
  2. middleware2.before_agent()
  3. middleware3.before_agent()
Agent loop starts
  1. middleware1.before_model()
  2. middleware2.before_model()
  3. middleware3.before_model()
Wrap hooks nest like function calls:
  1. middleware1.wrap_model_call()middleware2.wrap_model_call()middleware3.wrap_model_call() → model
After hooks run in reverse order:
  1. middleware3.after_model()
  2. middleware2.after_model()
  3. middleware1.after_model()
Agent loop ends
  1. middleware3.after_agent()
  2. middleware2.after_agent()
  3. middleware1.after_agent()
Key rules:
  • before_* hooks: First to last
  • after_* hooks: Last to first (reverse)
  • wrap_* hooks: Nested (first middleware wraps all others)

Agent jumps

To exit early from middleware, return a dictionary with jump_to:
Available jump targets:
  • "end": Jump to the end of the agent execution
  • "tools": Jump to the tools node
  • "model": Jump to the model node (or the first before_model hook)
Important: When jumping from before_model or after_model, jumping to "model" will cause all before_model middleware to run again. To enable jumping, decorate your hook with @hook_config(can_jump_to=[...]):

Best practices

  1. Keep middleware focused - each should do one thing well
  2. Handle errors gracefully - don’t let middleware errors crash the agent
  3. Use appropriate hook types:
    • Node-style for sequential logic (logging, validation)
    • Wrap-style for control flow (retry, fallback, caching)
  4. Clearly document any custom state properties
  5. Unit test middleware independently before integrating
  6. Consider execution order - place critical middleware first in the list
  7. Use built-in middleware when possible, don’t reinvent the wheel :)

Examples

Dynamically selecting tools

Select relevant tools at runtime to improve performance and accuracy.
Benefits:
  • Shorter prompts - Reduce complexity by exposing only relevant tools
  • Better accuracy - Models choose correctly from fewer options
  • Permission control - Dynamically filter tools based on user access

Additional resources


Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.