Exam Room · Advanced Generative AI Developer

Orchestrating Multiple Bedrock Agents

· 38 min read

Generative AI Development · part of The Exam Room

The situation

The assistant behind the subscriber help desk started life as a single agent: one foundation model, one knowledge base of help articles, one tool that could look up an account. It answered “when is my next delivery” and “how do I pause” without fuss.

The job has grown. A single request now often needs several distinct things done: pull the subscriber record, check the delivery schedule for their postcode, work out whether a prorated charge is correct against the billing rules, and, if it cannot be resolved automatically, open a ticket and draft a reply in the right tone. Some of that is retrieval, some is arithmetic against a documented policy, some is a call into an internal API, and some is generation. The steps are not always the same from one request to the next, and a few of them call for genuinely different expertise.

The team can keep piling tools and knowledge bases onto the one agent, split the work across specialist agents with an orchestrator on top, or pin the common path down as a fixed pipeline. Each choice moves the control flow, the decision about what happens next, to a different place.

What actually matters

Control flow is the first thing to place. In a single agent, the sequence comes out of the model: it takes the request, returns a call to a tool or a knowledge base, takes the result, and returns the next call, looping until it produces an answer. That is powerful when the path cannot be known in advance, and it is exactly what you give up when you want the same steps every time. A fixed workflow inverts this: you draw the graph, wiring prompts, retrieval, tool calls, and conditions together, and the runtime walks the graph you designed. The model still does the work inside each step, and the order stays as drawn.

The second is whether the task really decomposes into distinct specialisms. A supervisor topology puts one agent in front of several workers, each with its own instructions, tools, and knowledge. The supervisor takes the request, selects which workers to invoke, passes them sub-tasks, and stitches the results together. This is worth it when the sub-tasks need different system prompts, different tools, or different guardrails: a billing specialist held to figures the source data supports, a tone-of-voice drafter that can run looser. It is not worth it when you are really just chaining steps that one agent could carry under a single prompt.

The third is the budget for extra model hops. Every agent turn is at least one foundation-model call, often several as it reasons and calls tools. A supervisor delegating to three workers is not three calls; it is the supervisor’s own reasoning plus each worker’s full reasoning loop, and the results flowing back up. That multiplies both latency and token cost. A fixed graph with two prompt steps and a retrieval lookup is a handful of calls you can count in advance; a supervisor over workers is a tree you can only bound loosely.

The fourth is auditability and predictability. When a step must happen the same way every time, in a compliance path, a refund calculation, a data-handling sequence, model-driven control flow is a liability: it is hard to prove the required step happens on every run. A drawn graph gives you a diagram you can point at and a run you can trace step by step. The more the outcome has to be defensible, the more that matters.

Underneath all of it: the simplest workable shape wins. Not every multi-step task needs an agent at all. If the sequence is fixed and you already own the code, a loop in your own application, call the model, run a tool, call the model again, is the most predictable and the cheapest thing on the table.

What we’ll filter on

  1. Who decides the control flow, the model at run time, or the designer ahead of time?
  2. Does the task genuinely split into distinct specialisms with different tools, prompts, or guardrails?
  3. What is the latency and cost budget for extra model hops?
  4. How much does the path need to be predictable, auditable, and the same every time?
  5. How much orchestration and failure surface is the team willing to own?

The landscape

A single agent on Bedrock AgentCore

One reasoning loop, hosted on a managed serverless runtime with per-session isolation, reaching its tools through the gateway, which exposes existing APIs and Lambda functions as MCP tools rather than as bespoke agent actions. The model runs the familiar reason-act-observe cycle, either in code you own on the runtime or in AgentCore’s managed harness, which takes a model, a system prompt, and a set of tools as configuration and runs the loop for you. The predecessor is not a shape a new build can pick: Amazon Bedrock Agents was renamed Bedrock Agents Classic and closed to new customers on 30 July 2026. An account with Bedrock Agents activity in the previous twelve months carries on as before; any other account gets an AccessDeniedException from CreateAgent. Good for a bounded task where the path varies but the skills do not: “answer support questions about this account, using these tools.” Its strength is flexible model-driven control flow inside one context; its ceiling is that everything shares one instruction prompt and one set of guardrails.

A supervisor over specialist workers

The managed harness covers the simple form, one agent exposed to another as a tool; a full supervisor topology is framework code you deploy on the runtime yourself. Either way AgentCore runs it: each agent gets its own isolated session, they share the gateway for tools and the identity capability for scoped credentials, and AgentCore Observability collects OpenTelemetry spans from all of them into one CloudWatch trace view. The supervisor’s model selects which workers to call and with what sub-task, receives their outputs, and composes the final answer.

Because the routing is code, a clear-intent request can skip the supervisor’s reasoning pass and go straight to one worker, which is a decision you make rather than a mode you switch on.

Fits when the problem breaks into distinct domains, billing, delivery, tone, that each need their own prompt and tools. The cost is more model calls, higher and less predictable latency, and a larger failure surface: any worker can fail, time out, or return something the supervisor must handle.

Agent Squad

An open-source framework whose job is routing a request to the right specialist agent. AWS started it, under the name Multi-Agent Orchestrator, but it has since moved out of the awslabs organisation to 2FastLabs and is maintained in the community, so it carries no AWS support. A classifier takes the incoming request together with the conversation history, matches it against the descriptions of the agents registered with it, and hands the request to the one that scores best. Shared context travels with the hand-off, so the chosen agent sees what came before and a subscriber who changes topic mid-thread lands on a different specialist without having to repeat themselves. When the classifier places nothing, one setting controls what follows: by default a configured generalist agent takes the request, and with that setting off the orchestrator returns a fixed message asking the subscriber to rephrase.

The three names in this space get listed in one breath, which makes them look like alternatives. Agent Squad does not build the individual agent; that is Strands Agents, the SDK the loop, prompt, and tool definitions are written in. It does not host the agent either; that is the AgentCore runtime, with its session isolation and scoped identity. They compose: build each specialist on Strands, route between them with Agent Squad, run the lot on AgentCore. Only AgentCore is an AWS service, though. The other two are open-source projects the team takes on as dependencies.

Fits when the work has already split into specialists and what is missing is a front door. Its ceiling is that the classifier picks one agent per turn, so a request needing two specialists in sequence has to be handled somewhere above it.

Bedrock Flows

A visual builder and runtime for a deterministic workflow. You place nodes, prompt nodes, knowledge-base nodes, Lambda nodes, condition nodes, inline code, loops, iterators, input and output, and wire the data between them. The graph is fixed; the model runs inside nodes, and the order never varies at run time.

Fits when the steps are known ahead of time and you want predictability, traceability, and less nondeterminism than an agent gives. “Mostly fixed with one flexible step” is still expressible, but not through the built-in agent node: that node takes the alias ARN of a Bedrock Agents Classic agent, and Classic has been closed to accounts without prior usage since 30 July 2026. Reach an agent on AgentCore from a Lambda node instead.

The cost is that you design and maintain the graph, and a genuinely novel path that you did not draw cannot run.

Application-code chaining over the Converse API

No managed orchestration at all: your own code calls the model with the Converse API, inspects the response, runs a tool when it comes back with a stopReason of tool_use and a toolUse block, and calls again with the matching toolResult. You own the loop, the retries, the branching. Fits simple deterministic sequences and cases where you want full control and minimal managed surface.

The cost is that you build and operate everything the managed options would have handled, and complex branching becomes your code to maintain.

Step Functions around the pieces

For workflows that reach well beyond the model, long-running human approvals, fan-out across many records, integration with dozens of AWS services, a Step Functions state machine can orchestrate Bedrock calls, agents, and Flows as steps. Model invocation has an optimised integration; agents, Flows, and AgentCore are reachable through the SDK integrations. It is the general-purpose orchestrator when the GenAI work is one part of a larger business process rather than the whole of it. Using Step Functions to orchestrate agent design patterns also makes the loop itself durable: a reasoning cycle drawn as explicit states gets per-state retries and an execution history, which a model’s internal loop does not give you. Heavier to build; the right home when durability, retries, and broad service integration dominate.

Evaluation

Side by side

Option Control flow decided by Handles distinct specialisms Extra model hops Predictable / auditable Ops and failure surface
Single agent on AgentCore Model, at run time ✗ (one prompt, one guardrail) Low to moderate ✗ (path varies) Low
Supervisor over workers Model, at run time ✓ High, hard to bound ✗ High (many agents)
Agent Squad router Classifier, per request ✓ (one specialist per turn) Low (one classification call) Partial (routing is testable) Moderate
Bedrock Flows Designer, ahead of time ✓ (agent behind a Lambda node) Countable ✓ Moderate
Converse API chain Your code Partial (you route) Low, you control ✓ You own it all
Step Functions Designer, ahead of time ✓ (across services) Countable ✓ Moderate to high

Reading it for this situation, a support flow where some steps are fixed policy and one or two want real specialism, the field narrows to three: a single agent if the skills are close enough to share a prompt, a supervisor over workers if billing and drafting genuinely need to be separate, and a Flow if the path is stable enough to draw and needs to be defensible.

The three shapes side by side

Bedrock Flow designer fixes the order Single agent model picks each step Supervisor + workers model delegates by skill Input Knowledge base Condition Lambda Output predictable, traceable, fixed Agent reason + act loop Tool A Tool B Knowledge base flexible, one prompt shared Supervisor delegates Billing agent Delivery agent Drafting agent specialised, more hops and failure
Same request, three places to put the control flow: the designer draws it, one model reasons through it, or a supervisor model splits it by skill.

The solution

Split it into a supervisor over specialist workers. Both halves of what changed point here. The steps are not the same from one request to the next, which rules out drawing the sequence ahead of time, and a few of them need genuinely different expertise, which is the condition a supervisor topology exists for.

Give each worker the prompt, tools, and guardrails its job actually needs. The billing worker gets a tight prompt, a calculation tool, and a contextual grounding check, so a figure the retrieved records do not support is filtered rather than sent. The delivery worker gets the schedule tools and the knowledge base. The drafting worker gets a looser prompt, room to write warmly, and no numeric authority at all. The supervisor takes the request, selects the workers it needs, passes each a sub-task, and composes what comes back. Each specialist is simpler and safer than one generalist trying to be all three, and a drafting worker with no numeric authority also has no credential that could charge a card.

Determinism where money moves comes from the tool, not from the topology. A refund calculation that must happen the same way every time belongs behind a calculation tool with its own validation and its own scoped role, invoked by the billing worker. The model’s output proposes the call; the tool’s own validation settles whether it runs and what it returns. That gives the compliance property without freezing the path everything else takes, which is the trade a drawn graph would have forced.

The cost is real and worth planning for. The supervisor reasons, then each invoked worker runs its own full loop, so one user request fans out into many model calls and p99 latency climbs with the depth of delegation. Every worker is also a thing that can time out or return something unusable, and the supervisor has to handle each case. Spans from every worker landing in one trace view are what keep that debuggable rather than guesswork. Where clear-intent requests dominate, route them straight to one worker and skip the supervisor’s reasoning pass, which removes much of that latency.

That routing has a name and an implementation. Agent Squad in front of the workers takes one small classification call instead of a full reasoning turn from a supervisor model, and its behaviour is deterministic enough to write cases against, so “my box did not arrive” can be tested into the delivery worker and kept there as the prompts drift. It is a community project rather than an AWS service now, so the team carries that dependency itself, and a classifier of its own in front of the workers does the same job where that is not a dependency worth carrying. What a classifier cannot do is decompose. A request that needs the billing worker to produce a figure and then the drafting worker to write around it is two specialists in sequence, and a router choosing one agent per turn has no way to express that. Run both: the classifier takes the single-intent traffic, which is most of the volume, and anything it cannot place cleanly falls through to the supervisor to break apart.

Each worker gets its own isolated session on the runtime, shares the gateway rather than carrying its own copy of every integration, and draws scoped credentials from the identity capability, so specialisation in the prompt is matched by specialisation in what each worker can actually reach.

Why not a single agent. It is what they have, and it is the cheapest model-driven option while the skills stay close enough to share a prompt. They no longer are. “Never invent a figure” for billing and “write warmly and loosely” for the reply are instructions that pull against each other, and one prompt serving both is the strain the team is already feeling.

Why not a Flow. A drawn graph gives a diagram you can point at and a run you can trace node by node, which is genuinely what a compliance path needs. It needs a stable path to draw, and this assistant does not have one: the steps vary per request, and a Flow only runs the path that was drawn. Keep it in mind for the sub-flows that do stabilise, and call an agent from a Lambda node inside one when a single step needs judgement.

Why not application-code chaining. For two or three known steps, owning the loop over Converse is less machinery than anything managed. This branches wider than that, and hand-rolled control flow at this size becomes the thing you wish you had expressed as a topology, with the retries, routing, and observability all yours to build.

Worked example

A subscriber writes: “I paused last month but I have still been charged, and my box did not arrive this week either.”

As a single agent. The one agent reads the message, calls the account tool to fetch the subscription and its pause history, calls the billing tool to check the charge against the pause date, queries the delivery knowledge base for the postcode’s schedule, returns that the charge was an error and the delivery was correctly skipped, and drafts a reply. One prompt carried all of it, and the order came out of the model. It works because billing and drafting were close enough to share instructions. If the reply had needed a warmer register than the strict billing instructions allowed, the single prompt would have started to strain.

As a supervisor with workers. The supervisor reads the message and delegates: the billing worker (tight prompt, calculation tool, grounding check on figures) confirms the charge should be reversed and returns the amount; the delivery worker checks the schedule and confirms the skip was correct; the drafting worker (looser prompt, no numeric authority) takes both findings and writes the reply. The supervisor composes the outcome. Each specialist was simpler and safer than one generalist, and the numeric guardrail lived exactly where numbers were handled. The request took the supervisor’s reasoning plus three worker loops, and the reply came back a few seconds slower than the single agent managed.

As a Flow. Input node takes the message. A prompt node classifies it as a billing-and-delivery query. A knowledge-base node pulls the pause and delivery policy. A Lambda node computes whether the charge was valid against the pause date. A condition node branches: valid charge to a “explain the charge” prompt node, invalid charge to a Lambda that files a refund ticket and then a prompt node that drafts the apology. Output node returns the draft. Every run takes the same shape, and the refund step is provably always reached when the charge is invalid, which is exactly what you want when money moves.

All three resolve the subscriber’s problem. The difference is who decided the order, how many model calls it took, and whether you can point at a diagram afterwards and show it always does the right thing.

What’s worth remembering

  1. Control flow sits in one of two places: with the model at run time (agents), or with the designer ahead of time (Flows, Step Functions, your own code).
  2. A single agent is the floor. Reach past it only when one prompt and one set of guardrails can no longer serve the whole job.
  3. Multi-agent’s cost is real. Each worker runs its own reasoning loop, so latency and token spend climb with the depth of delegation, and every worker is a new thing that can fail.
  4. Bedrock Flows trade flexibility for predictability: a fixed graph you can trace node by node, which needs a path stable enough to draw in the first place.
  5. A compliance requirement does not automatically mean a drawn graph. Determinism where money moves can come from a guarded tool the model calls rather than from freezing the path everything else takes.
  6. A Flow is not all-or-nothing. A Lambda node that calls an agent drops one model-driven step into an otherwise deterministic pipeline, now that the built-in agent node reaches only Bedrock Agents Classic.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.