Subagents¶
A subagent is a child ReactAgent the planner can delegate an isolated subtask to. Its work runs in a fresh conversation with no access to the parent's history; only its final answer comes back as the task tool result. This keeps the planner's context clean and lets expensive multi-step subtasks run without polluting the parent's token budget.
How delegation works¶
When createDeepAgent is called, a SubAgentMiddleware is wired into the pipeline. It contributes a single task tool to the main agent's tool set and injects a system-prompt note listing the available subagents by name and description.
At runtime the planner calls:
TaskTool.execute then:
- Looks up the named subagent in its registry.
- Calls
createAgent(model:tools:systemPrompt:middleware:maxIterations:)with the subagent's settings. - Runs the new
ReactAgenton a single-message input - just the delegated task description. No parent history is forwarded. - Returns the subagent's committed final answer as the tool result.
The parent sees only the answer. The subagent's intermediate tool calls, reasoning, and partial outputs never enter the parent's conversation.
The SubAgent struct¶
public struct SubAgent: Sendable {
public init(
name: String,
description: String,
systemPrompt: String,
tools: [any AgentTool]? = nil,
model: (any ChatModel)? = nil,
middleware: [any AgentMiddleware] = [],
maxIterations: Int = 24
)
}
| Field | Required | Meaning |
|---|---|---|
name |
yes | Identifier the planner passes as subagent_type |
description |
yes | Surfaced to the planner (in the tool schema and prompt) so it knows when to delegate here |
systemPrompt |
yes | The subagent's own system prompt - not inherited from the parent |
tools |
no | nil inherits the deep agent's base tools; [] gives the subagent no tools |
model |
no | nil inherits the deep agent's model; supply an explicit model to use a different backend |
middleware |
no | Extra middleware for this subagent (logging, rate limiting, etc.) |
maxIterations |
no | Hard cap on ReAct rounds; defaults to 24 |
Subagents cannot spawn subagents
The task tool is contributed by SubAgentMiddleware and is intentionally NOT part of the base tool set that subagents inherit. A delegated subagent therefore has no way to spawn further subagents, which keeps the delegation graph shallow and predictable.
The general-purpose subagent¶
When includeGeneralPurpose: true (the default) is passed to createDeepAgent, a built-in general-purpose subagent is prepended to the registry:
name: "general-purpose"
description: "General-purpose agent for arbitrary multi-step subtasks. Use it to silo
isolated work (research, drafting, multi-step lookups) off your main
context. It has the same tools you do."
systemPrompt: "You are a focused subagent handling one delegated task. Do exactly what
was asked, using your tools as needed, and finish with a single clear,
self-contained answer the calling agent can use directly..."
tools: nil (inherits parent's base tools)
model: nil (inherits parent's model)
This covers the common delegation pattern without requiring any configuration. Disable it with includeGeneralPurpose: false if you want to control the registry entirely yourself.
Passing custom subagents¶
Supply an array of SubAgent values to createDeepAgent:
import DeepAgents
let researcher = SubAgent(
name: "researcher",
description: "Searches the web and synthesizes a concise factual answer.",
systemPrompt: "You are a research assistant. Use the fetch and grep tools to gather "
+ "information, then write a short, well-cited summary.",
tools: nil, // inherit base tools
maxIterations: 12
)
let codeReviewer = SubAgent(
name: "code-reviewer",
description: "Reviews a file for correctness and style issues.",
systemPrompt: "You are a Swift code reviewer. Read the file provided, then list "
+ "specific issues with file paths and line numbers.",
tools: nil,
maxIterations: 8
)
let agent = createDeepAgent(
model: model,
subagents: [researcher, codeReviewer],
includeGeneralPurpose: true // still prepended; total: [general-purpose, researcher, codeReviewer]
)
The planner's system prompt receives a list like:
## Delegating with `task`
For isolated, multi-step subtasks, call `task` with a thorough `description` and the
`subagent_type` of the right subagent below. The subagent runs on its own and returns a
single final result; it can't ask you follow-ups, so give it everything it needs.
Available subagents:
- `general-purpose`: General-purpose agent for arbitrary multi-step subtasks. ...
- `researcher`: Searches the web and synthesizes a concise factual answer.
- `code-reviewer`: Reviews a file for correctness and style issues.
Inheritance rules¶
| Setting | nil means |
[] means |
|---|---|---|
tools |
Inherit the deep agent's base tool set | Subagent has NO tools |
model |
Inherit the deep agent's model | N/A (use nil to inherit) |
"Base tools" are the tools that createDeepAgent built before SubAgentMiddleware was added - they include any explicit tools: you passed to the factory plus any catalog middleware tools, but NOT the task tool itself.
Shared filesystem and approval gating¶
SubAgentMiddleware threads two things into every subagent's middleware stack automatically:
- The shared
FilesystemBackend- ifcreateDeepAgentwas given abackend:, each subagent gets aFilesystemMiddlewarefor the same backend, so subagents can read and write the same working files as the parent and each other. - The
HumanInTheLoopMiddleware- ifcreateDeepAgentwas given anapprovalHandler:, it is passed into every subagent too. Delegation therefore never bypasses the user's approval gate.
SubAgentMiddleware and the middleware pipeline¶
SubAgentMiddleware is a structural pillar wired by createDeepAgent in a fixed position in the pipeline: after planning (TodoListMiddleware) and filesystem (FilesystemMiddleware), before any caller-supplied middleware. You cannot position it elsewhere; if you need subagents with createAgent instead, wire SubAgentMiddleware manually.
See Middleware for the full pipeline order and hook semantics.
Context isolation: why it matters¶
Each subagent run starts with only the delegated task string (and optionally a forwarded screenshot). It never sees:
- The parent's conversation history.
- Other subagents' tool calls or results.
- The parent's system prompt.
This means expensive tool results - large file reads, long web fetches, multi-step git diffs - accumulate in the subagent's context without growing the planner's token count. When the subtask finishes, only its final answer (typically a compact paragraph) is appended to the parent's history.