Deep AgentsDeep Agents 0.7 · Python 3.11+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
28 small wins to finish your pathNext lesson →

Subagents: delegating with the task tool

A subagent is a separate agent with its own prompt and tools that the main deep agent starts with the task tool; it works in a fresh context and hands back one final report.

Last updated: 29 Sep, 2026 · Deep Agents 0.7

What a subagent is: context quarantine · from the Complete Deep Agents Course With LangChain · 137:47 to 142:02

Subagents and context quarantine

The video reads the docs' definition: a deep agent can create subagents to delegate work, given in the subagents parameter. They give context quarantine, keeping the main agent's context clean, and specialized instructions. Each subagent has its own context, tools and instructions, does one focused job and returns a result; the main agent understands the goal, plans, delegates, collects the results and writes the final answer. These are synchronous subagents: the main agent waits, blocked, until a subagent finishes. The example: a research report on agentic AI split between a research agent, a writing agent, a data agent and a critic.

The main agent hands work to hotel-scout, sights-planner or the built-in general-purpose subagent through the task tool, and each returns one final report.
The task tool and its subagents
A research subagent with Tavily · from the Complete Deep Agents Course With LangChain · 144:00 to 149:32
The saved run starts with a to-do list because planning was built in at the time; since 0.7 it needs TodoListMiddleware. Its models, openai:gpt-5.4 and groq:qwen/qwen3-32b, are both google_genai:gemini-2.5-flash in the run below.

In code, a subagent is a dictionary. The video wraps its Tavily search in internet_search, then describes research_subagent: a name, a description the main agent reads to decide when to delegate, a system_prompt, its tools, and an optional model that falls back to the main agent's. Asked to research LLM gateways, the saved run first writes a to-do list with write_todos, then works through it and returns a detailed summary.

To run it, start research.py with web_search, the Tavily tool from Tools: a travel search the agent can call; it needs TAVILY_API_KEY. The agent runs on Gemini, so it also needs GOOGLE_API_KEY, the free key from Installation and setup; with the Groq line it needs GROQ_API_KEY instead. The video writes the same function again here as internet_search; the page reuses web_search.

python
import os
from tavily import TavilyClient
from typing import Literal

tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

def web_search(query: str, max_results: int = 5,
               topic: Literal["general", "sports", "news", "finance"] = "general"):
    """Run a web search"""
    return tavily_client.search(query, max_results=min(max_results, 5), topic=topic)

Add the video's code under web_search in research.py, with four changes. The first is the one above: web_search in place of internet_search. The second: both models are one google_genai:gemini-2.5-flash object with max_retries: a research run is long, so this one uses Gemini's free key and leaves Groq's daily limit for the other lessons; the Groq line from Setup works the same. Third, the subagent's prompt gains a sentence, because with only "You are a great researcher" the model wrote its report from memory without searching. Fourth, the loop prints each step shortened and the start of the summary with .text, where the video pretty-prints every message in full; .text gives plain text on every provider, while Gemini's .content is a list of parts.

ExampleAPI keyFrom the video, run on Gemini
from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("google_genai:gemini-2.5-flash", temperature=0, max_retries=6)

research_subagent = {
    "name": "research-agent",
    "description": "Used to research more in depth questions",
    "system_prompt": "You are a great researcher. Search with web_search and use only what it returns.",
    "tools": [web_search],
    "model": model,   # optional, defaults to the main agent's model
}
agent = create_deep_agent(model=model, subagents=[research_subagent])
result = agent.invoke(
    {"messages": [{"role": "user", "content": "Reserach about LLM Gateways and provide me a detailed summary."}]},
    config={"configurable": {"thread_id": "skills-demo-2"}},
)

for message in result["messages"][:-1]:     # the steps, shortened
    print(f"{message.type:<5}", message.text[:200] or [(c["name"], c["args"]) for c in message.tool_calls])
print(result["messages"][-1].text[:1500])   # the start of the summary

The main agent did not search itself: its one step was a task call with subagent_type research-agent and a description it wrote. The tool message is the subagent's report, headed "LLM Gateways: A Detailed Summary", and the final answer is the main agent's own summary built from it, covering purpose, benefits and features. Search results change from day to day, so your run will word it differently.

The subagent syntax

python
subagent = {
    "name": "hotel-scout",                 # what the main agent passes as subagent_type
    "description": "...",                  # when to use it
    "system_prompt": "...",                # its own instructions
    "tools": [search_travel],              # its own tools
}
agent = create_deep_agent(model=model, subagents=[subagent])

Two travel subagents

The trip is split the way the video splits a research report. Start trip.py with search_travel, the catalog tool from Tools: a travel search the agent can call. Everything below goes in the same file, under it.

python
from langchain.tools import tool

CATALOG = {
    "paris": {
        "flight": ["Return flight Delhi to Paris: 42,000 rupees"],
        "hotel": ["Seine Budget Inn, Latin Quarter: 5,200 rupees a night",
                  "Hotel Lumiere, Montmartre: 7,500 rupees a night",
                  "Le Grand Opera Hotel: 16,000 rupees a night"],
        "sight": ["Eiffel Tower summit: 3,100 rupees", "Louvre Museum: 2,000 rupees",
                  "Seine river cruise: 1,500 rupees", "Versailles day trip: 2,600 rupees",
                  "Montmartre walking tour: free"],
        "food": ["Cafe breakfast and bistro dinner: 3,000 rupees a day"],
    },
}


@tool
def search_travel(city: str, kind: str) -> str:
    """Search the travel catalog. kind is "flight", "hotel", "sight" or "food". Prices are in rupees."""
    entries = CATALOG.get(city.lower(), {}).get(kind)
    return "\n".join(entries) if entries else f"The catalog has no {kind} entries for {city}."
python
from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0, max_retries=6)
python
hotel_scout = {
    "name": "hotel-scout",
    "description": "Finds a hotel in a city that fits a nightly budget.",
    "system_prompt": "Look up hotels with search_travel. Reply with one hotel, its price per night and one reason. Use only catalog data.",
    "tools": [search_travel],
}
sights_planner = {
    "name": "sights-planner",
    "description": "Picks sights for each day of a trip and reports their ticket prices.",
    "system_prompt": "Look up sights with search_travel. Reply with one line per day: the sights and their ticket prices. Use only catalog data.",
    "tools": [search_travel],
}

Each subagent gets only search_travel and a prompt for its one job.

The main agent

The main agent has no travel tool of its own. Its prompt says which subagent gets which job.

python
agent = create_deep_agent(
    model=model,
    subagents=[hotel_scout, sights_planner],
    system_prompt="You plan trips. Give the hotel question to hotel-scout and the sightseeing to sights-planner "
                  "with the task tool. Then combine their answers in three short lines.",
)

Delegating a Paris request to two subagents

The loop prints each task call with the subagent's name and the job description the main agent wrote, each report that came back, and the final answer.

ExampleAPI keytrip.py, continued
request = "Paris, 3 nights: find a hotel under 8,000 rupees a night and plan sights for 2 days."
result = agent.invoke({"messages": [{"role": "user", "content": request}]})

for message in result["messages"][1:]:
    if message.type == "ai" and message.tool_calls:
        for call in message.tool_calls:
            print("task ->", call["args"]["subagent_type"], ":", call["args"]["description"])
    elif message.type == "tool":
        print("back <-", message.text.replace("\n", " | "))
    else:
        print("final:", message.text)

How the work was split

  • Two task calls: the main agent wrote a job description for hotel-scout and another for sights-planner, in its own words.
  • Each report came back as one tool message. The subagents' own tool calls and catalog results stayed in their contexts, not in the main conversation.
  • The final answer combines both reports: a hotel under the nightly budget and two days of sights with their prices.
  • hotel-scout added details the catalog does not hold, such as the rooms and the Wi-Fi. Its prompt says to use only catalog data, and a model can still drift; the prices, which came from the tool, are right. The structured-output lesson next narrows what a subagent may send back.

Doing it all in one agent vs delegating

One agent with all toolsMain agent with subagents
What the main context holdsEvery tool call and resultJob descriptions and short reports
InstructionsOne prompt for everythingA focused prompt per job
CostFewer model callsMore calls, cleaner context

Every deep agent also has a built-in general-purpose subagent with the main agent's tools. It is there even when you define no subagents, which is why the task tool appeared in the built-in tools lesson.

Where subagents help

  • Jobs with a lot of intermediate data, such as searching many pages, where only the conclusion matters.
  • Parts of a task that need different instructions or tools.
  • Keeping a long-running main agent's context small.
Watch out. A subagent sees only the job description it is given, not the conversation. If the main agent leaves out the budget or the number of nights, the subagent does not know them. Tell the main agent to put every detail into the task.
Try it yourself
  • Ask for a hotel under 5,000 rupees a night and read what hotel-scout reports.
  • Stream the run with subgraphs=True and print each namespace to see which agent is working.
  • Remove sights_planner from the list and see which subagent the main agent uses for sights.

You understood something today that you didn't yesterday.