Docs
Operate

Orchestration Agent Fanout

Quick Answer

Prefer agent-internal fanout when one coordinator agent should decide during
its own reasoning which specialist agents to run in parallel.

The preferred shape is:

  1. Define reusable specialists in top-level :agents.
  2. Give the coordinator agent :tools {:fanout {:agents [...]}}.
  3. Let the coordinator call the generated fanout_agents tool with a bounded
    item list.
  4. Let the coordinator synthesize the collected successes and failures into
    the final answer.

Flow-level flow/step :fanout with {:type :agent ...} items is still valid.
Use it when :flow already knows the sibling agent calls deterministically.

Do not put full agent definitions inside fanout items. Both patterns reference
top-level agents by id.

Choose The Pattern

SituationUse
A coordinator agent should decide whether and how to fan out while reasoningAgent-internal fanout tool
The parent :flow already has a deterministic item listflow/step :fanout
A specialist needs an adaptive one-at-a-time conversation with the coordinator:tools {:agents [...]} direct delegation
You want parallel raw tool calls rather than specialist agentsWrap those tool capabilities in specialist agents or packaged steps, then fan out to those agents
Each branch is a larger workflow or installed app boundaryflow/call-flow or :fanout :call-flow items

Preferred: Agent-Internal Fanout

This example exposes a bounded fanout_agents tool to the coordinator. The
model chooses which allowlisted specialists to call, but the runtime enforces
the configured agent allowlist, item limit, concurrency cap, and error policy.

{:slug :repo-multi-agent-review
 :name "Repo Multi-Agent Review"

 :agents [{:id :orchestrator/review
           :description "Coordinate specialist reviews."
           :objective "Review the request and repo tree. When parallel specialist review is useful, call fanout_agents with the smallest useful item list. Then synthesize the collected successes and failures into a concise Markdown action plan."
           :input-schema [:map
                          [:request :string]
                          [:repo-tree :any]]
           :tools {:fanout {:agents [:review/security
                                      :review/performance]
                            :max-items 5
                            :max-concurrency 3
                            :on-error :collect}}}

          {:id :review/security
           :description "Security reviewer."
           :objective "Review the selected area for security risks."
           :input-schema [:map
                          [:request :string]
                          [:repo-tree :any]
                          [:area :string]
                          [:focus {:optional true} :string]]
           :output {:format :json}}

          {:id :review/performance
           :description "Performance reviewer."
           :objective "Review the selected area for performance risks."
           :input-schema [:map
                          [:request :string]
                          [:repo-tree :any]
                          [:area :string]
                          [:focus {:optional true} :string]]
           :output {:format :json}}]

 :flow
 '(let [input (flow/input)]
    (flow/step :orchestrator/review :coordinate-review
               {:request (:request input)
                :repo-tree (:repo-tree input)}))}

The coordinator sees a tool shaped like:

{
  "items": [
    {
      "agent": "review/security",
      "input": {
        "area": "auth",
        "focus": "OAuth callback and token storage"
      }
    },
    {
      "agent": "review/performance",
      "input": {
        "area": "database queries"
      }
    }
  ]
}

By default, each item inherits the coordinator agent's current :inputs, so
the specialist calls above also receive request and repo-tree. Set
:inherit-inputs? false when each fanout item should be fully self-contained.
The fanout tool intentionally delegates only to the configured top-level
agents; it does not expose arbitrary tool execution to the model. When a branch
needs tool access, put those tools on the specialist agent definition.

Flow-Level Named-Agent Fanout

Use flow-level fanout when deterministic orchestration should build and run
the whole batch without asking a coordinator model to choose it.

'(let [input (flow/input)
       fanout (flow/step :fanout :parallel-review
                         {:items [{:type :agent
                                   :agent :review/security
                                   :input {:request (:request input)
                                           :repo-tree (:repo-tree input)
                                           :area "auth"}}
                                  {:type :agent
                                   :agent :review/performance
                                   :input {:request (:request input)
                                           :repo-tree (:repo-tree input)
                                           :area "database queries"}}]
                          :max-concurrency 2
                          :on-error :collect})]
   fanout)

This is also the right pattern when a deterministic :function step has
already shaped and allowlisted the item vector for the parent flow.

Guardrails

Use these defaults unless you have a specific reason not to:

  • Put reusable specialist behavior in top-level :agents.
  • Prefer the agent-internal fanout tool when the coordinator should choose the
    branches dynamically.
  • Use flow-level :fanout when the branch list is deterministic.
  • Keep the fanout tool's :agents allowlist small and explicit.
  • Use :on-error :collect when partial results are useful.
  • Keep :max-items and :max-concurrency below provider and workspace limits.
  • Pass compact inputs or resource refs, not huge inline payloads.
  • Put :input-schema on every specialist.
  • Use :output-schema or :output {:format :json} when downstream synthesis
    depends on structured specialist output.

Result Handling

The fanout tool returns the same collected result shape as flow/step :fanout:

{:successes [{:item ... :result ...}]
 :failures [{:item ... :error ...}]
 :total 2
 :success-count 1
 :failure-count 1}

With :on-error :collect, the coordinator can decide how to handle partial
failures in its final answer.

Typical synthesis behavior:

  • summarize successful findings
  • list failed specialist ids or areas
  • say whether the result is complete or partial
  • recommend reruns only for failed units

Direct Named-Agent Calls

When you do not need parallelism, call a named agent directly by using its
qualified id as the step type:

'(flow/step :review/security :scan-auth
   {:area "auth"
    :repo-tree repo-tree})

Inside an agent, publish the specialist directly with :tools {:agents [...]}:

{:id :orchestrator/review
 :objective "Delegate to the security specialist when needed."
 :tools {:agents [:review/security]}}

Use :tools {:fanout ...} only when the coordinator may need to launch several
sibling specialists and collect them as one tool result.

Common Mistakes

MistakeUse Instead
Put a full agent config inside fanout itemsDefine it once in top-level :agents and reference it by id
Give the model a broad or open-ended fanout surfaceConfigure :tools {:fanout {:agents [...] :max-items N :max-concurrency N}}
Let the planner invent arbitrary agent idsUse the configured fanout :agents allowlist
Use flow-level :fanout when the coordinator should decide branches dynamicallyUse the agent-internal fanout tool
Use the fanout tool for one sequential specialist conversationUse direct :tools {:agents [...]} delegation
Fail the whole batch on one specialist failureUse :on-error :collect and synthesize partial results

Related

As of May 28, 2026