Learning Finnish with GenAI: Part 3 - This one sucks
- Savidu Dias

- Mar 26
- 9 min read
I’m going to level with you here. I’ve been putting off making this one for a long time. Mainly because this one has nothing to do with AI, agents, or anything of the sort. Instead, this is about how we put everything together and deploy it to the cloud.
Why are you doing this to yourself?
Because things need to make sense, that's why. I want to get into the part where I REALLY start working with AI agents in a way that can help me learn Finnish. To do that, I need to have the proper infrastructure set up so that I don’t always have to have a server running in my local network. So yeah, this blog is about how I set up everything to run on AWS. It sucks, but it’s an important step. Feel free to skip this one, I'll understand.
I'm lost. Where am I?
Let’s rewind to where we left off in the previous blog. This is what our final product at the end of the blog will look like.
Here is what we have so far

We have our messaging application, which is a React frontend web application, communicating with our server, which is running on our computer, communicating with the agent system involving our teacher and extractor, who are running on AWS as an Agentcore runtime. The agents read our message, give feedback, and continue the conversation.
So here's where things are:
Component | Where is it? |
AI Agents (teacher, extractor) | Cloud |
Server | My computer |
Application/frontend | Also my computer |
You | Hospital. In a coma. None of this is real. Wake up. |
So what we have to do is bring all these components to the cloud so that I don’t have to have a computer around all the time. I need to bring the server and the frontend over to AWS.
How do we bring everything to the cloud?
Before we get to that, let’s talk about what the frontend and server is. The frontend is an application that we developed using the React framework. It can be accessed by opening the browser from the computer it is running on, and heading over to http://localhost:3000. If you are on a device such as your smartphone that is on the same network as the computer that is running the frontend, you can use the application by going to http://<your-computer’s-local-IP-address>:3000. This is basic stuff. I shouldn’t have to teach you this.
Similarly, our server is a backend application developed using Python and the FastAPI framework. This needs to be running on the same computer/network as the frontend, and can be accessed by our frontend application by sending requests to an endpoint at http://localhost:8000.
To make things easy for ourselves, we could start up a virtual machine on AWS and run the frontend and server there and all will be fine and dandy. But we’re not doing that because we hate ourselves. Instead, this is what we are going to do.

Frontend
Our frontend is now running on a service called AWS Amplify. Amplify is an AWS service that lets you host your developed frontend mobile and web applications on the cloud. In simple terms, you can simply write the code for your frontend project, push it to a repository on GitHub, and Amplify will automatically catch these changes, build your application, deploy it, and have it become available through a URL.
The cool thing here is that if you have a domain of your own, you can set your domain registrar to connect your domain to your amplify project by updating your DNS records. That’s a lot of big words, but all you need to know is that the application is hosted on AWS, and I can easily access it with https://www.sanora.savidude.com.
I’ve known about Amplify for a long time, but have never seen it being used in real life. So learning how to set everything up was an experience. To be honest, it was way easier than I thought.
Server
Can you guess which component the server is? You can? Then don't read this Mr. smartypants. It’s a combination of API Gateway and Lambda. The Messaging Lambda contains all the Python FastAPI code that runs your server. The API Gateway is a gateway for the API that connects the frontend (Amplify) and server (Messaging Lambda).
Basically what happens is that you configure the frontend application to send requests to an endpoint that is given by API Gateway. When a request is sent to this endpoint, API Gateway will route the request to the Lambda function that will execute the server code. What is the server code you ask? We already went through this in part 2, weren’t you paying attention? The server code calls our AI agents on Agentcore Runtime.
Agents
This is the same as last time. Nothing significant has changed here.
Cognito?
Wait. This is new. Cognito is an AWS service that provides secure authentication and authorization to web and mobile apps. I want you to take a minute before reading further and try to think why I would want to have authentication here.
You done? Have you figured it out? Good boy. When you make an application available on the internet, it’s available to the whole internet. Someone could go to the web application and spam a bunch of messages and use up a lot of my AWS, Gemini, or OpenAI resources. Running this application doesn’t cost must at this point in time. Actually it costs less than 2 euros per month to operate, but that might not be the case in the future, and I want to make this future proof.
So the only way to use this application is to authenticate yourself with an email and password before starting to send messages. Which is why when you go to https://www.sanora.savidude.com, you get hit with a login screen. If you want to try this out for yourself, feel free to reach out to me and I will hook you up with some credentials. Make sure to say "please" and "thank you".
Simply put, once you log in with the web application, you are given a token that is used to send API requests to the server (API Gateway). API Gateway will validate your token before routing the request to Lambda.
Anything else?
Well, yes actually. Glad you asked. There are a couple of more services here that I haven’t mentioned.
One of them being AWS Secrets Manager, which is a service you use to securely store things like passwords, and remains of the once who have wronged you. It is used to store the API keys for OpenAI and Gemini.
Another important component is the SSM Parameter Store. We have a few different components here. These components constantly communicate with each other. The frontend needs to know the API endpoint to send requests to on API gateway. Messaging Lambda needs to know which AgentCore runtime to invoke to talk to the agent. Both the frontend and API Gateway needs to know the Cognito user pool to authenticate and authorize the user. I could have all these values stored in the code, but that’s not very cash money is it? Instead, the best practice would be to store them as values in Parameter Store.
I’m bored already. Is that all?
Yes, but also no. I could now start using the application to learn Finnish. But that’s not good enough. When I set out to do this, my goal was not just to learn Finnish, I want to become a better engineer. That is, I want to do this as if this is a real application that is being used by real people, and not just a hobby project, and hopefully make life easier for future me.
The Extra Mile
Yes. While this is good enough, I don’t want to have to log in to the AWS console every time I want to make a new change. Instead, I want to automate this process. How do we automate changes to AWS services? We use a little something called CloudFormation. CloudFormation is an Infrastructure as Code (IaC) tool that lets you manage AWS resources through JSON or YAML templates.
You can define all your AWS infrastructure in JSON/YAML, but there is an easier way to do this. Introducing AWS Cloud Development Kit (CDK). CDK is a framework that lets your manage your cloud resources through CloudFormation using almost any programming language. To put it in simple terms, you can define the AWS cloud infrastructure using any of the supported languages, and CDK will convert it into CloudFormation behind the scenes. This way, you don’t have to know much about CloudFormation.

All of our AWS resources are put into 3 stacks.
Agents Stack - Core infrastructure for deploying and running AI agents (ECR repository for agent containers, IAM execution role for AgentCore, Secrets Manager secrets, SSM parameters)
Agent Messaging Stack - Resources used in the communication with agents (API Gateway, Lambda, Cognito)
Frontend Stack - Web application hosting and deployment resources (Amplify, GitHub integration)
The cool thing about defining the infrastructure as stacks is that if I now for example have to make some changes to my agent, I could make the changes, and just push the stack using the AWS CDK Command Line Interface (CLI) tool using
cdk deploy AgentsStack
and like magic, my changes are already deployed. But I want to take things a step further.
A st-step f-f-further?
Instead of deploying the infrastructure myself, I want to automate that as well. A normal developer would make changes, test them, and push the code into git(hub). I too am a normal developer, so I did just that.
With GitHub, I can define these things called Github Actions Workflows, where you can have GitHub deploy the changes you push. This workflow is defined in the deploy-cdk-stacks.yml file inside the project.
The idea here is that once I push some changes to GitHub, and if there are changes made to the files inside the deployment-stacks/stack_agents folder, GitHub actions will automatically deploy the AgentsStack for you. Similarly, if there are changes to deployment-stacks/lambdas/server/, Actions will deploy the AgentMessagingStack.
But there was one problem. Changes to the actual agent code in the agents folder doesn’t deploy the agent runtime. That is because CDK constructs didn’t support AgentCore runtime deployments at the time. So I had to get a little creative here. I had to create 2 more workflows.
If you bothered to read the previous blog, you would know that the deployment of AI agents to AgentCore involves two steps.
Configure: creates a configuration file and Docker file containing information needed to deploy the agent on AWS.
Launch: actually deploys the agent looking at the configuration file.
So I needed one workflow to create the configuration files and another to deploy to AWS.
How did you create the configuration workflow?
Since we can’t use CDK to do this, the workaround was to install the Agentcore CLI tool and execute the configure command. Keep in mind that we were doing this earlier locally. Now we have to do it on the GitHub Actions environment.
When running the configure command locally, the CLI tool asks for a bunch of information that you basically just say “yeah do whatever you want to”, but when this is automated, it’s not possible to give these inputs as it’s a script that is running the CLI tool. So what do you do? You give all the information that the command needs through arguments in the command. You can check the full command in the configuration script here.
But there was another problem, there was one prompt from the command that couldn’t be automated though CLI flags. Keep in mind that AgentCore is still a very new tool. So when I run this script on GitHub Actions, it gets stuck at this point since it’s not getting any input.
I’m not gonna lie, that annoyed me a little bit. I’ve come this far just to get stuck on something so stupid. I was left with several options. The code for the AgentCore toolkit happens to be publicly available on GitHub. I could find out where the bug is and submit an issue and wait for AWS to fix it. The easier option is to forget about it. This is a command that you only need to run once per agent. You run it once locally, store the configuration files on S3 and move on with your life.
Can you guess which option I went with? I went with the easier option. Of course not. When have I ever made decisions that made my life easier? I was too stubborn at this point. I want to do things that a “proper developer” would do. I wish I could slap myself right now.
Sooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo I submitted a bug to the toolkit repository. This was submitted on November of 2025. The issue is still open as of late March when I’m writing this. Amazon decided to ghost me. I guess they were too busy laying off their employees. So do I do the easy thing now?
Of course not. I make a fork of the repository and fix the issue myself. When I say “fix the issue”, what I mean is delete the part of code that asks for the prompt that is useless to me and move on. Does this make me a bad developer? Yes, and I don’t care. God, I hate myself.
Now we have a “working” tool I could install on the GitHub Actions environment and configure the agent. All I had to do is set up the workflow to use my forked version of the AgentCore toolkit instead of the original. Once the configuration files are generated, the script uploads them to an S3 bucket. After that, I don’t have to run the action ever again. What was the point of this?
What about deploying the agent?
I didn’t forget about that. I’m getting to it. Get off my back. This was relatively easy. Everything goes smoothly with the original version of the tool. The workflow runs a script that fetches the configuration files from S3 that we just uploaded, and then uses it to deploy the agent into AgentCore.
This is so much nicer, now every time I push something into the agents folder, it gets automatically deployed into AgentCore.
Is that it?
Yes. If you actually managed to read through this monstrosity of a blog, I’m impressed by your ability to pay attention. Congratulations.
Comments