LangChain中间件教程及DeepAgents应用

一、中间件概述

1.1 什么是中间件

LangChain 中间件是一套拦截并扩展 Agent 执行流程的机制,它允许开发者在不修改 Agent 核心逻辑的前提下,为 Agent 注入横切能力(如重试、限流、安全校验、上下文管理等),是构建生产级 Agent 的核心工具。

中间件基于钩子(Hook)机制运行,可以在 Agent 执行的特定节点插入自定义逻辑,也可以包裹整个模型/工具调用过程,实现对执行流的完全控制。

1.2 中间件的分类

LangChain 中间件分为两大类:

  1. 预置通用中间件:官方提供的开箱即用组件,兼容所有 LLM 厂商,覆盖绝大多数通用场景,无需自行开发。
  2. 自定义中间件:通过标准 Hook 接口开发,满足业务定制化需求,支持扩展状态、上下文、流处理等能力。

1.3 核心价值

  • 解耦业务与横切逻辑:重试、限流、日志、安全等通用能力与业务逻辑分离,便于维护。
  • 开箱即用:预置中间件经过生产验证,配置简单,快速落地。
  • 高度可扩展:自定义 Hook 机制支持任意复杂的定制逻辑。
  • 可组合性:多个中间件可串联使用,按顺序叠加能力。

二、预置通用中间件详解

所有通用中间件均可兼容 OpenAI、Anthropic、Google 等任意 LLM 提供商,按功能分为 6 大类。

2.1 上下文管理类

用于长对话场景下的 Token 控制与上下文优化,避免超出模型上下文窗口。

2.1.1 对话摘要中间件(summarizationMiddleware)

功能:当对话 Token 数接近阈值时,自动压缩历史消息生成摘要,仅保留最近 N 条完整消息,在保留上下文语义的同时控制 Token 总量。

适用场景

  • 长运行对话、多轮交互应用
  • 需要保留完整对话语义的场景
  • 模型上下文窗口有限的场景

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|--------------------------|------------|-----------------|--------------------|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| model | `string | BaseChatModel` | 是 | - | 用于生成摘要的模型。支持模型标识字符串(如 'openai:gpt-5.4-mini')或模型实例。 |
| trigger | `object | object\[\]` | 否 | 不自动触发 | 摘要触发条件: • 单对象:AND 逻辑 ,所有属性同时满足才触发 • 对象数组:OR 逻辑 ,任一条件满足即触发 每个条件可包含: • fraction:模型上下文窗口占比(0-1),依赖模型 profile 数据 • tokens:绝对 Token 数量阈值 • messages:消息条数阈值 |
| keep | object | 否 | { messages: 20 } | 摘要后保留的上下文量,三选一 : • fraction:保留模型上下文占比(0-1) • tokens:保留的绝对 Token 数 • messages:保留的最近消息条数 |
| tokenCounter | function | 否 | 字符计数 | 自定义 Token 统计函数,用于更精准的阈值判断。 |
| summaryPrompt | string | 否 | 内置模板 | 自定义摘要提示词模板,必须包含 {messages} 占位符。 |
| trimTokensToSummarize | number | 否 | 4000 | 生成摘要前裁剪历史消息的最大 Token 上限,避免摘要模型自身超限。 |
| summaryPrefix | string | 否 | 内置前缀 | 摘要消息的前缀文本,用于标识历史摘要。 |
| maxTokensBeforeSummary | number | 废弃 | - | 已废弃,改用 trigger: { tokens: 值 }。 |
| messagesToKeep | number | 废弃 | - | 已废弃,改用 keep: { messages: 值 }。 |

代码示例

php 复制代码
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, messages: 10 },
      keep: { messages: 20 },
    }),
  ],
});

// 多条件(OR逻辑)+ 占比阈值
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 },
    }),
  ],
});

注意:摘要仅压缩文本内容,图片、音视频等多模态数据不会被压缩。多模态场景建议将媒体存入文件系统,仅传递引用链接。

2.1.2 上下文编辑中间件(contextEditingMiddleware)

功能:通过清理旧的工具调用输出来回收 Token,保留最近的工具结果,比全量摘要更轻量,对对话语义影响更小。

适用场景

  • 工具调用频繁的长对话
  • 降低 Token 成本
  • 仅需保留近期工具结果的场景

核心配置参数

参数名 类型 必填 默认值 详细说明
edits ContextEdit[] [new ClearToolUsesEdit()] 上下文编辑策略数组,核心策略为 ClearToolUsesEdit

ClearToolUsesEdit 子配置

参数名 类型 必填 默认值 详细说明
triggerTokens number 100000 触发清理的对话 Token 阈值,超过该值则开始清理旧工具结果。
clearAtLeast number 0 单次清理最少回收的 Token 数;设为 0 表示按需清理。
keep number 3 必须保留的最近工具结果数量,永远不会被清理。
clearToolInputs boolean false 是否同时清空 AI 消息中的工具调用参数;为 true 时参数会被替换为空对象。
excludeTools string[] [] 排除清理的工具名称列表,列表内工具的输出永远保留。
placeholder string "[cleared]" 工具输出被清理后的占位替换文本。

代码示例

php 复制代码
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]",
        }),
      ],
    }),
  ],
});

2.1.3 服务端工具搜索中间件(providerToolSearchMiddleware)

功能:将工具延迟到模型厂商的服务端搜索,不把所有工具 Schema 一次性塞给模型,减少上下文膨胀,提升工具选择准确率。

适用场景

  • 工具数量多(数十个以上)
  • 减少上下文冗余
  • 提升工具选择准确率

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|-------------------|-----------|--------------------------------|-----|---------|-----------------------------------------------------------------------------------|
| searchableTools | `(string | StructuredToolInterface)\[\]` | 否 | 仅标记工具生效 | 需要延迟加载的工具列表,支持工具名或工具实例。 补充:工具构造时设置 extras.defer_loading: true 也可自动延迟,无需在此处重复声明。 |

限制:仅支持具备服务端工具搜索能力的模型,如 Anthropic Claude 4+ 系列、OpenAI gpt-5.5+。

代码示例

php 复制代码
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 nicheTools = [lookupOrderStatus];
const agent = createAgent({
  model: "anthropic:claude-opus-4-8",
  tools: [getWeather, ...nicheTools],
  middleware: [
    providerToolSearchMiddleware({ searchableTools: nicheTools }),
  ],
});

2.2 成本与限流类

用于控制 API 调用量,防止 Agent 失控导致成本飙升。

2.2.1 模型调用限流中间件(modelCallLimitMiddleware)

功能:限制单轮或单线程内的模型调用次数,防止无限循环与成本失控。

适用场景

  • 生产环境成本管控
  • 测试环境预算控制
  • 防止 runaway agent

核心配置参数

参数名 类型 必填 默认值 详细说明
threadLimit number 无限制 单个对话线程(跨多次 invoke 调用)的总模型调用上限,必须配合 checkpointer 使用才能持久化计数。
runLimit number 无限制 单次用户请求(一次 invoke 周期)内的模型调用上限,每次新请求自动重置。
exitBehavior string "end" 达到上限后的执行行为: • "end":优雅终止,返回结束消息 • "error":抛出异常,立即中断执行

代码示例

php 复制代码
import { createAgent, modelCallLimitMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";

const agent = createAgent({
  model: "gpt-5.5",
  checkpointer: new MemorySaver(), // 线程限流必需
  tools: [],
  middleware: [
    modelCallLimitMiddleware({
      threadLimit: 10,
      runLimit: 5,
      exitBehavior: "end",
    }),
  ],
});

2.2.2 工具调用限流中间件(toolCallLimitMiddleware)

功能:控制工具调用频次,可全局生效或针对单个工具精细化限流。

适用场景

  • 限制高成本外部 API
  • 保护数据库/搜索接口
  • 防速率超限
  • 防止 runaway agent 循环调用

核心配置参数

参数名 类型 必填 默认值 详细说明
toolName string 全局生效 指定单个工具的名称;不填则对所有工具全局生效。可多次实例化实现多工具分别限流。
threadLimit number 无限制 线程级工具调用上限,跨调用持久化,需 checkpointer 支持。
runLimit number 无限制 单次请求内工具调用上限,每轮对话重置。 ⚠️ threadLimitrunLimit 至少指定一个。
exitBehavior string "continue" 达到上限后的行为: • "continue:拦截超限调用并返回错误消息,Agent 继续运行 • "error":抛出 ToolCallLimitExceededError 异常,立即终止 • "end":立即停止并返回工具消息+AI消息,仅单工具限流下可用

代码示例

php 复制代码
import { createAgent, toolCallLimitMiddleware } from "langchain";

const globalLimiter = toolCallLimitMiddleware({ threadLimit: 20, runLimit: 10 });
const searchLimiter = toolCallLimitMiddleware({ toolName: "search", threadLimit: 5, runLimit: 3 });
const strictLimiter = toolCallLimitMiddleware({ toolName: "scrape_webpage", runLimit: 2, exitBehavior: "error" });

const agent = createAgent({
  model: "gpt-5.5",
  tools: [searchTool, databaseTool, scraperTool],
  middleware: [globalLimiter, searchLimiter, strictLimiter],
});

2.3 可靠性与容错类

提升 Agent 在网络波动、服务故障场景下的稳定性。

2.3.1 模型降级中间件(modelFallbackMiddleware)

功能:主模型调用失败时,自动按优先级顺序尝试备用模型,保障服务高可用。

适用场景

  • 高可用生产部署
  • 跨厂商容灾
  • 成本优化

核心配置参数

调用形式特殊:直接传入多个模型字符串作为可变长参数,而非配置对象。

参数名 类型 必填 默认值 详细说明
...models string[] - 按优先级排列的备用模型标识。主模型失败后,按传入顺序依次尝试。

代码示例

php 复制代码
import { createAgent, modelFallbackMiddleware } from "langchain";

const agent = createAgent({
  model: "gpt-5.5",
  tools: [],
  middleware: [
    modelFallbackMiddleware(
      "gpt-5.4-mini",
      "claude-3-5-sonnet-20241022"
    ),
  ],
});

2.3.2 模型重试中间件(modelRetryMiddleware)

功能:模型调用失败时,按指数退避策略自动重试,应对网络抖动、限流、临时故障。

适用场景

  • 处理网络超时、速率限制
  • 临时服务不可用等瞬时故障
  • 提升模型调用成功率

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|------------------|-------------|------------------------------|-----------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| maxRetries | number | 否 | 2 | 初始调用失败后的最大重试次数;总调用次数 = 1 + maxRetries。取值 ≥ 0。 |
| retryOn | `Error\[\] | (error: Error) => boolean` | 否 | () => true | 重试触发条件: • 错误构造函数数组:仅对指定类型的错误重试 • 自定义函数:接收错误对象,返回 true 则重试 |
| onFailure | `'error' | 'continue' | (error: Error) => string` | 否 | "continue" | 所有重试耗尽后的兜底行为: • "continue":返回包含错误信息的 AIMessage,Agent 继续处理 • "error":重新抛出异常,终止执行 • 自定义函数:返回字符串作为 AIMessage 内容 |
| backoffFactor | number | 否 | 2.0 | 指数退避乘数;第 n 次重试延迟 = initialDelayMs * (backoffFactor ** n)。设为 0 则为固定延迟。取值 ≥ 0。 |
| initialDelayMs | number | 否 | 1000 | 第一次重试前的初始等待时间,单位毫秒。取值 ≥ 0。 |
| maxDelayMs | number | 否 | 60000 | 重试间最大等待时间,防止指数退避无限增长。取值 ≥ 0。 |
| jitter | boolean | 否 | true | 是否给延迟添加 ±25% 的随机抖动,避免惊群效应。 |

代码示例

php 复制代码
import { createAgent, modelRetryMiddleware } from "langchain";

// 基础用法
const agent = createAgent({
  model: "gpt-5.5",
  tools: [searchTool],
  middleware: [modelRetryMiddleware()],
});

// 自定义错误过滤 + 固定延迟
const constantBackoff = modelRetryMiddleware({
  maxRetries: 5,
  backoffFactor: 0.0, // 无指数增长
  initialDelayMs: 2000, // 固定2秒
  retryOn: (error) => error.name === "RateLimitError",
});

2.3.3 工具重试中间件(toolRetryMiddleware)

功能:工具调用失败时自动指数退避重试,逻辑与模型重试一致,专为外部 API、数据库等网络依赖工具设计。

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|------------------|---------------|------------------------------|-----------------------------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
| tools | `(ClientTool | ServerTool | string)\[\]` | 否 | 全部工具 | 指定应用重试逻辑的工具列表,支持工具实例或工具名;不填则对所有工具生效。 |
| maxRetries | number | 否 | 2 | 初始调用后的最大重试次数。 |
| retryOn | `Error\[\] | (error: Error) => boolean` | 否 | () => true | 重试触发条件,同模型重试。 |
| onFailure | `'error' | 'continue' | (error: Error) => string` | 否 | "continue" | 重试耗尽后的行为: • "continue":返回带错误的 ToolMessage • "error":抛出异常终止 • 自定义函数:返回自定义错误文本 ⚠️ 废弃值:"raise" 对应 "error""return_message" 对应 "continue" |
| backoffFactor | number | 否 | 2.0 | 指数退避乘数。 |
| initialDelayMs | number | 否 | 1000 | 初始延迟毫秒数。 |
| maxDelayMs | number | 否 | 60000 | 最大延迟上限。 |
| jitter | boolean | 否 | true | 是否添加随机抖动。 |

代码示例

javascript 复制代码
import { createAgent, toolRetryMiddleware } from "langchain";

// 仅对特定工具生效 + 自定义错误文案
const formatError = (error: Error) =>
  "Database temporarily unavailable. Please try again later.";

const retrySpecificTools = toolRetryMiddleware({
  maxRetries: 4,
  tools: ["search_database"],
  onFailure: formatError,
});

2.4 安全合规类

2.4.1 人工介入中间件(humanInTheLoopMiddleware)

功能:在指定工具执行前暂停 Agent,等待人工审批、编辑或拒绝,为高风险操作增加人工把关。

适用场景

  • 高风险操作(数据库写入、财务交易)
  • 合规强监管场景
  • 需要人工引导的长任务

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|---------------|--------------------------|-----------|-----|------|----------------------------------------------------------------------------------------------------------------------------------|
| interruptOn | `Record<string, object | false>` | 是 | - | 按工具名配置中断规则: • 键:工具名称 • 值为 false:该工具不中断,直接执行 • 值为配置对象时: - allowedDecisions:允许的人工操作,可选 approve(批准)、edit(编辑参数)、reject(拒绝) |

⚠️ 必须配合 checkpointer 使用,用于持久化中断状态,支持跨会话恢复执行。

代码示例

php 复制代码
import { createAgent, humanInTheLoopMiddleware } from "langchain";

const agent = createAgent({
  model: "gpt-5.5",
  tools: [readEmailTool, sendEmailTool],
  middleware: [
    humanInTheLoopMiddleware({
      interruptOn: {
        sendEmailTool: {
          allowedDecisions: ["approve", "edit", "reject"],
        },
        readEmailTool: false,
      }
    })
  ]
});

2.4.2 PII 检测中间件(piiMiddleware)

功能:检测对话中的个人身份信息(邮箱、信用卡、手机号等),支持多种脱敏处理策略。

适用场景

  • 医疗/金融合规场景
  • 客服日志脱敏
  • 敏感数据防护

核心配置参数

调用形式:piiMiddleware(piiType, options),第一个为位置参数,第二个为配置对象。

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|----------------------|-----------|--------|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|-------|-----------------------------------------------------------------------------------------------|
| piiType(第1位位置参数) | string | 是 | - | PII 类型。内置类型:emailcredit_cardipmac_addressurl;也可自定义类型名配合 detector 使用。 |
| strategy | string | 否 | "redact" | 检测到 PII 后的处理策略: • "block":抛出错误,阻断执行 • "redact":完整替换为 [REDACTED_类型名]"mask":部分掩码(如信用卡保留后四位) • "hash":替换为确定性哈希,可跨会话关联但不可逆 |
| detector | `string | RegExp | (content: string) => PIIMatch\[\]` | 否 | 内置检测器 | 自定义检测器,三种形式: • 字符串:正则表达式字符串 • RegExp 对象:正则实例,可指定匹配标志 • 自定义函数:接收文本,返回 PIIMatch[] 数组,支持复杂校验 |
| applyToInput | boolean | 否 | true | 是否在模型调用前检查用户输入消息。 |
| applyToOutput | boolean | 否 | false | 是否在模型调用后检查 AI 输出消息。 |
| applyToToolResults | boolean | 否 | false | 是否检查工具执行后的返回结果。 |

PIIMatch 结构

typescript 复制代码
interface PIIMatch {
  text: string;    // 匹配到的文本
  start: number;   // 起始索引
  end: number;     // 结束索引
}

代码示例

php 复制代码
import { createAgent, piiMiddleware, type PIIMatch } from "langchain";

// 内置类型使用
const agent = createAgent({
  model: "gpt-5.5",
  tools: [],
  middleware: [
    piiMiddleware("email", { strategy: "redact", applyToInput: true }),
    piiMiddleware("credit_card", { strategy: "mask", applyToInput: true }),
  ],
});

// 自定义检测器(正则方式)
const agent2 = createAgent({
  model: "gpt-5.5",
  tools: [],
  middleware: [
    piiMiddleware("api_key", {
      detector: "sk-[a-zA-Z0-9]{32}",
      strategy: "block",
    }),
  ],
});

2.5 能力增强类

2.5.1 待办清单中间件(todoListMiddleware)

功能 :为 Agent 自动注入 write_todos 工具和配套系统提示,赋予任务规划与进度跟踪能力。

适用场景

  • 复杂多步骤任务
  • 长运行操作
  • 需要可视化进度的场景

配置参数:无配置参数,使用默认值即可。

代码示例

php 复制代码
import { createAgent, todoListMiddleware } from "langchain";

const agent = createAgent({
  model: "gpt-5.5",
  tools: [readFile, writeFile, runTests],
  middleware: [todoListMiddleware()],
});

2.5.2 LLM 工具选择器中间件(llmToolSelectorMiddleware)

功能:调用主模型前,先用一个轻量模型筛选出当前查询相关的工具,减少主模型的工具列表长度,降低 Token 消耗并提升准确率。

适用场景

  • 工具数量多(10+)
  • 降低 Token 消耗
  • 提升模型聚焦度与准确率

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|-----------------|------------|-----------------|------|-------------------------------------------|---------------------------|
| model | `string | BaseChatModel` | 否 | Agent 主模型 | 执行工具选择的模型,建议使用轻量小模型以降低成本。 |
| systemPrompt | string | 否 | 内置提示 | 工具选择模型的系统提示词,用于指导筛选逻辑。 |
| maxTools | number | 否 | 无限制 | 最多选中的工具数量;超过时仅取前 N 个。 |
| alwaysInclude | string[] | 否 | [] | 强制包含的工具名列表,不计入 maxTools 限额。 |

代码示例

php 复制代码
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"],
    }),
  ],
});

2.6 测试调试类

LLM 工具模拟器中间件(toolEmulatorMiddleware)

功能:用 LLM 生成模拟的工具返回结果,不执行真实工具,用于开发调试与原型验证。

适用场景

  • 开发阶段调试 Agent 逻辑
  • 外部工具不可用时的开发
  • 低成本原型验证

核心配置参数

| 参数名 | 类型 | 必填 | 默认值 | 详细说明 |
|---------|-----------|-----------------|-------------------|-----------|--------------|-----------------------------------------------------------------------|
| tools | `(string | ClientTool | ServerTool)\[\]` | 否 | 全部工具 | 指定要模拟的工具: • 不填/undefined:模拟所有工具 • 空数组 []:不模拟任何工具 • 非空数组:仅模拟列表内的工具 |
| model | `string | BaseChatModel` | 否 | Agent 主模型 | 生成模拟工具响应的模型。 |

代码示例

php 复制代码
import { createAgent, toolEmulatorMiddleware } from "langchain";

// 仅模拟指定工具
const agent2 = createAgent({
  model: "gpt-5.5",
  tools: [getWeather, sendEmail],
  middleware: [
    toolEmulatorMiddleware({
      tools: ["get_weather"],
    }),
  ],
});

三、自定义中间件开发指南

当预置中间件无法满足业务需求时,可通过 createMiddleware 函数基于 Hook 机制开发自定义中间件。

3.1 两种 Hook 范式

中间件提供两类 Hook,分别对应不同的使用场景:

3.1.1 Node-style 钩子(节点式)

在 Agent 执行的特定节点顺序执行,适合日志、校验、状态更新等无侵入逻辑。

钩子 触发时机
beforeAgent Agent 启动前(单次调用执行一次)
beforeModel 每次模型调用前
afterModel 每次模型响应后
afterAgent Agent 结束后(单次调用执行一次)

示例:日志中间件

javascript 复制代码
import { createMiddleware } from "langchain";

const loggingMiddleware = createMiddleware({
  name: "LoggingMiddleware",
  beforeModel: (state) => {
    console.log(`调用模型,当前消息数: ${state.messages.length}`);
  },
  afterModel: (state) => {
    const lastMsg = state.messages[state.messages.length - 1];
    console.log(`模型返回: ${lastMsg.content}`);
  },
});

3.1.2 Wrap-style 钩子(包裹式)

环绕包裹每次模型/工具调用,可以控制是否执行、执行次数、修改入参出参,适合重试、缓存、请求改写等控制流逻辑。

钩子 触发时机
wrapModelCall 包裹每次模型调用
wrapToolCall 包裹每次工具调用

示例:自定义重试中间件

typescript 复制代码
import { createMiddleware } from "langchain";

const createRetryMiddleware = (maxRetries: number = 3) => {
  return createMiddleware({
    name: "RetryMiddleware",
    wrapModelCall: async (request, handler) => {
      for (let attempt = 0; attempt < maxRetries; attempt++) {
        try {
          return await handler(request);
        } catch (e) {
          if (attempt === maxRetries - 1) throw e;
          console.log(`第 ${attempt + 1} 次重试,错误: ${e}`);
        }
      }
      throw new Error("Unreachable");
    },
  });
};

3.2 基础开发步骤

  1. 使用 createMiddleware 函数创建中间件,指定 name 作为唯一标识。
  2. 根据需求选择 Node-style 或 Wrap-style 钩子,实现对应逻辑。
  3. 如需扩展状态或上下文,定义 stateSchemacontextSchema
  4. 将中间件加入 createAgentmiddleware 数组中。

3.3 状态与上下文管理

3.3.1 自定义状态 Schema

中间件可以扩展 Agent 状态,在多个钩子间共享数据。使用 stateSchema 定义字段,以下划线 _ 开头的字段为私有字段,不会在最终结果中返回。

php 复制代码
import { createMiddleware } from "langchain";
import * as z from "zod";

const callCounterMiddleware = createMiddleware({
  name: "CallCounterMiddleware",
  stateSchema: z.object({
    modelCallCount: z.number().default(0),
    _internalFlag: z.boolean().default(false), // 私有字段,结果不返回
  }),
  afterModel: (state) => {
    return { modelCallCount: state.modelCallCount + 1 };
  },
});

3.3.2 自定义上下文 Context

上下文是单次调用的只读元数据,不会持久化,适合传递用户ID、租户信息、配置项等。通过 contextSchema 定义,在钩子中通过 request.runtime.context 访问。

javascript 复制代码
import { createAgent, createMiddleware, HumanMessage } from "langchain";
import * as z from "zod";

const contextSchema = z.object({
  userId: z.string(),
  tenantId: z.string(),
});

const userContextMiddleware = createMiddleware({
  name: "UserContextMiddleware",
  contextSchema,
  wrapModelCall: (request, handler) => {
    const { userId, tenantId } = request.runtime.context;
    const contextText = `User ID: ${userId}, Tenant: ${tenantId}`;
    const newSystemMessage = request.systemMessage.concat(contextText);
    return handler({
      ...request,
      systemMessage: newSystemMessage,
    });
  },
});

3.4 多中间件执行顺序

当配置多个中间件时,遵循洋葱模型

  • before* 钩子:按数组顺序正序执行
  • wrap* 钩子:数组第一个中间件在最外层,嵌套包裹后续中间件
  • after* 钩子:按数组顺序逆序执行

执行流程示意(middleware1 → middleware2 → middleware3):

erlang 复制代码
beforeAgent 1 → beforeAgent 2 → beforeAgent 3
  ↓
beforeModel 1 → beforeModel 2 → beforeModel 3
  ↓
wrapModel 1 ── wrapModel 2 ── wrapModel 3 ── 模型调用
  ↓
afterModel 3 → afterModel 2 → afterModel 1
  ↓
afterAgent 3 → afterAgent 2 → afterAgent 1

3.5 提前终止与跳转

在 Node-style 钩子中可以通过返回 jumpTo 提前跳转执行节点:

  • 'end':直接跳转到 Agent 结束
  • 'tools':跳转到工具执行节点
  • 'model':跳转到模型调用节点

示例:内容拦截中间件

php 复制代码
import { createAgent, createMiddleware, AIMessage } from "langchain";

const agent = createAgent({
  model: "gpt-5.5",
  middleware: [
    createMiddleware({
      name: "BlockedContentMiddleware",
      beforeModel: {
        canJumpTo: ["end"],
        hook: (state) => {
          if (state.messages.at(-1)?.content.includes("BLOCKED")) {
            return {
              messages: [new AIMessage("I cannot respond to that request.")],
              jumpTo: "end" as const,
            };
          }
          return;
        },
      },
    }),
  ],
});

3.6 常用自定义场景示例

动态修改系统提示

javascript 复制代码
import { createMiddleware, SystemMessage, createAgent } from "langchain";

const addContextMiddleware = createMiddleware({
  name: "AddContextMiddleware",
  wrapModelCall: async (request, handler) => {
    return handler({
      ...request,
      systemMessage: request.systemMessage.concat(`Additional context.`),
    });
  },
});

动态模型选择

ini 复制代码
import { createMiddleware, initChatModel } from "langchain";

const models = {
  complex: await initChatModel("claude-sonnet-4-6"),
  simple: await initChatModel("claude-haiku-4-5-20251001"),
};

const dynamicModelMiddleware = createMiddleware({
  name: "DynamicModelMiddleware",
  wrapModelCall: (request, handler) => {
    const modifiedRequest = { ...request };
    modifiedRequest.model = request.messages.length > 10 ? models.complex : models.simple;
    return handler(modifiedRequest);
  },
});

工具调用监控

javascript 复制代码
import { createMiddleware } from "langchain";

const toolMonitoringMiddleware = createMiddleware({
  name: "ToolMonitoringMiddleware",
  wrapToolCall: (request, handler) => {
    console.log(`Executing tool: ${request.toolCall.name}`);
    console.log(`Arguments: ${JSON.stringify(request.toolCall.args)}`);
    try {
      const result = handler(request);
      console.log("Tool completed successfully");
      return result;
    } catch (e) {
      console.log(`Tool failed: ${e}`);
      throw e;
    }
  },
});

3.7 开发最佳实践

  1. 单一职责:每个中间件只做一件事,便于复用和测试。
  2. 错误容错:中间件异常不应导致 Agent 崩溃,做好异常捕获。
  3. 选型合适的 Hook:顺序逻辑用 Node-style,控制流用 Wrap-style。
  4. 文档化状态:清晰说明自定义状态字段的含义。
  5. 独立单元测试:先单独测试中间件逻辑,再集成到 Agent。
  6. 合理排序:核心管控类中间件放在数组前面。
  7. 优先使用预置:能用预置中间件满足的场景,不重复造轮子。

四、在 Deep Agents 中应用中间件

Deep Agents 是基于 LangChain + LangGraph 构建的高级 Agent 框架,专注于复杂多步骤任务。它内置了一套核心中间件,同时完全兼容 LangChain 所有预置和自定义中间件。

4.1 Deep Agents 内置核心中间件

以下中间件是 Deep Agents 默认集成的基础能力,属于框架的核心骨架,不可移除但支持配置。

4.1.1 文件系统中间件(createFilesystemMiddleware)

功能:为 Agent 提供虚拟文件系统能力,是上下文管理、长时记忆、代码执行的基础载体。

内置工具lsread_filewrite_fileedit_fileglobgrep 等。

核心配置参数

参数名 类型 必填 默认值 详细说明
backend Backend 实例 StateBackend 存储后端: • StateBackend:状态存储,短期有效,仅当前线程内可见 • StoreBackend:持久化存储,跨线程共享,需配合 store 实例 • CompositeBackend:混合后端,按路径前缀路由到不同后端
systemPrompt string 内置提示 自定义文件系统相关的系统提示。
customToolDescriptions Record<string, string> - 自定义文件系统工具的描述,引导模型正确使用。

代码示例(混合存储后端)

javascript 复制代码
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() } // /memories/ 路径持久化
      ),
    }),
  ],
});

注:createDeepAgent 默认已包含文件系统中间件,无需手动引入,仅需自定义配置时传入即可。

4.1.2 子代理中间件(createSubAgentMiddleware)

功能 :赋予主代理生成子代理的能力,通过 task 工具委派子任务,实现上下文隔离与并行处理。

特性

  • 内置 general-purpose 通用子代理,继承主代理的所有工具
  • 支持自定义子代理,可单独配置模型、工具、中间件
  • 子代理在独立上下文窗口中运行,仅返回最终结果

核心配置参数

参数名 类型 必填 默认值 详细说明
defaultModel string - 子代理默认使用的模型,子代理未单独指定 model 时生效。
defaultTools Tool[] [] 所有子代理默认继承的工具列表。
subagents SubAgent[] [] 自定义子代理数组,每个子代理包含: • name:子代理名称 • description:功能描述 • systemPrompt:专属系统提示 • tools:可用工具 • model:可选,单独指定模型 • middleware:可选,专属中间件

代码示例

php 复制代码
import { createAgent } from "langchain";
import { createSubAgentMiddleware } from "deepagents";

const agent = createAgent({
  model: "claude-sonnet-4-6",
  middleware: [
    createSubAgentMiddleware({
      defaultModel: "claude-sonnet-4-6",
      subagents: [
        {
          name: "weather",
          description: "查询城市天气的子代理",
          systemPrompt: "使用 get_weather 工具获取天气",
          tools: [getWeather],
        },
      ],
    }),
  ],
});

4.2 挂载外部中间件的方法

createDeepAgent 支持 middleware 参数,可直接传入 LangChain 预置中间件或自定义中间件,用法与普通 Agent 完全一致。

完整示例

php 复制代码
import { createDeepAgent } from "deepagents";
import { 
  toolRetryMiddleware, 
  piiMiddleware, 
  todoListMiddleware,
  summarizationMiddleware 
} from "langchain";

const agent = await createDeepAgent({
  model: "openai:gpt-5.5",
  tools: [searchTool, databaseTool],
  middleware: [
    // 任务规划
    todoListMiddleware(),
    // 对话摘要
    summarizationMiddleware({ model: "gpt-5.4-mini", trigger: { fraction: 0.8 } }),
    // 工具重试
    toolRetryMiddleware({ maxRetries: 3 }),
    // PII 脱敏
    piiMiddleware("email", { strategy: "redact" }),
  ],
  systemPrompt: "你是一个高效的助手,按步骤完成任务",
});

4.3 使用注意事项

  1. 核心中间件不可移除 :Filesystem、Subagent 是 Deep Agents 的基础骨架,无法通过配置移除。若需隐藏对应工具,可通过 harness profileexcluded_tools 配置。
  2. 执行顺序:用户传入的中间件会与内置中间件合并,遵循标准洋葱模型执行。
  3. 子代理继承:子代理默认不继承主代理的所有中间件,如需子代理也具备对应能力,需在子代理配置中单独声明。
  4. 避免重复配置:Deep Agents 已内置上下文管理、提示词缓存等能力,挂载同类中间件时注意避免重复。

五、整体最佳实践

  1. 按需组合:根据业务场景选择合适的中间件组合,避免过度配置增加不必要的开销。
  2. 顺序优先:安全、限流类中间件放在最前面,确保在执行核心逻辑前完成校验。
  3. 测试验证:上线前充分测试中间件的边界行为,如限流触发、重试耗尽、异常兜底等。
  4. 监控埋点:结合日志中间件或自定义中间件,添加关键指标埋点,便于排查问题。
  5. 版本兼容:关注 LangChain 版本变更,及时替换已废弃的参数和 API。
相关推荐
huabuyu2 小时前
INP 自动化归因与自愈:如何自动定位、修复并验证一次卡顿
前端·javascript
BreezeJiang2 小时前
浏览器里的 DeepSeek 到底怎么跑起来?关键在 Worker、缓存和流式消息
javascript
BreezeJiang2 小时前
单例模式真正解决的不是“只能 new 一次”,而是统一资源入口
javascript
小林ixn2 小时前
WebGPU + Transformers.js:把 DeepSeek-R1 塞进浏览器,真香!
javascript·llm·gpu
xiaominlaopodaren2 小时前
three.js地图数学基础(四):瓦片金字塔
javascript·gis·three.js
mONESY3 小时前
🔥 React Hooks 进阶实战:受控组件 + 性能优化一站式掌握
javascript
修远客3 小时前
感知模块:Agent的眼睛和耳朵 — 三层降级策略让Agent永不"失明"
python·agent
Synmbrf3 小时前
flv播放设置hasAudio为true黑屏
javascript
看到我请叫我铁锤3 小时前
vue编写web端在线预览文档
前端·javascript·vue.js