Delegate Task Tool
Give an agent a one-line tool to spawn a sub-agent for a specific task
delegate_task gives an agent a single tool to spawn a specialist sub-agent for one task and return its output.
Quick Start
- Agent picks the tool
python from praisonaiagents import Agent from praisonaiagents.tools import delegate_task agent = Agent( name="Coordinator", instructions="Delegate specialist work to a sub-agent when useful.", tools=[delegate_task], ) agent.start("Delegate a market research task on competitor pricing.")The coordinator picks
delegate_task, spins up a sub-agent whose role is derived fromagent_type, and returns the result. - Direct call (headless / CI)
python import json from praisonaiagents.tools.delegation_tools import delegate_task # In headless / CI, set PRAISONAI_AUTO_APPROVE=true to bypass the approval gate. raw = delegate_task( task_description="Summarise Q3 competitor pricing changes", agent_type="research", priority="high", timeout=120, ) print(json.loads(raw)["output"])
Which Delegation Primitive?
Three delegation entry points overlap — pick the simplest one that fits.
| Feature | delegate_task (this page) | create_subagent_tool | parallel_handoffs |
|---|---|---|---|
| Shape | Single tool function | Tool factory returning {function, result_tool} | Async function over (agent, prompt) tuples |
| Sync / async | Sync | Sync or background | Async |
| Timeout | ✅ enforced (thread-bounded) | Per-call via subagent runtime | ✅ via HandoffConfig(timeout_seconds=...) |
| Approval | ✅ built-in @require_approval("medium") | Depends on agent config | Depends on agent config |
| Model / permission mode | Fixed lightweight defaults | ✅ Fully configurable | Each target is a full Agent |
| Background jobs | ❌ | ✅ (background=True, deliver=...) | ✅ concurrent fan-out (max_concurrent) |
| Named-agent resolution | ❌ | ✅ (agent_resolver=) | Pass the target Agent objects directly |
| When to use | You want a one-line drop-in delegation tool an agent can invoke in a prompt | You want full control over the sub-agent | You want async fan-out across several agents at once |
How It Works
delegate_task runs the sub-agent, waits for it (bounded by timeout), and returns a JSON string.
agent_type maps to the sub-agent’s identity:
role=agent_type(unlessagent_type == "general", in which caserole="assistant")name=f"{agent_type}_agent"goal=f"Complete delegated {agent_type} tasks accurately."verbose=False
This is intentionally lightweight — no extra config knobs and no separate registry of agent profiles. For richer control (custom LLM, permission mode, background execution, named-agent resolution), reach for create_subagent_tool directly — see Subagent Tool.
Signature
@require_approval(risk_level="medium")
def delegate_task(
task_description: str,
agent_type: str = "general",
priority: str = "medium",
timeout: int = 300,
) -> str| Parameter | Type | Default | Description |
|---|---|---|---|
task_description | str | required | Description of the task to delegate to the sub-agent |
agent_type | str | "general" | Type/role of agent (e.g. "research", "reviewer"). Used as the sub-agent’s role so the model can be steered toward a specialisation without extra config. "general" falls back to a plain "assistant" role. |
priority | str | "medium" | Task priority hint — "low", "medium", or "high". Echoed back in the success response for observability. |
timeout | int | 300 | Maximum execution time in seconds. Enforced via a daemon worker — on expiry the tool returns the structured timeout JSON immediately, without waiting for the sub-agent to finish. timeout <= 0 disables the bound. |
Return Shapes
The tool returns a JSON string in one of four shapes.
{
"success": true,
"task_description": "Summarise Q3 competitor pricing changes",
"agent_type": "research",
"priority": "high",
"output": "…sub-agent output text…"
}{
"success": false,
"task_description": "…",
"agent_type": "research",
"error": "delegation failed"
}{
"success": false,
"task_description": "…",
"agent_type": "research",
"error": "Delegated task timed out after 120s"
}{
"success": false,
"task_description": "…",
"agent_type": "research",
"error": "Error delegating task: <exception message>"
}Approval Gate
delegate_task is decorated with @require_approval(risk_level="medium") because spawning a sub-agent is a medium-risk action.
- Interactive: users get a prompt to approve or deny the call.
- Headless / CI: set
PRAISONAI_AUTO_APPROVE=true(or configure per-tool auto-approval) or the call is blocked before the sub-agent is spawned.
Since PR #4878, PRAISONAI_AUTO_APPROVE=true works even when delegate_task runs inside an agent that has an approval backend attached (the default ConsoleBackend on a TTY, or any chat/gateway front-end) — the env var is now checked before the backend prompt. See Standing grants and attached backends.
See Approval Protocol for how the gate works.
Timeout Enforcement
The timeout parameter is a hard wall-clock bound on how long delegate_task blocks the caller.
timeout > 0(default300): the sub-agent runs on a daemon worker thread. If it has not returned by the deadline, a structuredsuccess: falsetimeout JSON is returned immediately — the caller does not wait for the worker.timeout <= 0: bound disabled — the call blocks for as long as the sub-agent takes. Use this only when you have another way to cap runtime (e.g. an outer scheduler).
Bot / Registry Usage
delegate_task is not auto-injected into the bot default toolset. Opt in explicitly:
from praisonaiagents import BotConfig
config = BotConfig(
token="YOUR_TOKEN",
default_tools=[
"search_web", "store_memory",
"delegate_task", # opt in
],
)The tool is also available under the plain function and workspace-scoped forms:
from praisonaiagents.tools import delegate_task # module-level function
from praisonaiagents.tools.delegation_tools import DelegationTools, create_delegation_tools
# Optional: workspace-scoped instance
tools = create_delegation_tools(workspace=my_workspace)Common Patterns
Ad-hoc research delegation — a coordinator agent has delegate_task in its tool list; when it needs research it calls the tool and inlines the result.
from praisonaiagents import Agent
from praisonaiagents.tools import delegate_task
coordinator = Agent(
name="Coordinator",
instructions="When you need research, delegate it and summarise the result.",
tools=[delegate_task],
)
coordinator.start("Find competitor pricing changes this quarter.")Headless batch job — set PRAISONAI_AUTO_APPROVE=true, call delegate_task(...) directly for each work item, parse the JSON return.
import json
from praisonaiagents.tools.delegation_tools import delegate_task
items = ["Region A pricing", "Region B pricing"]
for item in items:
raw = delegate_task(task_description=item, agent_type="research", timeout=120)
result = json.loads(raw)
print(result["success"], result.get("output") or result.get("error"))Priority-tagged workqueue — pass priority="high" to record intent (echoed back in the response) even though it does not currently affect scheduling.
delegate_task(task_description="Urgent audit", agent_type="reviewer", priority="high")Best Practices
Prefer delegate_task for one-line delegation
Use delegate_task when you want a single-line tool an agent can pick up automatically. For anything else — background jobs, a custom model, or a named agent — reach for create_subagent_tool.
Always pass a realistic timeout
The default is 300 s, but a scoped task usually completes in under 60 s. Set timeout to bound runtime and get a structured timeout JSON instead of a hang.
Set PRAISONAI_AUTO_APPROVE in CI
In headless runs, set PRAISONAI_AUTO_APPROVE=true explicitly rather than removing the approval decorator — the gate stays in place for interactive use.
Treat priority as an observability tag
priority is echoed back in the success response only. There is no scheduler behind it — don’t rely on it to reorder work.