上一篇讲了 middleware 的概念——不动核心 loop,通过钩子横切注入。这一篇落到具体:钩子长在哪、每个位置适合干什么。
核心 loop 有哪些「钩子点」
Agent 的核心 loop 是「调模型 → 判断 → 调工具 → 循环」。在这个 loop 里,有固定的几个时刻,是 middleware 可以插入的「钩子点」:
最主要的两个:
- beforeModel(模型调用前):模型还没被调,你可以在这里改要传给模型的东西
- afterModel(模型调用后):模型已经输出,你可以在这里检查或改它的输出
另外还有工具执行前后的钩子,但最常用的是 beforeModel 和 afterModel。这一篇重点讲这两个。
beforeModel:模型调用前,能改输入
beforeModel 发生在「模型即将被调用」之前。在这个钩子里,你能拿到将要传给模型的内容(主要是 messages 列表),并在调用前修改它。
这个位置适合做什么?
- 注入历史消息:把之前的对话历史加进 messages——这就是「记忆」的实现位置(第 35 篇展开)
- 上下文压缩:messages 太长时,先把旧消息压缩/摘要,避免超出窗口
- 注入系统指令:根据情况动态加一段 system 提示
- 过滤敏感内容:把输入里的敏感信息脱敏后再给模型
共同点:都是在「内容到达模型前」做预处理。
afterModel:模型调用后,能查/改输出
afterModel 发生在「模型已经输出」之后。在这个钩子里,你能拿到模型的输出,检查它、改它。
这个位置适合做什么?
- 护栏检查:模型输出有没有违规、越界?有就拦截或改写
- 输出格式化:把模型输出整理成统一格式
- 日志记录:记录模型这次说了什么
- 结果缓存:相同输入直接返回缓存,省一次调用
共同点:都是在「模型输出后、交给下一步前」做后处理。
before/after 的对称性
beforeModel 和 afterModel 是对称的一对:一个管「进」,一个管「出」。夹在它们中间的,就是核心的模型调用。
这种对称设计让你能完整地控制「模型这一次调用」:进之前预处理(before),出之后后处理(after),中间的模型调用本身不变。
一个具体例子:记忆 middleware
把概念落到具体。一个「记忆 middleware」就是用 beforeModel 实现的:
beforeModel(state):
# 从某处取出历史消息
history = load_history(state.thread_id)
# 把历史插到要传给模型的消息前面
state.messages = history + state.messages
每次模型要被调用前,这个 middleware 先把历史消息注入进去——模型就能「记得」之前聊过什么。这就是 Agent 记忆的核心实现机制(第 35 篇会详细讲记忆)。
为什么是「钩子」而不是「重写」
再强调一次 middleware 的本质:它是钩子(hook),不是重写。
- 你不关心核心 loop 怎么转、怎么循环——那是
create_agent的事 - 你只关心「在我的钩子点,做我的事」
- 框架保证:核心 loop 每转一圈,都会经过你的钩子
这种关注点分离,让你写 middleware 时只想着自己那点事(注入历史、压缩、检查),不用管整个 Agent 怎么运转。这也是 middleware 能「可组合、可复用」的根源——每个 middleware 都是独立的、专注的。
收束:两个钩子点,覆盖大多数定制
这一篇讲了 middleware 的钩子点:
- 主要是 beforeModel(模型前,改输入)和 afterModel(模型后,查改输出)
- beforeModel 适合:记忆注入、上下文压缩、加指令、脱敏
- afterModel 适合:护栏、格式化、日志、缓存
- 两者对称,夹住模型调用
- middleware 是钩子不是重写,专注自己的事
下一篇讲 LangChain 提供的预置 middleware——那些最常见的横切能力,官方已经写好了,你直接用。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。我相信 AI 是程序员的最佳搭档。
如果你想跟完这套「图解 LangChain」,欢迎关注公众号 「十三Tech」。全系列 42 篇,会按认识基础、LangGraph 状态机、Agent 与 middleware、RAG 检索、Tools/MCP/记忆、生产化收束这条线更新。

