前面四篇把 Chain、Tool、Agent、记忆都跑通了,但代码还像是"玩具":模型调用失败就抛异常,工具报错就崩溃,敏感操作也没有审核。
我开始想:如果要把 Agent 放到生产环境里,至少得有加重试、有 fallback、关键操作前让人确认。这些能力在 LangChain 里叫Middleware(中间件),是 Agent Harness 的一部分。
这篇文章记录我第一次给 Agent 加中间件的经历。
一、我当时想干什么
我想让 Agent 具备三个能力:
- 容错:模型或工具调用失败时自动重试
- 人工审核:Agent 写文件前必须停下来等我确认
- 文件操作: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'FilesystemMiddleware和StateBackend来自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 的方式。
每个中间件只负责一件事:
| 中间件 | 作用 | 使用场景 |
|---|---|---|
ModelRetryMiddleware | 模型调用失败重试 | API 超时、rate limit |
ToolRetryMiddleware | 工具调用失败重试 | 外部服务抖动 |
HumanInTheLoopMiddleware | 关键操作前暂停确认 | 写文件、删数据、发邮件 |
FilesystemMiddleware | 给 Agent 文件系统能力 | 读配置、写报告 |
五、我的收获
生产级的 Agent,不是模型有多强,而是失败时能不能优雅地活下来。
写中间件之前,我的 Agent 像是一个只能在 demo 环境里跑的玩具:正常流程能走通,一旦出错就挂。
加了ModelRetryMiddleware和ToolRetryMiddleware后,它能应对网络抖动和偶发失败。加了HumanInTheLoopMiddleware后,它能在高风险操作前停下来等人确认。
这些能力模型本身不会给你,必须靠 Harness 来实现。理解了 Middleware,才算是从"会写 Agent"进阶到"敢把 Agent 放生产"。
到这里,我学 LangChain Agent 的五篇笔记就写完了。接下来准备进入 LangGraph,把循环、状态、持久化这些概念再往上拔一层。