Skip to content

Sub-Agent Delegation

Sub-agent delegation lets an agent call other A2A agents as LLM tools. The SubAgentToolBuilder produces a ToolSpec that, when invoked by the LLM, sends an A2A message/send request to a remote agent and returns the result. Delegation can be streaming (SSE) or synchronous (request/response).

[dependencies]
agent_sdk = { package = "pf_agent_sdk", path = "../../crates/agent_sdk", features = ["llm-engine", "sub-agents"] }
  1. Build a sub-agent tool

    SubAgentToolBuilder::new takes the remote agent’s name, URL, and a tool name for the LLM:

    use agent_sdk::sub_agent::{SubAgentToolBuilder, DelegationMode};
    let planner_tool = SubAgentToolBuilder::new(
    "planner",
    "http://planner.default.svc.cluster.local:3000/jsonrpc",
    "delegate_to_planner",
    )
    .description("Delegate planning tasks to the planner agent")
    .schema(serde_json::json!({
    "type": "object",
    "properties": {
    "task": { "type": "string", "description": "What to plan" }
    },
    "required": ["task"]
    }))
    .delegation_mode(DelegationMode::Streaming)
    .build();
  2. Register the tool in the agent’s LLM runtime

    The .build() call returns a ToolSpec — register it alongside any other tools:

    use agent_sdk::agent::tools::ToolRegistry;
    let mut tools = ToolRegistry::new();
    tools.register(planner_tool);
    // Add more sub-agent tools or local tools...
    agent.configure_llm_runtime(
    llm_client,
    "gpt-4o",
    tools,
    Some("You are a coordinator. Delegate to specialised agents.".into()),
    None,
    None,
    )?;
  3. The LLM calls the tool at runtime

    When the LLM invokes delegate_to_planner, the SDK sends an A2A JSON-RPC request to the planner’s URL. In Streaming mode, SSE events are relayed to the caller. In Synchronous mode, the full response is collected before returning.

use agent_sdk::Agent;
use agent_sdk::agent::tools::ToolRegistry;
use agent_sdk::sub_agent::{SubAgentToolBuilder, DelegationMode};
fn build_coordinator(
llm_client: impl agent_sdk::agent::llm_orchestrator::IntoLlmInvoker
+ agent_sdk::agent::llm_orchestrator::IntoLlmStreamInvoker + Clone,
) -> agent_sdk::SdkResult<Agent> {
let mut agent = Agent::new("coordinator")?;
agent
.add_skill("coordinate")
.description("Breaks work into sub-tasks and delegates to specialist agents")
.register()?;
let planner = SubAgentToolBuilder::new(
"planner",
"http://planner.default.agentmesh:3000/jsonrpc",
"delegate_planner",
)
.description("Delegate planning and decomposition tasks")
.delegation_mode(DelegationMode::Streaming)
.emit_handoff(true)
.build();
let researcher = SubAgentToolBuilder::new(
"researcher",
"http://researcher.default.agentmesh:3000/jsonrpc",
"delegate_researcher",
)
.description("Delegate research and fact-finding tasks")
.delegation_mode(DelegationMode::Synchronous)
.header("X-Priority", "high")
.build();
let mut tools = ToolRegistry::new();
tools.register(planner);
tools.register(researcher);
agent.configure_llm_runtime(
llm_client,
"gpt-4o",
tools,
Some("You are a coordinator agent. Delegate work to specialist agents.".into()),
None,
None,
)?;
Ok(agent)
}
MethodDescriptionDefault
SubAgentToolBuilder::new(agent_name, agent_url, tool_name)Create a new builder
.description(s)Tool description for the LLM"Delegate work to a remote sub-agent"
.schema(json)JSON Schema for the tool’s input{"type":"object","additionalProperties":true}
.delegation_mode(mode)Streaming or SynchronousStreaming
.adapter(arc)Custom SubAgentAdapter implementationA2aSubAgentAdapter
.result_transformer(fn)Post-process the sub-agent’s responseNone
.emit_handoff(bool)Emit handoff trace eventstrue
.header(key, value)Add a custom HTTP header to the A2A request
.build()Produce a ToolSpec ready for ToolRegistry::register
TypeModulePurpose
SubAgentToolBuilderagent_sdk::sub_agent::toolFluent builder for sub-agent delegation tools
DelegationModeagent_sdk::sub_agent::toolStreaming (SSE relay) or Synchronous (collect full response)
SubAgentAdapteragent_sdk::sub_agent::adapterTrait: execute(mode, name, url, args, headers, emit_handoff, ctx) → Future<Result<Value>>
SharedSubAgentAdapteragent_sdk::sub_agent::adapterArc<dyn SubAgentAdapter>
A2aSubAgentAdapteragent_sdk::a2a::sub_agentDefault adapter that sends A2A message/send over HTTP
SubAgentContextagent_sdk::sub_agent::adapterContext struct: agent_name, task_id, tool_call_id