当前位置: 首页 > news >正文

给 Agent 装“外挂“:Middleware 让我的代码开始像生产环境

前面四篇把 Chain、Tool、Agent、记忆都跑通了,但代码还像是"玩具":模型调用失败就抛异常,工具报错就崩溃,敏感操作也没有审核。

我开始想:如果要把 Agent 放到生产环境里,至少得有加重试、有 fallback、关键操作前让人确认。这些能力在 LangChain 里叫Middleware(中间件),是 Agent Harness 的一部分。

这篇文章记录我第一次给 Agent 加中间件的经历。


一、我当时想干什么

我想让 Agent 具备三个能力:

  1. 容错:模型或工具调用失败时自动重试
  2. 人工审核:Agent 写文件前必须停下来等我确认
  3. 文件操作:Agent 能把搜索结果写入文件

我最初的代码来自文档:

fromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimport(ModelRetryMiddleware,ToolRetryMiddleware,HumanInTheLoopMiddleware,)fromdeepagents.middlewareimportFilesystemMiddlewarefromdeepagents.backendsimportStateBackendfromlangchain_core.toolsimporttoolfromlangchain_openaiimportChatOpenAI backend=StateBackend()@tooldefsearch(query:str)->str:"""搜索工具"""returnf"搜索结果:{query}"@tooldefwrite_file(filename:str,content:str)->str:"""写入文件"""withopen(filename,"w",encoding="utf-8")asf:f.write(content)returnf"已写入{filename}"llm=ChatOpenAI(model="deepseek-chat",temperature=0,openai_api_key="你的 api key",openai_api_base="https://api.deepseek.com/v1",)agent=create_agent(model=llm,tools=[search,write_file],middleware=[FilesystemMiddleware(backend=backend),ModelRetryMiddleware(max_retries=3),ToolRetryMiddleware(max_retries=2),HumanInTheLoopMiddleware(interrupt_on={"write_file":True}),],)

但第一次运行就报错了。


二、我写的代码

问题出在HumanInTheLoopMiddleware配置了一个不存在的工具。我最初写:

HumanInTheLoopMiddleware(interrupt_on={"write_file":True})

tools里只有search,没有write_file。修改后把两个工具都加进去:

agent=create_agent(model=llm,tools=[search,write_file],middleware=[FilesystemMiddleware(backend=backend),ModelRetryMiddleware(max_retries=3),ToolRetryMiddleware(max_retries=2),HumanInTheLoopMiddleware(interrupt_on={"write_file":True}),],)stream=agent.stream_events({"messages":[{"role":"user","content":"搜索 AI 新闻并写入 report.txt"}]},config=config,version="v3",)forsnapshotinstream.values:latest=snapshot["messages"][-1]iflatest.content:print(f"Agent:{latest.content}")eliflatest.tool_calls:names=[tc["name"]fortcinlatest.tool_calls]print(f"准备调用工具:{names}")

现在 Agent 想写文件前会暂停,等人确认后再继续。


三、我遇到的坑

坑 1:deepagents没安装

第一次运行时报:

ModuleNotFoundError: No module named 'deepagents'

FilesystemMiddlewareStateBackend来自deepagents包,不是 LangChain 核心。需要先安装:

pipinstalldeepagents

如果只是想体验核心中间件,可以先用langchain.agents.middleware里的,不需要deepagents

坑 2:interrupt_on里写了不存在的工具

我一开始只有search工具,但配了:

HumanInTheLoopMiddleware(interrupt_on={"write_file":True})

这个配置不会报错,但永远也不会触发,因为 Agent 根本不会调用write_file

interrupt_on里的工具名必须在tools列表里存在,并且要写好 docstring,Agent 才知道什么时候该调用。

坑 3:stream_events()调用不完整

我一开始写:

result=agent.stream_events()

结果报错提示缺少参数。stream_events至少需要两个参数:

stream=agent.stream_events({"messages":[{"role":"user","content":"搜索 AI 新闻并总结"}]},version="v3",config=config,)

坑 4:中间件顺序没想清楚

我把HumanInTheLoopMiddleware放在最前面,把FilesystemMiddleware放在最后面试过一次。虽然没报错,但逻辑上不舒服:

  • 文件操作中间件应该先准备好执行环境
  • 重试中间件应该靠近实际调用
  • 人工审核应该放在最后把关

后来我改成:

middleware=[FilesystemMiddleware(backend=backend),# 1. 执行环境ModelRetryMiddleware(max_retries=3),# 2. 模型容错ToolRetryMiddleware(max_retries=2),# 3. 工具容错HumanInTheLoopMiddleware(...),# 4. 人工审核]

这个顺序更符合"先准备、再执行、再兜底、最后审核"的直觉。


四、搞清楚后的结论

LangChain 新架构里的 Agent 可以表示为:

Agent = Model + Harness

create_agent是 Harness 的一种实现,而 Middleware 是扩展 Harness 的方式。

Middleware

FilesystemMiddleware

ModelRetryMiddleware

ToolRetryMiddleware

HumanInTheLoopMiddleware

用户输入

Middleware 层

Model 推理

调用工具?

Tool 执行

最终输出

每个中间件只负责一件事:

中间件作用使用场景
ModelRetryMiddleware模型调用失败重试API 超时、rate limit
ToolRetryMiddleware工具调用失败重试外部服务抖动
HumanInTheLoopMiddleware关键操作前暂停确认写文件、删数据、发邮件
FilesystemMiddleware给 Agent 文件系统能力读配置、写报告

五、我的收获

生产级的 Agent,不是模型有多强,而是失败时能不能优雅地活下来。

写中间件之前,我的 Agent 像是一个只能在 demo 环境里跑的玩具:正常流程能走通,一旦出错就挂。

加了ModelRetryMiddlewareToolRetryMiddleware后,它能应对网络抖动和偶发失败。加了HumanInTheLoopMiddleware后,它能在高风险操作前停下来等人确认。

这些能力模型本身不会给你,必须靠 Harness 来实现。理解了 Middleware,才算是从"会写 Agent"进阶到"敢把 Agent 放生产"。


到这里,我学 LangChain Agent 的五篇笔记就写完了。接下来准备进入 LangGraph,把循环、状态、持久化这些概念再往上拔一层。

http://www.jsqmd.com/news/1142714/

相关文章:

  • 纽扣电池低功耗方案:NBM5100A与PIC18F4680优化设计
  • 多参数水质在线监测设备—水环境实时管控解决方案
  • 工程经营管理软件怎么选型,统筹营收成本回款规避项目经营亏损
  • GitHub热榜开源项目,Claude Code 10模块系统学习指南
  • 一文读懂 2026 AI Infra 推理加速选型与硬核优化方案
  • KingFusion系列33-如何向数据表格末尾追加一行新数据
  • 图片数字人 API 快速接入指南(小白版)
  • 腾讯混元携手多所高校,让3D网格生成快如闪电
  • Anthropic 发布 Claude Sonnet 5 反击,Gemini 3.5 Pro 与 DeepSeek V4 定档 7月17日同日对决
  • 【Python深度学习系列】一文搞明白“显存”与“内存”的区别和联系
  • 2026最新5款基础版免费AI编程助手平替实测合集
  • STM32与PAM8904构建高效压电蜂鸣器驱动系统
  • 从焊得住到微米级:Rubin全液冷给焊接的三大考题
  • Ideogram 4.0视觉编码器:从语义理解到设计意图解析的技术突破
  • 3步构建开源能源大脑:OpenEMS实战部署与智能控制指南
  • 用 Ace Data Cloud 快速接入 Kling Motion:让图片按指定动作生成视频
  • 深入解析openeuler/qla2xxx:Marvell QLogic FC HBA驱动完全指南
  • WebBuilder Layout 布局体系架构、渲染逻辑与底层实现全解析
  • 2026资阳黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • 我加了 `InMemorySaver`,为什么 Agent 还是没记忆?
  • Fastjson 1.2.68反序列化漏洞实战:绕过AutoType黑名单的攻防解析
  • Airtest自动化测试第二篇:图像识别脚本编写——核心API详解与实战技巧
  • lsof 命令使用参数
  • 食堂采购系统源码解析:核心功能设计与技术实现
  • 开启 Harness Engineering 探索之旅
  • PW7126+PW80N03*2三串锂电池充放电保护板方案,持续7A,过流保护10A
  • IRISMAN完整指南:PS3自定义固件玩家的终极游戏管理解决方案
  • 2026最新7款AI编程工具平替实测合集
  • [SQL实战] 对账总是对不上?先把主键、时间口径和异常明细拆出来再查
  • (最新安装包)告别繁琐配置!OpenClaw 一键脚本,轻松搞定本地 AI 自动化