Deep agent 可以创建 subagents 来 delegate work。你可以在 subagents parameter 中指定 custom subagents。Subagents 适用于 context quarantine(保持 main agent 的 context clean),也适用于提供 specialized instructions。 本页介绍 synchronous subagents,即 supervisor 会 block,直到 subagent 完成。对于 long-running tasks、parallel workstreams,或需要 mid-flight steering 和 cancellation 的场景,请参阅 Async subagents

Why use subagents?

Subagents 解决 context bloat problem。当 agents 使用具有 large outputs 的 tools(web search、file reads、database queries)时,context window 会很快被 intermediate results 填满。Subagents 会隔离这些 detailed work,main agent 只接收 final result,而不是生成该结果的 dozens of tool calls。 何时使用 subagents:
  • ✅ 会 clutter main agent context 的 multi-step tasks
  • ✅ 需要 custom instructions 或 tools 的 specialized domains
  • ✅ 需要 different model capabilities 的 tasks
  • ✅ 希望 main agent 专注于 high-level coordination 时
何时不使用 subagents:
  • ❌ Simple、single-step tasks
  • ❌ 需要 maintain intermediate context 时
  • ❌ Overhead 超过 benefits 时

Configuration

subagents 应是 dictionaries 或 CompiledSubAgent objects 的 list。有两种类型:

Default subagent

除非你已经提供同名 synchronous subagent,否则 Deep Agents 会自动添加 synchronous general-purpose subagent。 general-purpose subagent 默认具有 filesystem tools,并且可以用 additional tools/middleware 自定义。
  • 若要替换它,请传入你自己的名为 general-purpose 的 subagent。
  • 若要 rename 或 re-prompt auto-added version,请在 active harness profile 上设置 general_purpose_subagent=GeneralPurposeSubagentProfile(...)
  • 若要 disable 它,请参阅下面的 Running without subagents

Running without subagents

若要运行不带 task tool 的 agent,请执行两件事:
  1. 在 active harness profile 上设置 general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False)
  2. 不要在 create_deep_agent 上通过 subagents= 传入 synchronous subagents。
Deep Agents 只有在至少存在一个 synchronous subagent 时才会 attach SubAgentMiddleware(以及 task tool)。如果没有默认 subagent,也没有 caller-provided subagent,agent 会在没有 delegation 的情况下运行。 Async subagents 不受影响,它们通过自己的 middleware 和 tools 流转,详见 Async subagents
不要在这里使用 excluded_middlewareSubAgentMiddleware 是 required scaffolding,将其列出会引发 ValueErrorgeneral_purpose_subagent.enabled = False knob 是受支持的路径。

Custom subagents

你可以使用 subagents parameter 定义带 specific tools 的 specialized subagents。例如作为 code reviewer、web researcher 或 test runner。 对于多数 use cases,请使用 SubAgent dictionaries 将 subagents 定义为 dictionaries。对于 complex workflows,请使用 CompiledSubAgent

SubAgent (Dictionary-based)

将 subagents 定义为匹配 SubAgent spec 的 dictionaries,包含以下 fields:
FieldTypeDescription
namestrRequired。Subagent 的 unique identifier。Main agent 调用 task() tool 时使用此 name。Subagent name 会成为 AIMessages 和 streaming 的 metadata,有助于区分 agents。
descriptionstrRequired。描述此 subagent 做什么。应具体且 action-oriented。Main agent 使用它决定何时 delegate。
system_promptstrRequired。Subagent 的 instructions。Custom subagents 必须定义自己的 instructions。包含 tool usage guidance 和 output format requirements。
不从 main agent 继承。
toolslist[Callable]Optional。Subagent 可使用的 tools。保持 minimal,只包含所需内容。
默认从 main agent 继承。指定时会完全 override inherited tools。
modelstr | BaseChatModelOptional。Override main agent 的 model。省略时使用 main agent 的 model。
默认从 main agent 继承。你可以传入 model identifier string,例如 'openai:gpt-5.4'(使用 'provider:model' format),或 LangChain chat model object(await initChatModel("gpt-5.4")new ChatOpenAI({ model: "gpt-5.4" }))。
namestringRequired。Subagent 的 unique identifier。Main agent 调用 task() tool 时使用此 name。Subagent name 会成为 AIMessages 和 streaming 的 metadata,有助于区分 agents。
descriptionstringRequired。描述此 subagent 做什么。应具体且 action-oriented。Main agent 使用它决定何时 delegate。
systemPrompt stringRequired。Subagent 的 instructions。Custom subagents 必须定义自己的 instructions。包含 tool usage guidance 和 output format requirements。
不从 main agent 继承。
toolsStructuredTool[]Optional。Subagent 可使用的 tools。保持 minimal,只包含所需内容。
默认从 main agent 继承。指定时会完全 override inherited tools。
modelLanguageModelLike | stringOptional。Override main agent 的 model。省略时使用 main agent 的 model。
默认从 main agent 继承。你可以传入 model identifier string,例如 'openai:gpt-5.4'(使用 'provider:model' format),或 LangChain chat model object(await initChatModel("gpt-5.4")new ChatOpenAI({ model: "gpt-5.4" }))。
middlewareAgentMiddleware[]Optional。用于 custom behavior、logging 或 rate limiting 的 additional middleware。
不从 main agent 继承。
interruptOnRecord<string, boolean | InterruptOnConfig>Optional。为 specific tools 配置 human-in-the-loop。Options:TrueFalse,或带 allowed_decisionsInterruptOnConfig。需要 checkpointer。
默认从 main agent 继承。Subagent value 会 override default。
skillsstring[]Optional。Skills source paths。指定后,subagent 会从这些 directories 加载 skills(例如 ["/skills/research/", "/skills/web-search/"])。这让 subagents 可以拥有不同于 main agent 的 skill sets。
不从 main agent 继承。只有 general-purpose subagent 会继承 main agent 的 skills。当 subagent 有 skills 时,它会运行自己的 independent SkillsMiddleware instance。Skill state 是 fully isolated:subagent loaded skills 对 parent 不可见,反之亦然。
responseFormatResponseFormatOptional。Subagent 的 Structured output schema。设置后,parent 会接收 subagent result 的 JSON,而不是 free-form text。接受 Zod schemas、JSON schema objects、toolStrategy(...)providerStrategy(...)。请参阅 Structured output
permissionsFilesystemPermission[]Optional。Subagent 的 Filesystem permission rules。设置后,会完全 replaces parent agent 的 permissions。
默认从 main agent 继承。

CompiledSubAgent

对于 complex workflows,请将 prebuilt LangGraph graph 用作 CompiledSubAgent
FieldTypeDescription
namestrRequired。Subagent 的 unique identifier。Subagent name 会成为 AIMessages 和 streaming 的 metadata,有助于区分 agents。
descriptionstrRequired。此 subagent 做什么。
runnableRunnableRequired。Compiled LangGraph graph(必须先调用 .compile())。

Using SubAgent

import { tool } from "langchain";
import { TavilySearch } from "@langchain/tavily";
import { createDeepAgent, type SubAgent } from "deepagents";
import { z } from "zod";

const internetSearch = tool(
  async ({
    query,
    maxResults = 5,
    topic = "general",
    includeRawContent = false,
  }: {
    query: string;
    maxResults?: number;
    topic?: "general" | "news" | "finance";
    includeRawContent?: boolean;
  }) => {
    const tavilySearch = new TavilySearch({
      maxResults,
      tavilyApiKey: process.env.TAVILY_API_KEY,
      includeRawContent,
      topic,
    });
    return await tavilySearch._call({ query });
  },
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({
      query: z.string().describe("The search query"),
      maxResults: z.number().optional().default(5),
      topic: z
        .enum(["general", "news", "finance"])
        .optional()
        .default("general"),
      includeRawContent: z.boolean().optional().default(false),
    }),
  },
);

const researchSubagent: SubAgent = {
  name: "research-agent",
  description: "Used to research more in depth questions",
  systemPrompt: "You are a great researcher",
  tools: [internetSearch],
  model: "google-genai:gemini-3.5-flash", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  subagents,
});

Using CompiledSubAgent

对于更 complex 的 use cases,你可以用 CompiledSubAgent 提供 custom subagents。 你可以使用 LangChain 的 create_agent 创建 custom subagent,或使用 graph API 创建 custom LangGraph graph。 如果你正在创建 custom LangGraph graph,请确保 graph 有一个 名为 "messages" 的 state key
import { createDeepAgent, CompiledSubAgent } from "deepagents";
import { createAgent } from "langchain";

// Create a custom agent graph
const customGraph = createAgent({
  model: yourModel,
  tools: specializedTools,
  prompt: "You are a specialized agent for data analysis...",
});

// Use it as a custom subagent
const customSubagent: CompiledSubAgent = {
  name: "data-analyzer",
  description: "Specialized agent for complex data analysis tasks",
  runnable: customGraph,
};

const subagents = [customSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});

Streaming

Streaming tracing information 时,agents 的 names 会作为 metadata 中的 lc_agent_name 可用。 Review tracing information 时,你可以使用此 metadata 区分 data 来自哪个 agent。 下面的 example 创建一个名为 main-agent 的 deep agent,以及一个名为 research-agent 的 subagent:
import os
from typing import Literal
from tavily import TavilyClient
from deepagents import create_deep_agent

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": "google_genai:gemini-3.5-flash",  # Optional override, defaults to main agent model
}
subagents = [research_subagent]

agent = create_deep_agent(
    model="google_genai:gemini-3.5-flash",
    subagents=subagents,
    name="main-agent"
)
当你 prompt deep agent 时,由 subagent 或 deep agent 执行的所有 agent runs 都会在 metadata 中包含 agent name。 在此情况下,名为 "research-agent" 的 subagent 会在任何 associated agent run metadata 中包含 {'lc_agent_name': 'research-agent'} 显示 metadata 的 LangSmith example trace
LangSmith 中打开 run,以比较 supervisor trace 和每个 subagent run。按照 observability quickstart 完成设置。建议你同时设置 LangSmith Engine,它会监控 traces、检测问题,并提出修复建议。

Structured output

Subagents 支持 structured output,因此 parent agent 会接收 predictable、parseable JSON,而不是 free-form text。
Subagents 的 structured output 需要 deepagents>=1.8.4
在 subagent config 上传入 responseFormat。Subagent 完成后,其 structured response 会 JSON-serialized,并作为 ToolMessage content 返回给 parent agent。Schema 接受 createAgent 支持的任何内容:Zod schemas、JSON schema objects、toolStrategy(...)providerStrategy(...)
import { z } from "zod";
import { createDeepAgent } from "deepagents";

const ResearchFindings = z.object({
  summary: z.string().describe("Summary of findings"),
  confidence: z.number().describe("Confidence score from 0 to 1"),
  sources: z.array(z.string()).describe("List of source URLs"),
});

const researchSubagent = {
  name: "researcher",
  description: "Researches topics and returns structured findings",
  systemPrompt: "Research the given topic thoroughly. Return your findings.",
  tools: [webSearch],
  responseFormat: ResearchFindings,
};

const agent = createDeepAgent({
  model: "claude-sonnet-4-6",
  subagents: [researchSubagent],
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "Research recent advances in quantum computing" }],
});

// The parent's ToolMessage contains JSON-serialized structured data:
// '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'
没有 response_format 时,parent 会按原样接收 subagent 的 last message text。设置后,parent 始终会获得匹配 schema 的 valid JSON,这在 parent 需要 programmatically process result 或将其传给 downstream tools 时很有用。 有关 schema types 和 strategies(tool calling vs. provider-native)的完整 details,请参阅 Structured output

The general-purpose subagent

除了任何 user-defined subagents,每个 deep agent 始终都可以 access 一个 general-purpose subagent。此 subagent:

Override the general-purpose subagent

subagents list 中包含 name: "general-purpose" 的 subagent,以替换 default。使用此方式为 general-purpose subagent 配置不同 model、tools 或 system prompt:
import { createDeepAgent } from "deepagents";

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  tools: [internetSearch],
  subagents: [
    {
      name: "general-purpose",
      description: "General-purpose agent for research and multi-step tasks",
      systemPrompt: "You are a general-purpose assistant.",
      tools: [internetSearch],
      model: "openai:gpt-5.4",  // Different model for delegated tasks
    },
  ],
});
当你提供带 general-purpose name 的 subagent 时,default general-purpose subagent 不会被添加。你的 spec 会完全替换它。 若要完全移除 built-in general-purpose subagent 而不是替换它,请将 active harness profile 的 general-purpose subagent enabled flag 设置为 False

When to use it

General-purpose subagent 很适合在没有 specialized behavior 的情况下进行 context isolation。Main agent 可以将 complex multi-step task 委派给此 subagent,并获得 concise result,而不会受到 intermediate tool calls 的 bloat 影响。

Example

Main agent 不需要进行 10 次 web searches 并用 results 填满 context,而是委派给 general-purpose subagent:task(name="general-purpose", task="Research quantum computing trends")。Subagent 在内部执行所有 searches,并只返回 summary。

Skills inheritance

使用 create_deep_agent 配置 skills 时:
  • General-purpose subagent:自动从 main agent 继承 skills
  • Custom subagents:默认不继承 skills,请使用 skills parameter 给它们自己的 skills
只有配置了 skills 的 subagents 才会获得 SkillsMiddleware instance;没有 skills parameter 的 custom subagents 不会获得。存在该 middleware 时,skill state 在两个方向上都是 fully isolated:parent 的 skills 对 child 不可见,child 的 skills 也不会 propagate 回 parent。
import { createDeepAgent, SubAgent } from "deepagents";

// Research subagent with its own skills
const researchSubagent: SubAgent = {
  name: "researcher",
  description: "Research assistant with specialized skills",
  systemPrompt: "You are a researcher.",
  tools: [webSearch],
  skills: ["/skills/research/", "/skills/web-search/"],  // Subagent-specific skills
};

const agent = createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  skills: ["/skills/main/"],  // Main agent and GP subagent get these
  subagents: [researchSubagent],  // Gets only /skills/research/ and /skills/web-search/
});

Best practices

Write clear descriptions

Main agent 使用 descriptions 决定调用哪个 subagent。请保持具体: Good: "Analyzes financial data and generates investment insights with confidence scores" Bad: "Does finance stuff"

Keep system prompts detailed

包含关于如何 use tools 和 format outputs 的具体 guidance:
const researchSubagent = {
  name: "research-agent",
  description: "Conducts in-depth research using web search and synthesizes findings",
  systemPrompt: `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: [internetSearch],
};

Minimize tool sets

只给 subagents 它们需要的 tools。这会提升 focus 和 security:
// ✅ Good: Focused tool set
const emailAgent = {
  name: "email-sender",
  tools: [sendEmail, validateEmail],  // Only email-related
};

// ❌ Bad: Too many tools
const emailAgentBad = {
  name: "email-sender",
  tools: [sendEmail, webSearch, databaseQuery, fileUpload],  // Unfocused
};

Choose models by task

不同 models 擅长不同 tasks:
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "google_genai:gemini-3.5-flash",  // Large context for long documents
  },
  {
    name: "financial-analyst",
    description: "Analyzes financial data and market trends",
    systemPrompt: "You are an expert financial analyst...",
    tools: [getStockPrice, analyzeFundamentals],
    model: "gpt-5.4",  // Better for numerical analysis
  },
];

Return concise results

指示 subagents 返回 summaries,而不是 raw data:
const dataAnalyst = {
  systemPrompt: `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

为不同 domains 创建 specialized subagents:
import { createDeepAgent } from "deepagents";

const subagents = [
  {
    name: "data-collector",
    description: "Gathers raw data from various sources",
    systemPrompt: "Collect comprehensive data on the topic",
    tools: [webSearch, apiCall, databaseQuery],
  },
  {
    name: "data-analyzer",
    description: "Analyzes collected data for insights",
    systemPrompt: "Analyze data and extract key insights",
    tools: [statisticalAnalysis],
  },
  {
    name: "report-writer",
    description: "Writes polished reports from analysis",
    systemPrompt: "Create professional reports from insights",
    tools: [formatDocument],
  },
];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  systemPrompt: "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: subagents,
});
Workflow:
  1. Main agent 创建 high-level plan
  2. 将 data collection 委派给 data-collector
  3. 将 results 传给 data-analyzer
  4. 将 insights 发送给 report-writer
  5. 编译 final output
每个 subagent 都使用只聚焦于自身 task 的 clean context 工作。

Context management

当你使用 runtime context invoke parent agent 时,该 context 会自动 propagate 到所有 subagents。每个 subagent run 都会接收你在 parent invoke / ainvoke call 上传入的相同 runtime context。 这意味着在任何 subagent 内运行的 tools 都可以 access 你提供给 parent 的相同 context values:
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import type { ToolRuntime } from "@langchain/core/tools";
import { z } from "zod";

const contextSchema = z.object({
  userId: z.string(),
  sessionId: z.string(),
});

const getUserData = tool(
  async (input, runtime: ToolRuntime<unknown, typeof contextSchema>) => {
    const userId = runtime.context?.userId;
    return `Data for user ${userId}: ${input.query}`;
  },
  {
    name: "get_user_data",
    description: "Fetch data for the current user",
    schema: z.object({ query: z.string() }),
  }
);

const researchSubagent = {
  name: "researcher",
  description: "Conducts research for the current user",
  systemPrompt: "You are a research assistant.",
  tools: [getUserData],
};

const agent = createDeepAgent({
  model: "google_genai:gemini-3.5-flash",
  subagents: [researchSubagent],
  contextSchema,
});

// Context flows to the researcher subagent and its tools automatically
const result = await agent.invoke(
  { messages: [new HumanMessage("Look up my recent activity")] },
  { context: { userId: "user-123", sessionId: "abc" } },
);

Per-subagent context

所有 subagents 都会接收相同 parent context。若要传入特定于某个 subagent 的 configuration,请在 flat context mapping 中使用 namespaced keys(用 subagent name 作为 key prefix,例如 researcher:max_depth), 将这些 settings 建模为 context type 上的 separate fields:
import { tool } from "langchain";
import type { ToolRuntime } from "@langchain/core/tools";
import { z } from "zod";

const contextSchema = z.object({
  userId: z.string(),
  researcherMaxDepth: z.number().optional(),
  factCheckerStrictMode: z.boolean().optional(),
});

const result = await agent.invoke(
  { messages: [new HumanMessage("Research this and verify the claims")] },
  {
    context: {
      userId: "user-123",                        // shared by all agents
      "researcher:maxDepth": 3,                  // only for researcher
      "fact-checker:strictMode": true,           // only for fact-checker
    },
  },
);

const verifyClaim = tool(
  async (input, runtime: ToolRuntime<unknown, typeof contextSchema>) => {
    const strictMode = runtime.context?.factCheckerStrictMode ?? false;
    if (strictMode) {
      return strictVerification(input.claim);
    }
    return basicVerification(input.claim);
  },
  {
    name: "verify_claim",
    description: "Verify a factual claim",
    schema: z.object({ claim: z.string() }),
  }
);

Identifying which subagent called a tool

当 parent 和多个 subagents 共享同一个 tool 时,你可以使用 lc_agent_name metadata(与 streaming 中使用的值相同)来判断哪个 agent 发起了 call:
import { tool } from "langchain";
import type { ToolRuntime } from "@langchain/core/tools";

const sharedLookup = tool(
  async (input, runtime: ToolRuntime) => {
    const agentName = runtime.config?.metadata?.lc_agent_name;
    if (agentName === "fact-checker") {
      return strictLookup(input.query);
    }
    return generalLookup(input.query);
  },
  {
    name: "shared_lookup",
    description: "Look up information from various sources",
    schema: z.object({ query: z.string() }),
  }
);
你可以组合这两种 patterns:从 runtime.context 读取 agent-specific settings,并在 branching tool behavior 时从 runtime.config metadata 读取 lc_agent_name
const flexibleSearch = tool(
  async (input, runtime: ToolRuntime<unknown, typeof contextSchema>) => {
    const agentName = runtime.config?.metadata?.lc_agent_name ?? "unknown";
    const ctx = runtime.context;
    const maxResults =
      agentName === "researcher" ? ctx?.researcherMaxDepth ?? 5 : 5;
    const includeRaw = false;

    return performSearch(input.query, { maxResults, includeRaw });
  },
  {
    name: "flexible_search",
    description: "Search with agent-specific settings",
    schema: z.object({ query: z.string() }),
  }
);

Troubleshooting

Subagent not being called

Problem:Main agent 试图自己完成工作,而不是 delegating。 Solutions
  1. 让 descriptions 更具体:
    // ✅ Good
    { 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
    { name: "helper", description: "helps with stuff" }
    
  2. 指示 main agent delegate:
    const agent = createDeepAgent({
      systemPrompt: `...your instructions...
    
      IMPORTANT: For complex tasks, delegate to your subagents using the task() tool.
      This keeps your context clean and improves results.`,
      subagents: [...]
    });
    

Context still getting bloated

Problem:虽然使用了 subagents,但 context 仍然被填满。 Solutions
  1. 指示 subagent 返回 concise results:
    systemPrompt: `...
    
    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.`
    
  2. 对 large data 使用 filesystem:
    systemPrompt: `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 为 task 调用了不合适的 subagent。 Solution:在 descriptions 中清晰区分 subagents:
const subagents = [
  {
    name: "quick-researcher",
    description: "For simple, quick research questions that need 1-2 searches. Use when you need basic facts or definitions.",
  },
  {
    name: "deep-researcher",
    description: "For complex, in-depth research requiring multiple searches, synthesis, and analysis. Use for comprehensive reports.",
  }
];