Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Diagram of Agentforce subagents routed from a Super Agent router to connected specialist agents
Agentforce & AI

Agentforce Subagents: Split and Route an Overloaded Agent

A decomposition and routing guide for architects whose single Agentforce agent has outgrown itself. Covers start_agent, three seams for splitting scope, and why If/Else transitions cannot reach a Connected Subagent.

The short answer

In a multi-agent Agentforce setup you cannot reach a Connected Subagent with `transition to` from an If/Else; you call the action that fronts it, `run @actions.go_to_X`. Because that is an ordinary action call, its outputs come back to the Super Agent, which is why the router can still consolidate the reply.

Key takeaways Use several `subagent` blocks in one script when all you need is instruction isolation; move to Connected Subagents only when independent ownership, release cadence or reuse across Super Agents is a real requirement. Route to a Connected Subagent with `run @actions.go_to_X`. `transition to` cannot reach one, and `invoke` is not an Agent Script keyword. Keep verification, compliance guardrails, disambiguation and escalation decisions in `start_agent` — it is the only entry point, and it never supervises a subagent mid-run. Bind `connected_subagent` inputs with the callee's expected parameter name on the left and `@variables.your_value` on the right, and let Agentforce Builder fill in `target`. Where an action has already resolved the fact that decides the route, enforce it with `available when` gates and nested `if` blocks instead of trusting descriptions, which land around 80%.

You shipped one Agentforce agent. It started with six topics, it now carries twenty-two, two of them answer the same question differently depending on what the customer said first, and every instruction you add to fix one flow quietly degrades another. The decision in front of you is whether to split it into a Super Agent with Connected Subagents — and if you do, how routing actually behaves once those pieces are separate agents in the org rather than subagent blocks in one script. It does not behave the way single-agent tutorials imply.

The ceiling is instruction interference, not topic count

The Salesforce Admins blog names the failure mode plainly: asking one agent to do more makes it harder to "write clean instructions, test predictable behavior, and maintain it over time." The mechanism shows up in the flow documentation. When Agentforce transitions to a subagent, it discards the previous subagent's prompt instructions and processes the new subagent's instructions from the top, so the final prompt contains only the second subagent's instructions. Isolation is the whole benefit. A single agent with twenty-two topics has none — every instruction sits in every prompt, competing for attention on every turn.

Agentforce subagents come in two forms, and they are not interchangeable:

  • Several subagent blocks in one script. You get instruction isolation immediately, with one deployable artifact and one test suite.
  • Connected Subagents. Separate agents in the org, referenced by URI, each with its own builder, owner and release. You also get a routing layer that behaves unlike anything in a single-agent script.

I would only pay for the second when independent ownership, independent release cadence, or reuse across more than one Super Agent is a real requirement rather than an aspiration. If one team owns everything and nothing is reused, keep it in one script and split into subagents. The multi-agent version puts a distribution problem on top of a prompt problem.

start_agent is the only front door, and it is not a supervisor

Every customer utterance enters at start_agent. Exactly one is required per script, and it is what the Admins blog calls the Agent Router: the entry point for every utterance, responsible for subagent classification, filtering, routing and variable initialization. It shares all of its fields with a subagent block, and @start_agent aliases @subagent, so anything legal in a subagent is legal in the router.

What it is not is a live supervisor. Execution comes back to start_agent only after the current subagent completes and Agentforce is waiting for the next utterance. Nothing in the router observes a subagent mid-run, so any rule you need enforced during a subagent's turn has to be written inside that subagent.

That settles what belongs in the router: compliance and guardrails, user verification and context priming, disambiguation, and the decision to escalate to a human — the rules that have to hold on every interaction. Here is the router from a facilities org with a local verification subagent and two Connected Subagents, warranty claims and on-site engineer scheduling.

config:
   name: "Facilities Router"
   description: "Front door for asset service requests from site managers."

variables:
   asset_tag: mutable string = ""
   asset_id: mutable string = ""
   service_state: mutable string = ""
   caller_verified: mutable boolean = False
   visit_window: mutable string = ""

system:
   You coordinate facilities service requests. You never quote warranty
   outcomes or engineer availability yourself.

start_agent Facilities_Router:
   label: "Facilities Router"
   description: "Classifies a facilities request and hands it to the right specialist."

   reasoning:
      instructions:
         - Confirm the caller against the asset record before discussing service history.
         - If the request covers both a warranty question and a site visit, ask which to handle first.
      actions:
         verify_caller: @utils.transition to @subagent.Caller_Verification
            available when @variables.caller_verified == False
         open_warranty_claim: @actions.go_to_Warranty_Claims_Agent
            available when @variables.caller_verified == True
            with [email protected]_id
            set @variables.service_state = @outputs.claim_state

available when gates an action's visibility to the reasoning engine using a boolean expression, and it is valid only inside reasoning.actions. Watch the transition form: in reasoning.actions a transition is the action @utils.transition to @subagent.X; in a directive block such as after_reasoning it is the bare statement transition to @subagent.X. I have reviewed scripts that mixed the two and lost an afternoon to it, because the validation error names the block rather than the offending line.

Pick the seam: domain, stage, or capability

The primary source offers three decompositions, and they are not equally good defaults.

By functional domain mirrors how the business is organised — warranty, scheduling, procurement. This is the split I reach for first, because it lines up with who gets paged when a subagent misbehaves. Ownership boundaries that match org-chart boundaries survive contact with a release calendar.

By workflow stage structures around process phases, where each stage has distinct actions you can reuse. Use it when the same record moves through phases with genuinely different action sets — intake, triage, dispatch, closeout — and when two stages would otherwise duplicate half of each other's instructions.

By capability means one agent retrieves, one analyses, one acts. It reads well, and I would not start here. It creates the most agent-to-agent chatter per user turn, and whether Connected Subagents can run concurrently is not something any current documentation confirms — so design as if every call is sequential and budget the latency. If retrieval genuinely serves three domains, carve out the retriever alone and leave the rest split by domain.

Whichever seam you pick, apply the clarity test from the source: if you cannot state a subagent's job in one sentence, the scope is wrong.

The transition-to gotcha: only one construct reaches a Connected Subagent

This is the part that breaks working single-agent patterns. The Admins blog is direct about it: with a single agent you can use transition to in an If/Else statement to move to the subagent, and in a multi-agent orchestration "that doesn't work." The documented fix is to route by invoking the action that calls the Connected Subagent.

There are exactly two control-movement constructs, and only one of them can see a Connected Subagent.

Construct Reaches a Connected Subagent? Outputs to the caller Caller's instructions Where it is written
transition to @subagent.X (bare in directive blocks; @utils.transition to in reasoning.actions) No None — transitions are one-way and control does not return to the previous subagent Discarded; the final prompt holds only the target subagent's instructions Reasoning actions, reasoning instructions, before/after reasoning blocks
run @actions.go_to_X, the action fronting a Connected Subagent Yes — this is the documented route Yes — set @variables.x = @outputs.y captures them Retained; the Super Agent stays the point of contact and consolidates the response Anywhere an action call is legal, including inside an if in a directive block

run is the keyword. The spec describes it as a statement that "invokes a named action", first-class, traced, auditable and callback-capable. There is no invoke keyword, and no construct in the spec returns control to a caller. The action route works not because it returns, but because it is an ordinary action call: its outputs land in the calling agent's variables the way every other action's do. A transition hands the conversation away and throws your instructions out with it.

The Connected Subagent block is where the contract lives:

connected_subagent Engineer_Scheduling:
   target: "<filled in by Agentforce Builder when you connect the agent>"
   label: "Engineer Scheduling"
   description: "Books, moves and cancels on-site engineer visits for one known asset. Call when an asset needs a physical technician visit."
   loading_text: "Checking engineer availability for this site."
   inputs:
      asset_reference: string = @variables.asset_id
      requested_state: string = @variables.service_state
   after_response:
      if @variables.visit_window == "":
         transition to @subagent.Caller_Verification
   delegate_escalation: True

Two details bite here. target is a URI that Builder fills in when you connect the agent as a subagent — do not hand-author it. And inputs binds in the direction people get backwards: the left side is the name the Connected Subagent expects, the right side references the calling agent's variable. Above, asset_reference is the callee's parameter and @variables.asset_id is mine.

Closing the last 20% with deterministic logic

Description-based routing got the source's author to the right subagent about 80% of the time. That is a fine number for a demo and a bad one for a dispatch flow. The remedy given is negative guardrails that stop requests, plus deterministic logic to enforce conditions, rather than trusting reasoning alone.

In practice: when an action has already resolved the fact that decides the route, do not let a description decide it.

after_reasoning:
   run @actions.Resolve_Asset_Service_State
      with [email protected]_tag
      set @variables.asset_id = @outputs.asset_id
      set @variables.service_state = @outputs.service_state
   if @variables.service_state == "Awaiting Site Visit":
      if @variables.caller_verified == True:
         run @actions.go_to_Engineer_Scheduling_Agent
            with [email protected]_id
            set @variables.visit_window = @outputs.scheduled_window
      else:
         transition to @subagent.Caller_Verification

Once service_state is known, scheduling is not a judgement call. Nested if blocks are how you write that chain — Agent Script has no elif or else if — and booleans must be capitalised True / False.

Sequencing matters as much as syntax. Build and test each Connected Subagent standalone until it is boring, stand up the Super Agent with the deterministic routes first, then add Connected Subagents one at a time and re-test routing after each. Adding three at once and measuring accuracy afterwards tells you the number is bad without telling you which description caused it.

What to watch for

  • description on connected_subagent is required by the blocks documentation even though the spec lists it as optional. Write it as a trigger condition ("call when...") rather than a capability blurb; the reasoning engine is matching intent, not reading a brochure.
  • Variables are global across the script and persist across transitions. A service_state left over from the previous request will route the next one confidently and wrongly. Reset routing state in the router.
  • mutable variables require a default value. linked variables are read-only, supplied from external context through a source: annotation, take no default, and cannot use the list type.
  • delegate_escalation: True lets the Connected Subagent escalate to human support, and applies in handoff mode only. Decide deliberately whether escalation belongs to the specialist or the router — the primary source puts escalation decisions in the Super Agent's column.
  • Concurrency is unconfirmed. Design every route as sequential; if parallel invocation lands later, that assumption costs you nothing.
  • available when exists only in reasoning.actions. Guarding an action in a directive block means an if on a variable, not available when.

Originally reported by admin.salesforce.com

Frequently asked questions

Why can't transition to route to a Connected Subagent in Agentforce?

Because a transition is a one-way handoff and cannot target one: the Salesforce Admins blog states that the single-agent pattern of transitioning from an If/Else statement does not work in a multi-agent orchestration. Route by calling the action that fronts the Connected Subagent instead — `run @actions.go_to_X`.

Is it run or invoke to call an action in Agent Script?

It is `run`. The Agent Script spec describes `run` as a first-class statement that invokes a named action, traced and auditable with callback support. `invoke` is not part of the language, so a script using it will not parse.

Does control return to the Super Agent after a Connected Subagent responds?

Not through a transition — transitions are one-way and control does not return to the previous subagent. But the action fronting a Connected Subagent is an ordinary action call, so its outputs land in the caller's variables via `set @variables.x = @outputs.y`, which is how the Super Agent consolidates the response. Execution returns to `start_agent` once the current subagent completes and Agentforce waits for the next utterance.

Can Agentforce Connected Subagents run in parallel?

No current documentation confirms concurrent invocation, so treat parallelism as unverified. Design every route as sequential and price the latency accordingly.

How do I scope an Agentforce subagent without overlapping topics?

Apply the one-sentence clarity test from the primary source: if you cannot state the subagent's job in a single sentence, the scope is wrong. A description that needs an "and also" is a description the router will misclassify.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment