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

📅 2026/7/7 18:10:50 👁️ 阅读次数 📝 编程学习
给 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,把循环、状态、持久化这些概念再往上拔一层。