| name | adk-task-delegation |
| description | Delegate work to sub-agents with the ADK 2.0 Task API โ `mode='task'`, `mode='single_turn'`, `mode='chat'` on `LlmAgent`, the auto-attached `finish_task` tool, and typed contracts via `input_schema` / `output_schema`. Load this skill when one agent needs to hand a bounded unit of work to another and get a validated result back, or when migrating off SequentialAgent / ParallelAgent / LoopAgent.
|
ADK 2.0 Task Delegation
Version 1.0 | Requires google-adk>=2.0.0
In ADK 2.0 an LlmAgent declares how it is reachable through its mode
field. The framework reads that field on the sub-agents of a parent (and on
LlmAgent nodes inside a Workflow) and wires the right delegation machinery
automatically โ no AgentTool, no second Runner, no manual plumbing.
For the graph primitive these agents plug into, load adk-workflow-graphs.
For choosing the top-level shape of an agent, load adk-agent-patterns.
The three modes
mode: Literal['chat', 'task', 'single_turn'] | None
| Mode | Talks to the user? | Reached via | Finishes when |
|---|
chat | yes | transfer_to_agent โ control moves to it | it transfers back / the turn ends |
task | yes (can ask for clarification) | a delegation tool call from the parent | it calls finish_task |
single_turn | no | a normal tool call from the parent | its first response completes |
Defaults (do not set mode unless you want to override):
- as a sub-agent of an
LlmAgent โ chat
- as a node inside a
Workflow โ single_turn
Only task, single_turn, and chat are legal for an LlmAgent used as a
workflow node; anything else raises at graph-build time.
Picking a mode
Does the sub-agent need to come back to the user mid-work
(clarifying questions, approvals, several turns)?
YES โ mode='task'
NO โ
Is it one bounded transform โ input in, structured result out?
YES โ mode='single_turn'
NO โ
Should the user effectively be handed over to it for a while
(a specialist that owns the conversation)?
YES โ mode='chat'
Rule of thumb: single_turn is the default you want for pipeline steps.
Reach for task only when the sub-agent legitimately needs multiple turns or
user input. Use chat for router/specialist hierarchies where the user should
notice they are now talking to the specialist.
What each mode wires up
Set on the sub-agent; the parent does the wiring in model_post_init:
from google.adk.agents import LlmAgent
researcher = LlmAgent(
model=FAST_MODEL,
name="researcher",
description="Researches a topic and returns sourced findings.",
instruction="...",
mode="task",
output_schema=Findings,
)
coordinator = LlmAgent(
model=FAST_MODEL,
name="coordinator",
instruction="Delegate research to the researcher, then summarize.",
sub_agents=[researcher],
)
mode='task' on researcher โ the parent gets a delegation tool named
researcher; calling it runs the sub-agent inline in the parent's
session via ctx.run_node().
mode='task' also causes FinishTaskTool to be appended to
researcher.tools โ the sub-agent gets a finish_task tool it did not
declare.
mode='single_turn' โ the parent gets a plain tool that runs the sub-agent
once and returns its output.
mode='chat' โ no tool; the sub-agent stays a transfer_to_agent target.
Do not wrap sub-agents in AgentTool for this. AgentTool spins up a
separate runner and an isolated session; the mode-based path keeps everything
in the parent's session, so events, artifacts, state, and plugins all flow
through one place. AgentTool is explicitly discouraged in ADK 2.0.
finish_task
Auto-attached to every mode='task' agent. Its parameters are generated from
the agent's output_schema; with no output_schema it falls back to a single
required result: str.
The framework also injects an instruction telling the model not to call it
prematurely, and to call it alone with no accompanying text.
class Findings(BaseModel):
summary: str
sources: list[str]
confidence: float
researcher = LlmAgent(..., mode="task", output_schema=Findings)
If the model's arguments fail validation, finish_task returns an error
string describing the ValidationError instead of completing โ the model sees
it as a tool result and retries. The task is only finished on a successful
call. Validation is therefore the enforcement point for the contract: a task
agent cannot end without producing schema-valid output.
Typed contracts
| Field | On | Shapes |
|---|
output_schema | task / single_turn sub-agent | finish_task params; the value handed back to the parent |
input_schema | task / single_turn sub-agent | the parameters of the delegation tool the parent calls |
With no input_schema, the defaults are {goal, background} for task mode
and a single request: str for single_turn. Declaring your own is how you
stop the parent from passing vague prose:
class ResearchRequest(BaseModel):
topic: str
depth: Literal["shallow", "deep"]
must_cover: list[str] = []
researcher = LlmAgent(
..., mode="task", input_schema=ResearchRequest, output_schema=Findings,
)
Both schemas are ordinary Pydantic models, so the contract is unit-testable
without running a model.
Task agents in a Workflow
An LlmAgent used as a graph node defaults to single_turn; set
mode='task' when the node must be able to come back to the user.
single_turn nodes also get include_contents='none' by default โ they see
only their node_input, not the conversation history. Set include_contents
explicitly to override.
from google.adk.workflow import Workflow
root_agent = Workflow(
name="research_pipeline",
edges=[
("START", planner),
(planner, researcher),
(researcher, writer),
],
)
A node's output โ the validated output_schema value for a task node โ
becomes the next node's node_input.
Migrating off Sequential / Parallel / Loop
SequentialAgent, ParallelAgent, and LoopAgent still exist as
convenience wrappers but are no longer the idiomatic way to compose steps.
Their replacement is a Workflow whose nodes are mode-declaring LlmAgents.
See references/workflow-migration.md for the three mechanical rewrites.
References
| Resource | Load when |
|---|
references/task-mode-examples.md | You need full runnable code โ coordinator + task sub-agent, workflow with task nodes, finish_task retry behavior, testing contracts |
references/workflow-migration.md | Rewriting a SequentialAgent / ParallelAgent / LoopAgent into a Workflow + task modes |
Load a reference with
load_skill_resource("adk-task-delegation", "<resource>.md").
Common mistakes
| Mistake | Fix |
|---|
Wrapping a sub-agent in AgentTool to call it | Set mode='single_turn' (or 'task') and put it in sub_agents=[...] |
Adding a finish_task tool by hand | It's auto-attached to mode='task' agents; declaring your own shadows it |
mode='task' for a one-shot transform | Use single_turn โ task costs extra turns and can stall waiting on the user |
| Expecting a task agent to end after one reply | It runs until it calls finish_task; give it an output_schema so "done" is well-defined |
| Relying on prose to constrain sub-agent output | Put the contract in output_schema โ finish_task validates it |
A single_turn node that needs conversation history | Set include_contents='default' explicitly, or make it mode='task' |
| Calling the delegation tool in parallel with other tools | The declaration says not to; run it alone |
A routed edge that maps both a named route and __DEFAULT__ to the same node | Graph validation rejects duplicate edges between a pair of nodes โ pick one key |