LangChain v1 Agent Middleware 笔记
LangChain v1 Agent Middleware 笔记
整理自课程 notebook 与 LangChain 中文文档 中间件 章节。
一句话定位
Middleware 是插入在 Agent 核心循环前后的 hooks, 用来控制输入、输出、工具调用、消息修剪、日志监控等。
核心循环:
调用模型 → 模型选择工具 → 执行工具 → 重复直到模型不再调用工具
中间件在以上步骤的之前和之后暴露钩子。
1. 为什么需要中间件
Agent 自动调用模型和工具, 但有时你需要在关键节点”插手”:
- 历史消息太长 → 自动摘要或截断
- 工具结果太脏 → 清洗后再给模型
- 某些 ToolMessage 不想进上下文 → 删掉
- 想记录日志、监控耗时、做权限检查
- 想修改模型输入或输出
这些横切关注点不该写进每个 tool 或 prompt 里, 用中间件统一处理最干净。
2. 两种写法
2.1 内置中间件
SummarizationMiddleware: 自动摘要历史消息。
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="anthropic:deepseek-v4-flash",
checkpointer=InMemorySaver(),
middleware=[
SummarizationMiddleware(
model="anthropic:deepseek-v4-flash", # 负责摘要的模型
trigger=("tokens", 100), # token 超过 100 触发
keep=("messages", 1) # 保留最近 1 条原消息
)
],
)
参数含义:
| 参数 | 含义 |
|---|---|
model | 做摘要的模型, 可与主模型不同 |
trigger | 触发条件, 这里是总 token 数 |
keep | 摘要后保留最近几条原消息 |
效果: 老对话被压缩成 summary + 最近消息, 省 token 又不失上下文。
2.2 自定义中间件
用 @before_agent 或 @after_agent 装饰函数。
from typing import Any
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import before_agent
from langchain.messages import RemoveMessage, ToolMessage
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.runtime import Runtime
@before_agent
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""每次 agent 运行前, 删除所有 ToolMessage"""
messages = state["messages"]
tool_messages = [m for m in messages if isinstance(m, ToolMessage)]
return {"messages": [RemoveMessage(id=m.id) for m in tool_messages]}
agent = create_agent(
model="anthropic:deepseek-v4-flash",
checkpointer=InMemorySaver(),
middleware=[trim_messages],
)
函数签名:
def hook(state: AgentState, runtime: Runtime) -> dict | None:
...
-
state: 当前 Agent 状态, 含messages -
runtime: 运行时上下文, 含config、context等 - 返回
dict: 合并进 state - 返回
None: 不修改 state
3. 与 hook 的关系
概念上完全等价:
| 通用说法 | LangChain v1 名字 | 执行时机 |
|---|---|---|
| pre-hook | @before_agent | 模型调用前 |
| post-hook | @after_agent | 模型调用后 |
| pre-tool hook | @before_tool | 工具执行前 |
| post-tool hook | @after_tool | 工具执行后 |
| middleware | middleware=[...] 列表 | 统一注册 |
4. 执行顺序
假设:
middleware=[A, B, C]
before 阶段正序, after 阶段逆序:
输入 → A.before → B.before → C.before → agent 核心 → C.after → B.after → A.after → 输出
和常见 Web 框架的中间件洋葱模型一致。
5. 常见用途总结
| 用途 | 实现方式 |
|---|---|
| 消息摘要 | SummarizationMiddleware |
| 消息修剪/删除 | @before_agent 返回 RemoveMessage |
| 工具参数校验 | @before_tool |
| 工具结果清洗 | @after_tool |
| 输入输出日志 | @before_agent + @after_agent |
| 权限检查 | @before_agent |
| 缓存 | @before_agent 查缓存, @after_agent 写缓存 |
6. 关键对象
AgentState
Agent 的当前状态, 至少包含:
{"messages": [HumanMessage, AIMessage, ToolMessage, ...]}
中间件可以读、改这个 state。
Runtime
运行时容器, 包含:
-
config: 当前调用配置, 如{"configurable": {"thread_id": "1"}} -
context: 通过context_schema注入的用户上下文 - 其他运行信息
RemoveMessage
用于从 state 中删除消息:
from langchain.messages import RemoveMessage
RemoveMessage(id=message.id)
返回它, LangGraph 会在状态更新时移除对应消息。
7. 一句话总结
LangChain v1 的 middleware 是 Agent 核心循环上的 hook 层: @before_agent 在模型前改输入, @after_agent 在模型后改输出, 内置 SummarizationMiddleware 做自动摘要, 所有逻辑通过 middleware=[...] 注册到 agent 上统一执行。
Enjoy Reading This Article?
Here are some more articles you might like to read next:
- Google Gemini updates: Flash 1.5, Gemma 2 and Project Astra
- Displaying External Posts on Your al-folio Blog
- Agent 评测体系与评测集构建——美团《评测漫谈》+《评测白皮书 01》笔记
- 多模态 LLM 用户智能体做推荐系统离线 A/B 测试
- 自我改进 Agent 统一拆解:θ / Σ 双路线
- CS146S 学习笔记(Week 4-8):从智能体管理者到多栈 AI 构建
- CS146S 学习笔记:从 Prompt 技术全景到 AI IDE 设计文档规范
- 二分查找双模板 + searchInsert 逐行拆解:从模板到边界
- Agent Memory 全景:30 个记忆技术的模块化拆解
- LightRAG 深度解析:简单快速的图增强 RAG