서브에이전트

서브에이전트 (Subagents)

서브에이전트로 작업을 위임하고 컨텍스트를 깨끗하게 유지하는 방법을 알아보세요.

deep agent는 작업을 위임하기 위해 서브에이전트를 만들 수 있습니다. subagents 파라미터에 커스텀 서브에이전트를 지정할 수 있습니다. 서브에이전트는 컨텍스트 격리(주 에이전트의 컨텍스트를 깨끗하게 유지)와 특화된 지침 제공에 유용합니다.

이 페이지는 동기식 서브에이전트를 다룹니다. 여기서 감독자는 서브에이전트가 끝날 때까지 블로킹합니다. 오래 걸리는 작업, 병렬 작업 스트림, 또는 진행 중 조종과 취소가 필요한 경우는 Async subagents를 참조하세요.

graph TB
    Main[Main Agent] --> |task tool| Sub[Subagent]

    Sub --> Research[Research]
    Sub --> Code[Code]
    Sub --> General[General]

    Research --> |isolated work| Result[Final Result]
    Code --> |isolated work| Result
    General --> |isolated work| Result

    Result --> Main

왜 서브에이전트를 사용하나요? (Why use subagents?)

서브에이전트는 컨텍스트 비대 문제를 해결합니다. 에이전트가 큰 출력을 가진 도구(웹 검색, 파일 읽기, 데이터베이스 쿼리)를 사용하면 컨텍스트 창이 중간 결과로 빨리 채워집니다. 서브에이전트는 이 상세한 작업을 격리합니다. 주 에이전트는 그것을 만들어낸 수십 개의 도구 호출이 아니라 최종 결과만 받습니다.

서브에이전트를 사용할 때:

  • ✅ 주 에이전트의 컨텍스트를 어지럽힐 다단계 작업
  • ✅ 커스텀 지침이나 도구가 필요한 특화 도메인
  • ✅ 다른 모델 기능이 필요한 작업
  • ✅ 주 에이전트를 상위 수준 조정에 집중시키고 싶을 때

서브에이전트를 사용하지 않을 때:

  • ❌ 단순하고 단일 단계인 작업
  • ❌ 중간 컨텍스트를 유지해야 할 때
  • ❌ 오버헤드가 이점보다 클 때

구성 (Configuration)

subagents는 딕셔너리나 CompiledSubAgent 객체의 목록이어야 합니다. 두 가지 유형이 있습니다:

기본 서브에이전트 (Default subagent)

Deep Agents는 이미 general-purpose라는 이름의 동기식 서브에이전트를 제공하지 않는 한 자동으로 동기식 general-purpose 서브에이전트를 추가합니다.

general-purpose 서브에이전트는 기본적으로 파일 시스템 도구를 가지며 추가 도구/미들웨어로 커스터마이즈할 수 있습니다.

  • 그것을 대체하려면 general-purpose라는 이름의 자체 서브에이전트를 전달하세요.
  • 자동 추가 버전의 이름을 바꾸거나 재프롬프트하려면 활성 harness profile에서 general_purpose_subagent=GeneralPurposeSubagentProfile(...)을 설정하세요.
  • 그것을 비활성화하려면 아래 서브에이전트 없이 실행을 참조하세요.

서브에이전트 없이 실행 (Running without subagents)

task 도구 없이 에이전트를 실행하려면 두 가지를 하세요:

  1. 활성 harness profile에서 general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False)를 설정하세요.
  2. create_deep_agentsubagents=에 동기식 서브에이전트를 전달하지 마세요.

Deep Agents는 동기식 서브에이전트가 하나 이상 있을 때만 SubAgentMiddleware(그리고 task 도구)를 연결합니다. 기본값이나 호출자 제공 버전이 모두 없으면 에이전트는 위임 없이 실행됩니다.

비동기 서브에이전트는 영향받지 않습니다. Async subagents에서 설명하는 자체 미들웨어와 도구를 통해 흐릅니다.

여기서 `excluded_middleware`를 사용하지 마세요. `SubAgentMiddleware`는 필수 scaffolding이며 목록에 포함하면 `ValueError`가 발생합니다. `general_purpose_subagent.enabled = False` 노브가 지원되는 경로입니다.

커스텀 서브에이전트 (Custom subagents)

subagents 파라미터를 사용해 특정 도구를 가진 특화된 서브에이전트를 정의할 수 있습니다. 예를 들어 코드 리뷰어, 웹 리서처, 테스트 실행자로 사용할 수 있습니다.

대부분의 사용 사례에서는 SubAgent 딕셔너리로 서브에이전트를 정의하세요. 복잡한 워크플로에는 CompiledSubAgent를 사용하세요:

SubAgent (딕셔너리 기반)

SubAgent 스펙과 일치하는 딕셔너리로 서브에이전트를 정의하세요. 다음 필드를 가집니다:

필드 유형 설명
name string 필수. 서브에이전트의 고유 식별자. 주 에이전트는 task() 도구를 호출할 때 이 이름을 사용합니다. 서브에이전트 이름은 AIMessage와 스트리밍의 메타데이터가 되어 에이전트를 구분하는 데 도움이 됩니다.
description string 필수. 이 서브에이전트가 무엇을 하는지에 대한 설명. 구체적이고 행동 지향적으로 하세요. 주 에이전트는 언제 위임할지 결정할 때 이것을 사용합니다.
systemPrompt string mode: "isolated"(기본값)에 필수. 서브에이전트 지침. 커스텀 격리 서브에이전트는 자신만의 지침을 정의해야 합니다. 도구 사용 안내와 출력 형식 요구 사항을 포함하세요.
주 에이전트에서 상속되지 않습니다. mode: "fork"에서는 분기 전용 부록이 필요하지 않으면 이 필드를 생략하세요. Forked subagents를 참조하세요.
mode "isolated" | "fork" 선택. 컨텍스트 모드. 기본값은 "isolated"이며, 서브에이전트는 위임된 작업만 봅니다. 대신 부모의 대화와 시스템 프롬프트를 상속하려면 "fork"로 설정하세요. Forked subagents를 참조하세요.
tools StructuredTool[] 선택. 서브에이전트가 사용할 수 있는 도구. 최소로 유지하고 필요한 것만 포함하세요.
기본적으로 주 에이전트에서 상속합니다. 지정하면 상속된 도구를 완전히 덮어씁니다.
model LanguageModelLike | string 선택. 주 에이전트의 모델을 재정의합니다. 생략하면 주 에이전트의 모델을 사용합니다.
기본적으로 주 에이전트에서 상속합니다. 'openai:gpt-5.5' 같은 모델 식별자 문자열('provider:model' 형식)이나 LangChain 채팅 모델 객체(await initChatModel("gpt-5.5") 또는 new ChatOpenAI({ model: "gpt-5.5" }))를 전달할 수 있습니다.
middleware AgentMiddleware[] 선택. 커스텀 동작, 로깅, 속도 제한을 위한 추가 미들웨어.
주 에이전트에서 상속하지 않습니다. 동기식 서브에이전트 스택에 덧붙습니다.
interruptOn Record<string, boolean | InterruptOnConfig> 선택. 특정 도구에 대한 human-in-the-loop 구성. 옵션: True, False, 또는 allowed_decisions가 있는 InterruptOnConfig. 체크포인터가 필요합니다.
기본적으로 주 에이전트에서 상속합니다. 서브에이전트 값이 기본값을 재정의합니다.
skills string[] 선택. 스킬 소스 경로. 지정하면 서브에이전트가 이 디렉터리들에서 스킬을 로드합니다(예: ["/skills/research/", "/skills/web-search/"]). 이렇게 하면 서브에이전트가 주 에이전트와 다른 스킬 세트를 가질 수 있습니다.
주 에이전트에서 상속하지 않습니다. general-purpose 서브에이전트만 주 에이전트의 스킬을 상속합니다. 서브에이전트가 스킬을 가지면 자체 독립적인 SkillsMiddleware 인스턴스를 실행합니다. 스킬 상태는 완전히 격리됩니다. 서브에이전트의 로드된 스킬은 부모에게 보이지 않고, 그 반대도 마찬가지입니다.
responseFormat ResponseFormat 선택. 서브에이전트의 구조화된 출력 스키마. 설정하면 부모는 서브에이전트 결과를 자유 형식 텍스트 대신 JSON으로 받습니다. Zod 스키마, JSON 스키마 객체, toolStrategy(...), 또는 providerStrategy(...)를 받습니다. 구조화된 출력을 참조하세요.
permissions FilesystemPermission[] 선택. 서브에이전트의 파일 시스템 권한 규칙. 설정하면 부모 에이전트의 권한을 완전히 대체합니다.
기본적으로 주 에이전트에서 상속합니다.

CompiledSubAgent

복잡한 워크플로에는 사전 구축 LangGraph 그래프를 CompiledSubAgent로 사용하세요:

필드 유형 설명
name str 필수. 서브에이전트의 고유 식별자. 서브에이전트 이름은 AIMessage와 스트리밍의 메타데이터가 되어 에이전트를 구분하는 데 도움이 됩니다.
description str 필수. 이 서브에이전트가 하는 일.
runnable Runnable 필수. 컴파일된 LangGraph 그래프(먼저 .compile()을 호출해야 함).
mode "isolated" | "fork" 선택. 기본값은 "isolated". 부모의 메시지 기록을 상속하려면 "fork"로 설정. 컴파일된 그래프는 어느 쪽이든 자체 시스템 프롬프트를 유지합니다. Forked subagents를 참조하세요.

SubAgent 사용 (Using SubAgent)

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} 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: proces...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.6-flash", // Optional override, defaults to main agent model }; const subagents = [researchSubagent];

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


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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: proces...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: "openai:gpt-5.5", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  subagents,
});
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: proces...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: "anthropic:claude-sonnet-5", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  subagents,
});
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: proces...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: "openrouter:z-ai/glm-5.2", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  subagents,
});
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: proces...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: "fireworks:accounts/fireworks/models/glm-5p2", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  subagents,
});
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: proces...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: "baseten:zai-org/GLM-5.2", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

const agent = createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  subagents,
});
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: proces...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: "ollama:north-mini-code-1.0", // Optional override, defaults to main agent model
};
const subagents = [researchSubagent];

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

CompiledSubAgent 사용 (Using CompiledSubAgent)

더 복잡한 사용 사례에서는 CompiledSubAgent로 커스텀 서브에이전트를 제공할 수 있습니다. LangChain의 create_agent를 사용하거나 graph API로 커스텀 LangGraph 그래프를 만들어 커스텀 서브에이전트를 만들 수 있습니다.

커스텀 LangGraph 그래프를 만들 때 그래프에 "messages"라는 상태 키가 있는지 확인하세요:

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { CompiledSubAgent, createDeepAgent } from "deepagents"; import { createAgent } from "langchain"; import { tool } from "langchain"; import { z } from "zod";

const internetSearch = tool( async ({ query }: { query: string }) => search results for ${query}, { name: "internet_search", description: "Run a web search", schema: z.object({ query: z.string() }), }, );

const researchInstructions = "You are a research coordinator."; const yourModel = "google_genai:gemini-3.6-flash"; const specializedTools: never[] = [];

// 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.6-flash", tools: [internetSearch], systemPrompt: researchInstructions, subagents: subagents, });


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "openai:gpt-5.5",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "anthropic:claude-sonnet-5",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "openrouter:z-ai/glm-5.2",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "fireworks:accounts/fireworks/models/glm-5p2",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "baseten:zai-org/GLM-5.2",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});
import { CompiledSubAgent, createDeepAgent } from "deepagents";
import { createAgent } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

const researchInstructions = "You are a research coordinator.";
const yourModel = "google_genai:gemini-3.6-flash";
const specializedTools: never[] = [];

// 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: "ollama:north-mini-code-1.0",
  tools: [internetSearch],
  systemPrompt: researchInstructions,
  subagents: subagents,
});

포크된 서브에이전트 (Forked subagents)

기본적으로 서브에이전트는 mode: "isolated"로 실행됩니다. 즉, 여러분이 준 작업 설명만 보고 위임으로 이어진 대화에 대한 기억이 없습니다. 포크된 서브에이전트(mode: "fork")는 대신 부모의 전체 대화 기록과 정확한 시스템 프롬프트를 상속합니다.

서브에이전트의 작업이 부모가 이미 시작한 작업을 계속하는 것일 때 포크된 서브에이전트를 사용하세요. 예를 들어 부모가 이미 진단한 수정을 이어받는 워커 에이전트, 또는 인시던트 조사를 위한 포스트모템을 초안하는 서브에이전트가 있습니다. 포킹은 서브에이전트 자체에 설정하는 모드이므로, 이는 정의할 때 내리는 결정입니다.

%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 12, "bottom": 4}}}}%%
graph TD
    Message["'Review PR #482'"]
    Analysis["Parent already found:<br/>tokens logged in plaintext,<br/>no expiry check on refresh"]
    Delegate["Delegate: draft comments<br/>for the issues found"]
    Message --> Analysis --> Delegate

    subgraph Isolated["`**Isolated subagent**`"]
        IOut["Sees only the task description<br/>starts from nothing, re-reviews the diff"]
    end

    subgraph Forked["`**Forked subagent**`"]
        FOut["Sees parent history + continuation preamble<br/>already knows the issues, writes comments directly"]
    end

    Delegate -->|task description only| Isolated
    Delegate -->|parent history + continuation preamble| Forked

    classDef process fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710
    classDef output fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900
    class Message,Analysis,Delegate process
    class IOut,FOut output
서브에이전트 포킹은 `deepagents>=1.13.3`이 필요합니다. [**베타**](/oss/javascript/versioning) 상태이며, API와 동작은 릴리스 간에 바뀔 수 있습니다.

포크된 서브에이전트 구성 (Configure a forked subagent)

SubAgentmode: "fork"를 설정하세요(기본값은 mode: "isolated"). 모든 SubAgent 필드를 사용할 수 있습니다: name, description, tools, model, middleware, interruptOn, permissions, responseFormat.

skills는 제공되면 거부됩니다. systemPrompt는 허용되며 부모의 상속 프롬프트에 부록으로 덧붙지만, 그렇게 하면 보통 프롬프트 캐시가 깨지므로 fork 전용 지침에 특별한 필요가 없으면 설정하지 않은 채 두세요.

import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const readDiff = tool(
  async ({ path }: { path: string }) => `diff for ${path}`,
  {
    name: "read_diff",
    description: "Read a file's diff",
    schema: z.object({ path: z.string() }),
  },
);

const commentWriter = {
  name: "comment-writer",
  description: "Continues an in-progress PR review and drafts review comments",
  mode: "fork" as const,
  tools: [readDiff],
};

const agent = await createDeepAgent({
  model: "anthropic:claude-sonnet-4-6",
  tools: [readDiff],
  subagents: [commentWriter],
});

const result = await agent.invoke({
  messages: [
    {
      role: "user",
      content:
        "Review PR #482 and hand it off to comment-writer to draft comments for the issues found",
    },
  ],
});

작동 방식 (How it works)

포크는 새로운 작업 설명을 얻지 않습니다. 한 가지 변경과 함께 부모의 자체 대화를 얻습니다. 그것에 위임한 마지막 호출은 제거되고 위의 메시지가 새로운 요청이 아니라 연속임을 표시하는 짧은 도입부로 대체됩니다. 포크가 끝나면 그 답변은 일반 도구 결과로 돌아오고, 부모는 중단한 지점에서 바로 이어갑니다.

// What the parent has, right before delegating
[
  new HumanMessage("Review the changes in PR #482"),
  new AIMessage({ content: "", tool_calls: [{ name: "read_diff", args: { path: "src/auth/session.py" } }] }),
  new ToolMessage({ content: "- session tokens are logged in plaintext\n- no expiry check on refresh", tool_call_id: "1" }),
  new AIMessage("Found two issues: session tokens are logged in plaintext, and there's no expiry check on refresh."),
  new HumanMessage("Good catch. Draft review comments for those."),
  new AIMessage({ content: "", tool_calls: [{ name: "task", args: { subagentType: "comment-writer", description: "Draft review comments for the two issues found above." } }] }),
]

// What the fork actually sees
[
  new HumanMessage("Review the changes in PR #482"),
  new AIMessage({ content: "", tool_calls: [{ name: "read_diff", args: { path: "src/auth/session.py" } }] }),
  new ToolMessage({ content: "- session tokens are logged in plaintext\n- no expiry check on refresh", tool_call_id: "1" }),
  new AIMessage("Found two issues: session tokens are logged in plaintext, and there's no expiry check on refresh."),
  new HumanMessage("Good catch. Draft review comments for those."),
  new HumanMessage("Continuing as the subagent that was just invoked. Draft review comments for the two issues found above."),
]

부모의 정확한 접두사를 재사용한다는 것은 포크가 콜드 시작 대신 부모의 프롬프트 캐시를 재사용할 수 있다는 뜻이기도 하지만, 부모와 다른 도구 사용은 여전히 미스가 됩니다.

CompiledSubAgentmode: "fork"를 지원하지만, 그래프가 이미 빌드되어 있으므로 자체 시스템 프롬프트를 유지합니다.

포킹을 언제 사용하나요? (When to use forking)

다음 차원에 따라 isolated와 forked 모드를 비교하세요:

차원 Isolated(기본값) Forked
컨텍스트 여러분이 전달한 작업 설명만 부모의 전체 대화 기록과 시스템 프롬프트
시스템 프롬프트와 스킬 서브에이전트에 설정 스킬 설정 불가; 시스템 프롬프트는 부모에 덧붙음(캐싱을 깨므로 보통 설정하지 않음)
다른 서브에이전트 호출 task 도구 사용 가능 task 사용 불가; 스스로 작업을 끝내야 함
가장 좋은 용도 사전 컨텍스트가 거의 필요 없는 집중 작업 부모가 이미 시작한 조사 계속하기

동적 서브에이전트 (Dynamic subagents)

기본적으로 주 에이전트는 task 도구 호출을 통해 서브에이전트에 위임합니다(한 턴에 여러 개를 발행해 병렬로 실행할 수 있습니다). 인터프리터가 연결되면 에이전트는 대신 코드에서 서브에이전트를 파견할 수 있습니다. 루프, 분기, 병렬 배치를 사용해 많은 항목에 걸쳐 작업을 펼치고 결과를 프로그래밍 방식으로 종합합니다. 이것을 dynamic subagents라고 합니다.

작업이 많은 독립 단위에 걸쳐 있을 때(디렉터리의 모든 파일 검토, 티켓 배치 분류), 여러 관점이 필요할 때, 또는 재귀 분석의 이점이 있을 때 동적 서브에이전트를 사용하세요.

동적 서브에이전트는 [**베타**](/oss/javascript/versioning) 상태인 인터프리터 런타임을 사용합니다. API와 수명 주기 동작은 릴리스 간에 바뀔 수 있습니다.

동적 서브에이전트 활성화 (Enable dynamic subagents)

에이전트가 서브에이전트와 인터프리터 미들웨어를 모두 가지는 즉시 동적 서브에이전트를 사용할 수 있습니다. QuickJS 인터프리터 패키지를 설치한 뒤 에이전트에 CodeInterpreterMiddleware를 추가하세요.

```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} npm install deepagents @langchain/quickjs ```
pnpm add deepagents @langchain/quickjs
yarn add deepagents @langchain/quickjs
```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { createDeepAgent } from "deepagents"; import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({ model: "google-genai:gemini-3.6-flash", subagents: [{ name: "reviewer", description: "Reviews code for security issues, citing lines and severity", systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.", }], middleware: [createCodeInterpreterMiddleware()], });


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "openai:gpt-5.5",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "anthropic:claude-sonnet-5",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "openrouter:z-ai/glm-5.2",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "fireworks:accounts/fireworks/models/glm-5p2",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "baseten:zai-org/GLM-5.2",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
  model: "ollama:north-mini-code-1.0",
  subagents: [{
    name: "reviewer",
    description: "Reviews code for security issues, citing lines and severity",
    systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
  }],
  middleware: [createCodeInterpreterMiddleware()],
});
에이전트가 서브에이전트와 인터프리터 미들웨어를 가질 때마다 동적 서브에이전트 파견이 기본적으로 켜집니다. 일반 `task` 도구 경로를 통한 파견을 요구하려면 `createCodeInterpreterMiddleware({ subagents: false })`를 전달하세요.

동적 조정 트리거 (Trigger dynamic orchestration)

동적 파견은 암시적입니다. 에이전트는 per-call 플래그가 아니라 작업의 형태에 따라 코드에서 작업을 펼칠지 결정합니다.

**"workflow"라는 단어는 유용한 트리거입니다.** 내장 인터프리터 시스템 프롬프트는 "workflow"를 코드에서 `task()`로 서브에이전트를 파견하며 인터프리터를 통해 작업을 조직하라는 신호로 취급합니다. 요청을 "workflow"로 표현하는 것은 동적 조정에 옵트인하기 위해 당길 수 있는 의도적인 레버입니다. 코드에서 작업을 펼치길 원할 때 포함하세요. 단일 직접 위임에는 요청을 평범하게 표현하세요.

예를 들어 요청을 "workflow"로 표현하면 코드에서 팬아웃에 옵트인합니다:

const result = await agent.invoke({
  messages: [{ role: "user", content: "Run a workflow that reviews every file in src/routes/ and summarizes the top risks." }],
});

구성, 고급 조정 패턴, 안전 참고 사항은 Dynamic subagents를 참조하세요.

코딩 에이전트와 함께 사용 (Use with a coding agent)

동적 서브에이전트를 시도하는 가장 빠른 방법은 Deep Agent 위에 구축된 LangChain 터미널 코딩 에이전트인 dcode를 사용하는 것입니다. 코드 인터프리터가 활성화되어 배송되므로 동적 서브에이전트가 설정 없이 바로 작동합니다.

dcode 설치:

curl -LsSf https://langch.in/dcode | bash

실행:

dcode

동적 서브에이전트를 트리거하려면 "workflow"를 요청하세요. 작업 자체를 스스로 처리하거나 네이티브 task 도구로 팬아웃을 관리하는 대신, 에이전트가 내장 task() 전역을 호출하는 조정 스크립트를 작성해 코드 인터프리터에서 실행합니다. 예: "Run a workflow to review every file in src/ for SQL injection."

서브에이전트가 생성되면 dcode는 파견별 단계로 그룹화된 동적 서브에이전트 패널에서 그것들을 실시간으로 보여줍니다.

파견별 단계로 그룹화된 생성된 서브에이전트를 보여주는 dcode 동적 서브에이전트 패널

dcode는 이것을 시도하는 가장 빠른 방법이지만, 선택한 코딩 에이전트에서 ACP를 통해(예: Zed) 동적 서브에이전트를 사용할 수도 있습니다.

스트리밍 (Streaming)

Deep Agents는 코디네이터와 위임된 각 서브에이전트 양쪽에서의 스트리밍 업데이트를 지원합니다.

streamEvents를 사용해 타입이 지정된 프로젝션을 얻으세요. 서브에이전트, 메시지, 도구 호출, 값에 대한 별도의 이터레이터가 있어 각각 독립적으로 소비할 수 있습니다.

서브에이전트 진행 상황 스트리밍 (Stream subagent progress)

가장 간단한 패턴은 stream.subagents를 반복해 각 위임된 작업이 시작, 실행, 완료되는 것을 추적하는 것입니다. 각 서브에이전트 핸들은 .name, .messages, .tool_calls, .output을 노출합니다.

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({ model: "google-genai:gemini-3.6-flash", name: "main-agent", systemPrompt: "You are a project coordinator with no research knowledge. " + "For every user request, you must call the task() tool with " + "subagent_type set to research-agent. Never answer research " + "questions yourself.", subagents: [ { name: "research-agent", description: "Delegate research to this subagent. Give one topic at a time.", systemPrompt: "You are a great researcher. Return a brief summary.", }, ], });

async function streamSubagentProgress() { const stream = await agent.streamEvents( { messages: [ { role: "user", content: "Research one recent advance in quantum computing.", }, ], }, { version: "v3" }, );

const coordinatorMessages: string[] = [];
const subagentHandles: { name: string }[] = [];

await Promise.all([
  (async () => {
    for await (const message of stream.messages) {
      const text = await message.text;
      console.log("[coordinator]", text);
      coordinatorMessages.push(text);
    }
  })(),
  (async () => {
    for await (const subagent of stream.subagents) {
      console.log(`[${subagent.name}] started`);
      subagentHandles.push({ name: subagent.name });
      for await (const message of subagent.messages) {
        console.log(`[${subagent.name}]`, await message.text);
      }
    }
  })(),
  stream.output,
]);

return { coordinatorMessages, subagentHandles };

}


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "openai:gpt-5.5",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "anthropic:claude-sonnet-5",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "openrouter:z-ai/glm-5.2",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "fireworks:accounts/fireworks/models/glm-5p2",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "baseten:zai-org/GLM-5.2",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}
import { createDeepAgent } from "deepagents";

const agent = createDeepAgent({
  model: "ollama:north-mini-code-1.0",
  name: "main-agent",
  systemPrompt:
    "You are a project coordinator with no research knowledge. " +
    "For every user request, you must call the task() tool with " +
    "subagent_type set to research-agent. Never answer research " +
    "questions yourself.",
  subagents: [
    {
      name: "research-agent",
      description:
        "Delegate research to this subagent. Give one topic at a time.",
      systemPrompt: "You are a great researcher. Return a brief summary.",
    },
  ],
});

async function streamSubagentProgress() {
  const stream = await agent.streamEvents(
    {
      messages: [
        {
          role: "user",
          content: "Research one recent advance in quantum computing.",
        },
      ],
    },
    { version: "v3" },
  );

  const coordinatorMessages: string[] = [];
  const subagentHandles: { name: string }[] = [];

  await Promise.all([
    (async () => {
      for await (const message of stream.messages) {
        const text = await message.text;
        console.log("[coordinator]", text);
        coordinatorMessages.push(text);
      }
    })(),
    (async () => {
      for await (const subagent of stream.subagents) {
        console.log(`[${subagent.name}] started`);
        subagentHandles.push({ name: subagent.name });
        for await (const message of subagent.messages) {
          console.log(`[${subagent.name}]`, await message.text);
        }
      }
    })(),
    stream.output,
  ]);

  return { coordinatorMessages, subagentHandles };
}

LangSmith 추적 (LangSmith tracing)

deep agent가 실행되는 동안 서브에이전트나 코디네이터가 실행한 모든 실행은 메타데이터의 lc_agent_name 키 아래에 에이전트 이름을 가집니다. 예: {'lc_agent_name': 'research-agent'}. 이를 통해 LangSmith에서 서브에이전트별로 실행을 식별하고 필터링할 수 있습니다.

메타데이터를 보여주는 LangSmith 예시 추적 코디네이터 추적과 각 서브에이전트 실행을 비교하려면 [LangSmith](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=oss-deepagents-subagents)에서 실행을 여세요. 설정은 [observability quickstart](/langsmith/observability-quickstart)를 참조하세요. 또한 추적을 모니터링하고, 문제를 감지하고, 수정을 제안하는 [LangSmith Engine](/langsmith/engine)도 설정하는 것을 권장합니다.

LangSmith에서 서브에이전트별 필터링 (Filter by subagent in LangSmith)

각 서브에이전트의 name이 그것이 생성하는 모든 실행의 lc_agent_name 메타데이터 키에 기록되므로, LangSmith의 메타데이터 필터링을 사용해 특정 서브에이전트의 모든 실행을 격리할 수 있습니다. 디버깅, 모니터링, 또는 시간 경과에 따른 서브에이전트 동작 비교에 유용합니다.

LangSmith UI에서 필터링 (Filter in the LangSmith UI)

  1. LangSmith에서 추적 프로젝트를 엽니다.
  2. Tracing 프로젝트 페이지에서 뷰를 Runs로 전환해 개별 스팬을 봅니다.
  3. Add filter를 클릭하고 Metadata를 선택합니다.
  4. Keylc_agent_name으로, Value를 서브에이전트 이름(예: coordinator)으로 설정합니다.
lc_agent_name을 coordinator로 설정한 메타데이터 필터가 있는 LangSmith Runs 뷰

이렇게 하면 그 서브에이전트가 생성한 실행만 표시됩니다. 재사용을 위해 필터를 이름 있는 뷰로 저장할 수 있습니다. 필터링 옵션의 전체 레퍼런스는 Filter traces를 참조하세요.

SDK로 프로그래밍 방식 필터링 (Filter programmatically with the SDK)

LangSmith 필터 쿼리 언어에서 has 비교자를 사용해 메타데이터 키-값 쌍으로 실행을 일치시키세요:

from langsmith import Client

client = Client()

runs = client.list_runs(
    project_name="<your-project>",
    filter='has(metadata, \'{"lc_agent_name": "research-agent"}\')',
)

for run in runs:
    print(run.name, run.start_time, run.status)

모든 이름 있는 서브에이전트(주 에이전트 제외)의 실행을 가져오려면 lc_agent_name 키가 아예 있는 실행을 필터링하세요:

runs = client.list_runs(
    project_name="<your-project>",
    filter="has(metadata, 'lc_agent_name')",
)

전체 필터 쿼리 언어 레퍼런스는 Trace query syntax를 참조하세요.

구조화된 출력 (Structured output)

서브에이전트는 구조화된 출력을 지원하므로 부모 에이전트는 자유 형식 텍스트 대신 예측 가능하고 파싱 가능한 JSON을 받습니다.

서브에이전트의 구조화된 출력은 `deepagents>=1.8.4`가 필요합니다.

서브에이전트 구성에 responseFormat을 전달하세요. 서브에이전트가 끝나면 그 구조화된 응답은 JSON 직렬화되어 부모 에이전트에게 ToolMessage 콘텐츠로 반환됩니다. 스키마는 createAgent가 지원하는 무엇이든 받습니다: Zod 스키마, JSON 스키마 객체, toolStrategy(...), 또는 providerStrategy(...).

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { z } from "zod"; import { createDeepAgent } from "deepagents"; import { tool } from "langchain";

const webSearch = tool( async ({ query }: { query: string }) => web results for ${query}, { name: "web_search", description: "Search the web", schema: z.object({ query: z.string() }), }, );

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: "google-genai:gemini-3.6-flash", 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://..."]}'


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "openai:gpt-5.5",
  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://..."]}'
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "anthropic:claude-sonnet-5",
  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://..."]}'
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "openrouter:z-ai/glm-5.2",
  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://..."]}'
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "fireworks:accounts/fireworks/models/glm-5p2",
  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://..."]}'
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "baseten:zai-org/GLM-5.2",
  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://..."]}'
import { z } from "zod";
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";

const webSearch = tool(
  async ({ query }: { query: string }) => `web results for ${query}`,
  {
    name: "web_search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  },
);

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: "ollama:north-mini-code-1.0",
  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://..."]}'
이 예시의 공개 LangSmith 실행을 엽니다.

response_format 없이 부모는 서브에이전트의 마지막 메시지 텍스트를 그대로 받습니다. 그것이 있으면 부모는 항상 스키마와 일치하는 유효한 JSON을 받습니다. 이는 부모가 결과를 프로그래밍 방식으로 처리하거나 다운스트림 도구에 전달해야 할 때 유용합니다.

스키마 유형과 전략(도구 호출 vs 프로바이더 네이티브)에 대한 자세한 내용은 구조화된 출력을 참조하세요.

general-purpose 서브에이전트 (The general-purpose subagent)

사용자 정의 서브에이전트 외에도 모든 deep agent는 항상 general-purpose 서브에이전트를 사용할 수 있습니다. 이 서브에이전트는:

  • 프로파일 오버레이가 적용된 자체 기본 시스템 프롬프트를 사용합니다
  • 모든 같은 도구에 접근할 수 있습니다
  • 같은 모델을 사용합니다(재정의되지 않는 한)
  • 주 에이전트에서 스킬을 상속합니다(스킬이 구성된 경우)

general-purpose 서브에이전트 재정의 (Override the general-purpose subagent)

subagents 목록에 name: "general-purpose"가 있는 서브에이전트를 포함해 기본값을 대체하세요. general-purpose 서브에이전트에 다른 모델, 도구, 또는 시스템 프롬프트를 구성하는 데 사용하세요:

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { createDeepAgent } from "deepagents"; import { tool } from "langchain"; import { z } from "zod";

const internetSearch = tool( async ({ query }: { query: string }) => search results for ${query}, { name: "internet_search", description: "Run a web search", schema: z.object({ query: z.string() }), }, );

// Main agent uses Gemini; general-purpose subagent uses GPT const agent = await createDeepAgent({ model: "google-genai:gemini-3.6-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.5", // Different model for delegated tasks }, ], });


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "openai:gpt-5.5",
  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.5", // Different model for delegated tasks
    },
  ],
});
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "anthropic:claude-sonnet-5",
  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.5", // Different model for delegated tasks
    },
  ],
});
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "openrouter:z-ai/glm-5.2",
  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.5", // Different model for delegated tasks
    },
  ],
});
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "fireworks:accounts/fireworks/models/glm-5p2",
  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.5", // Different model for delegated tasks
    },
  ],
});
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "baseten:zai-org/GLM-5.2",
  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.5", // Different model for delegated tasks
    },
  ],
});
import { createDeepAgent } from "deepagents";
import { tool } from "langchain";
import { z } from "zod";

const internetSearch = tool(
  async ({ query }: { query: string }) => `search results for ${query}`,
  {
    name: "internet_search",
    description: "Run a web search",
    schema: z.object({ query: z.string() }),
  },
);

// Main agent uses Gemini; general-purpose subagent uses GPT
const agent = await createDeepAgent({
  model: "ollama:north-mini-code-1.0",
  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.5", // Different model for delegated tasks
    },
  ],
});

general-purpose 이름의 서브에이전트를 제공하면 기본 general-purpose 서브에이전트는 추가되지 않습니다. 스펙이 완전히 대체합니다.

대체하는 대신 내장 general-purpose 서브에이전트를 완전히 제거하려면 활성 harness profile의 general-purpose 서브에이전트 enabled 플래그를 False로 설정하세요.

언제 사용하나요? (When to use it)

general-purpose 서브에이전트는 특화된 동작 없이 컨텍스트 격리에 이상적입니다. 주 에이전트는 복잡한 다단계 작업을 이 서브에이전트에 위임하고 중간 도구 호출의 비대 없이 간결한 결과를 돌려받을 수 있습니다.

주 에이전트가 10번의 웹 검색을 하고 결과로 컨텍스트를 채우는 대신, general-purpose 서브에이전트에 위임합니다: `task(name="general-purpose", task="Research quantum computing trends")`. 서브에이전트는 모든 검색을 내부에서 수행하고 요약만 반환합니다.

스킬 상속 (Skills inheritance)

create_deep_agent스킬을 구성할 때:

  • General-purpose 서브에이전트: 주 에이전트에서 스킬을 자동으로 상속
  • 커스텀 서브에이전트: 기본적으로 스킬을 상속하지 않음 — skills 파라미터를 사용해 자신만의 스킬을 부여
스킬로 구성된 서브에이전트만 `SkillsMiddleware` 인스턴스를 얻습니다. `skills` 파라미터가 없는 커스텀 서브에이전트는 얻지 못합니다. 존재할 때 스킬 상태는 양방향으로 완전히 격리됩니다. 부모의 스킬은 자식에게 보이지 않고, 자식의 스킬은 부모로 다시 전파되지 않습니다.
import { createDeepAgent } from "deepagents";

const researchSubagent = {
  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 = await createDeepAgent({
  model: "google_genai:gemini-3.6-flash",
  skills: ["/skills/main/"], // Main agent and GP subagent get these
  subagents: [researchSubagent], // Researcher gets only its own skills
});

모범 사례 (Best practices)

명확한 설명 작성 (Write clear descriptions)

주 에이전트는 설명을 사용해 어떤 서브에이전트를 호출할지 결정합니다. 구체적으로 하세요:

좋음: "Analyzes financial data and generates investment insights with confidence scores"

나쁨: "Does finance stuff"

시스템 프롬프트를 상세하게 유지 (Keep system prompts detailed)

도구 사용 방법과 출력 형식에 대한 구체적인 안내를 포함하세요:

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)

서브에이전트에게 필요한 도구만 주세요. 집중도와 보안이 향상됩니다:

// ✅ 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)

서로 다른 모델은 서로 다른 작업에 뛰어납니다:

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} 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.6-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: "openai:gpt-5.5", // Better for numerical analysis }, ]; ```
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "openai:gpt-5.5", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "anthropic:claude-sonnet-5", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "openrouter:z-ai/glm-5.2", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "fireworks:accounts/fireworks/models/glm-5p2", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "baseten:zai-org/GLM-5.2", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];
const subagents = [
  {
    name: "contract-reviewer",
    description: "Reviews legal documents and contracts",
    systemPrompt: "You are an expert legal reviewer...",
    tools: [readDocument, analyzeContract],
    model: "ollama:north-mini-code-1.0", // 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: "openai:gpt-5.5", // Better for numerical analysis
  },
];

간결한 결과 반환 (Return concise results)

서브에이전트에게 원시 데이터가 아니라 요약을 반환하라고 지시하세요:

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)

서로 다른 도메인을 위한 특화 서브에이전트를 만드세요:

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} 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.6-flash", systemPrompt: "You coordinate data analysis and reporting. Use subagents for specialized tasks.", subagents: subagents, });


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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: "openai:gpt-5.5",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: 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: "anthropic:claude-sonnet-5",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: 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: "openrouter:z-ai/glm-5.2",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: 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: "fireworks:accounts/fireworks/models/glm-5p2",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: 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: "baseten:zai-org/GLM-5.2",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: 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: "ollama:north-mini-code-1.0",
  systemPrompt:
    "You coordinate data analysis and reporting. Use subagents for specialized tasks.",
  subagents: subagents,
});

워크플로:

  1. 주 에이전트가 상위 수준 계획을 만듭니다
  2. 데이터 수집을 data-collector에 위임
  3. 결과를 data-analyzer에 전달
  4. 인사이트를 report-writer에 전송
  5. 최종 출력을 편집

각 서브에이전트는 자신의 작업에만 집중된 깨끗한 컨텍스트로 작동합니다.

컨텍스트 관리 (Context management)

부모 에이전트를 런타임 컨텍스트로 호출하면 그 컨텍스트가 자동으로 모든 서브에이전트로 전파됩니다. 각 서브에이전트 실행은 부모 invoke / ainvoke 호출에서 전달한 것과 같은 런타임 컨텍스트를 받습니다.

즉 모든 서브에이전트 안에서 실행되는 도구가 부모에 제공한 것과 같은 컨텍스트 값에 접근할 수 있습니다:

```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} 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.6-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" } }, );


```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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: "openai:gpt-5.5",
  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" } },
);
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: "anthropic:claude-sonnet-5",
  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" } },
);
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: "openrouter:z-ai/glm-5.2",
  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" } },
);
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: "fireworks:accounts/fireworks/models/glm-5p2",
  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" } },
);
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: "baseten:zai-org/GLM-5.2",
  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" } },
);
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: "ollama:north-mini-code-1.0",
  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)

모든 서브에이전트는 같은 부모 컨텍스트를 받습니다. 특정 서브에이전트에 고유한 구성을 전달하려면 평평한 context 매핑에서 네임스페이스 키(그 접두사에 서브에이전트 이름, 예: researcher:max_depth)를 사용하거나, 컨텍스트 유형의 별도 필드로 그 설정을 모델링하세요:

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

같은 도구가 부모와 여러 서브에이전트 사이에서 공유될 때 lc_agent_name 메타데이터(스트리밍에서 사용하는 것과 같은 값)를 사용해 호출을 시작한 에이전트를 결정할 수 있습니다:

import { tool } from "langchain";
import type { ToolRuntime } from "@langchain/core/tools";
import { z } from "zod";

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() }),
  },
);

두 패턴을 결합할 수 있습니다. 도구 동작을 분기할 때 runtime.context에서 에이전트 특정 설정을 읽고 runtime.config 메타데이터에서 lc_agent_name을 읽으세요.

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

문제: 주 에이전트가 위임하는 대신 스스로 작업을 하려고 함.

해결책:

  1. 설명을 더 구체적으로 만들기:

    // ✅ Good
    const goodDescription = {
      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
    const badDescription = {
      name: "helper",
      description: "helps with stuff",
    };
    
  2. 주 에이전트에게 위임하라고 지시:

    import { createDeepAgent } from "deepagents";
    
    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: [
        {
          name: "research-agent",
          description: "Conducts research",
          systemPrompt: "You are a researcher.",
        },
      ],
    });
    

컨텍스트가 여전히 비대해짐 (Context still getting bloated)

문제: 서브에이전트를 사용하는데도 컨텍스트가 채워짐.

해결책:

  1. 서브에이전트에게 간결한 결과를 반환하라고 지시:

    const 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. 큰 데이터에는 파일 시스템 사용:

    const filesystemPrompt = `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)

문제: 주 에이전트가 작업에 부적절한 서브에이전트를 호출함.

해결책: 설명에서 서브에이전트를 명확히 구분하세요:

const subagents = [
  {
    name: "quick-researcher",
    description:
      "For simple, quick research questions that need 1-2 searches. Use when you need basic facts or definitions.",
    systemPrompt: "You are the quick-researcher subagent.",
  },
  {
    name: "deep-researcher",
    description:
      "For complex, in-depth research requiring multiple searches, synthesis, and analysis. Use for comprehensive reports.",
    systemPrompt: "You are the deep-researcher subagent.",
  },
];

더 알아보기