ag2-subagent-delegation
Skill252 starsChanged 6 months agoApache-2.0View on GitHub
Delegate work from one AG2 beta `Agent` to another. Two patterns — auto-injected `run_subtask` / `run_subtasks(parallel=True)` (opt in via `tasks=TaskConfig(...)`) for self-delegation and parallel fan-out, and `Agent.as_tool()` for named delegates between distinct agents. Use when one coordinator should spawn sub-tasks, fan out concurrent work, or hand off to a specialist agent. Covers context flow, recursion safety, and `persistent_stream` for sub-task history.
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 128
If you want a named self-delegate (sub_task instead of generic run_subtask), give an agent its own tool:
- Distinctive: line 170
When a sub-agent benefits from seeing its prior calls (e.g.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
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, 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; synced back on success | Concurrent-safe — sibling subtasks won't race-clobber a shared dict |
| 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 havetasks=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 — concurrent sub-tasks each copy variables; sibling mutations don't propagate. Each sub-task's variable mutations stay local until sync-back on success.
- Treating
dependenciesas 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=onas_tool()— the LLM doesn't know when to call it. Required parameter. run_subtasks(parallel=False)when work is concurrent — defaults toTruefor a reason; only setFalsewhen later tasks depend on earlier results.- Confusing
task_{agent.name}collisions — passname=to override if you want shorter names or distinct delegates of the same agent.
In this repository15 other files it gives its agents.
- ag2-add-custom-toolSkill
- ag2-ag-uiSkill
- ag2-hitlSkill
- ag2-knowledge-and-memorySkill
- ag2-middlewareSkill
- ag2-multimodal-inputSkill
- ag2-observers-and-alertsSkill
- ag2-overviewSkill
- ag2-quickstartSkill
- ag2-shell-toolSkill
- ag2-structured-outputSkill
- ag2-telemetrySkill
- ag2-testingSkill
- ag2-use-builtin-toolsSkill
- calculatorSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

