Skip to content

Sub-agents

A sub-agent is an agent used as a tool by another agent. When the parent decides to call it, the sub-agent runs on its own — its own instructions, model, and tools — and returns its answer to the parent as the tool result.

Use this to delegate specialised work: a support agent that hands billing questions to a billing agent, a coordinator that delegates research, and so on. Each sub-agent keeps its own focused instructions, so the parent's prompt stays small.

Because a sub-agent is just a tool, it behaves like any other tool everywhere — it fires the same events, streams to your UI in real time, and is recorded in persistence the same way.

You could always hand-roll this — write a custom tool that runs another agent itself. The built-in sub-agent support does that wiring for you and adds context isolation, depth/cycle guards, and parent → child audit tracking automatically.

Declaring a sub-agent

List an agent class in another agent's tools(). Atlas exposes it to the parent model as a delegation tool automatically:

php
use Atlasphp\Atlas\Agent;

class SupportAgent extends Agent
{
    public function tools(): array
    {
        return [
            BillingAgent::class,   // delegated to as a sub-agent
            SearchKnowledgeBase::class, // a normal tool
        ];
    }
}

The tool's name and description come from the sub-agent's key() and description().

What the parent passes

The parent model sees a single string parameter, task, which it fills with a clear, self-contained instruction. That becomes the sub-agent's message, and the sub-agent's final text is returned as the tool result.

Context isolation

Each sub-agent invocation runs in isolation: its own instructions, model, and tools, starting from a fresh history. The parent's conversation history is not shared — pass everything the sub-agent needs in the task. Context you set with withMeta() (auth, tenant) is forwarded, so authorization still works.

Running sub-agents concurrently

By default delegations run one at a time. If the parent agent enables concurrent execution (via concurrent() or ->withConcurrent()) and the model delegates to several sub-agents in a single step, those sub-agents run at the same time — each in its own forked process:

php
class CoordinatorAgent extends Agent
{
    public function concurrent(): bool
    {
        return true; // fan out to sub-agents in parallel
    }

    public function tools(): array
    {
        return [ResearchAgent::class, PricingAgent::class, LegalAgent::class];
    }
}

The parent's tool loop waits for all of them to finish, then continues with the complete set of responses — so the parent always reasons over every sub-agent's result, exactly as it would sequentially. Only wall-clock time changes: three sub-agents that each take ~3s finish in ~3s instead of ~9s.

Everything in this page still holds under concurrency. The full lineage tree, each sub-agent's own and rolled-up token usage, and the depth/cycle guards are all preserved across the fork boundary, and persistence tracking is automatically fork-safe (Atlas resets database connections before forking).

One nuance to know: a concurrently-delegated sub-agent's internal real-time events (its own step and tool-call events) fire inside its forked process and are not delivered to in-process listeners in the parent — the delegation still surfaces as a parent tool-call event, and the full tree is still persisted for auditing. Requirements, the CLI/queue-only nature of fork-based parallelism, and this event caveat are covered in Concurrent Tool Execution.

Guards

Delegation is bounded to prevent runaway nesting:

  • Depthatlas.agents.max_delegation_depth (default 5) caps how deep agents may delegate; exceeding it throws MaxDelegationDepthException.
  • Cycles — delegating to an agent already in the chain (A → B → A) throws DelegationCycleException.

atlas.agents.delegation_errors controls sub-agent failures: throw (default) surfaces a failed tool call; return hands the error message back to the parent model so it can recover.

php
// config/atlas.php
'agents' => [
    'max_delegation_depth' => 5,
    'delegation_errors' => 'throw',
],

Auditing the delegation tree

With persistence enabled, every delegation is recorded as an auditable tree. Each sub-agent run is its own Execution, linked to its parent by parent_execution_id, parent_tool_call_id (the delegating call, typed agent), and depth (0 for the root, incremented per level):

php
use Atlasphp\Atlas\Persistence\Models\Execution;

$run = Execution::find($id);
$run->parent;    // the execution that delegated to this one (null for a root)
$run->children;  // sub-agent executions it spawned

// Each agent's own token cost is stored on its own row.
$run->usage; // ['input_tokens' => …, 'output_tokens' => …]

// Total tokens for the whole chain (this run plus every sub-agent it called).
$run->totalUsage(); // ['input_tokens' => …, 'output_tokens' => …, 'total_tokens' => …]

So you can report what each individual agent cost, as well as the cost of an entire delegation chain.

Released under the MIT License.