SubAgentMiddleware¶
SubAgentMiddleware
¶
Bases: AgentMiddleware
Middleware for providing subagents to an agent via a task tool.
This middleware adds a task tool to the agent that can be used to invoke subagents.
Subagents are useful for handling complex tasks that require multiple steps, or tasks
that require a lot of context to resolve.
A chief benefit of subagents is that they can handle multi-step tasks, and then return a clean, concise response to the main agent.
Subagents are also great for different domains of expertise that require a narrower subset of tools and focus.
This middleware comes with a default general-purpose subagent that can be used to handle the same tasks as the main agent, but with isolated context.
| PARAMETER | DESCRIPTION |
|---|---|
default_model
|
The model to use for subagents. Can be a
TYPE:
|
default_tools
|
The tools to use for the default general-purpose subagent.
TYPE:
|
default_middleware
|
Default middleware to apply to all subagents. If Pass a list to specify custom middleware.
TYPE:
|
default_interrupt_on
|
The tool configs to use for the default general-purpose subagent. These are also the fallback for any subagents that don't specify their own tool configs.
TYPE:
|
subagents
|
A list of additional subagents to provide to the agent.
TYPE:
|
system_prompt
|
Full system prompt override. When provided, completely replaces the agent's system prompt.
TYPE:
|
general_purpose_agent
|
Whether to include the general-purpose agent.
TYPE:
|
task_description
|
Custom description for the task tool. If
TYPE:
|
Example
from langchain.agents.middleware.subagents import SubAgentMiddleware
from langchain.agents import create_agent
# Basic usage with defaults (no default middleware)
agent = create_agent(
"openai:gpt-4o",
middleware=[
SubAgentMiddleware(
default_model="openai:gpt-4o",
subagents=[],
)
],
)
# Add custom middleware to subagents
agent = create_agent(
"openai:gpt-4o",
middleware=[
SubAgentMiddleware(
default_model="openai:gpt-4o",
default_middleware=[TodoListMiddleware()],
subagents=[],
)
],
)
| METHOD | DESCRIPTION |
|---|---|
__init__ |
Initialize the |
wrap_model_call |
Update the system message to include instructions on using subagents. |
awrap_model_call |
(async) Update the system message to include instructions on using subagents. |
before_agent |
Logic to run before the agent execution starts. |
abefore_agent |
Async logic to run before the agent execution starts. |
before_model |
Logic to run before the model is called. |
abefore_model |
Async logic to run before the model is called. |
after_model |
Logic to run after the model is called. |
aafter_model |
Async logic to run after the model is called. |
after_agent |
Logic to run after the agent execution completes. |
aafter_agent |
Async logic to run after the agent execution completes. |
wrap_tool_call |
Intercept tool execution for retries, monitoring, or modification. |
awrap_tool_call |
Intercept and control async tool execution via handler callback. |
state_schema
class-attribute
instance-attribute
¶
The schema for state passed to the middleware nodes.
name
property
¶
name: str
The name of the middleware instance.
Defaults to the class name, but can be overridden for custom naming.
__init__
¶
__init__(
*,
default_model: str | BaseChatModel,
default_tools: Sequence[BaseTool | Callable | dict[str, Any]] | None = None,
default_middleware: list[AgentMiddleware] | None = None,
default_interrupt_on: dict[str, bool | InterruptOnConfig] | None = None,
subagents: list[SubAgent | CompiledSubAgent] | None = None,
system_prompt: str | None = TASK_SYSTEM_PROMPT,
general_purpose_agent: bool = True,
task_description: str | None = None,
) -> None
Initialize the SubAgentMiddleware.
wrap_model_call
¶
wrap_model_call(
request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse
Update the system message to include instructions on using subagents.
awrap_model_call
async
¶
awrap_model_call(
request: ModelRequest, handler: Callable[[ModelRequest], Awaitable[ModelResponse]]
) -> ModelResponse
(async) Update the system message to include instructions on using subagents.
before_agent
¶
abefore_agent
async
¶
before_model
¶
abefore_model
async
¶
after_model
¶
aafter_model
async
¶
after_agent
¶
aafter_agent
async
¶
wrap_tool_call
¶
wrap_tool_call(
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]
Intercept tool execution for retries, monitoring, or modification.
Async version is awrap_tool_call
Multiple middleware compose automatically (first defined = outermost).
Exceptions propagate unless handle_tool_errors is configured on ToolNode.
| PARAMETER | DESCRIPTION |
|---|---|
request
|
Tool call request with call Access state via
TYPE:
|
handler
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ToolMessage | Command[Any]
|
|
The handler Callable can be invoked multiple times for retry logic.
Each call to handler is independent and stateless.
Examples:
Modify request before execution
Retry on error (call handler multiple times)
awrap_tool_call
async
¶
awrap_tool_call(
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]
Intercept and control async tool execution via handler callback.
The handler callback executes the tool call and returns a ToolMessage or
Command. Middleware can call the handler multiple times for retry logic, skip
calling it to short-circuit, or modify the request/response. Multiple middleware
compose with first in list as outermost layer.
| PARAMETER | DESCRIPTION |
|---|---|
request
|
Tool call request with call Access state via
TYPE:
|
handler
|
Async callable to execute the tool and returns Call this to execute the tool. Can be called multiple times for retry logic. Can skip calling it to short-circuit.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ToolMessage | Command[Any]
|
|
The handler Callable can be invoked multiple times for retry logic.
Each call to handler is independent and stateless.
Examples:
SubAgent
¶
Bases: TypedDict
Specification for an agent.
When specifying custom agents, the default_middleware from SubAgentMiddleware
will be applied first, followed by any middleware specified in this spec.
To use only custom middleware without the defaults, pass default_middleware=[]
to SubAgentMiddleware.
Required fields
name: Unique identifier for the subagent.
The main agent uses this name when calling the `task()` tool.
description: What this subagent does.
Be specific and action-oriented. The main agent uses this to decide when to delegate.
system_prompt: Instructions for the subagent.
Include tool usage guidance and output format requirements.
tools: Tools the subagent can use.
Keep this minimal and include only what's needed.
Optional fields
model: Override the main agent's model.
Use the format `'provider:model-name'` (e.g., `'openai:gpt-4o'`).
middleware: Additional middleware for custom behavior, logging, or rate limiting. interrupt_on: Configure human-in-the-loop for specific tools.
Requires a checkpointer.
description
instance-attribute
¶
description: str
What this subagent does. The main agent uses this to decide when to delegate.
tools
instance-attribute
¶
Tools the subagent can use.
model
instance-attribute
¶
model: NotRequired[str | BaseChatModel]
Override the main agent's model. Use 'provider:model-name' format.
middleware
instance-attribute
¶
middleware: NotRequired[list[AgentMiddleware]]
Additional middleware for custom behavior.
interrupt_on
instance-attribute
¶
interrupt_on: NotRequired[dict[str, bool | InterruptOnConfig]]
Configure human-in-the-loop for specific tools.
CompiledSubAgent
¶
Bases: TypedDict
A pre-compiled agent spec.
Note
The runnable's state schema must include a 'messages' key.
This is required for the subagent to communicate results back to the main agent.
When the subagent completes, the final message in the 'messages' list will be
extracted and returned as a ToolMessage to the parent agent.
runnable
instance-attribute
¶
runnable: Runnable
A custom agent implementation.
Create a custom agent using either:
- LangChain's
create_agent() - A custom graph using
langgraph
If you're creating a custom graph, make sure the state schema includes a 'messages' key. This is required for the subagent to communicate results back to the main agent.