概述
Agent middleware 是在不修改 agent 或 model 代码的前提下,向 agent 执行流程中的关键位置注入自定义逻辑(日志、追踪、输入改写、访问控制等)的机制。 AgentScope 暴露了 6 个 hook 位置外加一个 tool-provider hook,覆盖了从外层 reply 流程一路下沉到底层模型 API 调用的全链路:
三种类型的差别:
- Onion(洋葱式)—— middleware 包裹下一层 handler,可以在
next_handler()前后插入逻辑、观察中间事件流。 - Transformer(变换式)—— middleware 之间串成流水线,前一个的输出作为后一个的输入,不存在「内层」概念。
- Tool source(工具来源)—— 不在运行时路径上的 hook。
Agent.__init__不会调用list_tools();需要显式从 middleware 中收集工具并自行传入 toolkit。
on_system_prompt 嵌入在 on_reasoning 内部,因为它在 reasoning 步骤组装 system prompt 时被触发;on_compress_context 位于每轮 ReAct 顶部,在 reasoning 之前:
on_reply
ReAct loop(每一轮)
on_compress_context(上下文压缩判定)
on_reasoning
on_system_prompt(组装 system prompt)
on_model_call(模型 API 调用)
on_acting(每个工具调用一次)
当前
on_acting 只包裹 agent 运行时内部的工具执行;通过 external execution 在 agent 外部执行的工具不会被 on_acting 追踪到。装备 Middleware
AgentScope 把一组 hook 装在一个类里 —— 同一个 middleware 类可以同时实现 6 个 hook 位置(外加可选的list_tools tool-provider hook)中的任意子集。把实例传入 Agent(middlewares=[...]) 即可装备:
内置 Middleware
TracingMiddleware
TracingMiddleware 为 agent 全生命周期接入 OpenTelemetry 追踪。它在 on_reply、on_model_call、on_acting 三个位置打点,按层级生成 span。
使用前先在进程中注册 TracerProvider 与 OTLP 导出器:
TracingMiddleware 装到 agent 上即可:
- Agent Reply Span
- Model Call Span
- Tool Execution Span
来自
on_reply:- Agent 名称、session ID、reply ID
- 输入消息与最终输出消息
- HITL 等待中的工具调用
- External execution 等待中的工具调用
TracerProvider 时,所有 hook 会直接短路到 next_handler(),不创建 span 也不计算属性 —— 几乎零开销。
Agent 收到
ExternalExecutionResultEvent(外部执行的工具结果)时,TracingMiddleware 会为每条外部执行结果合成一个补偿 span,让外部系统执行的工具也能保留完整可观测性。添加自定义 Span
如需在 agent 生命周期内追踪自定义操作,可直接使用 OpenTelemetry Python SDK。获取一个作用域为 AgentScope 的 tracer,并将目标代码包裹在 span 中:TracerProvider 中配置的同一个 OTLP collector。
TTSMiddleware
TTSMiddleware 拦截 agent 的文本输出并合成语音,将 DataBlockStartEvent / DataBlockDeltaEvent / DataBlockEndEvent 音频事件注入到事件流中。它在 on_reply 阶段拦截,观察每个 TextBlockDeltaEvent 和 TextBlockEndEvent。
输出事件流因模式而异:
标准模式 — 音频在文本块结束后输出:
DataBlockDeltaEvent.data 携带增量 base64 编码的音频 chunk;完整音频为所有 delta 解码后的字节拼接,按 block_id 关联。
自定义 Middleware
继承MiddlewareBase,只实现需要的 hook 即可,其它的不用管。
下面的示例用一个 middleware 覆盖了所有位置。每个 onion hook 都会收到一个 input_kwargs 字典,承载流入下一层的字段,通过 next_handler(**input_kwargs) 透传,也可以用关键字参数覆写其中某些字段:
执行顺序
Onion 类 hook(on_reply、on_reasoning、on_acting、on_model_call)—— 列表中第一个 middleware 处于最外层:
on_system_prompt)—— middleware 从左到右串行接力:
list_tools 不在每次 reply 的执行路径上,agent 不会自动调用它 —— 它是一个便捷接口,让 middleware 可以声明自己的工具。是否收集这些工具由组装 toolkit 的调用方决定。