Overview
Agent middleware is the mechanism for injecting custom logic — logging, tracing, input rewriting, access control — into key points of the agent execution pipeline, without modifying the agent or model code. AgentScope exposes 6 hook positions plus a tool-provider hook, covering the full path from the outer reply process down to the raw model API call:These hooks operate at the agent level. For per-tool onion hooks that fire on every invocation of a specific tool — regardless of whether it’s called inside or outside an agent — see Tool Middleware.
- Onion — middleware wraps the next handler, allowing logic before/after
next_handler()and observation of the intermediate event stream. - Transformer — middlewares form a pipeline; the previous one’s output feeds into the next one. There is no “inner layer” concept.
- Tool source — not a hook on the runtime path.
Agent.__init__does not calllist_tools(); you opt in explicitly by collecting the tools from your middlewares and passing them into the toolkit yourself.
on_system_prompt is embedded inside on_reasoning because it fires when the reasoning step assembles the system prompt; on_compress_context sits at the top of each ReAct round, before reasoning:
on_reply
ReAct loop (per round)
on_compress_context (context compression decision)
on_reasoning
on_system_prompt (system prompt assembly)
on_model_call (model API call)
on_acting (once per tool call)
on_acting currently wraps only tool execution inside the agent runtime; tools dispatched outside the agent via external execution are not tracked by on_acting.Equip Middleware
AgentScope packages a set of hooks into a class — a single middleware class can implement any subset of the 6 hook positions (plus the optionallist_tools tool-provider hook) at the same time. Pass instances to Agent(middlewares=[...]) to equip them:
Built-in Middleware
AgentScope currently supports the following middleware implementations:Tracing
TracingMiddleware wires the full agent lifecycle to OpenTelemetry tracing. It instruments on_reply, on_model_call, and on_acting, producing hierarchical spans.
Before using it, register a TracerProvider and an OTLP exporter in the process:
TracingMiddleware to the agent:
- Agent Reply Span
- Model Call Span
- Tool Execution Span
From
on_reply:- Agent name, session ID, reply ID
- Input messages and the final output message
- HITL pending tool calls
- External execution pending tool calls
TracerProvider is configured, every hook short-circuits directly to next_handler() — no spans are created, no attributes are computed — making the overhead negligible.
When the agent receives an
ExternalExecutionResultEvent (a tool executed outside the agent), TracingMiddleware synthesizes a compensating span for each external execution result, preserving full observability for tools run by external systems.Add Additional Spans
To trace custom operations within the agent lifecycle, use the standard OpenTelemetry Python SDK directly. Obtain a tracer scoped to AgentScope and wrap any target code in a span:TracerProvider.
Budget Control
ReplyBudgetControlMiddleware enforces a weighted token budget per reply. It tracks cumulative token usage across all reasoning steps within a single reply and, once the budget is exhausted, instructs the agent to wrap up immediately without invoking any further tools. This is useful for capping the cost or latency of long, tool-heavy ReAct loops.
The weighted cost is computed on every model call as:
token_budget, the middleware:
- Appends a
HintBlockto the last assistant message in the agent’s context (or creates a newAssistantMsgif needed), reminding the model to produce a final concluding response. - Overrides
tool_choicetoToolChoice(mode="none")for the next reasoning step, preventing any further tool calls.
float
required
Maximum weighted token cost allowed per reply. Once the accumulated cost reaches this threshold, the agent is instructed to wrap up without calling any more tools.
float
default:"1"
Multiplier applied to input tokens when computing the weighted cost.
float
default:"1"
Multiplier applied to output tokens when computing the weighted cost. Set this higher than
input_token_weight to reflect that output tokens are typically more expensive.str
The message injected into the agent’s context when the budget is exceeded. Defaults to a built-in wrap-up prompt that asks the model to provide a final concluding response without invoking any tools.
The budget is scoped per reply, not per agent lifetime. Each new reply starts with a fresh counter, so the limit applies independently to every call to
agent(...).Speech Generation
TTSMiddleware intercepts the agent’s text output and synthesizes speech audio, injecting DataBlockStartEvent / DataBlockDeltaEvent / DataBlockEndEvent into the event stream alongside the text. It hooks into on_reply to observe every TextBlockDeltaEvent and TextBlockEndEvent.
The output event stream differs by mode:
Non-realtime — audio follows after the text block completes:
DataBlockDeltaEvent.data carries an incremental base64-encoded audio chunk; the full audio is the concatenation of every delta’s decoded bytes, keyed by block_id.
Long-Term Memory
AgentScope supports long-term memory as middleware, so agents can persist and recall information across sessions. See Long-Term Memory for details.RAG
AgentScope also provides knowledge-base access through middleware, allowing agents to access external knowledge bases during reasoning. See RAG for details.Custom Middleware
SubclassMiddlewareBase and implement only the hook positions you need.
The example below covers every position in a single middleware. Each onion hook receives an input_kwargs dict carrying the fields that flow into the wrapped layer; forward it with next_handler(**input_kwargs), or pass keyword arguments to override specific fields:
Execution Order
Onion hooks (on_reply, on_reasoning, on_acting, on_model_call) — the first middleware in the list is the outermost layer:
on_system_prompt) — middlewares chain left to right:
list_tools is not part of the per-reply execution path and is not invoked automatically by the agent — it is a convenience interface so a middleware can advertise its own tools. The caller assembling the toolkit decides whether to collect them.