DEV Community

Cover image for What if building an AI chatbot was as easy as snapping LEGO bricks together? — AWS Blocks

What if building an AI chatbot was as easy as snapping LEGO bricks together? — AWS Blocks

1. What is Blocks?

It is a recently launched open-source framework that simplifies deploying projects to AWS; you only need to develop your project as you normally do in TypeScript. The framework takes care of converting your code into AWS services.

2. Benefits

  • Development without needing an AWS account.
  • You can test your code locally with the certainty that it will work the same in production.
  • Low-cost oriented using serverless technology.
  • AI-powered development experience by including guides that enable the use of AI coding tools, such as Kiro, since the framework guides the agent on how to improve your architectures.
  • You will have an isolated and fully functional development environment.

3. What is a Block?

A Block is a piece of functionality that encapsulates logic + AWS infrastructure in a single abstraction that you use with one to three lines of code.

Analogy:
Think of a Block as a LEGO module: each piece has a specific shape and function, and you combine them to build whatever you want. You don't worry about how it's made internally.

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
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

AuthBasic

For authentication configuration using email and password.

const auth = new AuthBasic(scope, 'auth');
Enter fullscreen mode Exit fullscreen mode

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');
      },
    }),
  }),
});
Enter fullscreen mode Exit fullscreen mode

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',"
});
Enter fullscreen mode Exit fullscreen mode

ApiNamespace

For API communication via JSON-RPC.

export const api = new ApiNamespace(scope, 'api', (context) => ({}));
Enter fullscreen mode Exit fullscreen mode

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": "*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode
  • Short stack names

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')
});
Enter fullscreen mode Exit fullscreen mode
  • 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' });
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Delete deployment:

npm run destroy
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)