LangChain Agent 中间件全解与实战
前言为什么 Agent 需要中间件如果你用过 LangChain 构建过 AI Agent大概率遇到过这样的困境测试阶段一切正常部署到生产环境后却问题频发——上下文管理混乱、Agent 行为不可预测、工具调用失控……最后不得不写一堆自定义代码来补救。问题根源在于Agent 的可靠性取决于上下文工程。模型接收什么信息决定了它输出什么结果。而过多的配置参数、散落在各处的自定义逻辑让 Agent 变得难以维护和扩展。LangChain 1.0 引入的Agent 中间件Middleware正是为了解决这个问题。它借鉴了 Web 框架如 FastAPI的中间件模式在 Agent 执行流程的关键节点提供标准化的钩子Hooks让开发者能系统化地控制上下文、注入业务逻辑、增强生产可靠性。本文将带你全面理解 Agent 中间件的设计理念、核心钩子、内置实现并通过实战案例掌握自定义中间件的开发。一、中间件是什么Agent 的“信息调度中心”1.1 核心思想中间件本质上是一组可组合的执行钩子在 Agent 循环的各个阶段运行。它不改变 Agent 的核心逻辑模型调用 → 工具执行 → 循环而是在这些步骤前后插入自定义逻辑实现输入预处理PII 脱敏、提示词动态生成、对话摘要执行控制工具权限校验、人工审批HITL、重试与降级输出后处理结果验证、日志记录、状态更新1.2 中间件 vs 传统参数配置在 v1.0 之前Agent 的定制依赖大量参数和手写循环代码臃肿且难以复用。例如旧版AgentExecutor需要配置max_iterations、handle_parsing_errors、trim_intermediate_steps等十几个参数。中间件的优势在于模块化每个中间件独立负责一个功能如摘要、缓存、日志可组合多个中间件按顺序叠加互不干扰可复用写好的中间件可在不同项目间共享二、中间件的核心钩子HooksLangChain 中间件提供了 6 个核心钩子覆盖 Agent 的完整生命周期钩子执行时机典型用途before_agentAgent 启动前仅一次加载外部记忆、初始化资源、输入校验before_model每次模型调用前动态更新提示词、对话摘要、PII 脱敏wrap_model_call包裹模型调用重试机制、模型降级、动态工具绑定after_model模型响应后、工具执行前人工审批HITL、输出格式校验wrap_tool_call包裹工具调用工具权限控制、参数修改、缓存结果after_agentAgent 完成后仅一次保存结果、释放资源、发送通知钩子分为两种风格节点式钩子Node-style顺序执行通过返回dict更新 Agent 状态。如before_model、after_model包裹式钩子Wrap-style包裹核心调用可控制是否执行、重试或短路。如wrap_model_call、wrap_tool_call三、开箱即用的内置中间件LangChain 提供了多个生产级中间件可直接引入使用。3.1 对话摘要中间件SummarizationMiddleware长对话容易超出模型上下文窗口。该中间件在before_model阶段检测消息数量或 Token 阈值自动生成摘要替换历史消息。pythonfrom langchain.agents import create_agent from langchain.agents.middleware import SummarizationMiddleware agent create_agent( modelclaude-sonnet-4-6, tools[...], middleware[ SummarizationMiddleware( modelclaude-sonnet-4-6, trigger{tokens: 500} # 超过 500 token 触发摘要 ) ] )3.2 人工审批中间件HumanInTheLoopMiddleware敏感操作如发送邮件、删除数据需人工确认。该中间件在after_model阶段拦截指定工具调用挂起执行等待人工决策。pythonfrom langchain.agents.middleware import HumanInTheLoopMiddleware agent create_agent( modelclaude-sonnet-4-6, tools[send_email, read_email], middleware[ HumanInTheLoopMiddleware( interrupt_on{ send_email: { allowed_decisions: [approve, edit, reject] } } ) ], checkpointerMemorySaver() # 必须配合 checkpointer 实现暂停与恢复 )恢复挂起的执行pythonfrom langgraph.types import Command agent.invoke( Command(resume{decisions: [{type: approve}]}), config{configurable: {thread_id: session-123}} )3.3 PII 脱敏中间件PIIRedactionMiddleware在模型调用前自动识别并脱敏敏感信息邮箱、电话、身份证号等防止数据泄露。pythonfrom langchain.agents.middleware import PIIRedactionMiddleware agent create_agent( modelclaude-sonnet-4-6, middleware[ PIIRedactionMiddleware( patterns[email, phone, ssn], strategyredact # 或 mask, block ) ] )3.4 Anthropic 专属中间件针对 Claude 模型langchain-anthropic提供了专用中间件Prompt Caching缓存系统提示词和工具定义降低成本和延迟Bash Tool提供生产级的 Bash 命令执行支持 Docker 隔离Text Editor文件编辑工具支持状态持久化和文件系统两种模式pythonfrom langchain_anthropic.middleware import ( AnthropicPromptCachingMiddleware, ClaudeBashToolMiddleware ) agent create_agent( modelChatAnthropic(modelclaude-sonnet-4-6), middleware[ AnthropicPromptCachingMiddleware(ttl5m), ClaudeBashToolMiddleware( workspace_root/workspace, execution_policyDockerExecutionPolicy(imagepython:3.11) ) ] )四、实战自定义中间件开发内置中间件无法覆盖所有场景自定义中间件才是真正的杀手锏。我们通过两个实战案例演示如何开发。4.1 装饰器式中间件快速开发适合单钩子、逻辑简单的场景。使用before_model、wrap_tool_call等装饰器。需求为所有工具调用添加重试机制失败时返回友好提示。pythonfrom langchain.agents.middleware import wrap_tool_call, ToolCallRequest from langchain_core.messages import ToolMessage from typing import Callable wrap_tool_call def retry_on_tool_failure( request: ToolCallRequest, handler: Callable[[ToolCallRequest], ToolMessage] ) - ToolMessage: for attempt in range(3): try: return handler(request) except Exception as e: print(f工具 {request.tool_call[name]} 第 {attempt1} 次尝试失败: {e}) if attempt 2: return ToolMessage( contentf工具执行失败请重试或换一种方式。错误{e}, tool_call_idrequest.tool_call[id] ) # 不会执行到这里使用中间件pythonagent create_agent( modelgpt-4, tools[search, calculator], middleware[retry_on_tool_failure] )4.2 类式中间件复杂场景适合多钩子、需维护状态的场景。继承AgentMiddleware并实现对应钩子方法。需求根据用户技术水平动态调整模型和工具。专家用户使用高级模型 复杂工具新手使用轻量模型 基础工具。pythonfrom langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse from typing import Callable, Any from pydantic import BaseModel # 1. 定义上下文 Schema运行时传入不持久化 class UserContext(BaseModel): user_expertise: str beginner # beginner 或 expert # 2. 自定义中间件 class ExpertiseBasedToolMiddleware(AgentMiddleware): context_schema UserContext def wrap_model_call( self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse] ) - ModelResponse: # 从运行时上下文读取用户级别 user_level request.runtime.context.get(user_expertise, beginner) if user_level expert: # 专家用户切换更强大的模型 高级工具 request request.override( modelgpt-4, tools[advanced_search, data_analysis, calculator] ) else: # 新手用户轻量模型 基础工具 request request.override( modelgpt-3.5-turbo, tools[simple_search, basic_calculator] ) return handler(request)使用中间件并在调用时传入上下文pythonagent create_agent( modelgpt-4, # 默认模型会被中间件覆盖 tools[simple_search, advanced_search, basic_calculator, data_analysis], middleware[ExpertiseBasedToolMiddleware()] ) # 专家用户调用 result agent.invoke( {messages: [HumanMessage(分析这份销售数据)]}, config{context: {user_expertise: expert}} )4.3 中间件状态管理如果中间件需要跨轮次持久化数据如调用计数、Token 用量可扩展 Agent 的状态 Schema。pythonfrom langchain.agents.middleware import AgentMiddleware, AgentState from typing import NotRequired class TrackingState(AgentState): model_call_count: NotRequired[int] class UsageTrackingMiddleware(AgentMiddleware): state_schema TrackingState def after_model(self, state: TrackingState, runtime) - dict[str, Any] | None: # 每次模型调用后更新计数 current state.get(model_call_count, 0) return {model_call_count: current 1}五、进阶中间件组合与执行顺序多个中间件按注册顺序执行类似洋葱模型进入阶段before_model按注册顺序执行包裹阶段wrap_model_call外层 → 内层退出阶段after_model按注册顺序的逆序执行pythonagent create_agent( modelgpt-4, middleware[ LoggingMiddleware(), # ① 先记录日志 RetryMiddleware(), # ② 再添加重试 PIIRedactionMiddleware() # ③ 最后脱敏 ] )执行顺序Logging.before_model→Retry.before_model→PII.before_model→模型调用→PII.after_model→Retry.after_model→Logging.after_model六、中间件 LangGraph在复杂工作流中使用中间件并非仅限单 Agent 使用。create_agent返回的 Agent 本身是一个 LangGraph 图可嵌入更大的工作流中中间件钩子依然有效。pythonfrom langgraph.graph import StateGraph, START from langchain.agents import create_agent, AgentState # 创建带中间件的 Agent email_agent create_agent( modelclaude-sonnet-4-6, tools[read_email, send_email], middleware[HumanInTheLoopMiddleware(interrupt_on{send_email: True})] ) # 嵌入更大的工作流 graph ( StateGraph(AgentState) .add_node(classify, classify_node) # 分类节点 .add_node(email_agent, email_agent) # Agent 节点 .add_edge(START, classify) .add_conditional_edges(classify, route) # 动态路由到不同 Agent .compile() )这种模式适合多 Agent 协作或预处理-处理-后处理的流水线场景。七、总结与最佳实践何时使用中间件生产级 Agent需要日志、重试、监控、降级复杂上下文管理动态提示词、对话摘要、PII 脱敏安全与合规工具权限控制、人工审批、数据隐私多租户或个性化根据用户角色动态调整模型和工具

相关新闻

2026年Java端口监听新突破:SO_REUSEPORT实现多进程并发监听

2026年Java端口监听新突破:SO_REUSEPORT实现多进程并发监听

同一个端口能不能被两个程序同时监听?这道经典的Java面试题在2026年有了新的技术答案。传统的网络编程教材告诉我们,一个端口在同一时间只能被一个进程监听,但随着现代操作系统和网络技术的发展,这个看似铁律的规则正在被打破。如…

2026/10/15 12:19:46 阅读更多 →
C#动态修改Windows默认打印机:从Win32 API调用到权限处理的完整指南

C#动态修改Windows默认打印机:从Win32 API调用到权限处理的完整指南

1. 项目概述与核心价值最近在做一个企业内部的小工具,需要根据不同的业务单据类型,自动切换到对应的打印机进行打印。比如,销售单用针式打印机打三联单,发货单用激光打印机打A4纸。这就涉及到一个核心需求:以编程方式动…

2026/10/15 2:19:06 阅读更多 →
PID控制算法详解:从原理到嵌入式C语言实现与调参

PID控制算法详解:从原理到嵌入式C语言实现与调参

1. 从“失控”到“掌控”:PID算法的核心价值如果你曾经尝试过让一个电机稳定在某个转速,或者让一个加热器保持恒温,又或者让一架无人机悬停在半空,那你大概率已经和“失控”打过交道了。设定一个目标值,系统要么反应迟…

2026/10/13 10:09:55 阅读更多 →

最新新闻

Django 后端实战指南_3:插入 RBAC 测试数据

Django 后端实战指南_3:插入 RBAC 测试数据

阶段 3:插入 RBAC 测试数据 合辑:Django 系统阶段开发手册 | 阶段:3 / 18 | 定位:以自定义管理命令初始化 RBAC 测试数据,并多渠道验证四张表中的数据关系 | 建议学时:3–…

2026/10/15 12:19:54 阅读更多 →
Skills的shepherd技能实战:自动分诊评审评论、修复CI失败的AI PR/MR守护神

Skills的shepherd技能实战:自动分诊评审评论、修复CI失败的AI PR/MR守护神

【免费下载链接】skills Skills for Kotlin, Jetpack Compose, and Android development 项目地址: https://gitcode.com/gh_mirrors/skills33/skills 点击查看 免费下载 Skills 项目的 shepherd 技能是一位专为合并请求(PR/MR)打造的 AI 守…

2026/10/15 12:19:54 阅读更多 →
视频字幕去除的效果怎么评估:指标与工具

视频字幕去除的效果怎么评估:指标与工具

硬字幕去掉以后,怎么判断处理得好不好?光凭播放一遍的印象说不清。这篇把评估拆成六个维度,每个维度给出检查方法和合格表现,再附一张对照表。文末顺带介绍字幕清洁工,一款去视频硬字幕和水印的微信小程序,…

2026/10/15 12:19:54 阅读更多 →
GluonCV 损失函数模块 gluoncv.loss 详解:FocalLoss、SSDMultiBoxLoss 与检测/分割/跟踪损失实战

GluonCV 损失函数模块 gluoncv.loss 详解:FocalLoss、SSDMultiBoxLoss 与检测/分割/跟踪损失实战

计算机视觉深度学习预训练 【免费下载链接】gluon-cv Gluon CV Toolkit 项目地址: https://gitcode.com/gh_mirrors/gl/gluon-cv 点击查看 免费下载 本文围绕 GluonCV 的官方 API 文档页 docs/api/loss.rst 展开,系统讲解 gluoncv.loss 模块中全部自定义损失函数的…

2026/10/15 12:19:54 阅读更多 →
VS2010+OpenCV+zxing-cpp:老平台工业扫码实战与优化

VS2010+OpenCV+zxing-cpp:老平台工业扫码实战与优化

简介:这是一套面向C开发者的二维码识别工程资源包,基于VS2010开发环境,整合OpenCV图像处理库与ZXing-C解码库,适合需要在Windows桌面端实现条码/二维码扫描功能的初中级开发者,也可作为计算机视觉入门练手项目。压缩包…

2026/10/15 12:19:54 阅读更多 →
Flutter跨平台开发实战:鸿蒙适配与生物知识APP优化

Flutter跨平台开发实战:鸿蒙适配与生物知识APP优化

1. 项目背景与核心价值去年接手这个生物知识APP项目时,客户明确要求同时覆盖iOS、Android和即将发布的鸿蒙设备。作为从业十年的移动端开发者,我第一时间想到了Flutter这个老朋友——但要在鸿蒙系统上跑通Flutter,确实遇到了不少技术深坑。经…

2026/10/15 12:18:53 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →