跳到主要内容

自定义中间件

参数传递机制

ModelRequest/Response

单次调用级别的"细粒度控制"。

  • 作用域:仅作用于单次模型或工具调用的生命周期
  • 数据内容:包含当前调用的具体参数(model, messages, tools, config)
  • 典型来源:wrap_model_call, wrap_tool_call
# ModelRequest 结构示例
request = ModelRequest(
model=ChatOpenAI(...), # 可动态替换的模型实例
messages=[...], # 本次调用的消息列表(可被修改)
tools=[...], # 本次可用的工具列表(可被过滤)
runtime=context # 包含 AgentState 的引用
)
# 典型结构
request.model # 当前要调用的模型实例(可修改)
request.tools # 可用工具列表(可修改)
request.messages # 消息历史
request.state # Agent 的当前状态
request.runtime # 运行时上下文,包含 request.runtime.context 等

关键特性:

  • 可变性:你可以在中间件中直接修改 request.model、request.tools 等属性,实现动态路由

  • 信息丰富:携带了完整的执行上下文,便于实现条件逻辑

  • 拦截点:在模型实际调用前,允许你审查或修改任何参数

handler

实际执行模型调用的函数,它是 LangChain 内部封装的"下一个"执行环节。

  • 控制权:可以选择是否调用 handler(request),甚至多次调用(如重试)
  • 责任链:调用 handler 相当于将请求传递给链条的下一环
  • 不可变性:handler 本身不可修改,但可以通过修改 request 来影响其行为
# handler 本质上等价于:
def handler(request: ModelRequest) -> ModelResponse:
# 实际调用 LLM 模型并返回响应
return actual_model_call(request)

AgentState

整个 Agent 生命周期的"全局状态容器"。

  • 作用域:贯穿整个 Agent 执行流程,跨多次模型调用
  • 数据内容:完整的对话历史、用户元数据、业务状态字段
  • 典型来源:before_model, after_model, before_agent 等

总结

  • 在 wrap_model_call 和 wrap_tool_call 等包裹类 Hook 中,所有 request 字段都可直接赋值修改
  • 在 before_model、after_model 等 Hook 中,只能读取 state,不能访问 request
参数类型本质所属模块生命周期
ModelRequest数据类(dataclass)langchain.agents.middleware.base单次模型调用
ModelResponse数据类(dataclass)langchain.agents.middleware.base单次模型调用
AgentState字典结构(TypedDict)langchain.agents.agent整个 Agent 会话
handler函数对象(可调用)动态传递单次包装调用

自定义中间件开发流程

  1. 需求分析:深入理解将要解决的问题域,与业务专家沟通
  2. 设计阶段:考虑可扩展性、与其他中间件的依赖关系
  3. 开发实现:实现具体的中间件逻辑
  4. 测试验证:确保中间件正确工作
  5. 部署上线:集成到生产环境