Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions sfn-parallel-bedrock-agentcore-multi-agent-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Multi-agent orchestration with AWS Step Functions and Amazon Bedrock AgentCore

This pattern deploys an AWS Step Functions workflow that orchestrates multiple specialized AI agents running on Amazon Bedrock AgentCore. Using the native Step Functions SDK integration for Bedrock AgentCore, a Parallel state fans out three branches simultaneously, each invoking a specialized AgentCore runtime built with the Strands Agents SDK. When all branches complete, a synthesis agent combines the findings into a single result.

Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/sfn-parallel-bedrock-agentcore-multi-agent-cdk](https://serverlessland.com/patterns/sfn-parallel-bedrock-agentcore-multi-agent-cdk)

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (latest available version) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) (version 2.221.0 or later) installed and configured
* [Node.js 22.x](https://nodejs.org/) installed
* [Finch](https://runfinch.com/), [Docker](https://www.docker.com/products/docker-desktop/) or a compatible tool (required to build the agent container image)

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:

```bash
git clone https://github.com/aws-samples/serverless-patterns
```

1. Change directory to the pattern directory:

```bash
cd sfn-parallel-bedrock-agentcore-multi-agent-cdk
```

1. Install the project dependencies:

```bash
npm install
```

1. Deploy the CDK stacks:

```bash
cdk deploy --all
```

Note: This deploys two stacks — `MultiAgentCoreStack` (the agent runtimes) and `MultiAgentOrchestratorStack` (the Step Functions workflow). Deploy to your default AWS region. Please refer to the [AWS capabilities explorer](https://builder.aws.com/build/capabilities/explore) for feature availability in your desired region.

1. Note the outputs from the CDK deployment process. These contain the resource ARNs used for testing.

## How it works

This pattern creates two stacks:

![state-machine](state-machine.png)

1. **MultiAgentOrchestratorStack** — Deploys a Step Functions state machine that orchestrates the multi-agent workflow using the native [AWS SDK integration for Bedrock AgentCore](https://aws.amazon.com/about-aws/whats-new/2026/03/aws-step-functions-sdk-integrations/) (`aws-sdk:bedrockagentcore:invokeAgentRuntime`):
- A **Parallel** state fans out three branches simultaneously
- Each branch includes built-in retries (2 attempts with exponential backoff) for fault tolerance
- A **SynthesisAgent** Task state combines the three research outputs into a synthesis prompt, invokes the synthesis runtime, and formats the final output

2. **MultiAgentCoreStack** — Deploys two containerized Python agent runtimes on Amazon Bedrock AgentCore using the Strands Agents SDK:
- A **research runtime** that handles three specialized roles (market data, competitive analysis, and news) based on the `role` field in the payload
- A **synthesis runtime** that combines research findings into a cohesive executive report

## Testing

After deployment, start an execution using the AWS CLI or from the AWS Console.

### Start an execution

Use the state machine ARN from the CDK output (`StateMachineArn`):

```bash
aws stepfunctions start-execution \
--state-machine-arn <StateMachineArn> \
--input '{"prompt": "What is the current state of the electric vehicle market in Europe?"}' \
--query 'executionArn' --output text
```

### Check execution status

```bash
aws stepfunctions describe-execution \
--execution-arn <executionArn> \
--query '{status: status, output: output}'
```

### Expected Output

Once the execution completes (typically 60–90 seconds), the output contains:

```json
{
"question": "What is the current state of the electric vehicle market in Europe?",
"report": "{\"role\": \"synthesis\", \"answer\": \"# Executive Research Report: European EV Market\\n\\n## Key Findings\\n...\"}",
"timestamp": "2026-03-27T22:09:46.253Z"
}
```

The three research agents run in parallel, and the synthesis agent produces a unified report combining market data, competitive intelligence, and recent news.

## Cleanup

1. Delete the stacks:

```bash
cdk destroy --all
```

1. Confirm the stacks have been deleted by checking the AWS CloudFormation console or running:

```bash
aws cloudformation list-stacks --stack-status-filter DELETE_COMPLETE
```

----
Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__
*.pyc
.git
18 changes: 18 additions & 0 deletions sfn-parallel-bedrock-agentcore-multi-agent-cdk/agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM ghcr.io/astral-sh/uv:python3.13-alpine

WORKDIR /app

ENV UV_SYSTEM_PYTHON=1 \
UV_COMPILE_BYTECODE=1

COPY requirements.txt requirements.txt
RUN uv pip install -r requirements.txt

RUN adduser -D -u 1000 bedrock_agentcore
USER bedrock_agentcore

EXPOSE 8080 8000

COPY . .

CMD ["python", "-m", "agent"]
68 changes: 68 additions & 0 deletions sfn-parallel-bedrock-agentcore-multi-agent-cdk/agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
Strands Agent for AgentCore Runtime.
Accepts a role-based prompt, invokes the LLM, and returns the result.
Used by Step Functions via synchronous InvokeAgentRuntime calls.
"""
import json
import logging

from strands import Agent
from strands.models import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp

logger = logging.getLogger(__name__)
app = BedrockAgentCoreApp()

SYSTEM_PROMPTS = {
"market_data": (
"You are a market data research analyst. "
"Provide quantitative market data, trends, market size, growth rates, "
"and key financial metrics relevant to the topic."
),
"competitive_analysis": (
"You are a competitive intelligence analyst. "
"Identify key competitors, their strengths and weaknesses, market positioning, "
"and strategic differentiators relevant to the topic."
),
"news": (
"You are a news research analyst. "
"Summarize the most recent and relevant news, announcements, regulatory changes, "
"and industry developments related to the topic."
),
"synthesis": (
"You are a senior research director. "
"You receive research findings from three analysts covering market data, "
"competitive analysis, and recent news. Synthesize these into a single cohesive "
"executive report with key findings, implications, and recommendations."
),
}


@app.entrypoint
def entrypoint(payload):
"""
Main entrypoint invoked by AgentCore Runtime.

Expects payload:
- prompt: the research question or combined findings
- role: one of market_data, competitive_analysis, news, synthesis
- model (optional): { modelId: "..." }
"""
prompt = payload.get("prompt", "")
role = payload.get("role", "synthesis")
model_config = payload.get("model", {})
model_id = model_config.get(
"modelId", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
)

system_prompt = SYSTEM_PROMPTS.get(role, SYSTEM_PROMPTS["synthesis"])

model = BedrockModel(model_id=model_id, max_tokens=4096, temperature=0.7)
agent = Agent(model=model, system_prompt=system_prompt)

result = agent(prompt)
return {"role": role, "answer": str(result)}


if __name__ == "__main__":
app.run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
strands-agents
bedrock-agentcore
boto3
22 changes: 22 additions & 0 deletions sfn-parallel-bedrock-agentcore-multi-agent-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { AgentCoreStack } from '../lib/agentcore-stack';
import { OrchestratorStack } from '../lib/orchestrator-stack';

const app = new cdk.App();

const env = {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
};

const agentCoreStack = new AgentCoreStack(app, 'MultiAgentCoreStack', { env });

new OrchestratorStack(app, 'MultiAgentOrchestratorStack', {
env,
researchRuntimeArn: agentCoreStack.researchRuntimeArn,
researchEndpointUrl: agentCoreStack.researchEndpointUrl,
synthesisRuntimeArn: agentCoreStack.synthesisRuntimeArn,
synthesisEndpointUrl: agentCoreStack.synthesisEndpointUrl,
});
8 changes: 8 additions & 0 deletions sfn-parallel-bedrock-agentcore-multi-agent-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts",
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"title": "Multi-agent orchestration with AWS Step Functions",
"description": "Orchestrate parallel specialized AI agents on Amazon Bedrock AgentCore using the native Step Functions SDK integration with Parallel state for fan-out",
"language": "TypeScript",
"level": "300",
"framework": "AWS CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern deploys an AWS Step Functions workflow that orchestrates multiple specialized AI agents running on Amazon Bedrock AgentCore and synthesizes their results into a single output.",
"Using the native Step Functions SDK integration for Bedrock AgentCore, a Parallel state fans out three research branches simultaneously: market data, competitive analysis, and recent news. When all branches complete, a synthesis agent receives the combined outputs and produces the final report. Each branch includes built-in retries for fault tolerance, and Step Functions handles the fan-out, join, and result aggregation automatically."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-parallel-bedrock-agentcore-multi-agent-cdk",
"templateURL": "serverless-patterns/sfn-parallel-bedrock-agentcore-multi-agent-cdk",
"projectFolder": "sfn-parallel-bedrock-agentcore-multi-agent-cdk",
"templateFile": "lib/orchestrator-stack.ts"
}
},
"resources": {
"bullets": [
{
"text": "AWS Step Functions adds 28 new service integrations, including Amazon Bedrock AgentCore",
"link": "https://aws.amazon.com/about-aws/whats-new/2026/03/aws-step-functions-sdk-integrations/"
},
{
"text": "AWS Step Functions SDK service integrations",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html"
},
{
"text": "AWS Step Functions Parallel state documentation",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-parallel-state.html"
},
{
"text": "Amazon Bedrock AgentCore documentation",
"link": "https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore.html"
},
{
"text": "Strands Agents SDK",
"link": "https://github.com/strands-agents/sdk-python"
},
{
"text": "AWS CDK Developer Guide",
"link": "https://docs.aws.amazon.com/cdk/latest/guide/"
}
]
},
"deploy": {
"text": [
"npm install",
"cdk deploy --all"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stacks: <code>cdk destroy --all</code>."
]
},
"authors": [
{
"name": "Ben Freiberg",
"image": "https://serverlessland.com/assets/images/resources/contributors/ben-freiberg.jpg",
"bio": "Ben is a Senior Solutions Architect at Amazon Web Services (AWS) based in Frankfurt, Germany.",
"linkedin": "benfreiberg"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';
import { Construct } from 'constructs';
import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha';

export class AgentCoreStack extends cdk.Stack {
public readonly researchRuntimeArn: string;
public readonly researchEndpointUrl: string;
public readonly synthesisRuntimeArn: string;
public readonly synthesisEndpointUrl: string;

constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const agentRuntimeArtifact = agentcore.AgentRuntimeArtifact.fromAsset(
path.join(__dirname, '../agent'),
);

const bedrockPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'bedrock:InvokeModel',
'bedrock:InvokeModelWithResponseStream',
],
resources: [
'arn:aws:bedrock:*::foundation-model/*',
'arn:aws:bedrock:*:*:inference-profile/*',
],
});

const ecrPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'ecr:GetAuthorizationToken',
'ecr:BatchGetImage',
'ecr:GetDownloadUrlForLayer',
'ecr:BatchCheckLayerAvailability',
],
resources: ['*'],
});

// Research runtime — handles market data, competitive analysis, and news roles
const researchRuntime = new agentcore.Runtime(this, 'ResearchAgentRuntime', {
runtimeName: 'researchAgent',
agentRuntimeArtifact,
description: 'Specialized research agent for market data, competitive analysis, and news',
});
researchRuntime.addToRolePolicy(bedrockPolicy);
researchRuntime.addToRolePolicy(ecrPolicy);

// Synthesis runtime — combines findings into a final report
const synthesisRuntime = new agentcore.Runtime(this, 'SynthesisAgentRuntime', {
runtimeName: 'synthesisAgent',
agentRuntimeArtifact,
description: 'Synthesis agent that combines research findings into an executive report',
});
synthesisRuntime.addToRolePolicy(bedrockPolicy);
synthesisRuntime.addToRolePolicy(ecrPolicy);

this.researchRuntimeArn = researchRuntime.agentRuntimeArn;
this.researchEndpointUrl = `https://bedrock-agentcore-runtime.${this.region}.amazonaws.com/runtimes/${researchRuntime.agentRuntimeId}/endpoints/DEFAULT`;
this.synthesisRuntimeArn = synthesisRuntime.agentRuntimeArn;
this.synthesisEndpointUrl = `https://bedrock-agentcore-runtime.${this.region}.amazonaws.com/runtimes/${synthesisRuntime.agentRuntimeId}/endpoints/DEFAULT`;

new cdk.CfnOutput(this, 'ResearchRuntimeArn', { value: researchRuntime.agentRuntimeArn });
new cdk.CfnOutput(this, 'SynthesisRuntimeArn', { value: synthesisRuntime.agentRuntimeArn });
}
}
Loading