ag2-subagent-delegation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSubagent delegation
子代理委派
When to use
适用场景
- "Coordinator + specialists" — a parent agent should hand parts of a task to a research agent, math agent, etc.
- "Fan out then collect" — multi-part questions where each part is independent and parallel execution saves wall time.
- "Self-delegation" — one agent breaks complex work into focused sub-tasks for itself.
- "协调者+专家"——父代理应将任务的部分内容交给研究代理、数学代理等处理。
- "扇出后收集"——多部分问题,每个部分相互独立,并行执行可节省时间。
- "自我委派"——单个代理将复杂工作拆分为专注的子任务自行处理。
Two patterns
两种模式
| Pattern | Reach for it when | API |
|---|---|---|
Auto-injected | Lightweight self-delegation, dynamic fan-out, parallel sub-questions | |
| Distinct named delegates the LLM should reason about ("call the researcher", "call the writer") | Wrap a child Agent as a tool on the parent |
The two compose — a coordinator can have both.
| 模式 | 适用场景 | API |
|---|---|---|
自动注入的 | 轻量级自我委派、动态扇出、并行子问题 | 父代理配置 |
| 需由LLM明确调用的独立命名代理(如"调用研究代理"、"调用写作代理") | 将子Agent包装为父代理的工具 |
两种模式可组合使用——协调者可同时具备两种能力。
Pattern 1 — auto-injected run_subtasks
run_subtasks模式1——自动注入的run_subtasks
run_subtasksSubtask tools are off by default (). Opt in with and the agent gains:
tasks=Falsetasks=TaskConfig(...)- — one isolated sub-task agent.
run_subtask(task: str) - — fan out multiple in one tool call (default concurrent).
run_subtasks(tasks: list[str], parallel: bool = True)
python
from ag2 import Agent, TaskConfig
from ag2.config import GeminiConfig
config = GeminiConfig(model="gemini-3-flash-preview")
coordinator = Agent(
"coordinator",
prompt=(
"You answer multi-part questions by dispatching run_subtasks "
"with parallel=True. Use one tool call with every sub-question "
"packed into the 'tasks' list."
),
config=config,
tasks=TaskConfig(), # opt in
)
reply = await coordinator.ask(
"In one run_subtasks call, answer: "
"(a) tallest waterfall, (b) Eiffel Tower year, (c) boiling point of nitrogen."
)TaskConfigpython
@dataclass
class TaskConfig:
config: ModelConfig | None = None # falls back to parent's config
prompt: str = "You are a task agent..."
include_tools: Iterable[str] | None = None # None = inherit all parent tools
exclude_tools: Iterable[str] = ()
extra_tools: Iterable[Callable | Tool] = ()Common shape — cheaper model for sub-tasks, narrow tool surface:
python
TaskConfig(
config=worker_config, # smaller model
prompt="You are a focused worker; one step only.",
include_tools=["search", "fetch_url"], # don't expose `summarize` to children
)Sub-task agents are built with — they never gain tools themselves. Recursive delegation is structurally impossible; no depth limit needed.
tasks=Falserun_subtask子任务工具默认关闭()。通过启用后,代理将获得:
tasks=Falsetasks=TaskConfig(...)- ——创建一个独立的子任务代理。
run_subtask(task: str) - ——一次工具调用中执行多个子任务(默认并发执行)。
run_subtasks(tasks: list[str], parallel: bool = True)
python
from ag2 import Agent, TaskConfig
from ag2.config import GeminiConfig
config = GeminiConfig(model="gemini-3-flash-preview")
coordinator = Agent(
"coordinator",
prompt=(
"You answer multi-part questions by dispatching run_subtasks "
"with parallel=True. Use one tool call with every sub-question "
"packed into the 'tasks' list."
),
config=config,
tasks=TaskConfig(), # opt in
)
reply = await coordinator.ask(
"In one run_subtasks call, answer: "
"(a) tallest waterfall, (b) Eiffel Tower year, (c) boiling point of nitrogen."
)TaskConfigpython
@dataclass
class TaskConfig:
config: ModelConfig | None = None # falls back to parent's config
prompt: str = "You are a task agent..."
include_tools: Iterable[str] | None = None # None = inherit all parent tools
exclude_tools: Iterable[str] = ()
extra_tools: Iterable[Callable | Tool] = ()常见配置——为子任务使用更轻量化的模型,限制工具范围:
python
TaskConfig(
config=worker_config, # smaller model
prompt="You are a focused worker; one step only.",
include_tools=["search", "fetch_url"], # don't expose `summarize` to children
)子任务代理的参数为——它们不会获得工具。递归委派在结构上无法实现,无需设置深度限制。
tasksFalserun_subtaskPattern 2 — Agent.as_tool()
Agent.as_tool()模式2——Agent.as_tool()
Agent.as_tool()Expose a whole agent as a tool the LLM can name and call:
python
from ag2 import Agent
from ag2.config import AnthropicConfig
config = AnthropicConfig(model="claude-sonnet-4-6")
researcher = Agent("researcher", prompt="Provide concise factual findings.", config=config, tools=[search_tool])
writer = Agent("writer", prompt="Turn research into clear prose.", config=config)
coordinator = Agent(
"coordinator",
prompt="First delegate research, then pass findings to the writer.",
config=config,
tools=[
researcher.as_tool(description="Research a topic and return findings."),
writer.as_tool(description="Write an article. Pass research notes in the context parameter."),
],
)The coordinator's LLM sees and . Each call has two parameters:
task_researchertask_writer- (required) — what the sub-task should do.
objective - (optional) — relevant info the parent wants to share.
context
as_tool()| Parameter | Description |
|---|---|
| Tool description shown to the LLM (required) |
| Override the default |
| |
| |
For more control, use directly:
subagent_tool()python
from ag2.tools.subagents import subagent_tool
coordinator = Agent("coordinator", config=config, tools=[
subagent_tool(researcher, description="Research a topic."),
])将整个代理暴露为LLM可命名调用的工具:
python
from ag2 import Agent
from ag2.config import AnthropicConfig
config = AnthropicConfig(model="claude-sonnet-4-6")
researcher = Agent("researcher", prompt="Provide concise factual findings.", config=config, tools=[search_tool])
writer = Agent("writer", prompt="Turn research into clear prose.", config=config)
coordinator = Agent(
"coordinator",
prompt="First delegate research, then pass findings to the writer.",
config=config,
tools=[
researcher.as_tool(description="Research a topic and return findings."),
writer.as_tool(description="Write an article. Pass research notes in the context parameter."),
],
)协调者的LLM会看到和。每次调用包含两个参数:
task_researchertask_writer- (必填)——子任务需完成的目标。
objective - (可选)——父代理需传递的相关信息。
context
as_tool()| 参数 | 描述 |
|---|---|
| 展示给LLM的工具描述(必填) |
| 覆盖默认的 |
| 用于自定义子任务流的 |
| |
如需更多控制,可直接使用:
subagent_tool()python
from ag2.tools.subagents import subagent_tool
coordinator = Agent("coordinator", config=config, tools=[
subagent_tool(researcher, description="Research a topic."),
])Self-delegation via as_tool()
as_tool()通过as_tool()
实现自我委派
as_tool()If you want a named self-delegate ( instead of generic ), give an agent its own tool:
sub_taskrun_subtaskpython
analyst = Agent(
"analyst",
prompt=(
"You have search and sub_task tools. "
"Only use sub_task when the task has clearly independent parts."
),
config=config,
tools=[search_tool],
)
analyst.add_tool(
analyst.as_tool(
description="Break work into a focused sub-task for independent analysis.",
name="sub_task",
)
)如果需要命名的自我代理(而非通用的),可为代理添加自身工具:
run_subtaskpython
analyst = Agent(
"analyst",
prompt=(
"You have search and sub_task tools. "
"Only use sub_task when the task has clearly independent parts."
),
config=config,
tools=[search_tool],
)
analyst.add_tool(
analyst.as_tool(
description="Break work into a focused sub-task for independent analysis.",
name="sub_task",
)
)Recursion safety
递归安全
Self-delegation via can recurse — the child has the same tool, so without a guard the LLM may chain calls indefinitely.
as_tool()sub_taskThe simplest safe pattern is to prefer the auto-injected / path for self-delegation. Sub-tasks spawned that way are constructed with , so they have no tools and recursion is structurally impossible.
run_subtaskrun_subtaskstasks=Falserun_subtaskIf you genuinely need recursive self-delegation, write a tool middleware that increments a depth counter in and short-circuits past a threshold. The module exports , , , and from — verify the current public surface there before relying on a built-in depth-limiting helper.
as_tool()context.dependenciessubagentssubagent_toolbackground_agent_toolpersistent_streamStreamFactoryag2.tools.subagents通过实现的自我委派可能发生递归——子代理拥有相同的工具,若没有防护机制,LLM可能无限链式调用。
as_tool()sub_task最简单的安全模式是优先选择自动注入的/路径进行自我委派。通过该方式生成的子任务代理,因此没有工具,递归在结构上无法实现。
run_subtaskrun_subtaskstasks=Falserun_subtask如果确实需要通过实现递归自我委派,可编写工具中间件,在中增加深度计数器,并在超过阈值时终止调用。模块从导出、、和——在依赖内置深度限制工具前,请先确认当前的公开接口。
as_tool()context.dependenciessubagentsag2.tools.subagentssubagent_toolbackground_agent_toolpersistent_streamStreamFactorySub-task streams
子任务流
By default, each sub-task gets a fresh — its history is isolated and starts empty. Context flow:
MemoryStream| What | Behaviour | Why |
|---|---|---|
| Dependencies | Copied (top-level shallow) | Isolated; treat dependencies as read-only inside subtasks |
| Variables | Copied; not synced back to the parent | Concurrent-safe — with siblings running via |
| History | Fresh stream | Clean context; relevant info passes via the |
| Tools | Inherited from parent (filtered by | Sub-tasks need real capabilities to do work |
默认情况下,每个子任务会获得全新的——其历史记录独立且初始为空。上下文流转规则:
MemoryStream| 内容 | 行为 | 原因 |
|---|---|---|
| Dependencies | 浅拷贝(顶层) | 隔离环境;子任务内将dependencies视为只读 |
| Variables | 拷贝;不会同步回父代理 | 并发安全——当子任务通过 |
| History | 全新流 | 干净的上下文;相关信息通过 |
| Tools | 继承自父代理(受 | 子任务需要实际能力来完成工作 |
persistent_stream()
persistent_stream()persistent_stream()
persistent_stream()When a sub-agent benefits from seeing its prior calls (e.g. avoid repeating searches), give it a stream that persists across invocations within the parent context:
python
from ag2.tools.subagents import persistent_stream
researcher.as_tool(
description="Research a topic",
stream=persistent_stream(),
)Stores stream id in keyed by and reuses the parent stream's storage backend.
context.dependenciesf"ag:{agent.name}:stream"当子代理需要查看其之前的调用记录(如避免重复搜索)时,可为其提供在父代理上下文内跨调用持久化的流:
python
from ag2.tools.subagents import persistent_stream
researcher.as_tool(
description="Research a topic",
stream=persistent_stream(),
)流ID存储在中,键为,并复用父代理流的存储后端。
context.dependenciesf"ag:{agent.name}:stream"Custom factory
自定义工厂
python
from ag2 import Agent, Context
from ag2.streams.redis import RedisStream
def make_redis_stream(agent: Agent, ctx: Context) -> RedisStream:
return RedisStream(MY_REDIS_URL, prefix=f"ag2:sub:{agent.name}")
researcher.as_tool(description="Research a topic", stream=make_redis_stream)python
from ag2 import Agent, Context
from ag2.streams.redis import RedisStream
def make_redis_stream(agent: Agent, ctx: Context) -> RedisStream:
return RedisStream(MY_REDIS_URL, prefix=f"ag2:sub:{agent.name}")
researcher.as_tool(description="Research a topic", stream=make_redis_stream)Going deeper
深入了解
- Working starter: (mirrors
assets/research_squad.py) — covers bothcode_examples/05andrun_subtasks(parallel=True), withAgent.as_tool()/TaskStartedlifecycle events.TaskCompleted - Full reference: .
website/docs/user-guide/subagents.mdx - constructor knob (with
tasks=, etc.):KnowledgeConfig.website/docs/user-guide/agent_harness.mdx
- 入门示例:(对应
assets/research_squad.py)——涵盖code_examples/05和run_subtasks(parallel=True)两种模式,包含Agent.as_tool()/TaskStarted生命周期事件。TaskCompleted - 完整参考:。
website/docs/user-guide/subagents.mdx - 构造函数参数(结合
tasks=等):KnowledgeConfig。website/docs/user-guide/agent_harness.mdx
Common pitfalls
常见陷阱
- Forgetting to opt in — is the default. No
tasks=False, noTaskConfigtools.run_subtask - Expecting sub-tasks to recurse with — they can't. Sub-tasks themselves have
run_subtask. If you need deeper trees, usetasks=Falseself-delegation with a manual depth-counter middleware (see "Recursion safety" above).Agent.as_tool() - Sharing mutable variables expecting them to merge — each sub-task copies the parent's variables, and mutations are never synced back to the parent (not even on success). Sibling mutations don't propagate either. Pass any result you need back through the sub-task's return value, not via shared variables.
- Treating as scoped per sub-task — only the top-level dict is copied. Mutable values inside it are still shared by reference. Treat dependencies as read-only inside sub-tasks.
dependencies - No on
description=— the LLM doesn't know when to call it. Required parameter.as_tool() - when work is concurrent — defaults to
run_subtasks(parallel=False)for a reason; only setTruewhen later tasks depend on earlier results.False - Confusing collisions — pass
task_{agent.name}to override if you want shorter names or distinct delegates of the same agent.name=
- 忘记启用——默认。不配置
tasks=False则无法使用TaskConfig工具。run_subtask - 期望子任务通过递归——不可能。子任务自身的
run_subtask。如需更深层级的任务树,请使用tasks=False自我委派,并手动添加深度计数器中间件(见上文“递归安全”部分)。Agent.as_tool() - 共享可变变量并期望合并——每个子任务都会拷贝父代理的变量,且变更永远不会同步回父代理(即使成功)。子任务之间的变更也不会相互传递。需通过子任务的返回值传递所需结果,而非共享变量。
- 将视为子任务专属——仅顶层字典会被拷贝。其中的可变值仍通过引用共享。子任务内请将dependencies视为只读。
dependencies - 未设置
as_tool()——LLM无法知晓何时调用该工具。此参数为必填项。description= - 工作可并发时设置——默认
run_subtasks(parallel=False)是有原因的;仅当后续任务依赖前期结果时才设置为True。False - 命名冲突——如需更短名称或同一代理的不同实例,请传递
task_{agent.name}参数覆盖默认名称。name=