Skip to main content
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.

Why 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:
  1. Set general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False) on the active harness profile.
  2. Pass no synchronous subagents via subagents= on create_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.
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:

CompiledSubAgent

For complex workflows, use a prebuilt LangGraph graph as a CompiledSubAgent:

Using SubAgent

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":

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.
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.
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.

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.
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:
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:
Run it:
To 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.
The dcode dynamic subagents panel showing spawned subagents 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.

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. LangSmith Example trace showing the metadata
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

  1. Open your tracing project in LangSmith.
  2. Switch the view to Runs on the Tracing project page to see individual spans.
  3. Click Add filter and select Metadata.
  4. Set the Key to lc_agent_name and the Value to the subagent name, for example coordinator.
LangSmith Runs view with a metadata filter on lc_agent_name set to coordinator 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:
To fetch runs from any named subagent (excluding the main agent), filter for runs that have the lc_agent_name key at all:
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.
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.
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:

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:
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 skills parameter to give them their own skills
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.

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:

Minimize tool sets

Only give subagents the tools they need. This improves focus and security:

Choose models by task

Different models excel at different tasks:

Return concise results

Instruct subagents to return summaries, not raw data:

Common patterns

Multiple specialized subagents

Create specialized subagents for different domains:
Workflow:
  1. Main agent creates high-level plan
  2. Delegates data collection to data-collector
  3. Passes results to data-analyzer
  4. Sends insights to report-writer
  5. 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:

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:

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:
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.

Troubleshooting

Subagent not being called

Problem: Main agent tries to do work itself instead of delegating. Solutions:
  1. Make descriptions more specific:
  2. Instruct main agent to delegate:

Context still getting bloated

Problem: Context fills up despite using subagents. Solutions:
  1. Instruct subagent to return concise results:
  2. Use filesystem for large data:

Wrong subagent being selected

Problem: Main agent calls inappropriate subagent for the task. Solution: Differentiate subagents clearly in descriptions: