Skip to content

Supervisor agent

A supervisor agent runs a ReAct loop whose tool list is augmented with one synthetic delegate_to_<sub_agent_id> tool per sub-agent — so the LLM decides on each turn whether to call one of its own tools or to delegate the message to a specialist. Pick this when you want "router + specialists": one agent that owns the conversation but forwards work to a fleet of focused sub-agents (a coder, a researcher, a writer, …) instead of stuffing every capability into one prompt.

Type identifier

YAML form:

agents:
  - id: orchestrator
    type: supervisor
    model: { ... }
    system_prompt: "You route work to specialist sub-agents."
    sub_agents:
      - { id: coder, type: react, model: { ... }, tools: [ ... ] }
      - { id: researcher, type: react, model: { ... }, tools: [ ... ] }

sub_agents: only takes effect under type: supervisor — react silently ignores sub-agents (a known gotcha; see the user-memory note on feedback_supervisor_for_subagents).

What it does

runSupervisor in pkg/agent/adk/run_orchestration.go handles the three configurations:

  • No sub-agents and no tools — error (supervisor agent X has no sub-agents and no tools).
  • Tools only, no sub-agents — fall through to runReAct. Logs as a misconfigured supervisor but keeps running.
  • Sub-agents (with or without own tools) — build a synthetic tool.Registry containing every original tool PLUS one delegate_to_<id> tool per sub-agent (via newDelegateTool, which wraps NewAgentTool). Swap the registry under the agent's mutex, run a normal runReAct against the unified list, then restore the original registry on return.

Each delegate_to_<id> tool has a single string parameter message. When invoked it runs agent.Run(ctx, message) on the sub-agent and returns its final assistant content as the tool result. The supervisor LLM sees the sub-agent's reply as a tool message and can decide what to do next — synthesise, follow up with another delegate, call one of its own tools, or answer directly.

Because delegation runs through a full nested agent.Run, the sub-agent inherits the same context (including RunID, governance stack, approval handler, etc.). Sub-agents emit their own OnEvent / OnDebugEvent stream, so a Studio-style tracer sees the parent's tool_call delegate_to_X followed by the sub-agent's full start → llm_request → … trace.

Configuration

A supervisor accepts every ReAct field (see react) plus:

Field Type Default Description
sub_agents list — Nested AgentDefinitions. The reifier instantiates them as full *Agents and registers them on the supervisor. Empty + no tools = error.
tools list — Optional own tools. Combined with the synthesised delegate tools; the LLM picks from the unified list.

Sub-agents are typically themselves ReAct (the most common pattern) but can be any type — including another supervisor, allowing hierarchical delegation.

Behavior notes

  • The synthetic tool name is delegate_to_<id> with hyphens in the sub-agent ID replaced by underscores (LLM function-name restriction). NewAgentTool is the general primitive — you can use it from a plain ReAct agent too to expose any *Agent as a callable tool.
  • The registry swap is mutex-guarded so concurrent calls into the same supervisor instance don't corrupt each other.
  • Budget, governance, approval, and guardrails all live on the SUPERVISOR's config. Sub-agents carry their own — they are full agents in their own right.
  • A common composition is a supervisor with tools: for general helpers (web fetch, calculator) and sub_agents: for big domain specialists (each a ReAct with its own toolset). Without that, every domain tool fights for system-prompt attention.

Example

A supervisor with two specialist sub-agents and one own tool:

agents:
  - id: triage
    type: supervisor
    model:
      provider: openai
      model: gpt-4o-mini
    system_prompt: |
      Route the user's request: code questions to `coder`, research
      questions to `researcher`, simple lookups answer yourself with
      the web tool.
    tools:
      - { name: web-fetch, type: function, function: web.fetch }
    sub_agents:
      - id: coder
        type: react
        model: { provider: anthropic, model: claude-sonnet-4-7 }
        system_prompt: "You are an expert Go developer."
        tools:
          - { name: read-file, type: function, function: fs.read }
          - { name: shell-exec, type: function, function: shell.exec }
      - id: researcher
        type: react
        model: { provider: anthropic, model: claude-sonnet-4-7 }
        system_prompt: "You research questions on the web."
        tools:
          - { name: web-fetch, type: function, function: web.fetch }

Source

  • pkg/agent/adk/run_orchestration.go — runSupervisor (the swap + delegate registration).
  • pkg/agent/adk/agent_tool.go — NewAgentTool and newDelegateTool (the agent-as-tool primitive).
  • pkg/workflow/definition.go — AgentDefinition.SubAgents.

See also

  • react — what the inner loop actually does once the delegate tools are registered.
  • tools — how tool registration / categories / middleware works.
  • sequential, parallel — alternative multi-agent shapes.