4. What Blocks can I use?
- Data and storage
- Authentication
- Compute and background jobs
- Artificial intelligence
- Communication
- Configuration
- Observability
- Hosting and deployment
5. How do I start my project?
With a single npm command we can create the main project structure. To understand how everything works from scratch, I created a repository with a blank template that you can use to create your project. Run the following:
npx @luisfdeleonramirez/create-aws-blocks-base chatbot-ia
cd chatbot-ia
npm install
npm run dev
After running these commands, we will have a local service running at http://localhost:3000 where you can run your tests.
6. What are we going to build?
I developed an app that works as an agent using Amazon Nova Lite (amazon.nova-lite-v1:0) and responds to user queries. After several tests, I consider that an adequate flow to organize development is the following:
- "First I define what my app does (index.ts)"
- "Then I define where it runs (index.cdk.ts)"
- "Finally I define how it looks for the user (app.tsx)"
7. What Blocks did I use?
Originally I thought I would only use blocks on the backend side, but on the web interface I ended up using Blocks' own functions for conversation management.
Scope
For resource grouping.
const scope = new Scope('chatbot');
AuthBasic
For authentication configuration using email and password.
const auth = new AuthBasic(scope, 'auth');
Agent
Block for the AI agent with Bedrock, which includes configuration, streaming, and tools. Something really interesting is that in just a couple of lines you can configure your agent's behavior for its interactions.
const agent = new Agent(scope, 'chat', {
model: { deployed: { provider: 'bedrock', modelId: 'amazon.nova-lite-v1:0' } },
systemPrompt: `You are a friendly agent that will help users solve their questions about technology.
You must respond in the same language the user asks the question.
When the user asks about the company, products, prices or specific information,
use the searchKnowledge tool to search the knowledge base before responding.
If you don't find relevant information in the knowledge base, respond with your general knowledge.`,
tools: (tool) => ({
searchKnowledge: tool({
description: 'Searches for relevant information in the company knowledge base. Use this tool when the user asks about products, prices, services or specific company information.',
parameters: z.object({
query: z.string().describe('Search query in natural language'),
}),
handler: async ({ input }) => {
const results = await kb.retrieve(input.query, { maxResults: 3 });
if (results.length === 0) {
return 'No relevant information found in the knowledge base.';
}
return results.map(r => r.text).join('\n\n---\n\n');
},
}),
}),
});
KnowledgeBase
RAG knowledge base with custom documents that the agent will use to respond.
const kb = new KnowledgeBase(scope, 'docs', {
source: './knowledge',
description: "'Documentation and chatbot knowledge base',"
});
ApiNamespace
For API communication via JSON-RPC.
export const api = new ApiNamespace(scope, 'api', (context) => ({}));
8. Development
After adjusting the code to make it functional, I wanted to test if it worked entirely locally and the answer is yes: it had basic functionality that I could simulate using mock data in the code. However, the agent functionality does require that we at least run the test in the sandbox. Having something ready, I moved on to the sandbox.
9. IAM Configuration
To accomplish this under the principle of least privilege, I tested with the minimum permissions that the user running the deploys should have. Here is the policy:
{"Version":"2012-10-17","Statement":[{"Sid":"AWSBlocksCloudFormationAndIAM","Effect":"Allow","Action":["cloudformation:*","iam:CreateRole","iam:DeleteRole","iam:GetRole","iam:GetRolePolicy","iam:ListRolePolicies","iam:ListAttachedRolePolicies","iam:PassRole","iam:AttachRolePolicy","iam:DetachRolePolicy","iam:PutRolePolicy","iam:DeleteRolePolicy","iam:TagRole"],"Resource":"*"},{"Sid":"AWSBlocksAllPossibleFrameworkServices","Effect":"Allow","Action":["lambda:*","dynamodb:*","apigateway:*","s3:*","bedrock:*","events:*","sqs:*","ecr:*","ssm:*","sts:AssumeRole"],"Resource":"*"}]}
10. Bootstrap
I started with the sandbox test, however, I needed to have the bootstrap ready first. The bootstrap is a base preparation so that Blocks applications can work correctly. This preparation is done once per account and region. You do it like this:
npx cdk bootstrap aws://{your_account_id}/{region}
The result of this execution is the creation of a CloudFormation stack and the biggest question: does this generate costs? The answer is no, it only creates an empty S3 bucket, IAM roles, and an SSM parameter. The cost is generated when CDK uploads the static code assets during deploys. Keep in mind that for the sandbox, CDK does not copy these files since the interface keeps running in your local environment.
11. Sandbox
During the sandbox creation, I encountered several challenges. Being a new technology, it is possible to find details that may cause unexpected behavior. You can start the sandbox by running:
npm run sandbox
I chose to test the sandbox in the us-west-2 region. The experience was good and I strengthened my knowledge of the framework. Here are some tips:
- Create tags in your project
To have control over your resources, as well as cost monitoring.
index.cdk.ts:
import * as cdk from 'aws-cdk-lib';
cdk.Tags.of(blocksStack).add('Project', 'blocks-chatbot-ia');
cdk.Tags.of(blocksStack).add('Environment', sandboxMode ? 'sandbox' : 'production');
cdk.Tags.of(blocksStack).add('Team', 'dev');
cdk.Tags.of(blocksStack).add('CostCenter', 'chatbot-ia');
Sometimes resources cannot be created if your project name is too long, so you can change the base name of your stack since resources are created with this prefix. In this case, I made changes so the stack would be the project name defined in the package:
index.cdk.ts:
const stackName = sandboxMode ? pkg.name + `-sandbox` : pkg.name + 'prod';
export const blocksStack = await BlocksStack.create(app, stackName, {
backendHandlerPath: join(__dirname, 'index.handler.ts'),
backendCDKPath: join(__dirname, 'index.ts')
});
- Bedrock quotas in Organizations
If your account is part of an organization, remember that you must set the quotas for Bedrock models, otherwise you won't be able to verify the model responses.
- Region bug in @strands-agents/sdk
The @aws-blocks/bb-agent block internally uses the @strands-agents/sdk module in its latest version available at the time of testing (1.7.0), to save agent conversations in S3. However, this module by default points to the us-east-1 region, so when trying to use the agent in another region you might encounter issues. To fix this, you only need to make the following change:
node_modules/@strands-agents/sdk/dist/src/session/s3-storage.js:
this._s3 = config.s3Client ?? new S3Client({ region: config.region ?? process.env.AWS_REGION ?? 'us-east-1' });
With this change, it will now check the region environment variable by default so you can see the history of your conversations. This fix is for that version. I filed report #120 to the AWS Blocks framework maintenance team and it was resolved, so it's unlikely you'll encounter this error anymore. Here are the details of the fix they applied:
https://github.com/aws-devtools-labs/aws-blocks/issues/120
After finishing the sandbox tests, I could see a CloudFormation with the various created resources. In this case, it is possible to better track them thanks to the tags, which identify which project each resource belongs to.
12. Deployment
Having completed the tests in the sandbox, the only thing left is to deploy so the application is accessible to everyone. It is a satisfying experience, since this framework allows you to take stable and secure applications to production using serverless architectures. For this step, the difference from the sandbox is that static hosting is generated, stored in S3 and distributed by CloudFront. The deployment time was 12 minutes.
Below we can see the chatbot working in the production environment.
13. Resource Cleanup
After testing and to avoid incurring costs, I destroyed the created infrastructure. This is as simple as running the following commands:
Delete Sandbox:
npm run sandbox:destroy
Delete deployment:
npm run destroy
Delete CDK (keep in mind that it does not generate costs as long as no assets are transferred):
aws s3 rm s3://cdk-hnb659fds-assets-xxxxx-{region} --recursive --region {region}
aws cloudformation delete-stack --stack-name CDKToolkit --region {region}
About the author
Luis Fernando de León — AWS Community Builder 🇬🇹
📸 Instagram: instagram.com/luisenlanube
📝 DEV: dev.to/luisferdeleon
👤 Facebook: facebook.com/luisenlanube
💼 LinkedIn: linkedin.com/in/luisfdeleonramirez