Subagents
A deep agent can create subagents to delegate work. You can specify custom subagents in the subagents parameter. Subagents are useful for context quarantine (keeping the main agent's context clean) and for providing specialized instructions.
This page covers synchronous subagents, where the supervisor blocks until the subagent finishes. For long-running tasks, parallel workstreams, or cases where you need mid-flight steering and cancellation, see Async subagents.
graph TB
Main[Main Agent] --> |task tool| Sub[Subagent]
Sub --> Research[Research]
Sub --> Code[Code]
Sub --> General[General]
Research --> |isolated work| Result[Final Result]
Code --> |isolated work| Result
General --> |isolated work| Result
Result --> MainWhy use subagents?
Subagents solve the context bloat problem. When agents use tools with large outputs (web search, file reads, database queries), the context window fills up quickly with intermediate results. Subagents isolate this detailed work—the main agent receives only the final result, not the dozens of tool calls that produced it.
When to use subagents:
- ✅ Multi-step tasks that would clutter the main agent's context
- ✅ Specialized domains that need custom instructions or tools
- ✅ Tasks requiring different model capabilities
- ✅ When you want to keep the main agent focused on high-level coordination
When NOT to use subagents:
- ❌ Simple, single-step tasks
- ❌ When you need to maintain intermediate context
- ❌ When the overhead outweighs benefits
Configuration
subagents should be a list of dictionaries or CompiledSubAgent objects. There are two types:
Default subagent
Deep Agents automatically adds a synchronous general-purpose subagent unless you already provide a synchronous subagent with that name.
The general-purpose subagent has filesystem tools by default and can be customized with additional tools/middleware.
- To replace it, pass your own subagent named
general-purpose. - To rename or re-prompt the auto-added version, set
general_purpose_subagent=GeneralPurposeSubagentProfile(...)on the active harness profile. - To disable it, see Running without subagents below.
Running without subagents
To run an agent without the task tool, do two things:
- Set
general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False)on the active harness profile. - Pass no synchronous subagents via
subagents=oncreate_deep_agent.
Deep Agents only attaches SubAgentMiddleware (and the task tool) when at least one synchronous subagent exists. With neither the default nor a caller-provided one, the agent runs without delegation.
Async subagents are unaffected—they flow through their own middleware and tools, described in Async subagents.
Tip
Don't reach for excluded_middleware here—SubAgentMiddleware is required scaffolding and listing it raises ValueError. The general_purpose_subagent.enabled = False knob is the supported path.
Custom subagents
You can define specialized subagents with specific tool by using the subagents parameter. For example to serve as a code reviewer, web researcher, or test runner.
For most use cases, define subagents as dictionaries with SubAgent dictionaries. For complex workflows, use a CompiledSubAgent:
SubAgent (Dictionary-based)
Define subagents as dictionaries matching the SubAgent spec with the following fields:
| Field | Type | Description |
|---|---|---|
name | str | Required. Unique identifier for the subagent. The main agent uses this name when calling the task() tool. The subagent name becomes metadata for AIMessages and for streaming, which helps to differentiate between agents. |
description | str | Required. Description of what this subagent does. Be specific and action-oriented. The main agent uses this to decide when to delegate. |
system_prompt | str | Required for mode: "isolated" (the default). Instructions for the subagent. Custom isolated subagents must define their own. Include tool usage guidance and output format requirements.Does not inherit from main agent. For mode: "fork", omit this field unless you need a fork-only addendum. See Forked subagents. |
mode | "isolated" | "fork" | Optional. Context mode. Defaults to "isolated", where the subagent only sees the delegated task. Set to "fork" to inherit the parent's conversation and system prompt instead. See Forked subagents. |
tools | list[Callable] | Optional. Tools the subagent can use. Keep this minimal and include only what's needed. Inherits from main agent by default. When specified, overrides the inherited tools entirely. |
model | str | BaseChatModel | Optional. Overrides the main agent's model. Omit to use the main agent's model. Inherits from main agent by default. You can pass either a model identifier string like 'openai:gpt-5.5' (using the 'provider:model' format) or a LangChain chat model object (init_chat_model("gpt-5.5") or ChatOpenAI(model="gpt-5.5")). |
middleware | list[Middleware] | Optional. Additional middleware for custom behavior, logging, or rate limiting. Isolated subagents do not inherit the main agent's custom middleware. For inheritance and overrides in fork mode, see Configure a forked subagent. Merged into the synchronous subagent stack: an instance whose .name matches a default replaces it in place, anything else lands after the last core middleware entry and before profile, prompt-caching, and memory. See Override a default middleware instance. For example, include a FilesystemMiddleware instance with a tools allowlist here to restrict the subagent's filesystem tools independently of the main agent. For more information, see the "Restricting filesystem tools" section under Virtual filesystem access. |
interrupt_on | dict[str, bool | InterruptOnConfig] | Optional. Configure human-in-the-loop for specific tools. Options:True, False, or an InterruptOnConfig with allowed_decisions. Requires checkpointer.Inherits from main agent by default. Subagent value overrides the default. |
skills | list[str] | Optional. Skills source paths. When specified, the subagent loads skills from these directories (for example, ["/skills/researcher/"], a container whose subdirectories are skills). This allows subagents to have different skill sets than the main agent.Does not inherit from main agent. Only the general-purpose subagent inherits the main agent's skills. When a subagent has skills, it runs its own independent SkillsMiddleware instance. Skill state is fully isolated—a subagent's loaded skills are not visible to the parent, and vice versa. |
response_format | ResponseFormat | Optional. Structured output schema for the subagent. When set, the parent receives the subagent's result as JSON instead of free-form text. Accepts Pydantic models, ToolStrategy(...), ProviderStrategy(...), or a raw schema type. See Structured output. |
permissions | list[FilesystemPermission] | Optional. Filesystem permission rules for the subagent. When set, replaces the parent agent's permissions entirely. Inherits from main agent by default. |
CompiledSubAgent
For complex workflows, use a prebuilt LangGraph graph as a CompiledSubAgent:
| Field | Type | Description |
|---|---|---|
name | str | Required. Unique identifier for the subagent. The subagent name becomes metadata for AIMessages and for streaming, which helps to differentiate between agents. |
description | str | Required. What this subagent does. |
runnable | Runnable | Required. A compiled LangGraph graph (must call .compile() first). |
mode | "isolated" | "fork" | Optional. Defaults to "isolated". Set to "fork" to inherit the parent's message history. The compiled graph keeps its own system prompt either way. See Forked subagents. |
Using SubAgent
import os
from typing import Literal
from deepagents import create_deep_agent
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""Run a web search"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
research_subagent = {
"name": "research-agent",
"description": "Used to research more in depth questions",
"system_prompt": "You are a great researcher",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Optional override, defaults to main agent model
}
subagents = [research_subagent]
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=subagents,
)Using CompiledSubAgent
For more complex use cases, you can provide your custom subagents with CompiledSubAgent.
You can create a custom subagent using LangChain's create_agent or by making a custom LangGraph graph using the graph API.
If you're creating a custom LangGraph graph, make sure that the graph has a state key called "messages":
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="openai:gpt-5.5",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)from deepagents import CompiledSubAgent, create_deep_agent
from langchain.agents import create_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
research_instructions = "You are a research coordinator."
your_model = "openai:gpt-5.5"
specialized_tools: list = []
# Create a custom agent graph
custom_graph = create_agent(
model=your_model,
tools=specialized_tools,
system_prompt="You are a specialized agent for data analysis...",
)
# Use it as a custom subagent
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for complex data analysis tasks",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)Forked subagents
By default, a subagent runs with mode: "isolated": it sees only the task description you give it and has no memory of the conversation that led up to the delegation. A forked subagent (mode: "fork") inherits the parent's conversation history and state instead.
Use forked subagents when the subagent's task is to continue the work the parent already started, like a worker agent that picks up a fix the parent has already diagnosed, or a subagent that drafts the postmortem for an incident investigation. Since forking is a mode you set on the subagent itself, that's a decision you make when you define it.
%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 12, "bottom": 4}}}}%%
graph TD
Message["'Review PR #482'"]
Analysis["Parent already found:<br/>tokens logged in plaintext,<br/>no expiry check on refresh"]
Delegate["Delegate: draft comments<br/>for the issues found"]
Message --> Analysis --> Delegate
subgraph Isolated["`**Isolated subagent**`"]
IOut["Sees only the task description<br/>starts from nothing, re-reviews the diff"]
end
subgraph Forked["`**Forked subagent**`"]
FOut["Sees parent history + continuation preamble<br/>already knows the issues, writes comments directly"]
end
Delegate -->|task description only| Isolated
Delegate -->|parent history + continuation preamble| Forked
classDef process fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710
classDef output fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900
class Message,Analysis,Delegate process
class IOut,FOut outputNote
Subagent forking requires deepagents>=0.7.13. It is in beta; APIs and behavior may change between releases.
Configure a forked subagent
Set mode: "fork" on a SubAgent (the default is mode: "isolated"). You can configure these SubAgent fields:
name, description, tools, model, middleware, interrupt_on, permissions, and response_format.
Forks inherit the custom middleware passed to the parent's create_deep_agent. Middleware in the subagent's middleware list replaces an inherited instance with the same .name. Other instances are added to the subagent's default middleware stack.
By default, the fork inherits all tools passed to the parent. Set tools to replace those tools, not add to them. For example, a list containing one tool replaces the inherited list with that one tool. Filesystem and other middleware-provided tools are added separately.
skills is rejected if provided. system_prompt is appended to the parent's inherited prompt as an addendum. Leave it unset unless you need fork-only instructions.
A wrap_model_call hook receives the fork's inherited prompt through request.system_message. It can rewrite the fork's outgoing request with request.override(system_message=...) without changing the parent's prompt. This differs from system_prompt, which appends instructions.
Avoid rewriting the shared inherited prefix or changing tool definitions unless you accept reduced cache reuse and potentially higher model costs. For example, this hook fragment prepends text before the inherited prefix, preventing reuse of that prefix:
from langchain.messages import SystemMessage
rewritten = SystemMessage(content=[
{"type": "text", "text": "You are a security auditor.\n"},
*request.system_message.content_blocks,
])
return handler(request.override(system_message=rewritten))Additional middleware or appended instructions do not necessarily prevent reuse of an unchanged prefix.
from deepagents import create_deep_agent
def read_diff(path: str) -> str:
"""Read a file's diff."""
return f"diff for {path}"
comment_writer = {
"name": "comment-writer",
"description": "Continues an in-progress PR review and drafts review comments",
"mode": "fork",
"tools": [read_diff],
}
agent = create_deep_agent(
model="google_genai:gemini-3.7-flash",
tools=[read_diff],
subagents=[comment_writer],
)
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Review PR #482 and hand it off to comment-writer to draft comments for the issues found",
}
]
}
)Open a public LangSmith run for this example.
How it works
A fork receives the parent's effective conversation history plus the delegated task description. Deep Agents removes the trailing delegation call and appends the task description with a continuation preamble. If the parent has compacted its history, the fork receives that compacted context, not every original message.
Here, forking means inheriting context for delegated work, not copying a persisted thread's checkpoint history. The subagent receives state prepared for its invocation, with parent-only fields excluded. When it finishes, its answer returns as a normal tool result, and the parent continues.
# What the parent has, right before delegating
[
HumanMessage("Review the changes in PR #482"),
AIMessage("", tool_calls=[{"name": "read_diff", "args": {"path": "src/auth/session.py"}}]),
ToolMessage("- session tokens are logged in plaintext\n- no expiry check on refresh", tool_call_id="1"),
AIMessage("Found two issues: session tokens are logged in plaintext, and there's no expiry check on refresh."),
HumanMessage("Good catch. Draft review comments for those."),
AIMessage("", tool_calls=[{"name": "task", "args": {"subagent_type": "comment-writer", "description": "Draft review comments for the two issues found above."}}]),
]
# What the fork actually sees
[
HumanMessage("Review the changes in PR #482"),
AIMessage("", tool_calls=[{"name": "read_diff", "args": {"path": "src/auth/session.py"}}]),
ToolMessage("- session tokens are logged in plaintext\n- no expiry check on refresh", tool_call_id="1"),
AIMessage("Found two issues: session tokens are logged in plaintext, and there's no expiry check on refresh."),
HumanMessage("Good catch. Draft review comments for those."),
HumanMessage("Continuing as the subagent that was just invoked. Draft review comments for the two issues found above."),
]
A fork can reuse the parent's prompt cache for the matching prefix of their requests. Changing the system prompt, tools, or earlier messages shortens that shared prefix across providers. Leave the fork's configuration unchanged to maximize reuse, though cache hits are not guaranteed.
A CompiledSubAgent supports mode: "fork" too, though it keeps its own system prompt since the graph is already built.
When to use forking
Compare isolated and forked modes along these dimensions:
| Dimension | Isolated (default) | Forked |
|---|---|---|
| Context | Only the task description you pass in | The parent's effective conversation history and inherited state, plus the task description |
| System prompt and skills | You set them on the subagent | Inherits skills and the parent's base prompt. An optional system prompt appends instructions and can reduce cache reuse. |
| Calling other subagents | Can use the task tool | Cannot use task; must finish the work itself |
| Best for | Focused work that needs little prior context | Continuing an investigation the parent has already started |
Dynamic subagents
By default, the main agent delegates to subagents through task tool calls (it can issue several in a single turn to run them in parallel). With an interpreter attached, the agent can instead dispatch subagents from code—using loops, branches, and parallel batches to fan work out across many items and synthesize the results programmatically. This is called dynamic subagents.
Reach for dynamic subagents when work spans many independent units (reviewing every file in a directory, triaging a batch of tickets), needs multiple perspectives, or benefits from recursive analysis.
Warning
Dynamic subagents use the interpreter runtime, which is in beta. APIs and lifecycle behavior may change between releases.
Enable dynamic subagents
Dynamic subagents become available as soon as the agent has both subagents and the interpreter middleware. Install the QuickJS interpreter package, then add CodeInterpreterMiddleware to your agent.
pipuv
pip install -U "deepagents[quickjs]"uv add "deepagents[quickjs]"GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
Note
Dynamic subagent dispatch is on by default whenever the agent has subagents and the interpreter middleware. Pass CodeInterpreterMiddleware(subagents=False) to require dispatch through the normal task tool path. Interpreters require langchain-quickjs>=0.2.0 and Python 3.11 or later.
Trigger dynamic orchestration
Dynamic dispatch is implicit: the agent decides to fan work out from code based on the shape of the task, not a per-call flag.
Tip
The word "workflow" is a useful trigger. The built-in interpreter system prompt treats a "workflow" as a signal to organize work through the interpreter—dispatching subagents with task() from code. Phrasing a request as a "workflow" is a deliberate lever you can pull to opt into dynamic orchestration: include it when you want the agent to fan work out from code. For a single, direct delegation, phrase the request plainly instead.
For example, phrasing the request as a "workflow" opts into fan-out from code:
result = agent.invoke({
"messages": [{"role": "user", "content": "Run a workflow that reviews every file in src/routes/ and summarizes the top risks."}]
})For configuration, advanced orchestration patterns, and safety notes, see Dynamic subagents.
Use with a coding agent
The fastest way to try dynamic subagents is with dcode, the LangChain terminal coding agent built on a Deep Agent. It ships with the code interpreter enabled, so dynamic subagents work out of the box with nothing to wire up.
Install dcode:
curl -LsSf https://langch.in/dcode | bashRun it:
dcodeTo trigger dynamic subagents, ask for a "workflow". Instead of grinding through the work itself or managing fan-out through its native task tool, the agent writes an orchestration script that calls the built-in task() global and runs it in the code interpreter. For example: "Run a workflow to review every file in src/ for SQL injection."
As subagents spawn, dcode shows them live in the dynamic subagents panel, grouped into phases by dispatch.

dcode is the fastest way to try this, but you can also use dynamic subagents in the coding agent of your choice over ACP (for example, Zed).
Streaming
Deep Agents support streaming updates from both the coordinator and every delegated subagent.
Use stream_events to get typed projections—separate iterators for subagents, messages, tool calls, and values—so you can consume each independently.
Stream subagent progress
The simplest pattern is to iterate stream.subagents to track each delegated task as it starts, runs, and completes. Each subagent handle exposes .name, .messages, .tool_calls, and .output.
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="openai:gpt-5.5",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
from deepagents import (
create_deep_agent
)
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
system_prompt=(
"You are a project coordinator with no research knowledge. "
"For every user request, you must call the task() tool with "
"subagent_type set to research-agent. Never answer research "
"questions yourself."
),
subagents=[
{
"name": "research-agent",
"description": (
"Delegate research to this subagent. Give one topic at a time."
),
"system_prompt": (
"You are a great researcher. Return a brief summary."
),
},
],
name="main-agent",
)
if __name__ == "__main__":
stream = agent.stream_events(
{
"messages": [
{
"role": "user",
"content": "Research one recent advance in quantum computing.",
}
]
},
version="v3",
)
coordinator_messages: list[str] = []
subagent_handles = []
for name, item in stream.interleave("messages", "subagents"):
if name == "messages":
print("[coordinator]", item.text)
coordinator_messages.append(item.text)
else:
print(f"[{item.name}] started")
subagent_handles.append(item)
for message in item.messages:
print(f"[{item.name}]", message.text)
print(f"[{item.name}] status: {item.status}")
Open a public LangSmith run for this example.
LangSmith tracing
As your deep agent runs, all runs executed by a subagent or the coordinator will have the agent name in their metadata under the lc_agent_name key—for example, {'lc_agent_name': 'research-agent'}. This lets you identify and filter runs by subagent in LangSmith.

Tip
Open the run in LangSmith to compare the coordinator trace with each subagent run. Follow the observability quickstart to get set up. We recommend you also set up LangSmith Engine which monitors your traces, detects issues, and proposes fixes.
Filter by subagent in LangSmith
Because each subagent's name is written to the lc_agent_name metadata key on every run it produces, you can use LangSmith's metadata filtering to isolate all runs from a specific subagent — useful for debugging, monitoring, or comparing subagent behavior over time.
Filter in the LangSmith UI
- Open your tracing project in LangSmith.
- Switch the view to Runs on the Tracing project page to see individual spans.
- Click Add filter and select Metadata.
- Set the Key to
lc_agent_nameand the Value to the subagent name, for examplecoordinator.

This shows only the runs produced by that subagent. You can save the filter as a named view for reuse. For a full reference on filtering options, see Filter traces.
Filter programmatically with the SDK
Use the has comparator in the LangSmith filter query language to match runs by metadata key-value pair:
from langsmith import Client
client = Client()
runs = client.list_runs(
project_name="<your-project>",
filter='has(metadata, \'{"lc_agent_name": "research-agent"}\')',
)
for run in runs:
print(run.name, run.start_time, run.status)
To fetch runs from any named subagent (excluding the main agent), filter for runs that have the lc_agent_name key at all:
runs = client.list_runs(
project_name="<your-project>",
filter="has(metadata, 'lc_agent_name')",
)For the full filter query language reference, see Trace query syntax.
Structured output
Subagents support structured output, so the parent agent receives predictable, parseable JSON instead of free-form text.
Note
Structured output for subagents requires deepagents>=0.5.3.
Pass response_format on the subagent config. When the subagent finishes, its structured response is JSON-serialized and returned as the ToolMessage content to the parent agent. The schema accepts anything supported by create_agent: Pydantic models, ToolStrategy(...), ProviderStrategy(...), or a raw schema type.
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
import asyncio
from pydantic import BaseModel, Field
from deepagents import create_deep_agent
def web_search(query: str) -> str:
"""Search the web."""
return f"web results for {query}"
class ResearchFindings(BaseModel):
"""Structured findings from a research task."""
summary: str = Field(description="Summary of findings")
confidence: float = Field(description="Confidence score from 0 to 1")
sources: list[str] = Field(description="List of source URLs")
research_subagent = {
"name": "researcher",
"description": "Researches topics and returns structured findings",
"system_prompt": "Research the given topic thoroughly. Return your findings.",
"tools": [web_search],
"response_format": ResearchFindings,
}
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
subagents=[research_subagent],
)
async def main():
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Research recent advances in quantum computing"}]}
)
return result
result = asyncio.run(main())
# The parent's ToolMessage contains JSON-serialized structured data:
# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
Without response_format, the parent receives the subagent's last message text as-is. With it, the parent always gets valid JSON matching the schema, which is useful when the parent needs to process the result programmatically or pass it to downstream tools.
For full details on schema types and strategies (tool calling vs. provider-native), see Structured output.
The general-purpose subagent
In addition to any user-defined subagents, every deep agent has access to a general-purpose subagent at all times. This subagent:
- Uses its own default system prompt with profile overlays applied
- Has access to all the same tools
- Uses the same model (unless overridden)
- Inherits skills from the main agent (when skills are configured)
Override the general-purpose subagent
Include a subagent with name="general-purpose" in your subagents list to replace the default. Use this to configure a different model, tools, or system prompt for the general-purpose subagent:
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="openai:gpt-5.5",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return f"search results for {query}"
# Main agent uses Gemini; general-purpose subagent uses GPT
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
tools=[internet_search],
subagents=[
{
"name": "general-purpose",
"description": "General-purpose agent for research and multi-step tasks",
"system_prompt": "You are a general-purpose assistant.",
"tools": [internet_search],
"model": "openai:gpt-5.5", # Different model for delegated tasks
},
],
)
When you provide a subagent with the general-purpose name, the default general-purpose subagent is not added. Your spec fully replaces it.
To remove the built-in general-purpose subagent entirely instead of replacing it, set the active harness profile's general-purpose subagent enabled flag to False.
When to use it
The general-purpose subagent is ideal for context isolation without specialized behavior. The main agent can delegate a complex multi-step task to this subagent and get a concise result back without bloat from intermediate tool calls.
Example
Instead of the main agent making 10 web searches and filling its context with results, it delegates to the general-purpose subagent: task(name="general-purpose", task="Research quantum computing trends"). The subagent performs all the searches internally and returns only a summary.
Skills inheritance
When configuring skills with create_deep_agent:
- General-purpose subagent: Automatically inherits skills from the main agent
- Custom subagents: Do NOT inherit skills by default—use the
skillsparameter to give them their own skills
Note
Only subagents configured with skills get a SkillsMiddleware instance—custom subagents without a skills parameter do not. When present, skill state is fully isolated in both directions: the parent's skills are not visible to the child, and the child's skills are not propagated back to the parent.
from deepagents import create_deep_agent
research_subagent = {
"name": "researcher",
"description": "Research assistant with specialized skills",
"system_prompt": "You are a researcher.",
"tools": [web_search],
"skills": ["/skills/researcher/"], # Subagent-specific skills
}
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
skills=["/skills/main/"], # Main agent and GP subagent get these
subagents=[research_subagent], # Researcher gets only its own skills
)
Best practices
Write clear descriptions
The main agent uses descriptions to decide which subagent to call. Be specific:
✅ Good: "Analyzes financial data and generates investment insights with confidence scores"
❌ Bad: "Does finance stuff"
Keep system prompts detailed
Include specific guidance on how to use tools and format outputs:
research_subagent = {
"name": "research-agent",
"description": "Conducts in-depth research using web search and synthesizes findings",
"system_prompt": """You are a thorough researcher. Your job is to:
1. Break down the research question into searchable queries
2. Use internet_search to find relevant information
3. Synthesize findings into a comprehensive but concise summary
4. Cite sources when making claims
Output format:
- Summary (2-3 paragraphs)
- Key findings (bullet points)
- Sources (with URLs)
Keep your response under 500 words to maintain clean context.""",
"tools": [internet_search],
}
Minimize tool sets
Only give subagents the tools they need. This improves focus and security:
# ✅ Good: Focused tool set
email_agent = {
"name": "email-sender",
"tools": [send_email, validate_email], # Only email-related
}
# ❌ Bad: Too many tools
email_agent = {
"name": "email-sender",
"tools": [send_email, web_search_tool, database_query, format_document], # Unfocused
}
Choose models by task
Different models excel at different tasks:
subagents = [
{
"name": "contract-reviewer",
"description": "Reviews legal documents and contracts",
"system_prompt": "You are an expert legal reviewer...",
"tools": [read_document, analyze_contract],
"model": "google_genai:gemini-3.6-flash", # Large context for long documents
},
{
"name": "financial-analyst",
"description": "Analyzes financial data and market trends",
"system_prompt": "You are an expert financial analyst...",
"tools": [get_stock_price, analyze_fundamentals],
"model": "openai:gpt-5.5", # Better for numerical analysis
},
]
Return concise results
Instruct subagents to return summaries, not raw data:
data_analyst = {
"system_prompt": """Analyze the data and return:
1. Key insights (3-5 bullet points)
2. Overall confidence score
3. Recommended next actions
Do NOT include:
- Raw data
- Intermediate calculations
- Detailed tool outputs
Keep response under 300 words."""
}
Common patterns
Multiple specialized subagents
Create specialized subagents for different domains:
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="openai:gpt-5.5",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
from deepagents import create_deep_agent
subagents = [
{
"name": "data-collector",
"description": "Gathers raw data from various sources",
"system_prompt": "Collect comprehensive data on the topic",
"tools": [web_search_tool, api_call, database_query],
},
{
"name": "data-analyzer",
"description": "Analyzes collected data for insights",
"system_prompt": "Analyze data and extract key insights",
"tools": [statistical_analysis],
},
{
"name": "report-writer",
"description": "Writes polished reports from analysis",
"system_prompt": "Create professional reports from insights",
"tools": [format_document],
},
]
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
system_prompt="You coordinate data analysis and reporting. Use subagents for specialized tasks.",
subagents=subagents,
)
Workflow:
- Main agent creates high-level plan
- Delegates data collection to data-collector
- Passes results to data-analyzer
- Sends insights to report-writer
- Compiles final output
Each subagent works with clean context focused only on its task.
Context management
When you invoke a parent agent with runtime context, that context automatically propagates to all subagents. Each subagent run receives the same runtime context you passed on the parent invoke / ainvoke call.
This means tools running inside any subagent can access the same context values you provided to the parent:
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
session_id: str
@tool
def get_user_data(query: str, runtime: ToolRuntime[Context]) -> str:
"""Fetch data for the current user."""
user_id = runtime.context.user_id
return f"Data for user {user_id}: {query}"
research_subagent = {
"name": "researcher",
"description": "Conducts research for the current user",
"system_prompt": "You are a research assistant.",
"tools": [get_user_data],
}
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
subagents=[research_subagent],
context_schema=Context,
)
# Context flows to the researcher subagent and its tools automatically
result = agent.invoke(
{"messages": [HumanMessage("Look up my recent activity")]},
context=Context(user_id="user-123", session_id="abc"),
)
Per-subagent context
All subagents receive the same parent context. To pass configuration that is specific to a particular subagent, use namespaced keys (prefix keys with the subagent name, for example researcher:max_depth) in a flat context mapping, or model those settings as separate fields on your context type:
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="openrouter:z-ai/glm-5.2",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="baseten:zai-org/GLM-5.2",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
from dataclasses import dataclass
from deepagents import create_deep_agent
from langchain.messages import HumanMessage
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def verify_claim(claim: str, runtime: ToolRuntime[Context]) -> str:
"""Verify a factual claim."""
strict_mode = runtime.context.fact_checker_strict_mode or False
if strict_mode:
return strict_verification(claim)
return basic_verification(claim)
agent = create_deep_agent(
model="ollama:north-mini-code-1.0",
subagents=[
{
"name": "fact-checker",
"description": "Verifies factual claims",
"system_prompt": "You verify claims carefully.",
"tools": [verify_claim],
},
],
context_schema=Context,
)
result = agent.invoke(
{"messages": [HumanMessage("Research this and verify the claims")]},
context=Context(
user_id="user-123",
researcher_max_depth=3,
fact_checker_strict_mode=True,
),
)
Identifying which subagent called a tool
When the same tool is shared between the parent and multiple subagents, you can use the lc_agent_name metadata (the same value used in streaming) to determine which agent initiated the call:
# :snippet-start: subagents-shared-lookup-py
from langchain.tools import ToolRuntime, tool
@tool
def shared_lookup(query: str, runtime: ToolRuntime) -> str:
"""Look up information."""
agent_name = runtime.config.get("metadata", {}).get("lc_agent_name")
if agent_name == "fact-checker":
return strict_lookup(query)
return general_lookup(query)
You can combine both patterns—read agent-specific settings from runtime.context and read lc_agent_name from runtime.config metadata when branching tool behavior.
from dataclasses import dataclass
from langchain.tools import ToolRuntime, tool
@dataclass
class Context:
user_id: str
researcher_max_depth: int | None = None
fact_checker_strict_mode: bool | None = None
@tool
def flexible_search(query: str, runtime: ToolRuntime[Context]) -> str:
"""Search with agent-specific settings."""
agent_name = runtime.config.get("metadata", {}).get("lc_agent_name", "unknown")
ctx = runtime.context
if agent_name == "researcher":
max_results = ctx.researcher_max_depth or 5
else:
max_results = 5
include_raw = False
return perform_search(query, max_results=max_results, include_raw=include_raw)
Troubleshooting
Subagent not being called
Problem: Main agent tries to do work itself instead of delegating.
Solutions:
-
Make descriptions more specific:
# ✅ Good good_subagent = { "name": "research-specialist", "description": "Conducts in-depth research on specific topics using web search. Use when you need detailed information that requires multiple searches.", }# ❌ Bad bad_subagent = { "name": "helper", "description": "helps with stuff", } -
Instruct main agent to delegate:
GoogleOpenAIAnthropicOpenRouterFireworksBasetenOllama
from deepagents import create_deep_agent agent = create_deep_agent( model="google_genai:gemini-3.6-flash", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="openai:gpt-5.5", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="anthropic:claude-sonnet-5", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="openrouter:z-ai/glm-5.2", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="fireworks:accounts/fireworks/models/glm-5p2", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="baseten:zai-org/GLM-5.2", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )from deepagents import create_deep_agent agent = create_deep_agent( model="ollama:north-mini-code-1.0", system_prompt="""...your instructions... IMPORTANT: For complex tasks, delegate to your subagents using the task() tool. This keeps your context clean and improves results.""", subagents=[ { "name": "research-agent", "description": "Conducts research", "system_prompt": "You are a researcher.", }, ], )
Context still getting bloated
Problem: Context fills up despite using subagents.
Solutions:
-
Instruct subagent to return concise results:
system_prompt = """... IMPORTANT: Return only the essential summary. Do NOT include raw data, intermediate search results, or detailed tool outputs. Your response should be under 500 words.""" -
Use filesystem for large data:
system_prompt = """When you gather large amounts of data: 1. Save raw data to /data/raw_results.txt 2. Process and analyze the data 3. Return only the analysis summary This keeps context clean."""
Wrong subagent being selected
Problem: Main agent calls inappropriate subagent for the task.
Solution: Differentiate subagents clearly in descriptions:
subagents = [
{
"name": "quick-researcher",
"description": "For simple, quick research questions that need 1-2 searches. Use when you need basic facts or definitions.",
"system_prompt": "You are the quick-researcher subagent.",
},
{
"name": "deep-researcher",
"description": "For complex, in-depth research requiring multiple searches, synthesis, and analysis. Use for comprehensive reports.",
"system_prompt": "You are the deep-researcher subagent.",
},
]