Module 11: Deep Agents

Subagent Spawning and Delegation

Capsule overview

The agent can create specialized subagents on demand to handle subtasks. Unlike Module 10, where you designed the multi-agent system (who does what, how they communicate, what state they share), here the agent decides when and what to delegate at runtime. It's multi-agent orchestrated by the agent itself. The value is clear: for complex tasks, a single generalist agent produces mediocre results. A team of specialists — each focused on one aspect — produces better ones. With subagent spawning, that team forms dynamically based on what the task requires.


How subagent spawning works

The full flow

1. The main agent receives a complex task
2. It plans with write_todos (capsule 02)
3. It finds a subtask that requires specialization
4. It creates a subagent with a specific prompt and relevant tools
5. The subagent runs in an isolated context
6. The subagent returns its result to the main agent
7. The main agent integrates the result and continues

Conceptual example

Imagine a research agent that has to compare three technologies. Instead of researching all three sequentially (slow, growing context), it creates three subagents:

Main agent: "Compare Redis, Memcached, and DynamoDB for caching in production"
  │
  ├─ spawn: redis_researcher
  │   └─ Prompt: "Research Redis as a caching solution in production"
  │   └─ Tools: [web_search]
  │   └─ Returns: "Redis offers persistence, pub/sub, and rich data types..."
  │
  ├─ spawn: memcached_researcher
  │   └─ Prompt: "Research Memcached as a caching solution in production"
  │   └─ Tools: [web_search]
  │   └─ Returns: "Memcached is simple, fast, optimized for key-value..."
  │
  └─ spawn: dynamodb_researcher
      └─ Prompt: "Research DynamoDB DAX as a caching solution in production"
      └─ Tools: [web_search]
      └─ Returns: "DynamoDB DAX offers caching integrated with DynamoDB..."

Main agent: integrates the 3 results → generates a comparative report

Each subagent only sees its own task. It doesn't know about the other subagents. It has no access to the main agent's history. This is context isolation — and it's why the results are better: each subagent has a context window dedicated 100% to its subtask.


Subagent spawning in code

Basic example

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Research Orchestrator",
    instructions=(
        "You are a senior researcher who delegates subtasks to subagents.\n\n"
        "For every research topic:\n"
        "1. Plan with write_todos\n"
        "2. For specialized search subtasks, create subagents:\n"
        "   - Each subagent gets ONE specific aspect of the topic\n"
        "   - Each subagent has access to web_search\n"
        "3. Integrate the subagents' results\n"
        "4. Generate the final report in output/report.md"
    ),
)

result = agent.run(
    "Compare the security strategies of AWS, GCP, and Azure for AI applications"
)

print(f"=== Run ===")
print(f"  Subagents created: {len(result.subagents_spawned)}")
print(f"  Files generated: {len(result.files)}")
print(f"  Todos completed: {sum(1 for t in result.todos if t['status'] == 'completed')}/{len(result.todos)}")

print(f"\n=== Subagents ===")
for sa in result.subagents_spawned:
    print(f"  {sa['name']}: {sa['prompt'][:80]}...")

if "output/report.md" in result.files:
    print(f"\n=== Report: {len(result.files['output/report.md']):,} chars ===")
# Expected output (varies):
# === Run ===
#   Subagents created: 3
#   Files generated: 5
#   Todos completed: 5/5
#
# === Subagents ===
#   aws_security_researcher: Research AWS security practices for AI applications...
#   gcp_security_researcher: Research GCP security practices for AI applications...
#   azure_security_researcher: Research Azure security practices for AI applicati...
#
# === Report: 4,523 chars ===

The main agent decided to create 3 subagents — one per cloud provider. Each subagent researched in isolation. The main agent integrated the results.

The anatomy of a subagent

When the main agent creates a subagent, it defines:

# What the main agent generates internally:
spawn_subagent(
    name="aws_researcher",                    # Identifying name
    prompt="Research AWS security "           # Specific instructions
           "practices for AI. "
           "Focus on IAM, VPC, and "
           "data encryption.",
    tools=["web_search"],                     # Available tools
    model="openai:gpt-4.1-mini",             # Model (can differ from the main one)
)

The subagent:

  • ✅ Receives its prompt as system instructions
  • ✅ Has access only to the specified tools
  • ✅ Runs in a clean context window
  • ✅ Returns its result as a string to the main agent
  • ❌ Does not see the main agent's history
  • ❌ Cannot access the main agent's files
  • ❌ Cannot create its own subagents (by default)

Context isolation: why it matters

The problem without isolation

Without context isolation, if the main agent researched AWS and then moves on to GCP, all the AWS information is in the context window while it researches GCP:

The agent's context while researching GCP:
  [System prompt]
  [Planning: 5 todos]
  [AWS result: 3,000 tokens]              ← irrelevant to GCP
  [AWS tool results]                       ← irrelevant to GCP
  [Now researching GCP...]

Total: ~8,000 input tokens, only ~2,000 are relevant

The model loses attention because of the irrelevant data. On top of that, you pay to process AWS tokens while you're working on GCP.

The solution: isolated context per subagent

AWS subagent:
  [System prompt: "Research AWS security"]
  [web_search result about AWS]
  Total: ~3,000 tokens, 100% relevant

GCP subagent:
  [System prompt: "Research GCP security"]
  [web_search result about GCP]
  Total: ~3,000 tokens, 100% relevant

Main agent (when integrating):
  [System prompt]
  [AWS subagent result: summary]
  [GCP subagent result: summary]
  Total: ~4,000 tokens, all relevant

Each subagent runs with an optimized context window. The main agent receives only the summaries — not the raw data.

Measurable impact

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent_with_spawning = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="With Subagents",
    instructions=(
        "Research each technology using a dedicated subagent. "
        "Each subagent researches ONE technology. "
        "Integrate the results at the end."
    ),
)

agent_without_spawning = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Without Subagents",
    instructions=(
        "Research each technology sequentially, yourself. "
        "Do NOT create subagents. Do all the research directly."
    ),
)

task = "Compare PostgreSQL, MongoDB, and Redis for an e-commerce system"

result_spawn = agent_with_spawning.run(task)
result_solo = agent_without_spawning.run(task)

print("=== With subagents ===")
print(f"  Tokens: {result_spawn.usage.total_tokens:,}")
print(f"  Subagents: {len(result_spawn.subagents_spawned)}")
print(f"  Output: {len(result_spawn.output):,} chars")

print("\n=== Without subagents ===")
print(f"  Tokens: {result_solo.usage.total_tokens:,}")
print(f"  Subagents: {len(result_solo.subagents_spawned)}")
print(f"  Output: {len(result_solo.output):,} chars")
# Expected output (varies):
# === With subagents ===
#   Tokens: 22,456
#   Subagents: 3
#   Output: 5,234 chars
#
# === Without subagents ===
#   Tokens: 28,901
#   Subagents: 0
#   Output: 3,891 chars

Fewer tokens, better output. Context isolation keeps each investigation focused.


Practical example: a research agent with delegation

The full scenario

An agent that researches a complex topic, using the three tools of this block: planning (write_todos), the filesystem, and subagent spawning.

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=5)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Full Research Agent",
    instructions=(
        "You are a research director. Your process:\n\n"
        "1. PLAN: Use write_todos to define the aspects to research\n"
        "2. DELEGATE: For each aspect, create a specialized subagent\n"
        "   - The subagent searches for information and returns a summary\n"
        "3. STORE: Write each subagent's result to research/[aspect].md\n"
        "4. SYNTHESIZE: Read the files in research/, generate analysis/synthesis.md\n"
        "5. REPORT: Generate output/report.md pulling everything together\n\n"
        "Each subagent must receive precise instructions on what to look for. "
        "Don't delegate vague tasks like 'research AI' — be specific."
    ),
)

result = agent.run(
    "Analyze the state of the art of AI agents in production: "
    "available frameworks, deployment patterns, and success stories"
)

print("=== Plan ===")
for todo in result.todos:
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"  {icon} {todo['title']}")

print(f"\n=== Subagents ({len(result.subagents_spawned)}) ===")
for sa in result.subagents_spawned:
    print(f"  → {sa['name']}")

print(f"\n=== Files ({len(result.files)}) ===")
for path in sorted(result.files.keys()):
    print(f"  {path} ({len(result.files[path]):,} chars)")

print(f"\n=== Total tokens: {result.usage.total_tokens:,} ===")
# Expected output (varies):
# === Plan ===
#   ✅ Define the dimensions of the analysis
#   ✅ Research agent frameworks (LangGraph, CrewAI, AutoGen)
#   ✅ Research deployment patterns in production
#   ✅ Research documented success stories
#   ✅ Synthesize findings
#   ✅ Generate the final report
#
# === Subagents (3) ===
#   → frameworks_researcher
#   → deployment_researcher
#   → case_studies_researcher
#
# === Files (5) ===
#   analysis/synthesis.md (3,456 chars)
#   output/report.md (6,789 chars)
#   research/case_studies.md (2,345 chars)
#   research/deployment_patterns.md (2,678 chars)
#   research/frameworks.md (2,890 chars)
#
# === Total tokens: 35,678 ===

The full flow: planning → delegation → storage → synthesis → report. The three tools (write_todos, filesystem, subagent spawning) working together.


Limits and controls

Without limits, subagent spawning can blow up in cost and complexity. An enthusiastic agent might create 20 subagents for a task that needs 3. Each subagent consumes tokens, and an unconstrained agent can multiply costs fast.

Max subagents

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="Limited Spawner",
    instructions="Research the topic by delegating to subagents.",
    agent_config={
        "max_subagents": 3,        # Max 3 subagents per run
    },
)

result = agent.run(
    "Research 6 programming languages: "
    "Python, Rust, Go, TypeScript, Kotlin, Swift"
)

print(f"Subagents created: {len(result.subagents_spawned)}")
for sa in result.subagents_spawned:
    print(f"  {sa['name']}: {sa['prompt'][:60]}...")
# Expected output (varies):
# Subagents created: 3
#   systems_languages: Research Rust and Go as systems languages...
#   app_languages: Research TypeScript and Kotlin for app development...
#   general_languages: Research Python and Swift as general-purpose lang...

With a limit of 3 subagents and 6 languages, the agent groups them: 2 languages per subagent. The agent adapts to the limit instead of failing.

Timeout per subagent

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Timeout Demo",
    instructions="Research the topic using subagents.",
    agent_config={
        "max_subagents": 5,
        "subagent_timeout_seconds": 60,  # Max 60 seconds per subagent
    },
)

result = agent.run("Research cloud computing trends in 2025")

for sa in result.subagents_spawned:
    status = "✅" if sa.get("completed") else "⏰ timeout"
    print(f"  {sa['name']}: {status} ({sa.get('duration_seconds', 0):.1f}s)")
# Expected output (varies):
# aws_trends: ✅ (12.3s)
# gcp_trends: ✅ (15.7s)
# azure_trends: ✅ (11.2s)

The timeout prevents subagents from getting "stuck" in infinite loops or endless searches. If a subagent exceeds the timeout, its partial result is returned (if any) or it gets marked as failed.

Validating results

The main agent can validate the subagents' results before integrating them:

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="Validating Orchestrator",
    instructions=(
        "When you delegate to subagents, validate each result:\n"
        "1. Does the result answer the question you asked?\n"
        "2. Does it have specific data (not just generalities)?\n"
        "3. Is it consistent with what you already know?\n\n"
        "If a result fails validation, retry with "
        "more specific instructions or research it yourself."
    ),
)

result = agent.run("Compare the hosting costs of 3 platforms: Vercel, Railway, Fly.io")

print(f"Subagents: {len(result.subagents_spawned)}")
print(f"Files: {list(result.files.keys())}")
# Expected output (varies):
# Subagents: 3
# Files: ['research/vercel.md', 'research/railway.md', 'research/flyio.md', 'output/comparison.md']

Validation matters especially when subagents use external tools (web search, APIs) where results can be incomplete or off-target.


Comparison: manual M10 vs Deep Agents subagent spawning

This is the direct link back to what you built in Module 10.

M10: you design the multi-agent system

# M10: The supervisor YOU designed
from langgraph_supervisor import create_supervisor

search_agent = create_react_agent(
    model, tools=[web_search], name="search_agent",
    prompt="Search for information on the web."
)

analysis_agent = create_react_agent(
    model, tools=[], name="analysis_agent",
    prompt="Analyze data and generate insights."
)

report_agent = create_react_agent(
    model, tools=[], name="report_agent",
    prompt="Write professional reports."
)

supervisor = create_supervisor(
    model=model,
    agents=[search_agent, analysis_agent, report_agent],
    prompt="Coordinate the research across the agents.",
)

app = supervisor.compile()

Here, you decide:

  • ✅ Which agents exist (3 specific ones)
  • ✅ What tools each one has
  • ✅ How they coordinate (supervisor)
  • ✅ When each agent gets created (build time)

Deep Agents: the agent designs the system

# M11: The agent decides which subagents to create
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[TavilySearchResults(max_results=3)],
    name="Self-Organizing Researcher",
    instructions=(
        "Research the topic. Create specialized subagents when "
        "a subtask needs dedicated focus."
    ),
)

result = agent.run("Research the state of AI agents in production")

Here, the agent decides:

  • ✅ How many subagents to create (runtime)
  • ✅ What task to assign each one (runtime)
  • ✅ When to delegate vs research directly (runtime)

Trade-off table

CriterionM10 (Manual)M11 (Spawning)
Who designs the teamYou, at build timeThe agent, at runtime
PredictabilityHigh (you know which agents exist)Low (varies per run)
AdaptabilityLow (fixed team)High (dynamic team)
ControlTotal (you define everything)Partial (you define limits, not details)
DebuggingEasier (known flow)Harder (the team varies)
CostPredictable (N fixed agents)Variable (depends on how many it creates)
CodeMore (define each agent)Less (instructions + limits)
Flexibility across diverse tasksLow (team designed for one task)High (team adapts to the task)

When to use which

Use manual M10 when:

  • ✅ You know exactly which roles you need
  • ✅ The tasks are predictable and repetitive
  • ✅ You need guarantees about which agent does what
  • ✅ Debugging and reproducibility are the priority

Use subagent spawning when:

  • ✅ The tasks are diverse and you can't predict the roles you'll need
  • ✅ Flexibility matters more than predictability
  • ✅ You want a fast prototype without designing the multi-agent architecture
  • ✅ The agent needs to adapt to what it discovers mid-run

When spawning is overkill

Not every subtask needs a subagent. The overhead of creating one includes:

  1. An extra LLM call for the subagent to process its task
  2. Latency from setting the subagent up
  3. Complexity in integrating the result

Decision rule

Does the subtask need more than 2-3 tool calls?
  └─ YES → subagent (you give it isolated context so it works focused)
  └─ NO → the main agent does it directly

Is the subtask independent of the rest of the context?
  └─ YES → subagent (it doesn't need to see the full history)
  └─ NO → main agent (it needs the accumulated context)

Are there multiple parallel subtasks of the same kind?
  └─ YES → subagents (one per subtask, potential parallelism)
  └─ NO → main agent (simpler, less overhead)

Example: don't use subagents for this

# OVERKILL: creating a subagent for a simple question
agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    instructions="For EVERY question, create a subagent to answer it.",
)
result = agent.run("What is the capital of France?")
# The agent creates a subagent just to answer "Paris"
# Overhead: ~2x tokens, ~2x latency, for a trivial result
# CORRECT: the main agent answers directly
agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    instructions=(
        "Answer questions directly when they're simple. "
        "Create subagents only when the task requires specialized, "
        "multi-step research."
    ),
)
result = agent.run("What is the capital of France?")
# Direct answer, no overhead

The rule: if you can answer in 1-2 steps, don't delegate. Delegate when the subtask requires sustained focus.


Recursive subagents: agents that create agents

By default, subagents cannot create their own subagents. That's intentional — it prevents cost and complexity from exploding.

But in advanced cases, you can enable it with a depth limit:

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="Recursive Orchestrator",
    instructions=(
        "Research the topic. You may create subagents. "
        "Subagents may create sub-subagents if the task calls for it."
    ),
    agent_config={
        "max_subagents": 5,
        "max_spawn_depth": 2,  # Main → subagent → sub-subagent (max)
    },
)

result = agent.run(
    "Generate a complete analysis of the AI engineering ecosystem: "
    "frameworks, infrastructure, and team practices"
)

print(f"Level 1 subagents: {len(result.subagents_spawned)}")
for sa in result.subagents_spawned:
    nested = sa.get("subagents_spawned", [])
    print(f"  {sa['name']}{len(nested)} sub-subagents")
# Expected output (varies):
# Level 1 subagents: 3
#   frameworks_researcher → 2 sub-subagents
#   infra_researcher → 1 sub-subagents
#   team_practices_researcher → 0 sub-subagents

Use max_spawn_depth with caution. Every level multiplies tokens and latency. For most cases, depth 1 (only the main agent creates subagents) is enough.


Troubleshooting

Problem 1: The agent creates too many subagents

Symptom: For a moderate task, the agent creates 8-10 subagents when 3 would do. Cause: The instructions say "delegate subtasks" without defining when delegating is appropriate. Fix: Define delegation criteria:

instructions = (
    "Create subagents ONLY when:\n"
    "- The subtask requires more than 3 searches\n"
    "- The subtask is independent of the rest\n"
    "- There are 2+ parallel subtasks of the same kind\n\n"
    "For everything else, do the work yourself."
)

Problem 2: Subagent results are too vague

Symptom: The subagent returns "I researched the topic and found relevant information" with no concrete data. Cause: The subagent's prompt doesn't specify the expected output format. Fix: Instruct the main agent on how to define subagents:

instructions = (
    "When you create a subagent, include in its prompt:\n"
    "1. What to search for, specifically\n"
    "2. The expected output format (data, comparison, list, etc.)\n"
    "3. What information is mandatory in the response\n\n"
    "Example: 'Look up Vercel's pricing. Return: plan name, monthly price, "
    "main limits. Format: markdown table.'"
)

Problem 3: Subagents duplicate work

Symptom: Two subagents research the same topic because their prompts overlap. Cause: The main agent didn't define clear boundaries between subagents. Fix:

instructions = (
    "Before creating subagents, explicitly define each one's SCOPE:\n"
    "- Which aspects does this subagent cover?\n"
    "- Which aspects does it NOT cover (another subagent handles them)?\n"
    "The scopes must not overlap."
)

Problem 4: The agent doesn't integrate the results well

Symptom: The results of 3 subagents get stitched together as a concatenation, with no cross-analysis or real synthesis. Cause: The instructions don't distinguish between "combining results" and "synthesizing results." Fix:

instructions = (
    "After receiving the results from all the subagents:\n"
    "1. Read each result\n"
    "2. Identify points of agreement and contradictions\n"
    "3. Generate a synthesis that COMPARES and CONTRASTS, doesn't just concatenate\n"
    "4. Include your own assessment of the findings"
)

Problem 5: Timeouts on subagents with many searches

Symptom: A subagent fails on timeout because it tries to run 10 web searches. Cause: The timeout is too low for the amount of work assigned, or the assigned task is too broad. Fix: Split the task or adjust the timeout:

agent_config = {
    "subagent_timeout_seconds": 120,  # More time if the task calls for it
}

# Or instruct the agent to hand out more specific tasks:
instructions = (
    "Assign SPECIFIC tasks to each subagent. "
    "'Research AI' is too broad. "
    "'Find the 3 most cited papers on RAG in 2025' is specific."
)

Exercises

Exercise 1: A single subagent (Easy)

Create an agent that delegates ONE subtask to a subagent and integrates the result. Verify the subagent was created and its result was used.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Single Delegation",
    instructions=(
        "When you receive an analysis task:\n"
        "1. Identify the most technical aspect\n"
        "2. Delegate it to a specialized subagent\n"
        "3. Use the subagent's result in your final answer"
    ),
)

result = agent.run(
    "Analyze whether Python is a good choice for backend: "
    "performance, ecosystem, and ease of hiring"
)

print(f"Subagents: {len(result.subagents_spawned)}")
if result.subagents_spawned:
    sa = result.subagents_spawned[0]
    print(f"  Name:   {sa['name']}")
    print(f"  Prompt: {sa['prompt'][:100]}...")
print(f"\nFinal output: {len(result.output)} chars")
print(result.output[:200])
# Expected output (varies):
# Subagents: 1
#   Name:   performance_analyst
#   Prompt: Analyze Python's backend performance compared to Go and Node.js...
#
# Final output: 2345 chars
# Python is a solid choice for backend...

Explanation: The agent identified "performance" as the most technical aspect and delegated it to a specialized subagent. It handled the other aspects (ecosystem, hiring) directly.

Exercise 2: Multiple subagents in parallel (Easy)

Create an agent that researches 3 technologies in parallel, each with its own subagent. Compare the timings.

See solution
from dotenv import load_dotenv
load_dotenv()

import time
from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Parallel Researcher",
    instructions=(
        "Research the 3 technologies mentioned. "
        "Create a subagent for EACH technology — research them in parallel. "
        "Integrate the results into a final comparison."
    ),
    agent_config={"max_subagents": 3},
)

start = time.time()
result = agent.run("Compare FastAPI, Express.js, and Gin for REST APIs")
elapsed = time.time() - start

print(f"Total time: {elapsed:.1f}s")
print(f"Subagents: {len(result.subagents_spawned)}")
for sa in result.subagents_spawned:
    duration = sa.get("duration_seconds", 0)
    print(f"  {sa['name']}: {duration:.1f}s")
print(f"\nOutput: {len(result.output):,} chars")
# Expected output (varies):
# Total time: 18.3s
# Subagents: 3
#   fastapi_expert: 8.2s
#   express_expert: 7.5s
#   gin_expert: 9.1s
#
# Output: 3,456 chars

Explanation: The 3 subagents can run in parallel (depending on the Deep Agents implementation). The total time is close to that of the slowest subagent, not the sum of all three.

Exercise 3: Subagents with tools (Medium)

Create an agent that delegates web searches to subagents. Each subagent has TavilySearchResults and searches on a different aspect.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Web Research Orchestrator",
    instructions=(
        "For the research topic:\n"
        "1. Identify 3 key aspects\n"
        "2. Create a subagent for each aspect with access to web_search\n"
        "3. Each subagent must return: key findings, sources, and specific data\n"
        "4. Integrate the results into output/report.md\n"
        "5. Write each subagent's result to research/[aspect].md"
    ),
    agent_config={"max_subagents": 3},
)

result = agent.run("Research the state of AI coding assistants in 2025")

print(f"=== Result ===")
print(f"Subagents: {len(result.subagents_spawned)}")
print(f"Files: {len(result.files)}")
for path in sorted(result.files.keys()):
    print(f"  {path} ({len(result.files[path]):,} chars)")
# Expected output (varies):
# === Result ===
# Subagents: 3
# Files: 4
#   output/report.md (5,123 chars)
#   research/market_landscape.md (2,345 chars)
#   research/technical_capabilities.md (2,567 chars)
#   research/user_adoption.md (1,890 chars)

Explanation: Each subagent used web_search independently in its isolated context. The results were stored in files (the filesystem from capsule 03) and integrated into a final report. All three tools of the module working together.

Exercise 4: Cap the number of subagents (Medium)

Configure an agent with max_subagents: 2 and give it a task that would naturally call for 4+ subagents. Watch how it adapts.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Constrained Orchestrator",
    instructions=(
        "Research ALL the topics mentioned. "
        "Use subagents when you need specialization. "
        "If you hit the subagent limit, group related topics "
        "or research the remaining ones yourself."
    ),
    agent_config={"max_subagents": 2},
)

result = agent.run(
    "Compare 5 databases: PostgreSQL, MySQL, MongoDB, Redis, Cassandra"
)

print(f"Subagents created: {len(result.subagents_spawned)} (limit: 2)")
for sa in result.subagents_spawned:
    print(f"  {sa['name']}: {sa['prompt'][:80]}...")

print(f"\nOutput: {len(result.output):,} chars")
# Expected output (varies):
# Subagents created: 2 (limit: 2)
#   relational_db_expert: Compare PostgreSQL and MySQL: performance, features, ecosys...
#   nosql_db_expert: Compare MongoDB, Redis, and Cassandra: data models, scalability...
#
# Output: 4,567 chars

Explanation: With a limit of 2, the agent grouped them: SQL (PostgreSQL + MySQL) in one subagent and NoSQL (MongoDB + Redis + Cassandra) in another. Limits force the agent to get creative with its organization, not to fail.

Exercise 5: Full pipeline: planning + filesystem + spawning (Advanced)

Create an agent that uses all three tools of the module for a complex task. Print an execution report showing the plan, the subagents, and the files.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Full Pipeline Agent",
    instructions=(
        "You are a research director. The pipeline is mandatory:\n\n"
        "PHASE 1 - PLANNING:\n"
        "  Use write_todos to create a 5-7 step plan\n\n"
        "PHASE 2 - RESEARCH:\n"
        "  For each research step, create a subagent with web_search\n"
        "  Write each subagent's result to research/[topic].md\n\n"
        "PHASE 3 - ANALYSIS:\n"
        "  Read the files in research/\n"
        "  Generate analysis/synthesis.md with a cross-source comparison\n\n"
        "PHASE 4 - OUTPUT:\n"
        "  Generate output/report.md pulling everything together\n"
        "  Mark all the todos as completed"
    ),
    agent_config={"max_subagents": 4},
)

result = agent.run(
    "Analyze the AI infrastructure market: "
    "GPU cloud providers, training frameworks, and deployment platforms"
)

print("╔══════════════════════════════════════════════╗")
print("║              EXECUTION REPORT                ║")
print("╠══════════════════════════════════════════════╣")

print("║ PLAN:")
for todo in result.todos:
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"║   {icon} {todo['title']}")

print("║")
print(f"║ SUBAGENTS ({len(result.subagents_spawned)}):")
for sa in result.subagents_spawned:
    print(f"║   → {sa['name']}")

print("║")
print(f"║ FILES ({len(result.files)}):")
for path in sorted(result.files.keys()):
    size = len(result.files[path])
    print(f"║   {path} ({size:,} chars)")

print("║")
print(f"║ TOKENS: {result.usage.total_tokens:,}")
print(f"║ ESTIMATED COST: ${result.usage.total_tokens * 0.00001:.4f}")
print("╚══════════════════════════════════════════════╝")
# Expected output (varies):
# ╔══════════════════════════════════════════════╗
# ║              EXECUTION REPORT                ║
# ╠══════════════════════════════════════════════╣
# ║ PLAN:
# ║   ✅ Define the dimensions of the AI infrastructure market
# ║   ✅ Research GPU cloud providers
# ║   ✅ Research training frameworks
# ║   ✅ Research deployment platforms
# ║   ✅ Cross-analysis of the findings
# ║   ✅ Generate the final report
# ║
# ║ SUBAGENTS (3):
# ║   → gpu_cloud_researcher
# ║   → training_frameworks_researcher
# ║   → deployment_platforms_researcher
# ║
# ║ FILES (5):
# ║   analysis/synthesis.md (3,456 chars)
# ║   output/report.md (6,789 chars)
# ║   research/deployment_platforms.md (2,345 chars)
# ║   research/gpu_cloud.md (2,678 chars)
# ║   research/training_frameworks.md (2,890 chars)
# ║
# ║ TOKENS: 42,345
# ║ ESTIMATED COST: $0.4235
# ╚══════════════════════════════════════════════╝

Explanation: The full pipeline uses all three tools: write_todos to plan, subagent spawning to research in parallel with isolated context, and the filesystem to store results and keep the context window lean. This is the pattern you'll reimplement as the project in capsule 08.

Exercise 6: Side-by-side comparison: M10 vs M11 (Advanced)

For the same task, compare the result of a manual multi-agent system (M10 style, simulated with functions) against a Deep Agent with subagent spawning. Measure tokens, time, and output length.

See solution
from dotenv import load_dotenv
load_dotenv()

import time
from deep_agents import create_deep_agent

agent_m11 = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="M11 Deep Agent",
    instructions=(
        "Research the topic using specialized subagents. "
        "Create a subagent for each aspect. "
        "Integrate the results into a report."
    ),
    agent_config={"max_subagents": 3},
)

agent_m10_style = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="M10 Style (Sequential)",
    instructions=(
        "Research the topic sequentially. "
        "Do NOT use subagents. "
        "Research each aspect yourself, one after another. "
        "Generate a report at the end."
    ),
)

task = "Compare Docker, Kubernetes, and serverless for deploying AI applications"

start_m11 = time.time()
result_m11 = agent_m11.run(task)
time_m11 = time.time() - start_m11

start_m10 = time.time()
result_m10 = agent_m10_style.run(task)
time_m10 = time.time() - start_m10

print("=== M10 vs M11 comparison ===")
print(f"\n{'Metric':<25} {'M10 (sequential)':<20} {'M11 (subagents)'}")
print("-" * 65)
print(f"{'Time (s)':<25} {time_m10:<20.1f} {time_m11:.1f}")
print(f"{'Total tokens':<25} {result_m10.usage.total_tokens:<20,} {result_m11.usage.total_tokens:,}")
print(f"{'Output (chars)':<25} {len(result_m10.output):<20,} {len(result_m11.output):,}")
print(f"{'Subagents':<25} {len(result_m10.subagents_spawned):<20} {len(result_m11.subagents_spawned)}")
print(f"{'Files':<25} {len(result_m10.files):<20} {len(result_m11.files)}")
# Expected output (varies):
# === M10 vs M11 comparison ===
#
# Metric                    M10 (sequential)     M11 (subagents)
# -----------------------------------------------------------------
# Time (s)                  32.4                 21.7
# Total tokens              28,901               24,567
# Output (chars)            3,456                5,123
# Subagents                 0                    3
# Files                     1                    4

Explanation: M11 with subagents tends to be faster (parallelism) and to produce longer output (context isolation = better focus). But the M10 result can be more coherent (a single context = more internal consistency). The trade-off is: speed and depth (M11) vs coherence and control (M10).


Summary

  • Subagent spawning lets the agent create specialized subagents on demand. Unlike M10, where you designed the multi-agent team, here the agent decides which subagents to create at runtime based on what the task requires
  • Context isolation is the main benefit: each subagent has a context window dedicated 100% to its subtask, with no noise from other investigations. That improves quality and cuts tokens
  • The controls are essential: max_subagents to cap costs, timeout to prevent stuck subagents, validation to verify the results are useful
  • The comparison with M10 is direct: in M10 you design the team at build time with total control; with spawning, the agent forms its team at runtime with more flexibility but less predictability
  • Not everything needs subagents: simple tasks (1-2 steps) are more efficient without delegation. Use subagents when the subtask requires sustained focus, multiple tool calls, or there's natural parallelism
  • The three tools of the module complement each other: write_todos plans what to do, the filesystem stores the results, subagent spawning executes subtasks with focus. Together, they enable autonomous agents capable of complex research

Additional resources

  1. Deep Agents — Subagent Spawning — Official documentation for subagent spawning, configuration, and examples
  2. LangGraph — Multi-Agent Architectures — The manual multi-agent patterns that spawning abstracts
  3. Voyager: An Open-Ended Embodied Agent with LLMs — Paper on agents that create specialized subprograms, an inspiration for subagent spawning
  4. AutoGen — Multi-Agent Conversation Framework — Microsoft's multi-agent framework; useful for comparing delegation approaches
  5. The AI Scientist — Automated Research — Paper on autonomous research agents that use delegation and planning
  6. Cost Management for AI Agents — Best Practices — Guide on cost control in multi-agent systems, directly relevant to spawning limits

Module 11 — LangChain & LangGraph: From Chains to Agents

Next capsule: Long-term Memory — you'll learn how to configure persistent memory backends so your agent remembers information across sessions, with a filesystem backend, LangGraph Store, and composite backends.