通过实现在代理执行流程中特定点运行的钩子来构建自定义 middleware。
状态字段可以是公共的或私有的。以下划线(
必需的上下文字段
:当您在
Key rules:
生成的系统消息将是:
使用
钩子
Middleware 提供两种钩子样式来拦截代理执行:Node-style hooks
在特定执行点顺序运行。用于日志记录、验证和状态更新。 可用的钩子:-
beforeAgent- 代理启动前(每次调用执行一次) -
beforeModel- 每次模型调用前 -
afterModel- 每次模型响应后 -
afterAgent- 代理完成后(每次调用执行一次)
Wrap-style hooks
拦截执行并控制何时调用处理器。用于重试、缓存和转换。 您可以决定处理器是调用零次(短路)、一次(正常流程)还是多次(重试逻辑)。 可用的钩子:-
wrapModelCall- 包装每次模型调用 -
wrapToolCall- 包装每次工具调用
创建 middleware
使用
createMiddleware
函数定义自定义 middleware:
自定义状态模式
Middleware 可以使用自定义属性扩展代理的状态。这使得 middleware 能够:- 跨执行跟踪状态 :维护在代理执行生命周期中持续存在的计数器、标志或其他值
-
在钩子之间共享数据
:将信息从
beforeModel传递到afterModel或在不同的 middleware 实例之间传递 - 实现横切关注点 :添加速率限制、使用跟踪、用户上下文或审计日志等功能,而无需修改核心代理逻辑
- 做出条件决策 :使用累积状态来确定是否继续执行、跳转到不同节点或动态修改行为
_
)开头的字段被视为私有字段,不会包含在代理的结果中。只有公共字段(不带前导下划线的字段)会被返回。
这对于存储不应暴露给调用者的内部 middleware 状态非常有用,例如临时跟踪变量或内部标志:
自定义上下文
Middleware 可以定义自定义上下文模式来访问每次调用的元数据。与状态不同,上下文是只读的,不会在调用之间持久化。这使其非常适合:- 用户信息 :传递执行期间不变的用户 ID、角色或偏好设置
- 配置覆盖 :提供每次调用的设置,如速率限制或功能标志
- 租户/工作区上下文 :包含多租户应用程序的组织特定数据
- 请求元数据 :传递 middleware 所需的请求 ID、API 密钥或其他元数据
runtime.context
访问它。上下文模式中的必填字段将在 TypeScript 级别强制执行,确保您在调用
agent.invoke()
时必须提供它们。
contextSchema
中定义必需字段时(没有
.optional()
或
.default()
的字段),TypeScript 将强制要求在
agent.invoke()
调用期间必须提供这些字段。这确保了类型安全并防止因缺少必需上下文而导致的运行时错误。
Execution order
当使用多个中间件时,了解它们的执行方式:
执行流程
执行流程
前置钩子按顺序运行:
-
middleware1.before_agent() -
middleware2.before_agent() -
middleware3.before_agent()
-
middleware1.before_model() -
middleware2.before_model() -
middleware3.before_model()
-
middleware1.wrap_model_call()→middleware2.wrap_model_call()→middleware3.wrap_model_call()→ model
-
middleware3.after_model() -
middleware2.after_model() -
middleware1.after_model()
-
middleware3.after_agent() -
middleware2.after_agent() -
middleware1.after_agent()
-
before_*hooks: First to last -
after_*hooks: Last to first (reverse) -
wrap_*hooks: Nested (first middleware wraps all others)
Agent jumps
To exit early from middleware, return a dictionary with
jump_to
:
Available jump targets:
-
'end': Jump to the end of the agent execution (or the firstafter_agenthook) -
'tools': Jump to the tools node -
'model': Jump to the model node (or the firstbefore_modelhook)
Best practices
- 保持中间件专注 - 每个中间件应该只做好一件事
- 优雅地处理错误 - 不要让中间件错误导致智能体崩溃
-
使用适当的钩子类型
:
- Node 风格用于顺序逻辑(日志记录、验证)
- Wrap 风格用于控制流(重试、回退、缓存)
- 清楚地记录任何自定义状态属性
- 在集成之前独立对中间件进行单元测试
- 考虑执行顺序 - 将关键中间件放在列表首位
- 尽可能使用内置中间件
Examples
Dynamic model selection
Tool call monitoring
Dynamically selecting tools
Select relevant tools at runtime to improve performance and accuracy. Benefits:- Shorter prompts - Reduce complexity by exposing only relevant tools
- Better accuracy - Models choose correctly from fewer options
- Permission control - Dynamically filter tools based on user access
Working with system messages
Modify system messages in middleware using the
systemMessage
field in
ModelRequest
. It contains a
SystemMessage
object (even if the agent was created with a string
systemPrompt
).
Example: Chaining middleware
- Different middleware can use different approaches:
SystemMessage.concat
来保留其他中间件创建的缓存控制元数据或结构化内容块。
Additional resources
在 GitHub 上编辑此页面
或
提交问题
。