1
0
mirror of synced 2026-08-05 19:17:10 +00:00
Files
Bharathi Srinivasan e746bf7764 Features folder revamp (#1540)
adding scripts for agentcore features; jupyter notebooks moved to workshops; reorganising folders
2026-05-20 18:35:16 -07:00

1010 lines
38 KiB
YAML

AWSTemplateFormatVersion: "2010-09-09"
Description: "Multi-Agent AgentCore deployment - Two agents where agent1 orchestrates and calls agent2"
# ============================================================================
# PARAMETERS SECTION
# ============================================================================
Parameters:
# Agent Configuration
Agent1Name:
Type: String
Default: "OrchestratorAgent"
Description: "Name for the orchestrator agent runtime (agent1)"
AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]{0,47}$"
ConstraintDescription: "Must start with a letter, max 48 characters, alphanumeric and underscores only"
Agent2Name:
Type: String
Default: "SpecialistAgent"
Description: "Name for the specialist agent runtime (agent2)"
AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]{0,47}$"
ConstraintDescription: "Must start with a letter, max 48 characters, alphanumeric and underscores only"
# Container Configuration
ImageTag:
Type: String
Default: "latest"
Description: "Tag for the Docker images"
# Network Configuration
NetworkMode:
Type: String
Default: "PUBLIC"
Description: "Network mode for AgentCore resources"
AllowedValues:
- PUBLIC
- PRIVATE
# ECR Configuration
ECRRepositoryName:
Type: String
Default: "multi-agent"
Description: "Base name of the ECR repositories"
# ============================================================================
# METADATA SECTION
# ============================================================================
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Agent Configuration"
Parameters:
- Agent1Name
- Agent2Name
- NetworkMode
- Label:
default: "Container Configuration"
Parameters:
- ECRRepositoryName
- ImageTag
ParameterLabels:
Agent1Name:
default: "Agent 1 Name (Orchestrator)"
Agent2Name:
default: "Agent 2 Name (Specialist)"
NetworkMode:
default: "Network Mode"
ECRRepositoryName:
default: "ECR Repository Base Name"
ImageTag:
default: "Image Tag"
# ============================================================================
# RESOURCES SECTION
# ============================================================================
Resources:
# ========================================================================
# ECR MODULE - Container Registry
# ========================================================================
ECRRepositoryAgent1:
Type: AWS::ECR::Repository
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
RepositoryName: !Sub "${AWS::StackName}-${ECRRepositoryName}-agent1"
ImageTagMutability: IMMUTABLE
EmptyOnDelete: true
ImageScanningConfiguration:
ScanOnPush: true
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
- Sid: AllowPullFromAccount
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ecr-repository-agent1"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: ECR
ECRRepositoryAgent2:
Type: AWS::ECR::Repository
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
RepositoryName: !Sub "${AWS::StackName}-${ECRRepositoryName}-agent2"
ImageTagMutability: IMMUTABLE
EmptyOnDelete: true
ImageScanningConfiguration:
ScanOnPush: true
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
- Sid: AllowPullFromAccount
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ecr-repository-agent2"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: ECR
# ========================================================================
# IAM MODULE - Security and Permissions
# ========================================================================
# Agent1 Execution Role (with permissions to invoke Agent2)
Agent1ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-agent1-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AssumeRolePolicy
Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/BedrockAgentCoreFullAccess
Policies:
- PolicyName: Agent1ExecutionPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRImageAccess
Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Resource: !GetAtt ECRRepositoryAgent1.Arn
- Sid: ECRTokenAccess
Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:DescribeLogStreams
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- Sid: XRayTracing
Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource: "*"
- Sid: CloudWatchMetrics
Effect: Allow
Resource: "*"
Action: cloudwatch:PutMetricData
Condition:
StringEquals:
cloudwatch:namespace: bedrock-agentcore
- Sid: GetAgentAccessToken
Effect: Allow
Action:
- bedrock-agentcore:GetWorkloadAccessToken
- bedrock-agentcore:GetWorkloadAccessTokenForJWT
- bedrock-agentcore:GetWorkloadAccessTokenForUserId
Resource:
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default"
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/*"
- Sid: BedrockModelInvocation
Effect: Allow
Action:
- bedrock:InvokeModel
- bedrock:InvokeModelWithResponseStream
Resource: "*"
- Sid: InvokeAgent2Runtime
Effect: Allow
Action:
- bedrock-agentcore:InvokeAgentRuntime
Resource: !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:runtime/*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent1-execution-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# Agent2 Execution Role (basic permissions)
Agent2ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-agent2-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AssumeRolePolicy
Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/BedrockAgentCoreFullAccess
Policies:
- PolicyName: Agent2ExecutionPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRImageAccess
Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Resource: !GetAtt ECRRepositoryAgent2.Arn
- Sid: ECRTokenAccess
Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:DescribeLogStreams
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- Sid: XRayTracing
Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource: "*"
- Sid: CloudWatchMetrics
Effect: Allow
Resource: "*"
Action: cloudwatch:PutMetricData
Condition:
StringEquals:
cloudwatch:namespace: bedrock-agentcore
- Sid: GetAgentAccessToken
Effect: Allow
Action:
- bedrock-agentcore:GetWorkloadAccessToken
- bedrock-agentcore:GetWorkloadAccessTokenForJWT
- bedrock-agentcore:GetWorkloadAccessTokenForUserId
Resource:
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default"
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/*"
- Sid: BedrockModelInvocation
Effect: Allow
Action:
- bedrock:InvokeModel
- bedrock:InvokeModelWithResponseStream
Resource: "*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent2-execution-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# CodeBuild Service Role
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-codebuild-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- Sid: ECRAccess
Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:GetAuthorizationToken
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource:
- !GetAtt ECRRepositoryAgent1.Arn
- !GetAtt ECRRepositoryAgent2.Arn
- "*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# Lambda Custom Resource Role
CustomResourceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-custom-resource-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CustomResourcePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRAccess
Effect: Allow
Action:
- ecr:ListImages
- ecr:BatchDeleteImage
- ecr:GetAuthorizationToken
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource:
- !GetAtt ECRRepositoryAgent1.Arn
- !GetAtt ECRRepositoryAgent2.Arn
- Sid: CodeBuildAccess
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- codebuild:BatchGetProjects
Resource:
- !GetAtt Agent1ImageBuildProject.Arn
- !GetAtt Agent2ImageBuildProject.Arn
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-custom-resource-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# ========================================================================
# LAMBDA MODULE - Custom Resources
# ========================================================================
CodeBuildTriggerFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-codebuild-trigger"
Description: "Triggers CodeBuild projects as CloudFormation custom resource"
Handler: index.handler
Role: !GetAtt CustomResourceRole.Arn
Runtime: python3.9
Timeout: 900
Code:
ZipFile: |
import boto3
import cfnresponse
import json
import logging
import time
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info('Received event: %s', json.dumps(event))
try:
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
project_name = event['ResourceProperties']['ProjectName']
wait_for_completion = event['ResourceProperties'].get('WaitForCompletion', 'true').lower() == 'true'
logger.info(f"Attempting to start CodeBuild project: {project_name}")
logger.info(f"Wait for completion: {wait_for_completion}")
# Start the CodeBuild project
codebuild = boto3.client('codebuild')
# First, verify the project exists
try:
project_info = codebuild.batch_get_projects(names=[project_name])
if not project_info['projects']:
raise Exception(f"CodeBuild project '{project_name}' not found")
logger.info(f"CodeBuild project '{project_name}' found")
except Exception as e:
logger.error(f"Error checking project existence: {str(e)}")
raise
response = codebuild.start_build(projectName=project_name)
build_id = response['build']['id']
logger.info(f"Successfully started build: {build_id}")
if not wait_for_completion:
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': 'STARTED'
})
return
# Wait for the build to complete
max_wait_time = context.get_remaining_time_in_millis() / 1000 - 30 # Leave 30s buffer
start_time = time.time()
while True:
if time.time() - start_time > max_wait_time:
error_message = f"Build {build_id} timed out"
logger.error(error_message)
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': error_message})
return
build_response = codebuild.batch_get_builds(ids=[build_id])
build_status = build_response['builds'][0]['buildStatus']
if build_status == 'SUCCEEDED':
logger.info(f"Build {build_id} succeeded")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': build_status
})
return
elif build_status in ['FAILED', 'FAULT', 'STOPPED', 'TIMED_OUT']:
error_message = f"Build {build_id} failed with status: {build_status}"
logger.error(error_message)
# Get build logs for debugging
try:
logs_info = build_response['builds'][0].get('logs', {})
if logs_info.get('groupName') and logs_info.get('streamName'):
logger.info(f"Build logs available in CloudWatch")
except Exception as log_error:
logger.warning(f"Could not get log information: {log_error}")
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': error_message,
'BuildId': build_id
})
return
logger.info(f"Build {build_id} status: {build_status}")
time.sleep(30) # Check every 30 seconds
except Exception as e:
logger.error('Error: %s', str(e))
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': str(e)
})
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-trigger"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: Lambda
# ========================================================================
# CODEBUILD MODULE - Container Image Building
# ========================================================================
# Agent2 Build Project (build first as it's independent)
Agent2ImageBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub "${AWS::StackName}-agent2-build"
Description: !Sub "Build agent2 Docker image for ${AWS::StackName}"
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: ARM_CONTAINER
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: IMAGE_REPO_NAME
Value: !Ref ECRRepositoryAgent2
- Name: IMAGE_TAG
Value: !Ref ImageTag
- Name: STACK_NAME
Value: !Ref AWS::StackName
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image for agent2 ARM64...
# Create requirements.txt
- |
cat > requirements.txt << 'EOF'
strands-agents
boto3>=1.40.0
botocore>=1.40.0
bedrock-agentcore
EOF
# Create agent2.py - specialist agent that handles specific tasks
- |
cat > agent2.py << 'EOF'
from strands import Agent
import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
def create_specialist_agent() -> Agent:
"""Create a specialist agent that handles specific analytical tasks"""
system_prompt = """You are a specialist analytical agent.
You are an expert at analyzing data and providing detailed insights.
When asked questions, provide thorough, well-reasoned responses with specific details.
Focus on accuracy and completeness in your answers."""
return Agent(
system_prompt=system_prompt,
name="SpecialistAgent"
)
@app.entrypoint
async def invoke(payload=None):
"""Main entrypoint for agent2"""
try:
# Get the query from payload
query = payload.get("prompt", "Hello") if payload else "Hello"
# Create and use the specialist agent
agent = create_specialist_agent()
response = agent(query)
return {
"status": "success",
"agent": "agent2",
"response": response.message['content'][0]['text']
}
except Exception as e:
return {
"status": "error",
"agent": "agent2",
"error": str(e)
}
if __name__ == "__main__":
app.run()
EOF
# Create Dockerfile
- |
cat > Dockerfile << 'EOF'
FROM public.ecr.aws/docker/library/python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
RUN pip install aws-opentelemetry-distro>=0.10.1
# Region will be set by AgentCore runtime environment automatically
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
EXPOSE 8000
COPY . .
CMD ["opentelemetry-instrument", "python", "-m", "agent2"]
EOF
# Build the image
- echo Building ARM64 image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- echo ARM64 Docker image pushed successfully
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent2-build"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: CodeBuild
# Agent1 Build Project (orchestrator that calls agent2)
Agent1ImageBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub "${AWS::StackName}-agent1-build"
Description: !Sub "Build agent1 Docker image for ${AWS::StackName}"
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: ARM_CONTAINER
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: IMAGE_REPO_NAME
Value: !Ref ECRRepositoryAgent1
- Name: IMAGE_TAG
Value: !Ref ImageTag
- Name: STACK_NAME
Value: !Ref AWS::StackName
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image for agent1 ARM64...
# Create requirements.txt
- |
cat > requirements.txt << 'EOF'
strands-agents
boto3>=1.40.0
botocore>=1.40.0
bedrock-agentcore
EOF
# Create agent1.py - orchestrator agent with tool to call agent2
- |
cat > agent1.py << 'EOF'
from strands import Agent, tool
from typing import Dict, Any
import boto3
import json
import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
# Environment variable for Agent2 ARN (will be set by CloudFormation)
AGENT2_ARN = os.getenv('AGENT2_ARN', '')
def invoke_agent2(query: str) -> str:
"""Helper function to invoke agent2 using boto3"""
import uuid
try:
# Get region from environment or use default
region = os.getenv('AWS_REGION', 'us-west-2')
agentcore_client = boto3.client('bedrock-agentcore', region_name=region)
# Invoke agent2 runtime (using AWS sample format)
response = agentcore_client.invoke_agent_runtime(
agentRuntimeArn=AGENT2_ARN,
qualifier="DEFAULT",
payload=json.dumps({"prompt": query})
)
# Handle streaming response (text/event-stream)
if "text/event-stream" in response.get("contentType", ""):
result = ""
for line in response["response"].iter_lines(chunk_size=10):
if line:
line = line.decode("utf-8")
# Remove 'data: ' prefix if present
if line.startswith("data: "):
line = line[6:]
result += line
return result
# Handle JSON response
elif response.get("contentType") == "application/json":
content = []
for chunk in response.get("response", []):
content.append(chunk.decode('utf-8'))
response_data = json.loads(''.join(content))
return json.dumps(response_data)
# Handle other response types
else:
response_body = response['response'].read()
return response_body.decode('utf-8')
except Exception as e:
import traceback
error_details = traceback.format_exc()
return f"Error invoking agent2: {str(e)}\nDetails: {error_details}"
@tool
def call_specialist_agent(query: str) -> Dict[str, Any]:
"""
Call the specialist agent (agent2) for detailed analysis or complex tasks.
Use this tool when you need expert analysis or detailed information.
Args:
query: The question or task to send to the specialist agent
Returns:
The specialist agent's response
"""
result = invoke_agent2(query)
return {
"status": "success",
"content": [{"text": result}]
}
def create_orchestrator_agent() -> Agent:
"""Create the orchestrator agent with the tool to call agent2"""
system_prompt = """You are an orchestrator agent.
You can handle simple queries directly, but for complex analytical tasks,
you should delegate to the specialist agent using the call_specialist_agent tool.
Use the specialist agent when:
- The query requires detailed analysis
- The query is about complex topics
- The user explicitly asks for expert analysis
Handle simple queries (greetings, basic questions) yourself."""
return Agent(
tools=[call_specialist_agent],
system_prompt=system_prompt,
name="OrchestratorAgent"
)
@app.entrypoint
async def invoke(payload=None):
"""Main entrypoint for agent1"""
try:
# Get the query from payload
query = payload.get("prompt", "Hello, how are you?") if payload else "Hello, how are you?"
# Create and use the orchestrator agent
agent = create_orchestrator_agent()
response = agent(query)
return {
"status": "success",
"agent": "agent1",
"response": response.message['content'][0]['text']
}
except Exception as e:
return {
"status": "error",
"agent": "agent1",
"error": str(e)
}
if __name__ == "__main__":
app.run()
EOF
# Create Dockerfile
- |
cat > Dockerfile << 'EOF'
FROM public.ecr.aws/docker/library/python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
RUN pip install aws-opentelemetry-distro>=0.10.1
# Region will be set by AgentCore runtime environment automatically
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
EXPOSE 8000
COPY . .
CMD ["opentelemetry-instrument", "python", "-m", "agent1"]
EOF
# Build the image
- echo Building ARM64 image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- echo ARM64 Docker image pushed successfully
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent1-build"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: CodeBuild
# CUSTOM RESOURCE - Trigger Agent2 Image Build
TriggerAgent2ImageBuild:
Type: Custom::CodeBuildTrigger
DependsOn:
- ECRRepositoryAgent2
- Agent2ImageBuildProject
- CodeBuildTriggerFunction
Properties:
ServiceToken: !GetAtt CodeBuildTriggerFunction.Arn
ProjectName: !Ref Agent2ImageBuildProject
WaitForCompletion: "true"
# CUSTOM RESOURCE - Trigger Agent1 Image Build
TriggerAgent1ImageBuild:
Type: Custom::CodeBuildTrigger
DependsOn:
- ECRRepositoryAgent1
- Agent1ImageBuildProject
- CodeBuildTriggerFunction
Properties:
ServiceToken: !GetAtt CodeBuildTriggerFunction.Arn
ProjectName: !Ref Agent1ImageBuildProject
WaitForCompletion: "true"
# ========================================================================
# AGENTCORE MODULE - Runtime Resources
# ========================================================================
# Agent2 Runtime (deploy first as agent1 depends on it)
Agent2Runtime:
Type: AWS::BedrockAgentCore::Runtime
DependsOn:
- TriggerAgent2ImageBuild
Properties:
AgentRuntimeName: !Sub
- "${StackNameUnderscore}_${Agent2Name}"
- StackNameUnderscore: !Join ["_", !Split ["-", !Ref "AWS::StackName"]]
AgentRuntimeArtifact:
ContainerConfiguration:
ContainerUri: !Sub "${ECRRepositoryAgent2.RepositoryUri}:${ImageTag}"
RoleArn: !GetAtt Agent2ExecutionRole.Arn
NetworkConfiguration:
NetworkMode: !Ref NetworkMode
Description: !Sub "Specialist agent runtime for ${AWS::StackName}"
# Agent1 Runtime (orchestrator with agent2 ARN as environment variable)
Agent1Runtime:
Type: AWS::BedrockAgentCore::Runtime
DependsOn:
- TriggerAgent1ImageBuild
- Agent2Runtime
Properties:
AgentRuntimeName: !Sub
- "${StackNameUnderscore}_${Agent1Name}"
- StackNameUnderscore: !Join ["_", !Split ["-", !Ref "AWS::StackName"]]
AgentRuntimeArtifact:
ContainerConfiguration:
ContainerUri: !Sub "${ECRRepositoryAgent1.RepositoryUri}:${ImageTag}"
RoleArn: !GetAtt Agent1ExecutionRole.Arn
NetworkConfiguration:
NetworkMode: !Ref NetworkMode
Description: !Sub "Orchestrator agent runtime for ${AWS::StackName}"
EnvironmentVariables:
AGENT2_ARN: !GetAtt Agent2Runtime.AgentRuntimeArn
# ============================================================================
# OUTPUTS SECTION
# ============================================================================
Outputs:
# AGENT1 (ORCHESTRATOR) OUTPUTS
Agent1RuntimeId:
Description: "ID of agent1 (orchestrator) runtime"
Value: !GetAtt Agent1Runtime.AgentRuntimeId
Export:
Name: !Sub "${AWS::StackName}-Agent1RuntimeId"
Agent1RuntimeArn:
Description: "ARN of agent1 (orchestrator) runtime"
Value: !GetAtt Agent1Runtime.AgentRuntimeArn
Export:
Name: !Sub "${AWS::StackName}-Agent1RuntimeArn"
Agent1ECRRepositoryUri:
Description: "URI of the ECR repository for agent1"
Value: !GetAtt ECRRepositoryAgent1.RepositoryUri
Export:
Name: !Sub "${AWS::StackName}-Agent1ECRRepositoryUri"
Agent1ExecutionRoleArn:
Description: "ARN of agent1 execution role"
Value: !GetAtt Agent1ExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-Agent1ExecutionRoleArn"
# AGENT2 (SPECIALIST) OUTPUTS
Agent2RuntimeId:
Description: "ID of agent2 (specialist) runtime"
Value: !GetAtt Agent2Runtime.AgentRuntimeId
Export:
Name: !Sub "${AWS::StackName}-Agent2RuntimeId"
Agent2RuntimeArn:
Description: "ARN of agent2 (specialist) runtime"
Value: !GetAtt Agent2Runtime.AgentRuntimeArn
Export:
Name: !Sub "${AWS::StackName}-Agent2RuntimeArn"
Agent2ECRRepositoryUri:
Description: "URI of the ECR repository for agent2"
Value: !GetAtt ECRRepositoryAgent2.RepositoryUri
Export:
Name: !Sub "${AWS::StackName}-Agent2ECRRepositoryUri"
Agent2ExecutionRoleArn:
Description: "ARN of agent2 execution role"
Value: !GetAtt Agent2ExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-Agent2ExecutionRoleArn"