When to use async vs sync subagents
Use async subagents for long-running, parallelizable tasks that need mid-flight steering. Sync subagents are better when the agent should wait for results before continuing. Async subagents return a job ID immediately and allow mid-task updates and cancellation, while sync subagents block and are stateless. Async subagents are stateful and maintain state on their own thread across interactions.
AsyncSubAgent configuration fields
AsyncSubAgent specs require the following fields: name (str, unique identifier used by supervisor to launch tasks), description (str, what the subagent does for supervisor delegation decisions), and graph_id (str, the graph ID on Agent Protocol server matching a graph in langgraph.json). Optional fields are url (str, when omitted uses ASGI transport in-process, when set uses HTTP transport to remote server) and headers (dict[str, str], additional headers for remote server requests including custom authentication).
AsyncSubAgent configuration for JavaScript
AsyncSubAgent specs in JavaScript require name (string), description (string), and graphId (string). Optional fields are url (string) and headers (Record<string, string>).
Async subagent tools provided by AsyncSubAgentMiddleware
The AsyncSubAgentMiddleware included in the Deep Agents stack provides five tools to the supervisor: start_async_task (start new background task, returns task ID immediately), check_async_task (get current status and result of a task), update_async_task (send new instructions to running task), cancel_async_task (stop a running task), and list_async_tasks (list all tracked tasks with live statuses).
Async subagent lifecycle operations
Launch creates a new thread on the server and starts a run with the task description as input, returning the thread ID as task ID. Check fetches the current run status and extracts final output if succeeded. Update creates a new run on the same thread with interrupt multitask strategy, interrupting the previous run and restarting with full conversation history plus new instructions. Cancel calls runs.cancel() on the server. List iterates over all tracked tasks, fetching live status for non-terminal tasks in parallel and returning terminal statuses from cache.
Async task state management and storage
Task metadata is stored in a dedicated state channel (async_tasks in Python, asyncTasks in JavaScript) on the supervisor's graph, separate from message history. This is critical because deep agents compact their message history when the context window fills up, and task IDs stored only in tool messages would be lost during compaction. The dedicated channel ensures the supervisor can always recall its tasks through list_async_tasks. Each tracked task records task ID, agent name, thread ID, run ID, status, and timestamps (created_at, last_checked_at, last_updated_at in Python; createdAt, checkedAt, updatedAt in JavaScript).
ASGI transport for async subagents
When a subagent spec omits the url field, the LangGraph SDK uses ASGI transport where SDK calls are routed through in-process function calls rather than HTTP. For LangGraph-based deployments, both graphs must be registered in the same langgraph.json. ASGI transport eliminates network latency and requires no additional auth configuration. The subagent still runs as a separate thread with its own state. This is the recommended default.
HTTP transport for async subagents
HTTP transport is used when a url field is set in the subagent spec, routing SDK calls over the network to a remote Agent Protocol server. For LangGraph deployments, authentication is handled by the LangGraph SDK using LANGSMITH_API_KEY or LANGGRAPH_API_KEY environment variables. Self-hosted Agent Protocol servers may use different authentication mechanisms. Use HTTP transport when subagents need independent scaling, different resource profiles, or are maintained by different teams.
Async subagents availability
Async subagents are a preview feature available in deepagents 0.5.0 for Python and deepagents 1.9.0 for JavaScript. Preview features are under active development and APIs may change.
Single deployment topology for async subagents
Single deployment means all agents are co-deployed on the same server using ASGI transport. For LangGraph-based deployments, register all graphs in one langgraph.json. This is the recommended starting point with one server to manage and zero network latency between agents.
Async subagents definition and purpose
Async subagents allow a supervisor agent to launch background tasks that return immediately, so the supervisor can continue interacting with the user while subagents work concurrently. The supervisor can check progress, send follow-up instructions, or cancel tasks at any point. This differs from synchronous subagents which block the supervisor until completion.
Hybrid deployment topology for async subagents
Hybrid deployment runs some subagents co-deployed via ASGI and others remote via HTTP, allowing flexibility in which subagents share a deployment.
Worker pool sizing for local development with async subagents
When running locally with langgraph dev, increase the worker pool to accommodate concurrent subagent runs. Each active run occupies a worker slot. A supervisor with 3 concurrent subagent tasks requires 4 slots total (1 supervisor + 3 subagents). Under-provisioning causes launches to queue. Use langgraph dev --n-jobs-per-worker 10 to set the pool size.
Writing effective async subagent descriptions
Subagent descriptions should be specific and action-oriented because the supervisor uses them to decide which subagent to launch. Descriptions guide the supervisor's delegation decisions.
Tracing async subagents with thread IDs
Every async subagent run is a standard LangGraph run, fully visible in LangSmith. The supervisor's trace shows tool calls for launch, check, update, cancel, and list. Each subagent run appears as a separate trace linked by thread ID. Use the thread ID (task ID) to correlate supervisor orchestration traces with subagent execution traces.
Preventing supervisor polling after async task launch
The AsyncSubAgentMiddleware injects system prompt rules to prevent the supervisor from calling check_async_task in a loop immediately after launching, which would turn async execution into blocking. If polling persists, reinforce the behavior in the supervisor's system prompt to indicate that tasks should be left to run in the background.
Handling stale async task status reports
The middleware prompt instructs the model that task statuses in conversation history are always stale. If the supervisor still references old status from earlier conversation instead of making a fresh check_async_task call, add explicit instructions to always call check or list before reporting status.
Preventing task ID truncation in async subagents
The middleware prompt instructs the model to always use the full task ID. If the supervisor truncates or reformats the task ID causing check or cancel to fail, this is typically a model-specific issue. Try a different model or add explicit instruction 'always show the full task_id, never truncate or abbreviate it' to the system prompt.
Troubleshooting queued async subagent launches
If launching a subagent hangs or takes a long time to start, the worker pool is likely exhausted. Increase the pool size with --n-jobs-per-worker parameter.
LangGraph langgraph.json configuration for async subagents
For LangGraph-based deployments with async subagents, register all graphs in the same langgraph.json file. Example: graphs object with keys like 'supervisor', 'researcher', 'coder' mapping to file paths like './src/supervisor.py:graph'.
Subagents precedence rules
Subagents precedence order from lowest to highest: 1) `~/.deepagents/{agent}/agents/` (user-level), 2) `.deepagents/agents/` (project-level, highest). Each subagent is an `AGENTS.md` file with YAML frontmatter (`name`, `description`, optional `model`) and markdown body for system prompt.
Deep Agents Code subagents capability
Deep Agents Code supports delegating work to task-specific subagents for parallel execution.
Dynamic subagents visualization in dcode
As dynamic subagents spawn in dcode, they are shown live in the dynamic subagents panel, grouped into phases by dispatch.
Subagent file structure and location
Subagents in Deep Agents Code are defined as markdown files with YAML frontmatter in an AGENTS.md file. Each subagent lives in its own folder with the path .deepagents/agents/{subagent-name}/AGENTS.md for project-level subagents or ~/.deepagents/{agent}/agents/{subagent-name}/AGENTS.md for user-level subagents. Project subagents override user subagents with the same name.
Subagent frontmatter required fields
The YAML frontmatter in AGENTS.md files requires two fields: name (the subagent identifier) and description (what the subagent does). These match the SubAgent dictionary spec used elsewhere in Deep Agents.
Subagent system prompt from markdown body
The markdown body that follows the YAML frontmatter in an AGENTS.md file becomes the subagent's system_prompt, providing instructions and context for the subagent's behavior.
Subagent optional model override
AGENTS.md files support an optional model frontmatter field that overrides the main agent's model for that specific subagent. Use the provider:model-name format (e.g., anthropic:claude-opus-4-8, openai:gpt-5.5). If omitted, the subagent inherits the main agent's model.
Subagent non-configurable fields via AGENTS.md
The following SubAgent fields are not currently configurable via AGENTS.md frontmatter: tools, middleware, interrupt_on, and skills. Custom subagents defined via AGENTS.md inherit the main agent's tools. To configure these fields, use the SDK directly.
Async subagents not available in Deep Agents Code
Async subagents are not available to end-users in Deep Agents Code at this time. Only synchronous subagents can be defined via AGENTS.md files.
Dynamic subagents enabled by default
Deep Agents Code ships with the code interpreter enabled, so dynamic subagents work out of the box without additional configuration.
Triggering dynamic subagents with workflows
To trigger dynamic subagents, ask the agent for a workflow. Instead of managing delegation itself or using the native task tool, the agent writes an orchestration script that calls the built-in task() global function and runs it in the code interpreter. For example: 'Run a workflow to review every file in src/ for SQL injection.'
Example subagent AGENTS.md format
Subagent AGENTS.md files use YAML frontmatter followed by markdown body. Example:
---
name: researcher
description: Research topics on the web before writing content
model: anthropic:claude-haiku-4-5-20251001
---
You are a research assistant with access to web search.
## Your Process
1. Search for relevant information
2. Summarize findings clearly
Cost-efficient subagent pattern
You can use a cheaper, faster model for simple delegation tasks by creating a subagent with a lower-cost model (e.g., anthropic:claude-haiku-4-5-20251001) while keeping the main agent on a more capable model. This can be done by overriding the built-in general-purpose subagent, which routes all delegated tasks to the specified cheaper model.
Subagent hook events
Two subagent-specific events fire: SubagentStart fires when a subagent begins, matching on agent_type. SubagentStop fires when a subagent completes, with exit code 2 effect of Add context. Both carry agent_id and agent_type. SubagentStop also includes agent_transcript_path for the subagent's transcript, stop_hook_active flag, last_assistant_message, background_tasks, and session_crons.
subagents.yaml structure for researcher subagent
subagents.yaml defines subagents at the project root. The researcher subagent has: description (usage guidance), model (anthropic:claude-haiku-4-5-20251001), system_prompt (instructions), and tools list. The system_prompt instructs to use web_search and write_file tools, make 2-3 targeted searches, and save findings to the file path specified by the delegating user. The researcher subagent always requires the user to specify WHERE to save findings - use that exact path.
Runtime context propagation to subagents
Runtime context propagates to all subagents. When a subagent runs, it receives the same runtime context as the parent. See subagent documentation for per-subagent context using namespaced keys.
Subagent state schema inheritance
Declarative SubAgent specs passed to subagents= inherit the parent state_schema when Deep Agents compiles them for the task tool. CompiledSubAgent runnables and remote AsyncSubAgent specs do not inherit it because their graphs are already compiled or hosted separately. Compile those graphs with a compatible schema if they need the same state fields.
Context isolation with subagents overview
Subagents solve the context bloat problem. When the main agent uses tools with large outputs (web search, file reads, database queries), the context window fills quickly. Subagents isolate this work—the main agent receives only the final result, not the dozens of tool calls that produced it. You can also configure each subagent separately from the main agent (for example, model, tools, system prompt, and skills). How it works: main agent has a task tool to delegate work; subagent runs with its own fresh context; subagent executes autonomously until completion; subagent returns a single final report to the main agent; main agent's context stays clean.
Subagent best practices
Best practices for using subagents: (1) Delegate complex tasks—use subagents for multi-step work that would clutter the main agent's context; (2) Keep subagent responses concise—instruct subagents to return summaries, not raw data; (3) Use the filesystem for large data—subagents can write results to files; the main agent reads what it needs.
Subagent middleware inheritance
The general-purpose subagent, which Deep Agents adds automatically, inherits overrides for its default middleware from the main agent, without carrying over middleware that's specific to the main agent. Declarative subagents defined via subagents= do not inherit the main agent's middleware customization. Pass the override directly in that subagent's own middleware field to apply it there; that field is matched against the synchronous subagent stack, the same way middleware= is matched against the main agent's.
Subagent stack differs from main agent
The built-in general-purpose subagent and each declarative synchronous SubAgent graph use a stack that matches the main agent in broad shape (filesystem, summarization, Patch, profile extras, Anthropic and Bedrock caching, optional permissions) but differs in two ways: (1) Skills run after PatchToolCallsMiddleware on these inner agents (on the main agent, skills run before filesystem middleware when skills is set), (2) There is no SubAgentMiddleware inside a subagent graph (only the parent agent exposes the task tool). When a declarative subagent sets interrupt_on (Python) or interruptOn (JavaScript), that value is forwarded to create_agent/createAgent for the subagent, which wires up human-in-the-loop handling.
General-purpose subagent prompt resolution
The auto-added general-purpose subagent resolves its base prompt as: general_purpose_subagent.system_prompt (if set) -> HarnessProfile.base_system_prompt (if set) -> SDK general-purpose default, with the profile suffix layered on top. When both override fields are set, the general-purpose-specific one wins so a caller tuning both fields never sees their GP override silently dropped.
Subagents in deep research
Subagents conduct specialized research tasks with isolated context. They are used for parallel execution of research tasks and can be delegated to by the main orchestrator agent.
Subagent stream handle name binding
Each subagent stream handle's name is the sub-agent's configured name: the subagent_type the coordinator passes when it calls the task tool. Deep Agents binds that name to the delegated run, so the same label you defined in your subagent specs is what you filter and route on in the stream.
Difference between stream.subagents and stream.subgraphs
stream.subgraphs shows graph execution structure. stream.subagents shows product-level Deep Agents task delegations. Use stream.subagents for user-facing UI because it hides internal graph nodes and exposes the subagent concept directly.
Subagent stream field projections in TypeScript
In TypeScript, subagent stream projections use camelCase names. Each subagent stream can expose .messages, .toolCalls, .values, .subagents, and .output.
Subagent stream fields reference table
Subagent stream fields: name (Sub-agent name taken from the subagent_type the coordinator selects in its task call), messages (Messages emitted by the subagent), subagents (Nested subagent invocations), output (Final subagent state or completion signal for the delegated task), path (Namespace path for the subagent stream, Python only), status (Lifecycle status such as started, completed, failed, or interrupted, Python only), taskInput (Promise for the prompt passed to the task tool, TypeScript only), tool_calls (Tool calls scoped to the subagent, Python), toolCalls (Tool calls scoped to the subagent, TypeScript).
Lightweight subagent lifecycle tracking
Use stream.subagents when you only need to show which subagents started and finished. You do not need to subscribe to message or value streams unless you access those projections on an individual subagent.
Subagent stream field projections in Python
In Python, subagent stream projections use snake_case names. Each subagent stream can expose .messages, .tool_calls, .values, .subagents, and .output.
Adversarial verification pattern
The adversarial verification pattern is a two-pass workflow: the first pass produces findings, and the second pass sends each finding to independent verifiers, keeping only findings that survive agreement. This reduces false positives when confidence matters more than speed. Use cases include security audits where false positives are costly, compliance checks, and any review requiring high confidence in findings.
Dynamic subagents overview
Dynamic subagents let an agent dispatch subagents from interpreter code. Instead of asking the model to choose one subagent call at a time, the agent can use JavaScript loops, branches, and parallel batches to route work across configured subagents and synthesize the results. Use this pattern when work spans many independent units, needs multiple perspectives, or benefits from recursive analysis.
Dynamic subagents require interpreter middleware
Dynamic subagents require interpreter middleware. The built-in general-purpose subagent handles basic fan-out without extra configuration. For install steps and interpreter setup, see the Interpreters quickstart.
task() global function dispatches subagents
When an agent has subagents and interpreter middleware, the interpreter exposes a built-in task() global that dispatches subagents from code. A task spanning many independent units becomes a loop that fans the work out, so it runs deterministically instead of one model-chosen tool call at a time.
task() parameters and behavior
task() takes the following inputs: description (the prompt for the subagent), subagentType (which configured subagent to run), and responseSchema (optional, for structured output). A task() runs a full agentic loop and resolves to the subagent's result. When you pass responseSchema, the resolved value is already a typed JavaScript object; only call JSON.parse if a subagent intentionally returned a JSON string.
Dynamic subagents with recursive language models
Subagent orchestration supports recursive language model (RLM) workflows as described in the Recursive Language Models paper: keep the working set in interpreter variables, select slices, call subagents with task(), and synthesize the results.
Dynamic subagents combined with programmatic tool calling
Many orchestration workflows combine dynamic subagents with programmatic tool calling (PTC): use tools.* from interpreter code to discover or filter inputs, then dispatch subagents with task(). PTC is off by default; enable it with an explicit allowlist on interpreter middleware.
Multi-turn interpreter variable persistence
Multi-turn orchestration can persist interpreter variables across agent turns when using mode="thread" (the default). See Persistence on the interpreters page.
Disable dynamic subagents
Subagent dispatch is on by default whenever the agent has subagents. Disable it if you want subagents to be available only through the normal task tool path. For other middleware options, see Configuration on the interpreters page.
Classify and act pattern
In the classify and act pattern, items are classified first, then each item is handled by a specialized subagent based on its classification. This lets you process mixed inputs where different items need different expertise. Use cases include triaging support tickets, error logs, user feedback, or any batch of items that need different handling depending on their type.
Fan-out and synthesize pattern
In the fan-out and synthesize pattern, the agent dispatches the same kind of work across many items in parallel, then combines the results. Use cases include code review across a directory, analyzing a batch of documents, processing log files, and running the same check across many services. Discovering files from interpreter code requires programmatic tool calling (PTC) with glob enabled in the PTC allowlist.