Skip to main content

概述

智能体中间件是在不修改智能体或模型代码的前提下,向智能体执行流程中的关键位置注入自定义逻辑(日志、追踪、输入改写、访问控制等)的机制。 AgentScope 暴露了 6 个 hook 位置外加一个工具提供 hook,覆盖了从外层 reply 流程一路下沉到底层模型 API 调用的全链路:
以上 hook 均作用于智能体层面。若需要在单个工具实例上挂钩 —— 无论该工具是在智能体内部还是外部被调用 —— 请参见工具中间件
三种类型的差别:
  • Onion(洋葱式)—— 中间件包裹下一层 handler,可以在 next_handler() 前后插入逻辑、观察中间事件流。
  • Transformer(变换式)—— 中间件之间串成流水线,前一个的输出作为后一个的输入,不存在「内层」概念。
  • Tool source(工具来源)—— 不在运行时路径上的 hook。Agent.__init__ 不会调用 list_tools();需要显式从中间件中收集工具并自行传入 toolkit。
下图展示这些 hook 在智能体生命周期中的嵌套关系。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 只包裹智能体运行时内部的工具执行;通过 external execution 在智能体外部执行的工具不会被 on_acting 追踪到。

装备中间件

AgentScope 把一组 hook 装在一个类里 —— 同一个中间件类可以同时实现 6 个 hook 位置(外加可选的 list_tools tool-provider hook)中的任意子集。把实例传入 Agent(middlewares=[...]) 即可装备:
构造时智能体会扫描每个中间件实例,按它实际实现了哪些 hook,把它分配到对应位置的执行列表中。未实现的位置自动跳过,不产生任何调用开销。

内置中间件

AgentScope 目前支持如下的中间件实现:

链路追踪

TracingMiddleware 为智能体全生命周期接入 OpenTelemetry 追踪。它在 on_replyon_model_callon_acting 三个位置打点,按层级生成 span。 使用前先在进程中注册 TracerProvider 与 OTLP 导出器:
随后把 TracingMiddleware 装到智能体上即可:
每次 reply 会产出一棵嵌套 span 树,每个层级捕获的关键属性如下:
来自 on_reply
  • Agent 名称、session ID、reply ID
  • 输入消息与最终输出消息
  • HITL 等待中的工具调用
  • External execution 等待中的工具调用
未配置 TracerProvider 时,所有 hook 会直接短路到 next_handler(),不创建 span 也不计算属性 —— 几乎零开销。
Agent 收到 ExternalExecutionResultEvent(外部执行的工具结果)时,TracingMiddleware 会为每条外部执行结果合成一个补偿 span,让外部系统执行的工具也能保留完整可观测性。

添加自定义 Span

如需在智能体生命周期内追踪自定义操作,可直接使用 OpenTelemetry Python SDK。获取一个作用域为 AgentScope 的 tracer,并将目标代码包裹在 span 中:
这些自定义 span 会与 AgentScope 内置的 span 一并上报,发送到 TracerProvider 中配置的同一个 OTLP collector。

预算控制

ReplyBudgetControlMiddleware单次 reply 强制一个加权 token 预算。它累计统计一次 reply 内所有 reasoning 步骤的 token 消耗,一旦预算耗尽,便指示智能体立即收尾、不再调用任何工具。适用于为长链路、工具密集的 ReAct 循环设定成本或时延上限。 每次模型调用的加权成本按如下公式计算:
当累计成本达到 token_budget 时,middleware 会:
  1. 在智能体上下文最后一条 assistant 消息中追加一个 HintBlock(若不存在则新建一条 AssistantMsg),提醒模型给出最终结论性回复。
  2. 把下一步 reasoning 的 tool_choice 强制覆写为 ToolChoice(mode="none"),阻止继续调用工具。
像其他 middleware 一样挂载:
构造参数如下:
float
必填
单次 reply 允许的最大加权 token 成本。累计成本达到此阈值后,智能体会被指示立刻收尾、不再调用工具。
float
默认值:"1"
计算加权成本时应用于 input tokens 的乘数。
float
默认值:"1"
计算加权成本时应用于 output tokens 的乘数。通常将其设为高于 input_token_weight,以体现 output tokens 实际成本更高。
str
预算超出时注入智能体上下文的提示消息。默认是内置的收尾 prompt,要求模型给出最终结论性回复、不再调用任何工具。
该中间件实例自身无状态 —— 所有运行时状态存放在 agent.state.middle_context 中,以中间件 key 与当前 reply_id 作为索引。这意味着同一个中间件实例可以安全地在多个智能体间共享,且预算状态在 human-in-the-loop(HITL)中断与恢复中保持有效。reply 结束时状态会被自动清理。
预算的作用域是单次 reply,而非智能体生命周期。每次新的 reply 都会从零计数,因此该限制对每次 agent(...) 调用独立生效。

语音生成

TTSMiddleware 拦截智能体的文本输出并合成语音,将 DataBlockStartEvent / DataBlockDeltaEvent / DataBlockEndEvent 音频事件注入到事件流中。它在 on_reply 阶段拦截,观察每个 TextBlockDeltaEventTextBlockEndEvent
中间件根据 TTS 模型的模式自动适配: 输出事件流因模式而异: 标准模式 — 音频在文本块结束后输出:
实时模式 — 音频 chunk 与文本交替到达(并发合成):
每个 DataBlockDeltaEvent.data 携带增量 base64 编码的音频 chunk;完整音频为所有 delta 解码后的字节拼接,按 block_id 关联。

长期记忆

AgentScope 以中间件的形式支持长期记忆,让智能体能够在多个会话间持久保存并调取信息。详见 长期记忆 章节。

RAG

AgentScope 中知识库同样以中间件形式提供,允许智能体在推理时访问外部知识库。详见 RAG 章节。

自定义中间件

继承基类 MiddlewareBase 并实现所需位置的 hook 即可。 下面的示例用一个中间件覆盖了所有位置。每个 onion hook 都会收到一个 input_kwargs 字典,承载流入下一层的字段,通过 next_handler(**input_kwargs) 透传,也可以用关键字参数覆写其中某些字段:

执行顺序

洋葱型 hook(on_replyon_reasoningon_actingon_model_call)—— 列表中第一个中间件处于最外层
对于流式 / 产出事件的 hook,内层中间件先看到每一个 yield 出的事件:
Transformer 类 hook(on_system_prompt)—— 中间件从左到右串行接力
一次 reply 中各 hook 的整体执行顺序遵循 agent 生命周期:
list_tools 不在每次 reply 的执行路径上,智能体不会自动调用它 —— 它是一个便捷接口,让中间件可以声明自己的工具。是否收集这些工具由组装 toolkit 的调用方决定。

实用示例

计时中间件

下面的中间件记录每次模型调用的耗时:

限速中间件

下面的中间件在两次模型调用之间强制留出最小间隔:

动态系统提示中间件

下面的中间件在系统提示中注入实时上下文:

模型回退中间件

下面的中间件在主模型失败时切换到备用模型: