AWS Builder Center
Multi-Agents with Bedrock and Claude: One Supervisor and Three Specialists

Multi-Agents with Bedrock and Claude: One Supervisor and Three Specialists

MVP using multi-agents on AWS Bedrock with Anthropic models to generate flashcards from study material. All running on a serverless setup (API Gateway + Lambda + Bedrock), with scale-to-zero compute and a pay-as-you-go cost model.

Principal Data & AI Architect

The Problem We Were Trying to Solve

Generating high-quality flashcards from raw study material is not a single cognitive task. It actually breaks into three:
  1. Reading the material and deciding what the meaningful sub-topics are.
  2. Writing focused Q&A pairs for each of those sub-topics.
  3. Tutoring the student in real time when they get stuck on a card during a study session.
Each one of these has a different “shape”: one is analytical, one is generative with strict format, and the third is conversational and stateful. Squeezing all three into one monolithic prompt is the obvious first attempt, and also the one that produces the worst result the model spends tokens deciding which job it’s doing, the output schema becomes inconsistent, and a regression in one area silently breaks the others.
So we split the workload. And once we did, the question stopped being “what should the prompt look like” and became “what’s the shape of the system that orchestrates these three jobs”. That’s where Bedrock’s multi-agent collaboration feature became attractive it makes the split native and managed instead of something we have to glue together with our own orchestration code.

The Shape of the System

At the top level, every request flows through the same four layers, regardless of which agent ends up handling it:

Layer 1: The Edge — API Gateway

API Gateway is doing the essential infrastructure work that we explicitly do not want to leak into the agent code:
  • Routing. A single public surface that maps stable URLs to the right backend — clients don’t need to know anything about Lambda, Bedrock, or how agents are wired internally.
  • Throttling and quotas. Per-key rate limits and burst control sit at the edge, so an over-eager client never reaches our compute (or our model bill).
  • Schema enforcement. Request and response shapes are validated before invocation, which means a malformed payload fails cheaply instead of waking up a Lambda.
  • Auth delegation. Authorization is offloaded to a dedicated Lambda authorizer (next layer), so the same auth strategy can protect any number of routes without each backend reimplementing it.
  • Observability. Access logs, traces, and error metrics live at this layer, decoupled from the agent invocation pipeline.
The point is that none of these concerns belong in the agent layer. Pushing them to the edge is what lets the rest of the stack stay focused on the actual problem.

Layer 2: The Authorizer

A Lambda authorizer sits between the gateway and the business-logic Lambda. Its only job is: given a request, decide whether it gets to talk to anything downstream.
In our case it validates a bearer token, but the real architectural value is the separation:
  • The authorizer can evolve independently — we can swap a static token for JWT validation, mTLS, or a key-rotation scheme without touching any agent code.
  • Caching at this layer (API Gateway caches authorizer responses) means repeat callers don’t pay the auth cost on every request.
  • A single authorizer protects every route on the gateway, which gives us one place to revoke access and one place to audit.
This is a pattern that scales much better than wiring auth into each handler and on an agentic backend, where you’ll inevitably add more agents over time, that compounds.

Layer 3: The Invocation Lambda

Past the gateway and the authorizer, the request hits a single Lambda whose job is also narrow on purpose: map a logical agent name to a concrete Bedrock agent and forward the call.
The Lambda is essentially a registry plus a forwarder. Adding a new agent is a one-line change to a dictionary, no new infrastructure, no new endpoint, no new authorizer. This matters because as soon as you have one multi-agent system, you tend to want others (we already have agents for recipes, weather, and other experiments living behind the same Lambda).
The Lambda also handles the messy parts of the request shape — file attachments, for example, get base64-decoded and forwarded to Bedrock's native session-state file format, so the agents themselves can reason directly over uploaded PDFs and images without us standing up a separate extraction pipeline.
What the Lambda does not do: parse intent, call models directly, retry, or know anything about the prompts. Those are the supervisor's responsibility.

Layer 4: The Cognitive Layer — Supervisor and Specialists

This is where the actual work happens, and where the multi-agent pattern earns its keep.

The supervisor

The supervisor agent runs on Claude Haiku with agentCollaboration: SUPERVISOR_ROUTER. Its only job is intent classification and delegation, it never produces a final answer:
1
2
3
4
5
6
7
8
9
10
11
12
13
You are the central router for Snail Flashcards. You never answer
the user directly. Your sole function is to identify the intent
and delegate to the appropriate specialized agent.
AVAILABLE AGENTS & ACTIVATION SIGNALS
SPLITTER → [Subject] + [Material] + [Request for sub-subjects]
GENERATOR → [Subject] + [Sub-subject] + [Material] + [Request for cards]
TUTOR → [Card Q&A Context] + [Student question]
ROUTING RULES
MATCH — identify which signal the input fits.
DELEGATE — invoke the corresponding agent.
PASS-THROUGH — forward the input verbatim.
VERBATIM OUTPUT — return the agent's response unchanged.
FALLBACK — if nothing matches, return "".
The classifier reads the shape of the input rather than performing semantic analysis on it. That makes routing predictable, easy to test, and resistant to prompt drift.

The specialists

Each collaborator is single-purpose and contractually bound to a strict output format and each one runs on a different Claude model, deliberately picked for the shape of its task:
  • The splitter — Claude Haiku. Takes a subject and its material and returns a JSON array of sub-topic names. Nothing else. No markdown, no preamble, no commentary. Three to ten items, parsed straight into the next stage. The job is analytical and fast, the output schema is rigid, and it runs once per deck Haiku is the right tradeoff: speed, cost, and structured output are exactly what the task rewards.
  • The generator — Claude Opus. Takes one sub-topic plus the material and returns exactly ten Q&A pairs as JSON objects with front and back fields. The "exactly ten" rule isn't arbitrary it makes frontend progress tracking deterministic and removes a class of UI bugs. This is the agent whose output the user will actually study from, so quality matters more here than anywhere else: the difference between a card that drills the concept and one that paraphrases the source material is what separates a useful deck from a useless one. We pay for Opus on this leg deliberately.
  • The tutor — Claude Sonnet. The only conversational agent. It receives the current flashcard’s question and answer as context on every turn, plus the student’s message, and replies in a teaching register. Concise, grounded in the card, no walls of markdown. Sonnet is the sweet spot here: the tutor is in the user’s loop (latency matters), but the response needs more pedagogical nuance than Haiku can reliably deliver Sonnet gives us conversational quality without paying Opus prices on every chat turn.
The takeaway is that model choice is part of the architecture, not a global default. A monolithic prompt forces you to pick one model for everything; a multi-agent system lets each agent run on the model that best matches its job. Haiku for routing and parsing, Opus for the high-stakes generation, Sonnet for the conversational loop.

Why this beats one big prompt

Three things, in order of importance:
  1. Strict JSON contracts for the leaf agents eliminate parsing bugs. The splitter and generator are pure functions: input X, output JSON Y.
  2. The supervisor’s prompt only worries about routing. The specialists’ prompts only worry about doing one thing well. A change to one doesn’t cascade into the others.
  3. Specialization is observable. When something goes wrong, the failure is localized bad sub-topics is the splitter’s problem, malformed cards is the generator’s, off-topic answers is the tutor’s. A monolithic prompt blends those failure modes into one mush.

Two Patterns of Use

The same pipeline serves two very different access patterns.

Pattern A — Deck generation: stateless and sequential

When the user pastes study material, the frontend makes two rounds of supervisor calls, each with a fresh session ID:
  1. Round 1: one call to split the material into sub-topics. Returns a JSON array.
  2. Round 2: one call per sub-topic to generate ten cards each. Returns a JSON array of {front, back} pairs.

Pattern B — AI tutor: stateful and conversational

During a study session, a chat panel sits next to the active card. Every message the student sends carries the current card’s Q&A
1
2
3
4
5
Current flashcard:
- Question: <front>
- Answer: <back>

Student question: <message>
The session ID here is persistent per subject, stored locally and reused across turns. That triggers the supervisor’s session memory (SESSION_SUMMARY, last 10 sessions, 1-day retention), so the tutor remembers the last few exchanges and the student can ask natural follow-ups instead of restating context every time.
Same architecture, two memory regimes chosen per call rather than baked into the system.

UX: Making the Orchestration Visible

A deck takes five to ten supervisor calls to generate, each routed through a specialist. Hiding that behind a single spinner felt wrong — the user is watching a multi-agent system work, and the wait deserves to feel structured.
So the UI shows a live tracker:
It exposes the structure of the work without exposing any of the implementation. The orchestration becomes part of the product, not a hidden cost.

Tradeoffs and Lessons

A few things we’d repeat, and a few we’d do differently:
  • Push infrastructure concerns to the edge. Auth, throttling, and routing belong in API Gateway and the authorizer, not in agent code. It keeps the cognitive layer focused on cognition.
  • One Lambda for many agents. A logical-name → Bedrock-ID mapping makes new agents almost free to add. We’ve added several since.
  • Strict output contracts on leaf agents. Returning raw JSON (no markdown fences, no preamble) is non-negotiable. It’s the single biggest source of stability in the pipeline.
  • Match the model to the job, not to the system. Haiku for routing and parsing, Opus for the generation the user actually studies from, Sonnet for the in-the-loop tutor. A multi-agent setup makes that choice composable instead of global.
  • Pick the memory regime per call, not per system. Stateless for batch generation, stateful for the tutor. Same supervisor, different session strategies.
Thank you for reading! I hope this was useful!
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article