
Ag2 Subagent Delegation
- 35 installs
- 8 repo stars
- Updated July 27, 2026
- ag2ai/ag2-skills
ag2-subagent-delegation is a Claude Code skill that shows how one AG2 beta Agent can self-delegate or fan out parallel sub-tasks and call specialist agents as tools.
About
This skill covers single-agent recursion and parallel fan-out inside one AG2 beta Agent. A developer opts in with tasks=TaskConfig(...) to get auto-injected run_subtask and run_subtasks(parallel=True) tools for self-delegation, or uses Agent.as_tool() to call one named specialist agent from inside another. It documents context flow, recursion safety, and persistent_stream for sub-task history, and points to ag2-network-quickstart for full multi-agent collaboration.
- Self-delegation and parallel fan-out inside one AG2 beta Agent via run_subtask / run_subtasks(parallel=True)
- Agent.as_tool() exposes a whole agent as a named tool the coordinator's LLM can call
- Auto-injected sub-task agents are built with tasks=False so recursion is structurally impossible
Ag2 Subagent Delegation by the numbers
- 35 all-time installs (skills.sh)
- Ranked #8,740 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 1, 2026 (Skillselion catalog sync)
ag2-subagent-delegation capabilities & compatibility
Free skill; requires an LLM provider API key to run the coordinator agent.
- Capabilities
- subagent delegation · parallel fan out · agent as tool · task orchestration
- Use cases
- orchestration
- Pricing
- Bring your own API key
What ag2-subagent-delegation says it does
Subtask tools are **off by default** (`tasks=False`). Opt in with `tasks=TaskConfig(...)`
Sub-task agents are built with `tasks=False`** — they never gain `run_subtask` tools themselves. Recursive delegation is structurally impossible
npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-subagent-delegationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 35 |
|---|---|
| repo stars | ★ 8 |
| Last updated | July 27, 2026 |
| Repository | ag2ai/ag2-skills ↗ |
What it does
Make one AG2 coordinator agent break work into sub-tasks, fan out concurrent sub-tasks, or call a specialist agent as a tool.
Who is it for?
Developers building an AG2 coordinator agent that splits work into isolated or parallel sub-tasks.
Skip if: Two or more agents collaborating with a registry, durable channels, or governance (use ag2-network-quickstart).
When should I use this skill?
A single coordinator wants to break work into sub-tasks, fan out concurrent sub-tasks, or invoke a specialist agent as a tool.
What you get
The agent gains run_subtask/run_subtasks tools or agent-as-tool delegates with safe, non-recursive sub-tasks.
- AG2 Agent with TaskConfig sub-task tools
- agent-as-tool delegate wiring
By the numbers
- 2 delegation patterns compared in the patterns table
Files
Subagent 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 run_subtask / run_subtasks | Lightweight self-delegation, dynamic fan-out, parallel sub-questions | tasks=TaskConfig(...) on the parent |
| `Agent.as_tool()` | 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.
Pattern 1 — auto-injected run_subtasks
Subtask tools are off by default (tasks=False). Opt in with tasks=TaskConfig(...) and the agent gains:
run_subtask(task: str)— one isolated sub-task agent.run_subtasks(tasks: list[str], parallel: bool = True)— fan out multiple in one tool call (default concurrent).
from autogen.beta import Agent, TaskConfig
from autogen.beta.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."
)TaskConfig controls how the sub-task agents are built:
@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:
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 `tasks=False` — they never gain run_subtask tools themselves. Recursive delegation is structurally impossible; no depth limit needed.
Pattern 2 — Agent.as_tool()
Expose a whole agent as a tool the LLM can name and call:
from autogen.beta import Agent
from autogen.beta.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 task_researcher and task_writer. Each call has two parameters:
objective(required) — what the sub-task should do.context(optional) — relevant info the parent wants to share.
as_tool() accepts:
| Parameter | Description |
|---|---|
description | Tool description shown to the LLM (required) |
name | Override the default task_{agent.name} |
stream | StreamFactory for custom sub-task streams (see below) |
middleware | ToolMiddleware callables (e.g. approval_required) |
For more control, use subagent_tool() directly:
from autogen.beta.tools.subagents import subagent_tool
coordinator = Agent("coordinator", config=config, tools=[
subagent_tool(researcher, description="Research a topic."),
])Self-delegation via as_tool()
If you want a named self-delegate (sub_task instead of generic run_subtask), give an agent its own tool:
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 as_tool() can recurse — the child has the same sub_task tool, so without a guard the LLM may chain calls indefinitely.
The simplest safe pattern is to prefer the auto-injected `run_subtask` / `run_subtasks` path for self-delegation. Sub-tasks spawned that way are constructed with tasks=False, so they have no run_subtask tools and recursion is structurally impossible.
If you genuinely need recursive as_tool() self-delegation, write a tool middleware that increments a depth counter in context.dependencies and short-circuits past a threshold. The subagents module exports subagent_tool, background_agent_tool, persistent_stream, and StreamFactory from autogen.beta.tools.subagents — verify the current public surface there before relying on a built-in depth-limiting helper.
Sub-task streams
By default, each sub-task gets a fresh MemoryStream — its history is isolated and starts empty. Context flow:
| 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 asyncio.gather, last-writer-wins would silently clobber values, so child mutations stay scoped to the child by design |
| History | Fresh stream | Clean context; relevant info passes via the context tool parameter |
| Tools | Inherited from parent (filtered by TaskConfig) | Sub-tasks need real capabilities to do work |
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:
from autogen.beta.tools.subagents import persistent_stream
researcher.as_tool(
description="Research a topic",
stream=persistent_stream(),
)Stores stream id in context.dependencies keyed by f"ag:{agent.name}:stream" and reuses the parent stream's storage backend.
Custom factory
from autogen.beta import Agent, Context
from autogen.beta.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:
assets/research_squad.py(mirrorscode_examples/05) — covers bothrun_subtasks(parallel=True)andAgent.as_tool(), withTaskStarted/TaskCompletedlifecycle events. - Full reference:
website/docs/beta/task_delegation.mdx. tasks=constructor knob (withKnowledgeConfig, etc.):website/docs/beta/agent_harness.mdx.
Common pitfalls
- Forgetting to opt in —
tasks=Falseis the default. NoTaskConfig, norun_subtasktools. - Expecting sub-tasks to recurse with `run_subtask` — they can't. Sub-tasks themselves have
tasks=False. If you need deeper trees, useAgent.as_tool()self-delegation with a manual depth-counter middleware (see "Recursion safety" above). - 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 `dependencies` 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.
- No `description=` on `as_tool()` — the LLM doesn't know when to call it. Required parameter.
- `run_subtasks(parallel=False)` when work is concurrent — defaults to
Truefor a reason; only setFalsewhen later tasks depend on earlier results. - Confusing `task_{agent.name}` collisions — pass
name=to override if you want shorter names or distinct delegates of the same agent.
"""Research squad — parallel subtasks and sibling delegation.
Mirrors website/docs/beta/code_examples/05_research_squad.mdx. Two patterns
for multi-Agent orchestration:
1. **Opt-in subtask tools.** Pass tasks=TaskConfig(...) and the Agent gains
run_subtask / run_subtasks. The coordinator uses run_subtasks with
parallel=True to fan out three short investigations concurrently.
2. **Agent.as_tool().** A second Agent (math_expert) is exposed to the
coordinator as a callable tool.
Both patterns: spawned subtasks have no run_subtask tools (they default to
tasks=False), so recursion is structurally impossible — no depth limiter
needed.
Run::
python research_squad.py
"""
import asyncio
import time
from autogen.beta import Agent
from autogen.beta.agent import TaskConfig
from autogen.beta.config import GeminiConfig
from autogen.beta.events import TaskCompleted, TaskStarted
from autogen.beta.stream import MemoryStream
def section(title: str) -> None:
print(f"\n── {title} ───")
async def main() -> None:
config = GeminiConfig(model="gemini-3-flash-preview", temperature=0)
section("Parallel subtasks — fan out three lookups in one tool call")
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. Be concise."
),
config=config,
tasks=TaskConfig(), # Opt in to run_subtask / run_subtasks.
)
starts: list[TaskStarted] = []
completions: list[TaskCompleted] = []
stream = MemoryStream()
stream.where(TaskStarted).subscribe(lambda e: starts.append(e))
stream.where(TaskCompleted).subscribe(lambda e: completions.append(e))
start = time.monotonic()
reply = await coordinator.ask(
"Use run_subtasks(parallel=True) to answer, in one tool call: "
"(a) what is the tallest waterfall in the world, "
"(b) what year was the Eiffel Tower completed, "
"(c) what is the boiling point of nitrogen in Celsius. "
"Then list all three answers.",
stream=stream,
)
elapsed = time.monotonic() - start
print(reply.body)
print()
print(f"Subtasks dispatched: {len(starts)}")
print(f"Subtasks finished: {len(completions)}")
print(f"Wall time: {elapsed:.2f}s (3 concurrent LLM calls)")
section("Sibling delegation — math_expert is a tool on coordinator2")
math_expert = Agent(
"math-expert",
prompt="You are an arithmetic specialist. Reply with only the number.",
config=config,
)
coordinator2 = Agent(
"coordinator2",
prompt=(
"When arithmetic comes up, delegate to the task_math-expert tool "
"rather than computing yourself. Then present the answer in a "
"complete sentence."
),
config=config,
tools=[
math_expert.as_tool(
description="Delegate arithmetic problems to the math expert.",
)
],
)
reply2 = await coordinator2.ask("What is 237 times 19?")
print(reply2.body)
if __name__ == "__main__":
asyncio.run(main())
Related skills
FAQ
How do I fan out parallel sub-tasks in AG2?
Opt in with tasks=TaskConfig(...) and the agent gains run_subtasks(tasks, parallel=True), which fans out multiple sub-tasks in one tool call (default concurrent).
Is recursive delegation dangerous?
Auto-injected sub-task agents are built with tasks=False, so they never gain run_subtask tools and recursion is structurally impossible; no depth limit is needed.