木厶笔记存放站木厶笔记存放站
首页
文章
首页
文章
  • Web

    • 网络

      • 01-01 HTTP 基础
      • 01-02 HTTP1.1 首部字段
      • 01-03 HTTP 优化
      • 01-04 HTTPS
      • 01-05 TCP 和 UDP
      • 01-06 URI 和 URL
      • 01-07 Cookie、Session 与 Token
      • 01-08 输入URL_至页面显示的过程
    • 安全

      • CSRF 攻击
      • JSON Web Token
      • sql 注入
      • XSS 攻击
    • 浏览器机制

      • 提升SEO
      • 浏览器内核
      • 浏览器的进程和线程
      • 浏览器缓存机制
      • 跨域
    • 场景实现

      • 优化大量图片加载
      • 扫码登录
    • 性能优化

      • 性能分析
      • 资源请求优化
      • 运行时优化
      • 重排重绘
      • 静态资源优化
  • HTML

    • HTML5基础

      • 01-01 简介
      • 01-02 全局属性
      • 01-03 特殊字符编码
      • 01-04 标签
      • 02-01 图片标签
      • 02-02 a标签
      • 02-03-1 video&audio
      • 02-03-2 AudioContext
      • 02-04 iframe
      • 02-05 表格标签
      • 03-01 form
      • 03-02 fieldset
      • 03-03 label
      • 03-04 input
      • 03-05 select
      • 03-06 datalist
      • 03-07 textarea
      • 03-08 progress
      • 03-09 meter
      • 04-01 dialog
      • 04-02 折叠标签
    • WEBAPI

      • 01-01 Canvas
      • 01-02 Canvas-绘制图形
      • 01-03 Canvas-画布处理
      • 01-04 Canvas-像素滤镜
      • 01-05 OffscreenCanvas
      • 01-06 Canvas-性能优化
      • 02-01 Svg
      • 02-02 Svg-d属性
      • 02-03 Svg-动画
      • 02-04 Svg-foreignObject
      • 03-01 customElements
      • 03-02 shadow-dom
      • 03-03 template
      • 04-01 Drag
  • CSS

    • 基础

      • 01-01 CSS 架构
      • 01-02 At规则
      • 01-03 媒体查询
      • 01-04 @support
      • 02-01 层叠顺序与堆栈上下文
      • 02-02 盒模型
      • 02-03 文档流
      • 02-04 float 浮动
      • 02-05 元素不可见
      • 03-01 视觉格式化模型-基础概念
      • 03-02 视觉格式化模型-格式化上下文(FC)
      • 04-01-显示默认值
      • 05-01 RGB&HSL&HWB&LAB&LCH
      • 05-02 色域和颜色空间
      • 05-03 OKLCH&OKLAB
      • 05-04 相对颜色语法
      • 05-05 color-min()
      • 05-06 线性渐变
      • 05-07 径向渐变
      • 05-08 锥形渐变
      • 06-01 比较函数-min&max&clamp
      • 06-02 三角函数
      • 07-01 文本换行
      • 07-02 自定义字体
      • 08-01 CSS变量
      • 08-02 @property
      • 09-01 相对单位
      • 10-01 CSS 嵌套
      • 11-01 固定宽高比
      • 12-01 clip-path
      • 12-02 mask
      • 12-03 object-view-box
      • 13-01 filter
      • 13-02 backdrop-filter
    • CSS 选择器

      • 01 选择器优先级
      • 02 选择符
      • 03 属性选择器
      • 04 伪类
      • 05 树结构伪类
      • 06 逻辑选择器
      • 07 数学函数
    • 布局

      • flex 布局--实用
      • flex 布局
      • Grid 布局--实用
      • Grid 布局
      • 响应式布局
      • 换肤
      • 移动端布局
      • 锚点定位布局
    • 动画与过渡

      • 01-01 CSS 动画
      • 02-01 view transition
      • 03-01 缓动效果
      • 03-02 逐帧动画
      • 03-03 呼吸&摆钟效果
      • 03-04 打字动画
      • 03-05 沿圆形路径移动动画
      • 03-06 3D翻转卡片
      • 03-08 骨架屏效果
      • 03-09 描边动画
      • 03-10 液态玻璃效果
    • 小寄巧

      • 00-01 初始化 CSS
      • 01-01 半透明边框
      • 01-02 多重边框
      • 01-03 边框内圆角
      • 01-04 连续的图像边框
      • 02-01 自适应的椭圆
      • 02-02 平行四边形
      • 02-03 切角效果
      • 02-04 折角效果
      • 02-05 梯形标签页
      • 02-06 简单的饼图
      • 03-01 单侧投影
      • 03-02 不规则投影
      • 04-01 文字下划线
      • 04-02 渐变色字体
      • 04-03 字体描边
      • 04-04 字体阴影&艺术字体
      • 04-05 任意路径排布文字
      • 04-06 文本省略号
      • 05-01 扩大可点击区域
      • 05-02 交互式的图片对比控件
      • 05-03 简单视差效果
      • 06-01 自适应内部元素
      • 06-02 根据兄弟元素的数量来设置样式
      • 06-03 垂直居中
      • 06-04 消除 inline-block 间隙
      • 07-01 精确控制表格列宽
      • 08-01 修改svg图片颜色
      • 08-02 雪碧图
  • JavaScript

    • 基础

      • 2 script标签
      • 3-1 语法
      • 3-3 var let const
      • 3-4-1 typeof 和 instanceof
      • 3-4-2 Undefined 类型
      • 3-4-3 Null 类型
      • 3-4-4 Boolean 类型
      • 3-4-5 Number 类型
      • 3-4-6 String 类型
      • 3-4-7 Symbol 类型
      • 3-4-8 Object 类型
      • 3-4-9 BigInt 类型
      • 3-5 操作符
      • 3-6 隐式转换
      • 4-1 原始值和引用值
      • 4-2 执行上下文与作用域
      • 4-3 垃圾回收
      • 5-1 Date
      • 5-2 RegExp
      • 5-3 原始值包装类型
      • 5-4-1 Global 单例内置对象
      • 5-4-2 Math 单例内置对象
      • 6-2 Array
      • 6-3 Map
      • 6-4 WeakMap
      • 6-5 Set
      • 6-6 WeakSet
      • 7-1 迭代器与生成器
      • 8-1-1 Object 属性
      • 8-1-2 Object 构造函数方法
      • 8-1-3 Object 语法增强和解构
      • 8-2 Object 创建
      • 8-3 原型和继承
      • 8-4 class 类
      • 8-5 this
      • 8-6 可选链和空值合并运算符
      • 9 proxy 代理与反射
      • 10-1 Function 函数
      • 10-2 apply call bind
      • 10-3 闭包
      • 10-4 私有变量
      • 10-5 扩展运算符和 rest
      • 11-1 event loop 事件循环机制
      • 11-2 Promise 期约
      • 11-3 async await
      • 12-1 BOM
      • 14-1-1 DOM 节点
      • 14-1-2 DOM Document 类型
      • 14-1-3 DOM Element 类型
      • 14-1-4 DOM Text 和 Comment 类型
      • 14-2 动态脚本和动态样式
      • 14-3 MutationObserver 监听节点变化
      • 14-4 Intersection Observer
      • 16-2-1 操控样式
      • 16-2-2 元素尺寸
      • 16-3 DOM 深度优先遍历
      • 17-1 事件流
      • 17-2 事件处理程序
      • 17-3 事件对象
      • 17-4-1 UI 和焦点事件
      • 17-4-2 鼠标和滚轮事件
      • 17-4-3 键盘与输入事件
      • 17-4-4 HTML5 事件
      • 17-4-5 设备和触摸事件
      • 17-6 模拟事件
      • 18-1 requestAnimationFrame&requestIdleCallback
      • 18-2 canvas
      • 19-1 表单基础
      • 19-2 文本框编程
      • 19-3 选择框编程
      • 19-5 富文本编辑器
      • 20-10 Perfromance API
      • 20-11-1 HTML 模板
      • 20-11-2 影子 DOM
      • 20-11-3 自定义元素
      • 20-14 MessageChannel和BroadcastChannel
      • 20-15 AbortController
      • 20-4 File API 与 Blob API
      • 20-7 Notifications API
      • 20-8 Page Visibility API
      • 20-9 Streams API
      • 23 JSON
      • 24-03 Server-Sent-Events
      • 24-04 WebSocket
      • 24-1 XMLHttpRequest
      • 24-5 Fetch API
      • 25-01 cookie
      • 25-02 Web-Storage
      • 26-1 模块语法
      • 26-2 CommonJs 与 ES6 Module 的差异
      • 27-01 WebWorker
    • 设计模式

      • 单例模式
      • 发布订阅模式
      • 工厂模式
      • 策略模式
      • 装饰器模式
      • 观察者模式
      • 适配器模式
    • 手写实现

      • apply call bind
      • flat
      • instanceof
      • JSONP
      • new
      • Promise API
      • trim
      • 数组去重
      • 柯里化
      • 深拷贝
      • 防抖和节流
    • 场景题

      • 映射 URL 参数
      • 模拟红绿灯
      • 请求-丢弃旧时序的请求
      • 请求-并发请求
      • 通过 value 找 key
      • 闭包题
    • 移动端

      • touch 事件
      • visualViewport
      • 像素
      • 移动端布局
      • 视口 Viewport
    • 实用

      • better-scroll 滚动组件
      • Proxy实现英文字母升降序
      • 区别数组和对象
      • 图片懒加载
      • 按首字母排序的列表
      • 控制粘贴板
    • PIXI

      • 1 Application
      • 2 Graphics
      • 3 loader
      • 4 Sprite
      • 5 Spine
      • 6 事件
      • 7 Renderer
  • Node

    • 基础

      • crypto 加密模块
      • fs 模块
      • http 模块
      • mysql 模块
      • redis 模块
    • 框架

      • express
      • koa2
    • 实用

      • @elasticelasticsearch
      • restful Mock 数据
      • 自定义 Mock 数据
    • 错误

      • pm2-watch报错 502
      • spawn 中文乱码
  • Jquery

    • 基础

      • 事件
      • 动画
      • 工具方法
      • 操作 dom
      • 获取元素
  • TypeScript

    • 环境

      • config
      • 环境
    • 基础

      • 01 原始类型和特殊类型
      • 02 字面量和类型拓宽
      • 03 interface 和 type
      • 04 数组和元组
      • 05 class(类)
      • 06 函数和重载
      • 07 联合类型和交叉类型
      • 08 泛型
      • 09 类型推断和类型断言
      • 10 匹配提取
      • 11 重新构造
      • 12 递归循环
      • 13数组长度做计数
      • 14 特殊特性
      • 15 内置高级类型
      • 16 inter extends
      • 17 协变和逆变
  • Vue

    • 环境

      • 安装
      • 自定义环境变量
    • 基础(2.x)

      • data
      • keep-alive
      • nextTick
      • props和sync
      • ref
      • v-for和v-if
      • 事件绑定
      • 动态组件
      • 动画
      • 循环渲染
      • 插槽 slot
      • 条件渲染
      • 样式绑定
      • 模板语法
      • 生命周期
      • 组件通讯
      • 自定义指令
      • 表单绑定
      • 计算属性和监听器
    • 基础(3.x)

      • Composition API
      • Script setup
      • Suspense
      • sync 语法糖
      • Teleport
      • vue3的升级点
      • 常用动画
      • 生命周期
    • router

      • vue-router 3
      • vue-router 4
    • vuex

      • pinia
      • vuex
      • vuex4
      • 刷新不丢失 vuex
    • 底层

      • MVVM
      • 双向绑定
      • 响应式
      • 模板编译
      • 渲染过程
      • 虚拟DOM和diff算法
    • 应用

      • 权限管理
  • React

    • 环境

      • 安装
      • 自定义环境变量
    • 基础

      • JSX
      • ref
      • SCU
      • 不可变数据 setState
      • 事件
      • 动画
      • 异步组件
      • 插槽
      • 条件
      • 生命周期
      • 组件公共逻辑抽离
      • 组件通讯
      • 表单
      • 逃离组件 portals
    • hooks

      • hooks
      • react-query
      • useClickOutSide —— 点击外面
      • useDebounce —— 防抖
      • useURLQueryParam —— 输入框值与search绑定
    • router

      • react-router-com 6
      • react-router-dom hooks
      • react-router-dom
    • redux

      • react-redux
      • redux 与 hooks
      • redux-persist
      • redux-thunk
      • redux-toolkit
      • redux
    • 底层

      • JSX本质
      • setState 原理
      • 合成事件
    • 实用

      • CSS-in-JS
      • 字体库
      • 数组优化为哈希表
      • 组件的子元素只能是规定的元素
  • Echarts

    • 基础

      • 仪表盘
      • 基础
      • 折线图
      • 散点图
      • 柱状图
      • 雷达图
      • 饼图
  • Electron

    • 环境

      • 基本安装
      • 集合 react
    • 基础

      • Dialog
      • 原生菜单
      • 右键菜单
      • 进程
  • 前端工程化

    • babel

      • babel7 实践
      • 工作原理
      • 生态
    • Browserslist

      • 基础
    • npm

      • npm模块安装机制
      • npm脚本
    • qiankun

      • 隔离原理
    • Vite

      • css
      • gzip-打包
      • vite config 常见配置
      • 环境
      • 静态资源
    • webpack

      • css环境
      • js&ts环境
      • loader
      • loader和plugin的区别及编写
      • plugin
      • splitChunks
      • webpack5--模块联邦
      • 代码压缩
      • 优化性能
      • 图片
      • 性能分析工具
      • 提高构建速度
      • 构建性能--并行
      • 构建性能--持久化缓存
      • 热更新
    • 代码提交规范

      • husky&lint-staged
  • Java

    • 环境

      • idea 创建 maven-archetype-webapp
      • spring boot web 的配置参考
      • spring 从初始配置
      • 与 git 集合
    • Web 基础

      • mybatis pager的应用
      • [] 和 List 和 Set
      • 全局 cors 跨域
      • 接口例子——登录
      • 文件接口——图片
      • 有效时间的唯一字符串
      • 自定义类
      • 通用的泛型服务端响应对象
  • Elastic

    • 基础

      • 分词规则
      • 基础概念
      • 基础语法
      • 环境搭建
    • 实用

      • canal——数据库准实时导入
      • logstash——数据库基于时间轴导入
  • Mysql

    • 基础

      • 字段操作
      • 数据库操作
      • 查询操作
      • 表操作
  • Python

    • 基础

      • 1-1-Number
      • 1-2-String
      • 1-3-list和tuple
      • 1-4-序列
      • 1-5-set
      • 1-6-dict
      • 2-1-运算符
      • 2-2-对象比较
      • 3-1-条件判断
      • 3-2-循环
      • 4-1-模块-包
      • 4-2-模块-__init__
      • 4-3-模块-内置变量
      • 4-4-模块-导入
      • 5-1-解包
      • 5-2-函数参数
      • 5-3-作用域
      • 5-4-高阶函数&三元表达式
      • 6-1-类
      • 6-2-类的继承
      • 6-3-枚举
      • 7-1-json
      • 8-1-文件IO
      • 8-2-pathlib
  • Flutter

    • 环境

      • 创建及运行项目
      • 第三方库
      • 运行环境
      • 静态资源
    • Dart

      • dynamic var object
      • List
      • Map
      • Number
      • Set
      • String
      • URI
      • 函数
      • 变量
      • 库
      • 异步
      • 流程控制语句
      • 类
      • 运算符
    • 基础

      • 1 Widget
      • 2 StatelessWidget & StatefulWidget
      • 3 State生命周期
      • 4 状态管理
      • 5 路由
    • 基础组件

      • Button
      • Text
      • 单选开关和复选框
      • 图片及ICON
      • 表单
      • 输入框
      • 进度指示器
    • 布局组件

      • 1 布局约束
      • 2 线性布局(Row & Column)
      • 3 弹性布局(Flex)
      • 4 流式布局(Wrap & Flow)
      • 5 层叠布局(Stack & Positioned)
      • 6 绝对定位(Align)
      • 7 LayoutBuilder & AfterLayout
    • 容器类组件

      • Container
      • Scaffold
      • 变换-Transform
      • 空间适配-FittedBox
      • 裁剪-Clip
      • 装饰-DecoratedBox
    • 可滚动组件

      • SingleChildScrollView
      • 通用属性
  • Git

    • 基础

      • Git commit message 规范
    • 实用

      • cherry-pick
      • stash
      • 分支&修改最近一次commit
      • 撤销
  • 算法

    • 基础

      • leetcode
      • 时间复杂度
    • 收录

      • 阿拉伯数字转中文
    • 数据结构

      • 哈希表、集合
      • 并查集
      • 数组、链表、跳表
      • 栈、队列
      • 树、二叉树、二叉搜索树
    • 算法

      • DFS 和 BFS
      • 二分查找
      • 二叉树路径问题题目
      • 动态规划2--基础题
      • 动态规划2--背包问题
      • 区间类题目
      • 岛屿类题目
      • 排列组合子集类题目
      • 排序算法
      • 摩尔投票
      • 递归
      • 链表
  • 部署

    • centos8

      • docker
      • ElasticSearch
      • git
      • https及http2
      • mac 向 centos 传输文件
      • mysql
      • nginx
      • nvm
      • redis
      • 从服务器下载文件
    • docker

      • 1-1-docker
      • 2-1-redis
      • 2-2-mysql
      • 2-3-postgresql-16
      • 2-4-puppeteer
      • 2-5-searxng
      • 2-6-bitwarden_rs
    • 其他

      • sitemap
      • 重装系统
  • 图形学

    • 基础

      • 1 三角函数
      • 2 斜率k
      • 3 向量
      • 4 矩阵
  • AI

    • Agent

      • 01-01-提示链
    • huggingface

      • 01-01-使用和下载模型
      • 01-02-数据集
      • 01-03-文本分类任务微调
    • langchain

      • 00-01-web用例集合
      • 01-01-Message
      • 01-02-promptTemplate-提示词模版
      • 01-03-outputParser-输出解析器
      • 01-04-fewShot
      • 02-01-LECL&chain
      • 02-02-stream
      • 03-01-debug
      • 04-01-多模态
      • 05-01-tool
      • 05-02-mcp
      • 06-01-agent
      • 06-02-agent-短期记忆
      • 06-03-agent-中间件
      • 07-01-embedding基础
      • 07-02-向量数据库
      • 08-01-RAG
    • langgraph

      • 1-01-图
      • 1-02-1-状态
      • 1-02-2-消息状态
      • 1-03-节点
      • 1-04-边
      • 1-05-Send
      • 1-06-Command
      • 1-07-配置-configurable
      • 1-08-1-内存持久性
      • 1-08-2-删除持久消息
      • 1-08-3-向量数据库
      • 1-08-4-持久上下文token优化
      • 1-09-1-工具
      • 1-09-2-大量工具优化
      • 1-10-1-人机交互-interrupt
      • 1-10-2-人机交互-interrupt_before
      • 1-11-1-流式输出
      • 1-11-2-自定义流式传输
      • 1-11-3-输出特定的流式消息
      • 1-12-1-子图
      • 1-13-1-ReAct
    • mcp

      • 01-简介
      • MCP Client nodejs
      • MCP Server Python
      • MCP Server TS
    • prompt

      • 提示词策略
      • 输出output

Agent 中间件 (Middleware)

中间件是 LangChain 1.0 引入的核心机制,用于在 Agent 执行流程的关键环节(模型调用前后、工具调用前后等)插入可控的拦截与增强逻辑。它借鉴了 Web 框架(Express / Django)的中间件模式,使开发者无需修改 Agent 核心业务逻辑即可实现日志、安全、限流、上下文压缩等横切关注点。

middleware 模块文档

AgentMiddleware 基类文档

1. 中间件架构原理

中间件像洋葱一样层层包裹 Agent 核心循环。用户请求从外向内逐层通过各中间件的 before 逻辑,到达核心后再从内向外逐层通过 after 逻辑。wrap 类 Hook 则直接包裹模型 / 工具的调用过程。

中间件的四大分类:

分类核心功能典型中间件
Monitor (监控)观察执行状态、日志、成本统计自定义日志中间件
Modify (修改)修改输入/输出、上下文压缩SummarizationMiddleware、ContextEditingMiddleware
Control (控制)流程阻断、人工介入、重试HumanInTheLoopMiddleware、ToolRetryMiddleware
Enforce (强制)安全过滤、限流、合规PIIMiddleware、ModelCallLimitMiddleware

2. 中间件生命周期 — 6 个 Hook

Agent 执行过程中,中间件通过 6 个 Hook 切入。每个 Hook 有明确的职责和执行时机:

Hook执行时机典型用途
before_agentAgent 启动前(仅一次)全局初始化、环境校验、资源准备
before_model每次模型调用前输入预处理、PII 脱敏、上下文摘要、调用计数
wrap_model_call包裹模型调用过程缓存、熔断、降级、模型切换、上下文裁剪
after_model每次模型调用后输出校验、人工审批拦截、调用计数
wrap_tool_call包裹工具调用过程重试、权限校验、审计日志
after_agentAgent 结束后(仅一次)资源清理、最终状态记录、报告生成

使用中间件有两种方式:继承 AgentMiddleware 类或使用装饰器函数。

from langchain.agents import create_agent
from langchain.agents.middleware import before_model, wrap_model_call

# 方式一:装饰器(适合简单逻辑)
@before_model
def log_input(state, runtime):
    print(f"输入消息数: {len(state['messages'])}")

# 方式二:类(适合复杂逻辑,可组合多个 Hook)
from langchain.agents.middleware.types import AgentMiddleware

class MyMiddleware(AgentMiddleware):
    def before_model(self, state, runtime):
        print("before model")

    def after_model(self, state, runtime):
        print("after model")

3. 内置中间件

3.1 SummarizationMiddleware — 上下文摘要压缩

Hook: before_model | API 文档

当对话历史超出阈值时,自动将旧消息摘要为一段文本,保留最近的 N 条消息。大幅减少 Token 消耗。

from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
from langgraph.checkpoint.memory import MemorySaver

agent = create_agent(
    "openai:gpt-4o",
    tools=[],
    checkpointer=MemorySaver(),
    middleware=[
        SummarizationMiddleware(
            model="openai:gpt-4o-mini",
            trigger=("messages", 20),     # 消息数达到 20 时触发
            keep=("messages", 5),         # 保留最近 5 条,其余压缩为摘要
        )
    ],
)

trigger 支持多种阈值:("messages", N) 按消息数、("tokens", N) 按 Token 数、("fraction", 0.8) 按模型上下文窗口比例。也可以传列表,任一满足即触发。


3.2 PIIMiddleware — 敏感信息脱敏

Hook: before_model + after_model | API 文档

自动检测并处理对话中的 PII(个人身份信息),如邮箱、信用卡号、IP 地址等。

from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware

agent = create_agent(
    "openai:gpt-4o",
    middleware=[
        PIIMiddleware("credit_card", strategy="mask"),   # ****-****-****-1234
        PIIMiddleware("email", strategy="redact"),       # [REDACTED_EMAIL]
        PIIMiddleware("ip", strategy="hash"),            # <ip_hash:a1b2c3d4>
    ],
)
策略效果适用场景
block检测到 PII 直接抛异常完全禁止 PII 进入
redact替换为 [REDACTED_TYPE]日志脱敏、合规
mask部分遮盖(保留末位)客服 UI、可读性
hash替换为确定性哈希分析、调试(可关联)

支持自定义 PII 类型,通过正则或自定义检测函数:

PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block")

3.3 ModelCallLimitMiddleware — 模型调用限制

Hook: before_model + after_model | API 文档

防止 Agent 陷入无限循环,限制单次运行或线程级别的模型调用次数。

from langchain.agents.middleware import ModelCallLimitMiddleware

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[
        ModelCallLimitMiddleware(
            run_limit=10,              # 单次运行最多调用 10 次模型
            thread_limit=100,          # 整个线程最多 100 次
            exit_behavior="end",       # 超限时优雅结束(也可选 "error" 抛异常)
        )
    ],
)

3.4 ContextEditingMiddleware — 上下文裁剪

Hook: wrap_model_call | API 文档

当输入 Token 超过阈值时,自动裁剪旧的工具调用结果,释放上下文空间。默认使用 ClearToolUsesEdit 策略。

from langchain.agents.middleware import ContextEditingMiddleware

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[ContextEditingMiddleware()],
)

3.5 ModelFallbackMiddleware — 模型故障降级

Hook: wrap_model_call | API 文档

主模型调用失败时,自动按顺序尝试备选模型,直到成功或全部耗尽。

from langchain.agents.middleware import ModelFallbackMiddleware

agent = create_agent(
    model="openai:gpt-4o",                # 主模型
    tools=[...],
    middleware=[
        ModelFallbackMiddleware(
            "openai:gpt-4o-mini",          # 第一备选
            "anthropic:claude-sonnet-4-5-20250929",  # 第二备选
        )
    ],
)

3.6 LLMToolSelectorMiddleware — 智能工具筛选

Hook: wrap_model_call | API 文档

当 Agent 绑定大量工具时,先用一个轻量 LLM 筛选出与当前查询最相关的工具子集,减少 Token 消耗并提升准确率。

from langchain.agents.middleware import LLMToolSelectorMiddleware

agent = create_agent(
    "openai:gpt-4o",
    tools=[tool_1, tool_2, ..., tool_50],
    middleware=[
        LLMToolSelectorMiddleware(
            model="openai:gpt-4o-mini",
            max_tools=5,
            always_include=["search"],     # 始终保留的工具
        )
    ],
)

3.7 ToolRetryMiddleware — 工具调用自动重试

Hook: wrap_tool_call | API 文档

工具调用失败时自动重试,支持指数退避和抖动。

from langchain.agents.middleware import ToolRetryMiddleware

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[
        ToolRetryMiddleware(
            max_retries=3,
            backoff_factor=2.0,            # 指数退避倍数
            initial_delay=1.0,             # 首次重试延迟(秒)
            on_failure="continue",         # 全部失败后返回错误消息让 LLM 处理
        )
    ],
)

3.8 HumanInTheLoopMiddleware — 人工审批

Hook: after_model | API 文档

当模型决定调用敏感工具时,自动中断执行并等待人工审批(批准 / 编辑 / 拒绝)。需配合 checkpointer 使用以持久化中断状态。

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import MemorySaver

agent = create_agent(
    "openai:gpt-4o",
    tools=[send_email, search],
    checkpointer=MemorySaver(),
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={"send_email": True},     # send_email 需审批,其他工具自动放行
        )
    ],
)

审批后通过 Command 恢复执行:

from langgraph.types import Command
from langchain.agents.middleware.human_in_the_loop import ApproveDecision

# 批准执行
agent.invoke(Command(resume=ApproveDecision()), config=config)

4. 中间件组合

多个中间件通过 middleware 列表传入,按列表顺序依次执行 before / wrap 逻辑,after 逻辑则按逆序执行。

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    checkpointer=MemorySaver(),
    middleware=[
        PIIMiddleware("credit_card", strategy="mask"),
        SummarizationMiddleware(model="openai:gpt-4o-mini", trigger=("messages", 20)),
        ModelCallLimitMiddleware(run_limit=15),
        ModelFallbackMiddleware("openai:gpt-4o-mini"),
        ToolRetryMiddleware(max_retries=2),
        HumanInTheLoopMiddleware(interrupt_on={"send_email": True}),
    ],
)

5. 自定义中间件

当内置中间件无法满足需求时,可以通过继承 AgentMiddleware 类或使用装饰器编写自定义中间件。

5.1 参数传递机制

不同 Hook 能够访问的参数不同,理解它们是编写自定义中间件的基础:

参数类型生命周期可访问的 Hook
state (AgentState)TypedDict整个 Agent 会话before_agent、before_model、after_model、after_agent
request (ModelRequest)dataclass单次模型/工具调用wrap_model_call、wrap_tool_call、dynamic_prompt
handlerCallable单次包装调用wrap_model_call、wrap_tool_call
runtime运行时对象贯穿会话所有 Hook(通过 state 或 request.runtime)

关键规则:

  • wrap_model_call / wrap_tool_call 中,可通过 request.override(...) 修改 model、messages、tools 等字段
  • before_model / after_model 等钩子中,只能读取 state,不能访问 request
  • handler(request) 是责任链中的下一环,调用它才会真正执行模型/工具调用
# ModelRequest 的核心字段
request.model       # 当前模型实例(可通过 override 替换)
request.messages    # 消息列表
request.tools       # 可用工具列表
request.state       # Agent 当前状态
request.runtime     # 运行时上下文,含 request.runtime.context

5.2 装饰器方式 — @dynamic_prompt 动态提示词

@dynamic_prompt 是一个专门用于根据运行时上下文动态生成 System Prompt 的装饰器。

dynamic_prompt API 文档

from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest
from typing import TypedDict

class Context(TypedDict):
    user_role: str

@dynamic_prompt
def role_based_prompt(request: ModelRequest):
    role = request.runtime.context.get("user_role", "user")
    if role == "expert":
        return "你是一个专业气象分析师,提供详细数据"
    return "你是一个友善的助手,用简单语言回答"

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[role_based_prompt],
    context_schema=Context,
)

# 不同上下文会注入不同的 System Prompt
agent.invoke(
    {"messages": [{"role": "user", "content": "北京天气"}]},
    context={"user_role": "expert"},
)

5.3 装饰器方式 — @wrap_model_call 模型动态切换

通过 @wrap_model_call 包装模型调用过程,可实现根据输入复杂度动态切换模型。

from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from langchain_openai import ChatOpenAI
from typing import Callable

small_model = ChatOpenAI(model="gpt-4o-mini")
large_model = ChatOpenAI(model="gpt-4o")

HARD_KEYWORDS = ("分析", "推导", "规划", "复杂", "多步骤")

@wrap_model_call
async def dynamic_model_router(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    messages = request.state.get("messages", [])
    last_content = messages[-1].content if messages else ""

    if any(kw in last_content for kw in HARD_KEYWORDS):
        request = request.override(model=large_model)

    return await handler(request)

agent = create_agent(
    model=small_model,
    tools=[...],
    middleware=[dynamic_model_router],
)

5.4 类方式 — 会话持久化中间件

继承 AgentMiddleware 可以在一个类中组合多个 Hook,适合复杂逻辑。

from langchain.agents.middleware import AgentMiddleware, AgentState
import os, json, time

class PersistSessionMiddleware(AgentMiddleware):
    def __init__(self, path: str = "./sessions"):
        super().__init__()
        self.path = path
        os.makedirs(path, exist_ok=True)

    def after_model(self, state: AgentState, runtime) -> None:
        messages = state.get("messages", [])
        if not messages:
            return
        filename = os.path.join(self.path, f"state_{int(time.time())}.json")
        data = [{"role": m.type, "content": m.content} for m in messages]
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

agent = create_agent(
    "openai:gpt-4o",
    tools=[],
    middleware=[PersistSessionMiddleware("./my_sessions")],
)

5.5 类方式 — 安全护栏中间件

利用 @hook_config(can_jump_to=["end"]) 可以在检测到危险输入时直接终止 Agent 执行。

from langchain.agents.middleware import AgentMiddleware, AgentState, hook_config
from langchain_core.messages import AIMessage
from typing import Optional, Dict, Any

class SecurityGuardrail(AgentMiddleware):
    DANGEROUS_KEYWORDS = ["rm -rf", "drop database", "删除所有"]

    @hook_config(can_jump_to=["end"])
    def before_agent(self, state: AgentState, runtime) -> Optional[Dict[str, Any]]:
        messages = state.get("messages", [])
        if not messages:
            return None

        content = messages[-1].content.lower()
        for kw in self.DANGEROUS_KEYWORDS:
            if kw in content:
                return {
                    "messages": [AIMessage(content=f"安全警告:检测到危险操作 '{kw}',已拦截。")],
                    "jump_to": "end",
                }
        return None

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[SecurityGuardrail()],
)

5.6 类方式 — RBAC 权限控制中间件

通过 context_schema 传入用户上下文,在 before_agent 做权限校验,在 before_model 注入用户信息。

from langchain.agents.middleware import AgentMiddleware, AgentState, hook_config
from langchain_core.messages import AIMessage
from typing import TypedDict, Optional, Dict, Any

class UserContext(TypedDict):
    user_id: str
    username: str
    role: str

ROLE_PERMISSIONS = {
    "admin": ["view", "restart", "config"],
    "operator": ["view", "restart"],
    "viewer": ["view"],
}

class RBACMiddleware(AgentMiddleware):
    @hook_config(can_jump_to=["end"])
    def before_agent(self, state: AgentState, runtime) -> Optional[Dict[str, Any]]:
        role = runtime.context.get("role", "guest")
        permissions = ROLE_PERMISSIONS.get(role, [])
        if not permissions:
            return {
                "messages": [AIMessage(content="权限不足,请联系管理员。")],
                "jump_to": "end",
            }
        return None

    def before_model(self, state: AgentState, runtime) -> Optional[Dict[str, Any]]:
        return {"user_info": dict(runtime.context)}

agent = create_agent(
    "openai:gpt-4o",
    tools=[...],
    middleware=[RBACMiddleware()],
    context_schema=UserContext,
)

agent.invoke(
    {"messages": [{"role": "user", "content": "查看服务器状态"}]},
    context={"user_id": "001", "username": "zhang", "role": "operator"},
)
最近更新: 2026/4/1 23:57
Contributors: kingmusi
Prev
06-02-agent-短期记忆
Next
07-01-embedding基础