https://zhuanlan.zhihu.com/p/1979938537802597911
[Agent Loop:从 for 循环到自治系统的进化之路]
Agent Harness
Backends
Subagents
Protocols
Human-in-the-Loop
记忆
中间件(Middleware)
langchain/agents/middleware/types.py,基类 AgentMiddleware(L380)定义了以下钩子方法:
基类的这些方法都是空实现(直接返回 None),子类按需 override。EvoScientist 的 EvoMemoryMiddleware 就是继承这个基类,只重写了需要的钩子。
此外,LangChain 还提供了装饰器形式的快捷方式,让你不用写完整的类:
# 用装饰器快速创建一个只有 before_model 的 middleware
@before_model
def log_before(state, runtime):
print(f"Messages: {len(state['messages'])}")
agent = create_agent(model, middleware=[log_before])
这些装饰器(@before_model、@before_agent、@after_model、@after_agent、@dynamic_prompt、@wrap_model_call、@wrap_tool_call)内部会动态 type() 创建一个 AgentMiddleware 子类并实例化,本质上和写 class 继承是等价的。
六大生命周期钩子
LangChain 1.0 的 Middleware 系统提供了 两类六种钩子,覆盖了 Agent Loop 中每一个关键节点:
Node-style 钩子
before_agent 和 after_agent:在整个 Agent 调用的开始和结束各执行一次,适合做初始化(如加载用户偏好)和清理(如持久化最终状态)。
before_model:在每次 LLM 调用之前执行,可以完全访问和修改 Agent 状态。这是 Context Engineering 的主战场——你可以在这里做消息历史摘要、Token 计数检查、动态注入上下文,甚至直接跳转到其他节点来中断循环。
after_model:在模型返回之后、工具执行之前执行。适合做输出验证、安全护栏、Human-in-the-Loop 审批。值得注意的是,after_model 钩子以反向顺序执行——最后添加的 Middleware 最先看到模型输出。这是一种经典的分层策略模式。
Wrap-style 钩子
wrap_model_call:包裹整个模型调用过程。你可以决定调用零次(短路)、一次(正常流程)、或多次(重试逻辑)。例如:
from langchain.agents.middleware import wrap_model_call
@wrap_model_call
def retry_on_failure(request, handler):
for attempt in range(3):
try:
return handler(request)
except Exception as e:
if attempt == 2:
raise
print(f"Retry {attempt + 1}/3: {e}")
wrap_tool_call:包裹单个工具调用。适合实现工具级别的缓存、监控、速率限制、错误处理。例如对相同参数的工具调用做结果缓存:
from langchain.agents.middleware import wrap_tool_call
cache = {}
@wrap_tool_call
def cache_tool_results(request, handler):
key = f"{request.tool.name}:{json.dumps(request.toolCall.args)}"
if key in cache:
return cache[key]
result = handler(request)
cache[key] = result
return result
还有一个特别的便捷钩子
modify_model_request:这个钩子在 before_model 之后、实际模型调用之前执行。与 before_model 不同的是,它对状态的修改不会持久化——只影响当前这一次请求。这非常适合做临时性的 Prompt 注入、工具列表过滤或模型切换,而不污染全局状态。
完整的执行顺序
当你注册多个 Middleware 时,执行顺序如下:

这种"进去顺序执行、出来反向执行"的模式,与 Express.js、Django 的中间件机制完全同构。
13 个内置 Middleware
LangChain v1 提供了 13 个开箱即用的 Middleware 实现,覆盖了生产环境中最常见的需求:
| Middleware | 使用的钩子 | 功能 |
|---|---|---|
SummarizationMiddleware | before_model | 当消息历史接近 Token 上限时自动摘要 |
HumanInTheLoopMiddleware | after_model | 对指定工具调用要求人工审批 |
ContextEditingMiddleware | before_model | 超限时清除旧的工具输出 |
TodoListMiddleware | before_model + 工具注入 | 注入 write_todos 计划工具 |
ModelFallbackMiddleware | wrap_model_call | 主模型失败时自动切换备用模型 |
ToolRetryMiddleware | wrap_tool_call | 工具失败时指数退避重试 |
ToolCallLimitMiddleware | wrap_tool_call | 限制工具调用频率 |
ModelCallLimitMiddleware | wrap_model_call | 限制模型调用次数 |
ShellToolMiddleware | 工具注入 + wrap_tool_call | 提供持久化 Shell 会话 |
LLMToolSelectorMiddleware | before_model | 用 LLM 动态筛选相关工具 |
AnthropicPromptCachingMiddleware | modify_model_request | 缓存系统提示和工具定义 |
PIIRedactionMiddleware | before_model | 用模式匹配脱敏 PII 数据 |
LLMToolEmulatorMiddleware | wrap_tool_call | 用 LLM 模拟工具执行 |
这些 Middleware 可以像乐高积木一样任意组合。例如,一个典型的生产级配置可能是:
from langchain.agents import create_agent
from langchain.agents.middleware import (
SummarizationMiddleware,
HumanInTheLoopMiddleware,
ToolRetryMiddleware,
ModelFallbackMiddleware,
)
agent = create_agent(
model="anthropic:claude-sonnet-4-20250514",
tools=[search, execute_code, send_email],
middleware=[
SummarizationMiddleware(token_threshold=100_000),
ToolRetryMiddleware(max_retries=3, backoff_factor=2.0),
ModelFallbackMiddleware(fallback_models=["openai:gpt-4o"]),
HumanInTheLoopMiddleware(
interrupt_on={"send_email": True, "execute_code": True}
),
],
)