钩子
中间件提供两种风格的钩子来拦截代理执行:节点式钩子
在特定的执行点按顺序运行。用于日志记录、验证和状态更新。 可用钩子:beforeAgent- 代理开始前(每次调用一次)beforeModel- 每次模型调用前afterModel- 每次模型响应后afterAgent- 代理完成后(每次调用一次)
包装式钩子
拦截执行并控制处理程序的调用时机。用于重试、缓存和转换。 你决定处理程序是被调用零次(短路)、一次(正常流程)还是多次(重试逻辑)。 可用钩子:wrapModelCall- 围绕每个模型调用wrapToolCall- 围绕每个工具调用
创建中间件
使用createMiddleware 函数定义自定义中间件:
自定义状态模式
中间件可以使用自定义属性扩展代理的状态。这使得中间件能够:- 跨执行跟踪状态:维护在代理执行生命周期中持久存在的计数器、标志或其他值
- 在钩子之间共享数据:从
beforeModel传递信息到afterModel,或在不同的中间件实例之间传递 - 实现横切关注点:在不修改核心代理逻辑的情况下,添加速率限制、使用情况跟踪、用户上下文或审计日志等功能
- 做出条件决策:使用累积的状态来决定是否继续执行、跳转到不同节点或动态修改行为
_) 开头的字段被视为私有,不会包含在代理的结果中。只有公共字段(没有前导下划线的字段)会被返回。
这对于存储不应暴露给调用者的内部中间件状态非常有用,例如临时跟踪变量或内部标志:
自定义上下文
中间件可以定义自定义上下文模式来访问每次调用的元数据。与状态不同,上下文是只读的,并且不会在调用之间持久化。这使它非常适合:- 用户信息:传递在执行期间不会更改的用户 ID、角色或首选项
- 配置覆盖:提供每次调用的设置,如速率限制或功能标志
- 租户/工作区上下文:为多租户应用程序包含特定于组织的数据
- 请求元数据:传递中间件所需的请求 ID、API 密钥或其他元数据
runtime.context 访问它。上下文模式中的必填字段将在 TypeScript 级别强制执行,确保在调用 agent.invoke() 时必须提供它们。
contextSchema 中定义必填字段(没有 .optional() 或 .default() 的字段)时,TypeScript 将强制在 agent.invoke() 调用期间必须提供这些字段。这确保了类型安全,并防止因缺少所需上下文而导致的运行时错误。
执行顺序
当使用多个中间件时,了解它们如何执行:执行流程
执行流程
前置钩子按顺序运行:
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_*钩子:从第一个到最后一个after_*钩子:从最后一个到第一个(反向)wrap_*钩子:嵌套(第一个中间件包装所有其他中间件)
代理跳转
要提前退出中间件,返回一个带有jump_to 的字典:
可用跳转目标:
'end':跳转到代理执行的末尾(或第一个after_agent钩子)'tools':跳转到工具节点'model':跳转到模型节点(或第一个before_model钩子)
最佳实践
- 保持中间件专注 - 每个中间件应该做好一件事
- 优雅地处理错误 - 不要让中间件错误导致代理崩溃
- 使用适当的钩子类型:
- 节点式用于顺序逻辑(日志记录、验证)
- 包装式用于控制流(重试、回退、缓存)
- 清楚地记录任何自定义状态属性
- 在集成之前独立地进行单元测试中间件
- 考虑执行顺序 - 将关键中间件放在列表的首位
- 尽可能使用内置中间件
示例
动态模型选择
工具调用监控
动态选择工具
在运行时选择相关工具以提高性能和准确性。本节涵盖过滤预注册工具。关于注册在运行时发现的工具(例如,从 MCP 服务器),请参见 运行时工具注册。 好处:- 更短的提示词 - 通过仅暴露相关工具来降低复杂性
- 更高的准确性 - 模型从更少的选项中做出正确选择
- 权限控制 - 基于用户访问权限动态过滤工具
使用系统消息
在中间件中使用ModelRequest 中的 systemMessage 字段修改系统消息。它包含一个 SystemMessage 对象(即使代理是使用字符串 systemPrompt 创建的)。
示例:链式中间件 - 不同的中间件可以使用不同的方法:
SystemMessage.concat 来保留缓存控制元数据或由其他中间件创建的结构化内容块。
其他资源
将这些文档连接 到 Claude、VSCode 等,通过 MCP 获取实时答案。

