预置内置中间件 (Prebuilt middleware)
LangChain 与 Deep Agents 提供的开箱即用中间件合集
预置中间件为你处理了生产系统中常见的非功能性需求(如网络重试、模型故障转移、上下文压缩、敏感数据脱敏与熔断保护)。
通用中间件 (Provider-agnostic middleware)
以下中间件与底层大模型提供商完全解耦,可在任何受支持的模型上运行。
工具错误处理 (Tool error)
拦截工具执行抛出的异常,并将其转换为友好的格式化提示反馈给大语言模型,引导模型自行纠错重试。
工具调用重试 (Tool retry)
自动对网络超时、瞬时连接中断等非致命故障进行指数退避重试:
import { createAgent, toolRetryMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [searchTool, databaseTool],
middleware: [
toolRetryMiddleware({
maxRetries: 3,
backoffFactor: 2.0,
initialDelayMs: 1000,
}),
],
});标记可重试的错误类型
可配置仅针对特定异常类(如 TimeoutError)或 HTTP 状态码(502、503、504)触发重试,避免在参数验证失败等致命错误上盲目浪费时间。
模型调用重试 (Model retry)
针对模型提供商的 429 速率限制(Rate Limit)或服务端 5xx 故障自动触发带抖动(Jitter)的指数退避重试:
> import { createAgent, toolRetryMiddleware } from "langchain";
> import { tool } from "@langchain/core/tools";
> import { z } from "zod";
>
> // Basic usage with default settings (2 retries, exponential backoff)
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [searchTool, databaseTool],
> middleware: [toolRetryMiddleware()],
> });
>
> // Retry specific exceptions only
> const retry = toolRetryMiddleware({
> maxRetries: 4,
> retryOn: [TimeoutError, NetworkError],
> backoffFactor: 1.5,
> });
>
> // Custom exception filtering
> function shouldRetry(error: Error): boolean {
> // Only retry on 5xx errors
> if (error.name === "HTTPError" && "statusCode" in error) {
> const statusCode = (error as any).statusCode;
> return 500 <= statusCode && statusCode < 600;
> }
> return false;
> }
>
> const retryWithFilter = toolRetryMiddleware({
> maxRetries: 3,
> retryOn: shouldRetry,
> });
>
> // Apply to specific tools with custom error handling
> const formatError = (error: Error) =>
> "Database temporarily unavailable. Please try again later.";
>
> const retrySpecificTools = toolRetryMiddleware({
> maxRetries: 4,
> tools: ["search_database"],
> onFailure: formatError,
> });
>
> // Apply to specific tools using BaseTool instances
> const searchDatabase = tool(
> async ({ query }) => {
> // Search implementation
> return results;
> },
> {
> name: "search_database",
> description: "Search the database",
> schema: z.object({ query: z.string() }),
> }
> );
>
> const retryWithToolInstance = toolRetryMiddleware({
> maxRetries: 4,
> tools: [searchDatabase], // Pass BaseTool instance
> });
>
> // Constant backoff (no exponential growth)
> const constantBackoff = toolRetryMiddleware({
> maxRetries: 5,
> backoffFactor: 0.0, // No exponential growth
> initialDelayMs: 2000, // Always wait 2 seconds
> });
>
> // Raise exception on failure
> const strictRetry = toolRetryMiddleware({
> maxRetries: 2,
> onFailure: "error", // Re-raise exception instead of returning message
> });
> ```
### 模型降级回退 (Model fallback)
当主力大模型完全宕机不可用时,自动透明地将请求故障转移(Failover)至预设的备选模型:
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { stampRetryable } from "@langchain/core/errors";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const lookupRecord = tool(
async ({ id }) => {
const response = await fetch(`https://api.example.com/records/${id}`);
if (response.status === 422) {
// Retrying the same identifier fails the same way
throw stampRetryable(new Error("Malformed record identifier"), false);
}
if (response.status === 503) {
throw stampRetryable(new Error("Record service unavailable"), true);
}
return response.json();
},
{
name: "lookup_record",
description: "Look up a record by identifier",
schema: z.object({ id: z.string() }),
}
);上下文摘要 (Summarization)
实时监控对话历史的 Token 消耗总量。一旦超过触发阈值,在后台将过早轮次自动折叠压缩为精炼的事实摘要,只保留最近的关键上下文,防止模型窗口溢出:
import { createAgent, modelRetryMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [searchTool, databaseTool],
middleware: [
modelRetryMiddleware({
maxRetries: 3,
backoffFactor: 2.0,
initialDelayMs: 1000,
}),
],
});人在回路审批 (Human-in-the-loop)
在具有写操作或破坏性的敏感工具触发前优雅暂停执行,等待人工审批通过、修改入参或直接答复后恢复运行。
模型调用次数熔断 (Model call limit)
为单次任务调用设置最大模型调用次数上限,防止智能体陷入无限死循环并耗尽 API 预算。
工具执行次数熔断 (Tool call limit)
针对高成本或高频工具配置单次运行的最大执行配额。
敏感个人隐私数据检测 (PII detection)
在模型输入前或工具输出后自动检测并打码脱敏身份证号、信用卡、手机号等 PII 隐私数据:
> import { createAgent, modelRetryMiddleware } from "langchain";
>
> // Basic usage with default settings (2 retries, exponential backoff)
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [searchTool],
> middleware: [modelRetryMiddleware()],
> });
>
> class TimeoutError extends Error {
> // ...
> }
> class NetworkError extends Error {
> // ...
> }
>
> // Retry specific exceptions only
> const retry = modelRetryMiddleware({
> maxRetries: 4,
> retryOn: [TimeoutError, NetworkError],
> backoffFactor: 1.5,
> });
>
> // Custom exception filtering
> function shouldRetry(error: Error): boolean {
> // Only retry on rate limit errors
> if (error.name === "RateLimitError") {
> return true;
> }
> // Or check for specific HTTP status codes
> if (error.name === "HTTPError" && "statusCode" in error) {
> const statusCode = (error as any).statusCode;
> return statusCode === 429 || statusCode === 503;
> }
> return false;
> }
>
> const retryWithFilter = modelRetryMiddleware({
> maxRetries: 3,
> retryOn: shouldRetry,
> });
>
> // Return error message instead of raising
> const retryContinue = modelRetryMiddleware({
> maxRetries: 4,
> onFailure: "continue", // Return AIMessage with error instead of throwing
> });
>
> // Custom error message formatting
> const formatError = (error: Error) =>
> `Model call failed: ${error.message}. Please try again later.`;
>
> const retryWithFormatter = modelRetryMiddleware({
> maxRetries: 4,
> onFailure: formatError,
> });
>
> // Constant backoff (no exponential growth)
> const constantBackoff = modelRetryMiddleware({
> maxRetries: 5,
> backoffFactor: 0.0, // No exponential growth
> initialDelayMs: 2000, // Always wait 2 seconds
> });
>
> // Raise exception on failure
> const strictRetry = modelRetryMiddleware({
> maxRetries: 2,
> onFailure: "error", // Re-raise exception instead of returning message
> });
> ```
### 待办事项任务列表 (To-do list)
为智能体注入结构化的 `write_todos` 工具,赋予其在执行长篇多步任务时自主制定、维护与更新任务进度的规划能力。
### 智能工具选择器 (LLM tool selector)
当系统挂载了数十乃至上百个工具时,在调用大模型前先执行轻量级初筛,仅向大模型上下文动态暴露与当前任务高度相关的候选工具子集。
### 文件系统中间件 (Filesystem middleware)
为智能体挂载虚拟文件系统,并注入用于文件管理的 `ls`、`read_file`、`write_file` 等标准工具。
### 子智能体委派中间件 (Subagent)
注入内置的 `task` 工具,支持主智能体衍生隔离的子智能体执行繁重子任务。
## 提供商专属中间件 (Provider-specific middleware)
部分中间件利用特定提供商的原生功能(如 Anthropic 提示词缓存控制、OpenAI 内容安全审核网关等)进行了针对性深度集成。
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, modelFallbackMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
modelFallbackMiddleware(
"gpt-5.4-mini",
"claude-sonnet-4-6"
),
],
});Configuration options
The middleware accepts a variable number of string arguments representing fallback models in order:
...models
string[]requiredOne or more fallback model strings to try in order when the primary model fails
> modelFallbackMiddleware(
> "first-fallback-model",
> "second-fallback-model",
> // ... more models
> )
> ```
### Summarization
Automatically summarize conversation history when approaching token limits, preserving recent messages while compressing older context. Summarization is useful for the following:
* Long-running conversations that exceed context windows.
* Multi-turn dialogues with extensive history.
* Applications where preserving full conversation context matters.
> [!NOTE]
>
> Summarization is text-oriented context compression. It does not resize, downsample, or otherwise compress image/audio/video payloads. Recent messages retained by `keep` still include their original multimodal blocks, while older multimodal messages that are summarized are represented only by the generated text summary. For image-heavy applications, store media in a filesystem or object store and pass URLs or file references through message history.
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, summarizationMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [weatherTool, calculatorTool],
middleware: [
summarizationMiddleware({
model: "gpt-5.4-mini",
trigger: { tokens: 4000 },
keep: { messages: 20 },
}),
],
});Configuration options
TIP
The
fractionconditions fortriggerandkeep(shown below) rely on a chat model's profile data if usinglangchain@1.1.0. If data are not available, use another condition or specify manually:
> > const customProfile: ModelProfile = {
> > maxInputTokens: 100_000,
> > // ...
> > }
> > model = await initChatModel("...", {
> > profile: customProfile,
> > });
> > ```
>
> **model** `string | BaseChatModel` *required*
>
> Model for generating summaries. Can be a model identifier string (e.g., `'openai:gpt-5.4-mini'`) or a `BaseChatModel` instance.
>
> **trigger** `object | object[]`
>
> Conditions for triggering summarization. Can be:
>
> * **Single condition (AND logic):** a single object where all properties must be met simultaneously.
>
>
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
> // Trigger when tokens >= 4000 AND messages >= 10
> trigger: { tokens: 4000, messages: 10 }
> ```
>
> * **Multiple conditions (OR logic):** an array of condition objects. Summarization triggers when any object's conditions are met.
>
>
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
> // Trigger when tokens >= 3000 OR messages >= 6
> trigger: [{ tokens: 3000 }, { messages: 6 }]
> ```
>
> Each condition can include:
>
> * `fraction` (number): Fraction of model's context size (0-1)
> * `tokens` (number): Absolute token count
> * `messages` (number): Message count
>
> At least one property must be specified per condition. If not provided, summarization will not trigger automatically.
>
> **keep** `object` (default: \{messages: 20})
>
> How much context to preserve after summarization. Specify exactly one of:
>
> * `fraction` (number): Fraction of model's context size to keep (0-1)
> * `tokens` (number): Absolute token count to keep
> * `messages` (number): Number of recent messages to keep
>
> **tokenCounter** `function`
>
> Custom token counting function. Defaults to character-based counting.
>
> **summaryPrompt** `string`
>
> Custom prompt template for summarization. Uses built-in template if not specified. The template should include `{messages}` placeholder where conversation history will be inserted.
>
> **trimTokensToSummarize** `number` (default: 4000)
>
> Maximum number of tokens to include when generating the summary. Messages will be trimmed to fit this limit before summarization.
>
> **summaryPrefix** `string`
>
> Prefix to add to the summary message. If not provided, a default prefix is used.
>
> **maxTokensBeforeSummary** `number` *deprecated*
>
> **Deprecated:** Use `trigger: { tokens: value }` instead. Token threshold for triggering summarization.
>
> **messagesToKeep** `number` *deprecated*
>
> **Deprecated:** Use `keep: { messages: value }` instead. Recent messages to preserve.
> **Full example**
>
> The summarization middleware monitors message token counts and automatically summarizes older messages when thresholds are reached.
>
> **Trigger conditions** control when summarization runs:
>
> * A single threshold triggers when that threshold is met
> * A trigger clause with multiple thresholds triggers only when all thresholds are met (AND logic)
> * A list of trigger conditions triggers when any item is met (OR logic)
> * Each threshold can use `fraction` (of model's context size), `tokens` (absolute count), or `messages` (message count)
>
> **Keep condition** control how much context to preserve (specify exactly one):
>
> * `fraction` - Fraction of model's context size to keep
> * `tokens` - Absolute token count to keep
> * `messages` - Number of recent messages to keep
>
>
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
> import { createAgent, summarizationMiddleware } from "langchain";
>
> // Single condition (AND logic): trigger if tokens >= 4000 AND messages >= 10
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [weatherTool, calculatorTool],
> middleware: [
> summarizationMiddleware({
> model: "gpt-5.4-mini",
> trigger: { tokens: 4000, messages: 10 },
> keep: { messages: 20 },
> }),
> ],
> });
>
> // Multiple conditions (OR logic): trigger if tokens >= 3000 OR messages >= 6
> const agent2 = createAgent({
> model: "gpt-5.5",
> tools: [weatherTool, calculatorTool],
> middleware: [
> summarizationMiddleware({
> model: "gpt-5.4-mini",
> trigger: [
> { tokens: 3000 },
> { messages: 6 },
> ],
> keep: { messages: 20 },
> }),
> ],
> });
>
> // Using fractional limits
> const agent3 = createAgent({
> model: "gpt-5.5",
> tools: [weatherTool, calculatorTool],
> middleware: [
> summarizationMiddleware({
> model: "gpt-5.4-mini",
> trigger: { fraction: 0.8 },
> keep: { fraction: 0.3 },
> }),
> ],
> });
> ```
### Human-in-the-loop
Pause agent execution for human approval, editing, or rejection of tool calls before they execute. [Human-in-the-loop](/oss/javascript/langchain/human-in-the-loop.md) is useful for the following:
* High-stakes operations requiring human approval (e.g. database writes, financial transactions).
* Compliance workflows where human oversight is mandatory.
* Long-running conversations where human feedback guides the agent.
The middleware pauses once per model turn by default. Set `interruptMode: "per_call"` to pause once per tool call, with each interrupt answered by its ID. See [Set the interrupt mode](/oss/javascript/langchain/human-in-the-loop.md#set-the-interrupt-mode).
> [!WARNING]
>
> Human-in-the-loop middleware requires a [checkpointer](/oss/javascript/langgraph/checkpointers.md#checkpoints) to maintain state across interruptions.
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, humanInTheLoopMiddleware } from "langchain";
function readEmailTool(emailId: string): string {
/** Mock function to read an email by its ID. */
return `Email content for ID: ${emailId}`;
}
function sendEmailTool(recipient: string, subject: string, body: string): string {
/** Mock function to send an email. */
return `Email sent to ${recipient} with subject '${subject}'`;
}
const agent = createAgent({
model: "gpt-5.5",
tools: [readEmailTool, sendEmailTool],
middleware: [
humanInTheLoopMiddleware({
interruptOn: {
sendEmailTool: {
allowedDecisions: ["approve", "edit", "reject"],
},
readEmailTool: false,
}
})
]
});TIP
For complete examples, configuration options, and integration patterns, see the Human-in-the-loop documentation.
Watch this video guide demonstrating Human-in-the-loop middleware behavior.
Model call limit
Limit the number of model calls to prevent infinite loops or excessive costs. Model call limit is useful for the following:
- Preventing runaway agents from making too many API calls.
- Enforcing cost controls on production deployments.
- Testing agent behavior within specific call budgets.
import { createAgent, modelCallLimitMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const agent = createAgent({
model: "gpt-5.5",
checkpointer: new MemorySaver(), // Required for thread limiting
tools: [],
middleware: [
modelCallLimitMiddleware({
threadLimit: 10,
runLimit: 5,
exitBehavior: "end",
}),
],
});Watch this video guide demonstrating Model Call Limit middleware behavior.
Configuration options
threadLimit
numberMaximum model calls across all runs in a thread. Defaults to no limit.
runLimit
numberMaximum model calls per single invocation. Defaults to no limit.
exitBehavior
string(default: end)Behavior when limit is reached. Options:
'end'(graceful termination) or'error'(throw exception)
Tool call limit
Control agent execution by limiting the number of tool calls, either globally across all tools or for specific tools. Tool call limits are useful for the following:
- 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.
import { createAgent, toolCallLimitMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [searchTool, databaseTool],
middleware: [
toolCallLimitMiddleware({ threadLimit: 20, runLimit: 10 }),
toolCallLimitMiddleware({
toolName: "search",
threadLimit: 5,
runLimit: 3,
}),
],
});Watch this video guide demonstrating Tool Call Limit middleware behavior.
Configuration options
toolName
stringName of specific tool to limit. If not provided, limits apply to all tools globally.
threadLimit
numberMaximum tool calls across all runs in a thread (conversation). Persists across multiple invocations with the same thread ID. Requires a checkpointer to maintain state.
undefinedmeans no thread limit.runLimit
numberMaximum tool calls per single invocation (one user message → response cycle). Resets with each new user message.
undefinedmeans no run limit.Note: At least one of
threadLimitorrunLimitmust be specified.exitBehavior
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'- Throw aToolCallLimitExceededErrorexception, 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; throws error if other tools have pending calls.
Full example
Specify limits with:
- Thread limit - Max calls across all runs in a conversation (requires checkpointer)
- Run limit - Max calls per single invocation (resets each turn)
Exit behaviors:
'continue'(default) - Block exceeded calls with error messages, agent continues'error'- Raise exception immediately'end'- Stop with ToolMessage + AI message (single-tool scenarios only)
> import { createAgent, toolCallLimitMiddleware } from "langchain";
>
> const globalLimiter = toolCallLimitMiddleware({ threadLimit: 20, runLimit: 10 });
> const searchLimiter = toolCallLimitMiddleware({ toolName: "search", threadLimit: 5, runLimit: 3 });
> const databaseLimiter = toolCallLimitMiddleware({ toolName: "query_database", threadLimit: 10 });
> const strictLimiter = toolCallLimitMiddleware({ toolName: "scrape_webpage", runLimit: 2, exitBehavior: "error" });
>
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [searchTool, databaseTool, scraperTool],
> middleware: [globalLimiter, searchLimiter, databaseLimiter, strictLimiter],
> });
> ```
### PII detection
Detect and handle Personally Identifiable Information (PII) in conversations using configurable strategies. PII detection is useful for the following:
* Healthcare and financial applications with compliance requirements.
* Customer service agents that need to sanitize logs.
* Any application handling sensitive user data.
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, piiMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
piiMiddleware("email", { strategy: "redact", applyToInput: true }),
piiMiddleware("credit_card", { strategy: "mask", applyToInput: true }),
],
});Custom PII types
You can create custom PII types by providing a detector parameter. This allows you to detect patterns specific to your use case beyond the built-in types.
Three ways to create custom detectors:
Regex pattern string - Simple pattern matching
RegExp object - More control over regex flags
Custom function - Complex detection logic with validation
import { createAgent, piiMiddleware, type PIIMatch } from "langchain";
// Method 1: Regex pattern string
const agent1 = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
piiMiddleware("api_key", {
detector: "sk-[a-zA-Z0-9]{32}",
strategy: "block",
}),
],
});
// Method 2: RegExp object
const agent2 = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
piiMiddleware("phone_number", {
detector: /\+?\d{1,3}[\s.-]?\d{3,4}[\s.-]?\d{4}/,
strategy: "mask",
}),
],
});
// Method 3: Custom detector function
function detectSSN(content: string): PIIMatch[] {
const matches: PIIMatch[] = [];
const pattern = /\d{3}-\d{2}-\d{4}/g;
let match: RegExpExecArray | null;
while ((match = pattern.exec(content)) !== null) {
const ssn = match[0];
// Validate: first 3 digits shouldn't be 000, 666, or 900-999
const firstThree = parseInt(ssn.substring(0, 3), 10);
if (firstThree !== 0 && firstThree !== 666 && !(firstThree >= 900 && firstThree <= 999)) {
matches.push({
text: ssn,
start: match.index ?? 0,
end: (match.index ?? 0) + ssn.length,
});
}
}
return matches;
}
const agent3 = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
piiMiddleware("ssn", {
detector: detectSSN,
strategy: "hash",
}),
],
});Custom detector function signature:
The detector function must accept a string (content) and return matches:
Returns an array of PIIMatch objects:
interface PIIMatch {
text: string; // The matched text
start: number; // Start index in content
end: number; // End index in content
}
function detector(content: string): PIIMatch[] {
return [
{ text: "matched_text", start: 0, end: 12 },
// ... more matches
];
}TIP
For custom detectors:
- Use regex strings for simple patterns
- Use RegExp objects when you need flags (e.g., case-insensitive matching)
- Use custom functions when you need validation logic beyond pattern matching
- Custom functions give you full control over detection logic and can implement complex validation rules
Configuration options
piiType
stringrequiredType of PII to detect. Can be a built-in type (
credit_card,ip,mac_address,url) or a custom type name.strategy
string(default: redact)How to handle detected PII. Options:
'block'- Throw error when detected'redact'- Replace with[REDACTED_TYPE]'mask'- Partially mask (e.g.,****-****-****-1234)'hash'- Replace with deterministic hash (e.g.,<email_hash:a1b2c3d4>)detector
RegExp | string | functionCustom detector. Can be:
RegExp- Regex pattern for matchingstring- Regex pattern string (e.g.,"sk-[a-zA-Z0-9]{32}")function- Custom detector function(content: string) => PIIMatch[]If not provided, uses built-in detector for the PII type.
applyToInput
boolean(default: true)Check user messages before model call
applyToOutput
boolean(default: false)Check AI messages after model call
applyToToolResults
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. To-do lists are useful for the following:
- Complex multi-step tasks requiring coordination across multiple tools.
- Long-running operations where progress visibility is important.
NOTE
This middleware automatically provides agents with a write_todos tool and system prompts to guide effective task planning.
import { createAgent, todoListMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [readFile, writeFile, runTests],
middleware: [todoListMiddleware()],
});Watch this video guide demonstrating To-do List middleware behavior.
Configuration options
No configuration options available (uses defaults).
LLM tool selector
Use an LLM to intelligently select relevant tools before calling the main model. LLM tool selectors are useful for the following:
- Agents with many tools (10+) where most aren't relevant per query.
- Reducing token usage by filtering irrelevant tools.
- Improving model focus and accuracy.
This middleware uses structured output to ask an LLM which tools are most relevant for the current query. The structured output schema defines the available tool names and descriptions. Model providers often add this structured output information to the system prompt behind the scenes.
import { createAgent, llmToolSelectorMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [tool1, tool2, tool3, tool4, tool5, ...],
middleware: [
llmToolSelectorMiddleware({
model: "gpt-5.4-mini",
maxTools: 3,
alwaysInclude: ["search"],
}),
],
});Configuration options
model
string | BaseChatModelModel for tool selection. Can be a model identifier string (e.g.,
'openai:gpt-5.4-mini') or aBaseChatModelinstance. Defaults to the agent's main model.systemPrompt
stringInstructions for the selection model. Uses built-in prompt if not specified.
maxTools
numberMaximum number of tools to select. If the model selects more, only the first maxTools will be used. No limit if not specified.
alwaysInclude
string[]Tool names to always include regardless of selection. These do not count against the maxTools limit.
Provider tool search
Defer selected tools behind model providers' server-side tool search, so the model discovers them on demand instead of receiving every tool schema up front. Provider tool search is useful for:
- Reducing context bloat when using many tools.
- Improving tool selection accuracy by surfacing only relevant tools.
NOTE
Requires a model with server-side tool search support: Anthropic (Claude Sonnet 4+/Opus 4+/Haiku 4.5+) or OpenAI (gpt-5.5+). Other providers throw an error.
API reference: providerToolSearchMiddleware
import { createAgent, providerToolSearchMiddleware } from "langchain";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const getWeather = tool(async () => "Sunny, 22C", {
name: "get_weather",
description: "Get the current weather for a city",
schema: z.object({ city: z.string() }),
});
const lookupOrderStatus = tool(async () => "OUT_FOR_DELIVERY", {
name: "lookup_order_status",
description: "Look up the current delivery status of a customer order by ID",
schema: z.object({ orderId: z.string() }),
});
const nicheTools = [lookupOrderStatus];
const agent = createAgent({
model: "anthropic:claude-opus-4-8",
tools: [getWeather, ...nicheTools],
middleware: [
providerToolSearchMiddleware({ searchableTools: nicheTools }),
],
});Configuration options
searchableTools
(string | StructuredToolInterface)[]Tools to defer behind the provider's tool search, given by name or instance. Deferred tools are withheld from the model until its search surfaces them. Tools constructed with
extras.defer_loading: trueare deferred regardless of this option; ifsearchableToolsis omitted, only those pre-marked tools are deferred.
Full example
The middleware opts-in all tools included in the
searchableToolsfor deferral and search. A tool can also opt into deferral at construction time by settingextras.defer_loading: true
> import { createAgent, providerToolSearchMiddleware } from "langchain";
> import { tool } from "@langchain/core/tools";
> import { z } from "zod";
>
> // Marked `defer_loading` at construction, so it's deferred on its own —
> // no need to list it in `searchableTools`.
> const sendEmail = tool(async () => "sent", {
> name: "send_email",
> description: "Send an email",
> schema: z.object({ to: z.string() }),
> extras: { defer_loading: true },
> });
>
> const agent = createAgent({
> model: "anthropic:claude-opus-4-8",
> tools: [sendEmail],
> middleware: [providerToolSearchMiddleware()],
> });
> ```
### Filesystem middleware
Context engineering is a main challenge in building effective agents. This is particularly difficult when using tools that return variable-length results (for example, `web_search` and RAG), as long tool results can quickly fill your context window.
`FilesystemMiddleware` from [Deep Agents](/oss/javascript/deepagents/overview.md) provides four tools for interacting with both short-term and long-term memory:
* `ls`: List the files in the filesystem
* `read_file`: Read an entire file or a certain number of lines from a file
* `write_file`: Write a new file to the filesystem
* `edit_file`: Edit an existing file in the filesystem
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent } from "langchain";
import { createFilesystemMiddleware } from "deepagents";
// FilesystemMiddleware is included by default in createDeepAgent
// You can customize it if building a custom agent
const agent = createAgent({
model: "claude-sonnet-4-6",
middleware: [
createFilesystemMiddleware({
backend: undefined, // Optional: custom backend (defaults to StateBackend)
systemPrompt: "Write to the filesystem when...", // Optional custom system prompt override
customToolDescriptions: {
ls: "Use the ls tool when...",
read_file: "Use the read_file tool to...",
}, // Optional: Custom descriptions for filesystem tools
}),
],
});Short-term vs. long-term filesystem
By default, these tools write to a local "filesystem" in your graph state. To enable persistent storage across threads, configure a CompositeBackend that routes specific paths (like /memories/) to a StoreBackend.
import { createAgent } from "langchain";
import { createFilesystemMiddleware, CompositeBackend, StateBackend, StoreBackend } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph-checkpoint";
const store = new InMemoryStore();
const agent = createAgent({
model: "claude-sonnet-4-6",
store,
middleware: [
createFilesystemMiddleware({
backend: new CompositeBackend(
new StateBackend(),
{ "/memories/": new StoreBackend() }
),
systemPrompt: "Write to the filesystem when...", // Optional custom system prompt override
customToolDescriptions: {
ls: "Use the ls tool when...",
read_file: "Use the read_file tool to...",
}, // Optional: Custom descriptions for filesystem tools
}),
],
});When you configure a CompositeBackend with a StoreBackend for /memories/, any files prefixed with /memories/ are saved to persistent storage and survive across different threads. Files without this prefix remain in ephemeral state storage.
Subagent
Handing off tasks to subagents isolates context, keeping the main (supervisor) agent's context window clean while still going deep on a task.
The subagents middleware from Deep Agents allows you to supply subagents through a task tool.
import { tool } from "langchain";
import { createAgent } from "langchain";
import { createSubAgentMiddleware } from "deepagents";
import { z } from "zod";
const getWeather = tool(
async ({ city }: { city: string }) => {
return `The weather in ${city} is sunny.`;
},
{
name: "get_weather",
description: "Get the weather in a city.",
schema: z.object({
city: z.string(),
}),
},
);
const agent = createAgent({
model: "claude-sonnet-4-6",
middleware: [
createSubAgentMiddleware({
defaultModel: "claude-sonnet-4-6",
defaultTools: [],
subagents: [
{
name: "weather",
description: "This subagent can get weather in cities.",
systemPrompt: "Use the get_weather tool to get the weather in a city.",
tools: [getWeather],
model: "gpt-5.5",
middleware: [],
},
],
}),
],
});A subagent is defined with a name, description, system prompt, and tools. You can also provide a subagent with a custom model, or with additional middleware. This can be particularly useful when you want to give the subagent an additional state key to share with the main agent.
For more complex use cases, you can also provide your own prebuilt LangGraph graph as a subagent.
import { tool, createAgent } from "langchain";
import { createSubAgentMiddleware, type SubAgent } from "deepagents";
import { z } from "zod";
const getWeather = tool(
async ({ city }: { city: string }) => {
return `The weather in ${city} is sunny.`;
},
{
name: "get_weather",
description: "Get the weather in a city.",
schema: z.object({
city: z.string(),
}),
},
);
const weatherSubagent: SubAgent = {
name: "weather",
description: "This subagent can get weather in cities.",
systemPrompt: "Use the get_weather tool to get the weather in a city.",
tools: [getWeather],
model: "gpt-5.5",
middleware: [],
};
const agent = createAgent({
model: "claude-sonnet-4-6",
middleware: [
createSubAgentMiddleware({
defaultModel: "claude-sonnet-4-6",
defaultTools: [],
subagents: [weatherSubagent],
}),
],
});In addition to any user-defined subagents, the main agent has access to a general-purpose subagent at all times. This subagent has the same instructions as the main agent and all the tools it has access to. The primary purpose of the general-purpose subagent is context isolation—the main agent can delegate a complex task to this subagent and get a concise answer back without bloat from intermediate tool calls.
Context editing
Manage conversation context by clearing older tool call outputs when token limits are reached, while preserving recent results. This helps keep context windows manageable in long conversations with many tool calls. Context editing is useful for the following:
- Long conversations with many tool calls that exceed token limits
- Reducing token costs by removing older tool outputs that are no longer relevant
- Maintaining only the most recent N tool results in context
import { createAgent, contextEditingMiddleware, ClearToolUsesEdit } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
contextEditingMiddleware({
edits: [
new ClearToolUsesEdit({
triggerTokens: 100000,
keep: 3,
}),
],
}),
],
});Configuration options
edits
ContextEdit[](default: [new ClearToolUsesEdit()])Array of
ContextEditstrategies to apply
ClearToolUsesEditoptions:triggerTokens
number(default: 100000)Token count that triggers the edit. When the conversation exceeds this token count, older tool outputs will be cleared.
clearAtLeast
number(default: 0)Minimum number of tokens to reclaim when the edit runs. If set to 0, clears as much as needed.
keep
number(default: 3)Number of most recent tool results that must be preserved. These will never be cleared.
clearToolInputs
boolean(default: false)Whether to clear the originating tool call parameters on the AI message. When
true, tool call arguments are replaced with empty objects.excludeTools
string[](default: [])List of tool names to exclude from clearing. These tools will never have their outputs cleared.
placeholder
string(default: [cleared])Placeholder text inserted for cleared tool outputs. This replaces the original tool message content.
Full example
The middleware applies context editing strategies when token limits are reached. The most common strategy is
ClearToolUsesEdit, which clears older tool results while preserving recent ones.How it works:
- Monitor token count in conversation
- When threshold is reached, clear older tool outputs
- Keep most recent N tool results
- Optionally preserve tool call arguments for context
> import { createAgent, contextEditingMiddleware, ClearToolUsesEdit } from "langchain";
>
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [searchTool, calculatorTool, databaseTool],
> middleware: [
> contextEditingMiddleware({
> edits: [
> new ClearToolUsesEdit({
> triggerTokens: 2000,
> keep: 3,
> clearToolInputs: false,
> excludeTools: [],
> placeholder: "[cleared]",
> }),
> ],
> }),
> ],
> });
> ```
### LLM tool emulator
Emulate tool execution using an LLM for testing purposes, replacing actual tool calls with AI-generated responses. LLM tool emulators are useful for the following:
* Testing agent behavior without executing real tools.
* Developing agents when external tools are unavailable or expensive.
* Prototyping agent workflows before implementing actual tools.
```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, toolEmulatorMiddleware } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [getWeather, searchDatabase, sendEmail],
middleware: [
toolEmulatorMiddleware(), // Emulate all tools
],
});Configuration options
tools
(string | ClientTool | ServerTool)[]List of tool names (string) or tool instances to emulate. If
undefined(default), ALL tools will be emulated. If empty array[], no tools will be emulated. If array with tool names/instances, only those tools will be emulated.model
string | BaseChatModelModel to use for generating emulated tool responses. Can be a model identifier string (e.g.,
'google:gemini-3.6-flash') or aBaseChatModelinstance. Defaults to the agent's model if not specified.
Full example
The middleware uses an LLM to generate plausible responses for tool calls instead of executing the actual tools.
> import { createAgent, toolEmulatorMiddleware, tool } from "langchain";
> import * as z from "zod";
>
> const getWeather = tool(
> async ({ location }) => `Weather in ${location}`,
> {
> name: "get_weather",
> description: "Get the current weather for a location",
> schema: z.object({ location: z.string() }),
> }
> );
>
> const sendEmail = tool(
> async ({ to, subject, body }) => "Email sent",
> {
> name: "send_email",
> description: "Send an email",
> schema: z.object({
> to: z.string(),
> subject: z.string(),
> body: z.string(),
> }),
> }
> );
>
> // Emulate all tools (default behavior)
> const agent = createAgent({
> model: "gpt-5.5",
> tools: [getWeather, sendEmail],
> middleware: [toolEmulatorMiddleware()],
> });
>
> // Emulate specific tools by name
> const agent2 = createAgent({
> model: "gpt-5.5",
> tools: [getWeather, sendEmail],
> middleware: [
> toolEmulatorMiddleware({
> tools: ["get_weather"],
> }),
> ],
> });
>
> // Emulate specific tools by passing tool instances
> const agent3 = createAgent({
> model: "gpt-5.5",
> tools: [getWeather, sendEmail],
> middleware: [
> toolEmulatorMiddleware({
> tools: [getWeather],
> }),
> ],
> });
>
> // Use custom model for emulation
> const agent5 = createAgent({
> model: "gpt-5.5",
> tools: [getWeather, sendEmail],
> middleware: [
> toolEmulatorMiddleware({
> model: "claude-sonnet-4-6",
> }),
> ],
> });
> ```
## Provider-specific middleware
These middleware are optimized for specific LLM providers. See each provider's documentation for full details and examples.
> **[Anthropic](/oss/javascript/integrations/middleware/anthropic)**
>
> Prompt caching, bash tool, text editor, memory, and file search middleware for Claude models.
> **[AWS](/oss/javascript/integrations/middleware/aws)**
>
> Prompt caching middleware for Amazon Bedrock models.
***
[Connect these docs](/use-these-docs.md) to your agent of choice via MCP for real-time answers.
[Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/langchain/middleware/built-in.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).