第八章 中间件2——内置中间件

第七章介绍的是中间件的基本工作方式。

实际上框架内置了很多创建好的中间件,以方便使用,这些内置中间件已经能覆盖大部分场景。

一、容错类中间件

运行过程中不可避免的会产生偶尔的错误,比如网络抖动、模型偶尔的失败等,这些错误不应该直接导致Agent失败,而是重试后可能恢复正常。

框架已经实现如下中间件应对这种情况:

|-------------------------|-----------------------|
| 中间件 | 说明 |
| ToolErrorMiddleware | 捕获工具执行异常并转换为模型可见的错误 |
| ToolRetryMiddleware | 工具,瞬时错误自动重试 |
| ModelRetryMiddleware | 同 Tool Retry,但作用于模型调用 |
| ModelFallbackMiddleware | 主模型失败时回退 |

1.1 ToolErrorMiddleware

看名字,自然是用户捕获工具的异常。

它不重试 ,而是把异常转换成一条模型能读懂的 ToolMessage,让模型看到"哪里错了",然后自己决定是否修正参数后重试。

示例:

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ToolErrorMiddleware


def on_error(exc: Exception, request: ToolCallRequest) -> str | None:
    if isinstance(exc, ValueError):
        return f"`{request.tool_call['name']}` failed with {type(exc).__name__}."
    # 其他异常继续传播


agent = create_agent(
    model="gpt-5.5",
    tools=[your_tools],
    middleware=[ToolErrorMiddleware(on_error)],
)

上述代码,默认会捕获所有工具的ValueError的异常,如果需要捕获特定工具的异常,可以:

复制代码
ToolErrorMiddleware(on_error, tools=["search", "fetch_url"])

如果要捕获所有类型的异常,可以:

复制代码
def on_error(exc: Exception, request: ToolCallRequest) -> str:
    return f"`{request.tool_call['name']}` failed: {type(exc).__name__}. Please fix and retry."

1.2 ToolRetryMiddleware

可配置的指数退避自动重试失败的工具调用。

示例:

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import ToolRetryMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[search_tool, database_tool],
    middleware=[
        ToolRetryMiddleware(
            max_retries=3,
            tools=["search", "fetch_url"],          # 只对这两个工具重试
            backoff_factor=2.0,
            initial_delay=1.0,
            on_failure="continue",                    # 重试耗尽后返回错误消息给模型
        ),
    ],
)

关键参数:

|----------------|------------|---------------------------------------------------------------------------------|
| 参数 | 默认值 | 作用 |
| max_retries | 2 | 初始调用之后的重试次数,默认2,也就是说总共3次 |
| backoff_factor | 2.0 | 指数退避乘数,每次重试等待时间 = initial_delay * (backoff_factor ** retry_number) |
| initial_delay | 1.0 | 第一次重试前的等待时间 |
| max_delay | 60.0 | 等待时间的上限,防止指数增长失控 |
| jitter | True | 是否在延迟中加入 ±25% 的随机抖动,避免大量请求同时重试 |
| retry_on | 所有错误 | 只对指定异常类型重试,可以传异常元组或一个判断函数 |
| on_failure | "continue" | 重试耗尽后的行为:"continue" 返回错误 ToolMessage 让模型处理;"error" 重新抛出异常;也可以传一个函数自定义错误消息 |

1.2.1 与ToolErrorMiddleware组合

ToolErrorMiddleware在外,ToolRetryMiddleware在内。

retry 先干活,重试耗尽后异常"冒出来",外层的 Error 接住并转换。

复制代码
def on_error(exc: Exception, request: ToolCallRequest) -> str | None:
    if isinstance(exc, ValueError):
        return f"`{request.tool_call['name']}` failed: {type(exc).__name__}. Fix input and retry."
    return None   # 其他异常继续向上冒泡

middleware=[
    ToolErrorMiddleware(on_error=on_error),                 # 外层
    ToolRetryMiddleware(max_retries=3, on_failure="error"), # 内层
]

1.3 ModelRetryMiddleware

与 ToolRetryMiddleware 参数几乎一致,但作用于模型调用而非工具调用

示例:

复制代码
from langchain.agents.middleware import ModelRetryMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[search_tool, database_tool],
    middleware=[ModelRetryMiddleware(max_retries=3, backoff_factor=2.0, initial_delay=1.0)],
)

1.4 ModelFallbackMiddleware

主模型提供商出现故障(outage),或者你希望在成本上做优化。它会按顺序尝试备用模型,直到成功或全部耗尽。

复制代码
from langchain.agents.middleware import ModelFallbackMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[],
    middleware=[
        ModelFallbackMiddleware(
            "gpt-5.4-mini",
            "claude-sonnet-4-6",
        ),
    ],
)

二、成本与循环控制

|--------------------------|----------------------|
| 中间件 | 说明 |
| ModelCallLimitMiddleware | 限制模型调用次数,防止无限循环或过度成本 |
| ToolCallLimitMiddleware | |

2.1 ModelCallLimitMiddleware / ToolCallLimitMiddleware

防止 Agent 陷入无限循环,或者模型/工具调用次数失控导致成本爆炸。

复制代码
from langchain.agents.middleware import ModelCallLimitMiddleware

agent = create_agent(
    model="gpt-5.5",
    checkpointer=InMemorySaver(),
    tools=[],
    middleware=[
        ModelCallLimitMiddleware(thread_limit=10,run_limit=5,exit_behavior="end"),
        ToolCallLimitMiddleware(tool_name="search", thread_limit=5, run_limit=3),
    ],
)

参数:

|---------------|----------------------------------------|
| 参数 | 作用 |
| thread_limit | 同一个线程内所有运行的最大调用次数(需要 checkpointer) |
| run_limit | 单次运行的最大调用次数 |
| exit_behavior | 达到限制后的行为:"end" 优雅终止,"error" 抛出异常 |

三、安全与合规

|--------------------------|----------|
| 中间件 | 说明 |
| PIIMiddleware | 个人身份信息检测 |
| HumanInTheLoopMiddleware | 人机协同审批 |

3.1 PIIMiddleware

用户输入或模型输出中包含邮箱、信用卡号、API Key 等敏感信息,需要脱敏、遮蔽或阻止。

复制代码
from langchain.agents.middleware import PIIMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[],
    middleware=[
        PIIMiddleware("email", strategy="redact", apply_to_input=True),
        PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
    ],
)

安全检查的类型有内置的,也可以自定义;内置的类型有:

`pii_type`: `email`、`credit_card`、`ip`、`mac_address`、`url`

关键参数如下:

|-----------------------|-------------------------------------------------------------------------------------------|
| 参数 | 说明 |
| pii_type | 作为第一个参数,指明要检查的类型 |
| strategy | redact(替换为 [REDACTED_TYPE])、mask(部分遮蔽,如 ****-1234)、hash(确定性哈希)、block(检测到即抛异常) |
| detector | 自定义检测器:正则字符串、已编译正则或函数 |
| apply_to_input | 默认 True,检查用户输入 |
| apply_to_output | 默认 False,检查模型输出 |
| apply_to_tool_results | 默认 False,检查工具返回结果 |

3.2 HumanInTheLoopMiddleware

在工具调用执行前暂停 agent,等待人类批准、编辑或拒绝。适用于高风险操作(数据库写入、金融交易)。**需要 checkpointer,**因为暂停后需要从检查点恢复状态。

复制代码
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver

def your_read_email_tool(email_id: str) -> str:
    """Mock function to read an email by its ID."""
    return f"Email content for ID: {email_id}"

def your_send_email_tool(recipient: str, subject: str, body: str) -> str:
    """Mock function to send an email."""
    return f"Email sent to {recipient} with subject '{subject}'"

agent = create_agent(
    model="gpt-5.5",
    tools=[your_read_email_tool, your_send_email_tool],
    checkpointer=InMemorySaver(),
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={
                "your_send_email_tool": {
                    "allowed_decisions": ["approve", "edit", "reject"],
                },
                "your_read_email_tool": False,
            }
        ),
    ],
)

关键参数:

|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 参数 | 说明 |
| interrupt_on | 必需 。字典,键是工具名,值可以是 True(所有决策都允许)、False(不中断),或一个 InterruptOnConfig 对象。 每个工具可设置 `allowed_decisions`(如 `"approve", "edit", "reject"`),设为 `False` 表示不中断 |
| interrupt_mode | 默认每模型轮次暂停一次;设为 `"per_call"` 则每个工具调用暂停一次 |
| allowed_decisions | 允许的决策类型:approve(按原参数执行)、edit(修改参数后执行)、reject(跳过并反馈)、respond(直接返回人工消息作为工具结果,用于"ask user"类工具) |
| when | 条件中断 。一个接收 ToolCallRequest 的谓词函数,返回 True 才暂停。例如:只在 SQL 不是 SELECT 时暂停 |

四、上下文管理

|------------------------------|----------|
| 中间件 | 说明 |
| SummarizationMiddleware | 对话历史摘要压缩 |
| LLMToolSelectorMiddleware | 工具太多时先筛选 |
| ProviderToolSearchMiddleware | 提供商工具搜索 |

4.1 SummarizationMiddleware

解决的问题 :对话太长,超出模型的上下文窗口。它在达到触发条件 时,调用一个(可以更便宜的)模型对旧消息生成摘要,保留最近的消息,从而压缩上下文。

注意:它只压缩文本 ,不压缩图像/音频/视频载荷;被摘要掉的多模态消息会只剩文字摘要。

复制代码
from langchain.agents.middleware import SummarizationMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[your_weather_tool, your_calculator_tool],
    middleware=[
        SummarizationMiddleware(
            model="gpt-5.4-mini",
            trigger=("tokens", 4000),
            keep=("messages", 20),
        ),
    ],
)

关键参数:

|---------------|----------------------------------------------------------------------------------------|
| 参数 | 说明 |
| model | 必需。用于生成摘要的模型 |
| trigger | 触发条件。可以是 ("tokens", N)、("messages", N)、("fraction", 0.8);列表是 OR ,字典是 AND |
| keep | 摘要后保留多少原始消息,格式同 trigger,但同一时间只接受一种条件 |
| token_counter | 自定义 token 计数函数,默认使用近似计数 |

4.2 LLMToolSelectorMiddleware

Agent 有 10+ 个工具时,把全部工具 schema 塞给主模型会"淹没"它,选错工具的概率上升。它先用一个更小、更便宜的模型(通过结构化输出)筛选出与当前查询相关的工具,再交给主模型。

复制代码
from langchain.agents.middleware import LLMToolSelectorMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[tool1, tool2, tool3, tool4, tool5, ...],
    middleware=[
        LLMToolSelectorMiddleware(
            model="gpt-5.4-mini",
            max_tools=3,
            always_include=["search"],
        ),
    ],
)

4.3 ProviderToolSearchMiddleware

将选定工具延迟到模型提供商的服务器端工具搜索之后按需呈现,减少上下文膨胀。

需要支持服务器端工具搜索的模型(Anthropic Claude Sonnet 4+/Opus 4+/Haiku 4.5+ 或 OpenAI gpt-5.5+)。

解决的问题 :比 LLMToolSelectorMiddleware 更彻底------它把选定工具标记为"延迟加载",把工具目录交给提供商的服务端工具搜索 ,由提供商在需要时按需披露,从而彻底减少每轮发送的工具 schema 体积。


相关推荐
叼烟扛炮25 分钟前
[LangChain] 学习笔记(04):手搓 ChatGPT 打字机——流式输出(SSE)与模型初始化选型
人工智能·笔记·chatgpt·langchain
源于花海18 小时前
LangChain学习:理解构建模块与核心组件
langchain·prompt·api·agent·模块与核心组件
烬羽21 小时前
LangChain v1 中间件实战:不碰主流程,也能给 Agent 装上"外挂"
后端·langchain·agent
7哥♡ۣۖᝰꫛꫀꪝۣℋ2 天前
LangChain(tool)定义使用
langchain
終不似少年遊*2 天前
【安全业务Agent】从场景到编排
python·react.js·ai·langchain·agent·安全架构·工作流
我真会写代码3 天前
LangChain 从0入门:模型、消息、Prompt、Tools、记忆、Agent与LangSmith实战
langchain·prompt
GISer_Jing3 天前
SuperAgent
ai·langchain
前端精髓3 天前
langchain 短期记忆(状态)动态运行时上下文
python·langchain
在努力的前端小白4 天前
基于FastAPI+LangChain+LangGraph+Agent+多模态的AI Agent 有渔渔具网站
人工智能·langchain·vue·fastapi