概述
智能体中间件是在不修改智能体或模型代码的前提下,向智能体执行流程中的关键位置注入自定义逻辑(日志、追踪、输入改写、访问控制等)的机制。 AgentScope 暴露了 6 个 hook 位置外加一个工具提供 hook,覆盖了从外层 reply 流程一路下沉到底层模型 API 调用的全链路:以上 hook 均作用于智能体层面。若需要在单个工具实例上挂钩 —— 无论该工具是在智能体内部还是外部被调用 —— 请参见工具中间件。
- Onion(洋葱式)—— 中间件包裹下一层 handler,可以在
next_handler()前后插入逻辑、观察中间事件流。 - Transformer(变换式)—— 中间件之间串成流水线,前一个的输出作为后一个的输入,不存在「内层」概念。
- Tool source(工具来源)—— 不在运行时路径上的 hook。
Agent.__init__不会调用list_tools();需要显式从中间件中收集工具并自行传入 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 只包裹智能体运行时内部的工具执行;通过 external execution 在智能体外部执行的工具不会被 on_acting 追踪到。装备中间件
AgentScope 把一组 hook 装在一个类里 —— 同一个中间件类可以同时实现 6 个 hook 位置(外加可选的list_tools tool-provider hook)中的任意子集。把实例传入 Agent(middlewares=[...]) 即可装备:
内置中间件
AgentScope 目前支持如下的中间件实现:链路追踪
TracingMiddleware 为智能体全生命周期接入 OpenTelemetry 追踪。它在 on_reply、on_model_call、on_acting 三个位置打点,按层级生成 span。
使用前先在进程中注册 TracerProvider 与 OTLP 导出器:
TracingMiddleware 装到智能体上即可:
- 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
如需在智能体生命周期内追踪自定义操作,可直接使用 OpenTelemetry Python SDK。获取一个作用域为 AgentScope 的 tracer,并将目标代码包裹在 span 中:TracerProvider 中配置的同一个 OTLP collector。
预算控制
ReplyBudgetControlMiddleware 为单次 reply 强制一个加权 token 预算。它累计统计一次 reply 内所有 reasoning 步骤的 token 消耗,一旦预算耗尽,便指示智能体立即收尾、不再调用任何工具。适用于为长链路、工具密集的 ReAct 循环设定成本或时延上限。
每次模型调用的加权成本按如下公式计算:
token_budget 时,middleware 会:
- 在智能体上下文最后一条 assistant 消息中追加一个
HintBlock(若不存在则新建一条AssistantMsg),提醒模型给出最终结论性回复。 - 把下一步 reasoning 的
tool_choice强制覆写为ToolChoice(mode="none"),阻止继续调用工具。
float
必填
单次 reply 允许的最大加权 token 成本。累计成本达到此阈值后,智能体会被指示立刻收尾、不再调用工具。
float
默认值:"1"
计算加权成本时应用于 input tokens 的乘数。
float
默认值:"1"
计算加权成本时应用于 output tokens 的乘数。通常将其设为高于
input_token_weight,以体现 output tokens 实际成本更高。str
预算超出时注入智能体上下文的提示消息。默认是内置的收尾 prompt,要求模型给出最终结论性回复、不再调用任何工具。
预算的作用域是单次 reply,而非智能体生命周期。每次新的 reply 都会从零计数,因此该限制对每次
agent(...) 调用独立生效。语音生成
TTSMiddleware 拦截智能体的文本输出并合成语音,将 DataBlockStartEvent / DataBlockDeltaEvent / DataBlockEndEvent 音频事件注入到事件流中。它在 on_reply 阶段拦截,观察每个 TextBlockDeltaEvent 和 TextBlockEndEvent。
输出事件流因模式而异:
标准模式 — 音频在文本块结束后输出:
DataBlockDeltaEvent.data 携带增量 base64 编码的音频 chunk;完整音频为所有 delta 解码后的字节拼接,按 block_id 关联。
长期记忆
AgentScope 以中间件的形式支持长期记忆,让智能体能够在多个会话间持久保存并调取信息。详见 长期记忆 章节。RAG
AgentScope 中知识库同样以中间件形式提供,允许智能体在推理时访问外部知识库。详见 RAG 章节。自定义中间件
继承基类MiddlewareBase 并实现所需位置的 hook 即可。
下面的示例用一个中间件覆盖了所有位置。每个 onion hook 都会收到一个 input_kwargs 字典,承载流入下一层的字段,通过 next_handler(**input_kwargs) 透传,也可以用关键字参数覆写其中某些字段:
执行顺序
洋葱型 hook(on_reply、on_reasoning、on_acting、on_model_call)—— 列表中第一个中间件处于最外层:
on_system_prompt)—— 中间件从左到右串行接力:
list_tools 不在每次 reply 的执行路径上,智能体不会自动调用它 —— 它是一个便捷接口,让中间件可以声明自己的工具。是否收集这些工具由组装 toolkit 的调用方决定。