Welcome to this tutorial on building an AI-powered calendar assistant! This project is a great example of how AI tools can be extended with new abilities — allowing them to solve more complex, real-world problems.
🧠 What’s This All About?
Large language models (LLMs), cannot remember information across different sessions — they can only “remember” the messages within the current chat session. That’s a big limitation because most apps need to store and recall information over time. Think about a calendar app: it needs to store all your events so you can see them later!
To work around this limitation, we’ll use Google Sheets to store data. Then we’ll teach the AI how to interact with that data to manage a user’s calendar.
By the end of this project, your app will look like this:
calendar.gif
This tutorial is divided into two main parts:
Part 1: Set up a basic AI chat app Part 2: Teach the AI how to manage the user’s calendar using Google Sheets
Start by remixing this template, which gives you a basic chat app to build on. We’ll add new logic to it step by step.
play.creaticode.com/projects/682dad154dba4796824af2a6
We’ll use a Google Sheet to store all calendar events. This approach is free, easy to use, and simple to review.
Here’s what you need to do (or ask your teacher/guardian to help):
Create a new Google Sheet Set its sharing settings so that “anyone with the link” can edit Copy the sheet’s link6fe532c8-c01a-4dbd-ba03-b15462392d8a-image.png
If you can’t create your own Google Sheet, you can use this pre-made one:
https://docs.google.com/spreadsheets/d/1tH9J09laZNl94Qb1smO5LSmDwD_yzungb9d4lmkobxE/edit?usp=sharing
Then, in your app, store the sheet URL in a variable called Sheet URL. You’ll use this later to tell the AI where the calendar lives.
eae1657b-7a9d-4cf2-a745-4b3637f32d2e-image.png
Next, we’ll give the AI a new role by updating its System Instruction.
This tells the AI what it’s supposed to do. For now, keep it simple and clear:
197c4ae4-ba5d-4133-9953-3b190e6ec10a-image.png
Let’s make the welcome message match the AI’s new job.
667f26d2-e8a1-4297-bed5-0db881b29b55-image.png
📌 Tip: You could make the AI generate a different welcome message every time, but that would slow things down.
Because different users might use this app, we need a way to keep their data separate.
The solution: whenever a new user joins, we’ll create a new tab in the Google Sheet just for them. The tab name will be the user’s unique ID.
ae212509-2bdf-48aa-b854-bd4d886f5597-image.png
📝 If the tab already exists, this block will do nothing — which is exactly what we want.
Here’s an important thing to remember: AI models do not know the current date or time on their own. You can test that like this:
35c1e1ac-c623-4b17-90ee-932e5b1eabfa-image.png
That’s a problem when managing calendars! We’ll fix this by manually sending the current date and time to the AI.
Let’s start with the day of the week:
First, create a new list “weekdays”:
6298781c-e323-43ba-afd8-21448f235fbe-image.png
Then, use the current [day of week] block to get today’s number, and use it to pick the correct item from your list:
82c8a6e9-d0d6-402e-9fa9-925f23ce4cbe-image.png
📅 For example, if today is Wednesday, the “current day of the week” block will return 4, and item 4 in your list will be "Wednesday".
We can also get the full date and time using the current block and combine them into a single string:
6dfcfd51-db40-401c-a5d2-ad253e5d4d66-image.png
Now we’ll attach the time information to each message we send to the AI model.
bd8c0581-61a3-4da0-bc40-1d5eae91b171-image.png
For example, if the user says “hi”, then what we actually send to the AI will be:
That way, the AI will know exactly when the message was sent!
You can now test it to make sure the AI sees the correct time:
392697d0-3618-40dd-a401-0d1d5c871753-image.png
Our entire calendar lives in Google Sheets, yet the AI can’t touch that Sheet directly — it needs help from our helper code. Here’s a sample back‑and‑forth among the user, the AI, and the helper:
User -> AI: I will have a haircut tomorrow at 4pm AI -> Helper: add a new event for [date] [time] Helper adds it to the Google Sheet Helper -> AI: event added successfully AI -> User: I have added the haircut to your calendar.
From the user’s perspective, the Sheet and helper code are invisible—the AI simply “gets it done.”
Another way to think about it: the user speaks natural language, the helper speaks code, so the AI acts as the translator between them.
To teach the AI to use our helper, we need to define a new command “addEvent” in the system instruction:
You are an AI assistant that helps manage the user's calendar. ## Commands You can use the following tools to manage events through an external service: * To add a new event to the calendar, you can return this command, which will return its eventID: addEvent | [date] | [time] | [event description] ## Instructions * If you are using a command, then only return the one line of command and nothing else.Notes:
We separated Commands and Instructions with ## headers so the AI can “see” the sections clearly.
The addEvent syntax uses pipes (|) — easy for us to split into pieces later.
The instruction forces the AI to output only the command line when needed — no extra chit‑chat.
Testing now shows the AI emitting addEvent lines automatically:
324814be-e4dd-4867-9c6b-9b5b5925ad71-image.png
The AI currently formats dates like 05/01/2025, which sorts poorly—05/01/2025 appears “larger” than 02/01/2026. Times such as “3 p.m.” sort badly, too.
Add two lines to the Instructions section to enforce sortable formats:
You are an AI assistant that helps manage the user's calendar. ## Commands You can use the following tools to manage events through an external service: * To add a new event to the calendar, you can return this command, which will return its eventID: addEvent | [date] | [time] | [event description] ## Instructions * If you are using a command, then only return the one line of command and nothing else. * Use yyyy.mm.dd date format, such as "2025.02.21" * Use hh:mm time format, such as "13:30"Re‑run the app and confirm:
d1eb5487-12ab-4d69-a973-1cfc3adb3217-image.png
Whenever we receive AI output, we’ll check if it begins with "addEvent " (note the trailing space). If so, we execute helper code in “handle add event”.
ee6922cd-072a-4a8b-a75b-38d00263f53b-image.png
The addEvent line has four parts separated by three pipes. Use the “part of” block to pick them apart and trim whitespace:
b904221b-f61a-44b0-a897-5ae7264f3e73-image.png
Our add‑event logic is:
Read the user’s tab into a table called events. Add the new event. Write the entire table back.To start, we will read the sheet using a “read data” block:
c9234755-1b23-4d7b-ab29-747080b966df-image.png
In this block, we will use the “read from Google Sheet” block to read the first 10000 rows from this user’s tab, assuming there are less than 10000 events on the calendar:
333550a9-d29b-4284-8fcc-67a38af3aa7f-image.png
Run this block and remove all the widgets (or save and reload the project), you will find the “events” table looks like this:
5ed62169-3cac-423a-a4dd-977a5e16ce8a-image.png
That’s because the new tab is still empty, so the column headers are missing. To fix that, we can define the columns whenever there are 0 rows in the table:
61dffcd1-e3d2-4361-8535-533288186d35-image.png
The first column “id” will be used to store the unique eventID.
Generate a unique ID using row count + 1 (always unique) and prefix it with “E”. Then append a row:
d3c9015d-d19e-450b-9775-22dfd1a66eca-image.png
Since all the events have date and time, we should keep them sorted in the calendar. We will sort the rows by “time” first, then by “date”. This will ensure all events are sorted by date and then time:
4d864702-2d7b-4f65-8a59-bab1e3025a9e-image.png
Overwrite the user’s tab with the updated table:
05a6e9d1-f1d6-46fe-a66a-7ca12687e8e2-image.png
Let the AI know the command succeeded and pass along the new eventID:
64a7f312-336b-4591-a3ac-840599769b41-image.png
We clarify that this message is from addEvent, so the AI won’t mistake it for user input.
Test by adding events:
c49731ea-472d-498f-a385-ae98eb299472-image.png
…and confirm they appear in the Sheet:
48824007-a8a5-4e7a-9d2a-1ee82f9eb9d8-image.png
Next, let’s work on removing an evnet. We will still start with defining the command in the system instruction:
You are an AI assistant that helps manage the user's calendar. ## Commands You can use the following tools to manage events through an external service: * To add a new event to the calendar, you can return this command, which will return its eventID: addEvent | [date] | [time] | [event description] * To remove an existing event, after you have found its eventID, you can use this command: removeEvent | [eventID] ## Instructions * If you are using a command, then only return the one line of command and nothing else. * Use yyyy.mm.dd date format, such as "2025.02.21" * Use hh:mm time format, such as "13:30"removeEvent takes just one parameter — the event’s unique ID. If the AI doesn’t already know the ID, it must retrieve it using other commands first (those are defined later).
Detect and handle it with a new block:
fba93dd8-a0af-4516-9c8d-5316582db7f5-image.png
Pull the ID from the command string:
0180e05f-19f3-44cf-b351-6589c557a27f-image.png
Read the Sheet into events again. Find the index of the row with the matching eventID. Delete that row.
8637a6a4-4f4d-4cba-b236-d265a9214ae6-image.png
Before we write the table back, we need to first clear the tab. The reason is that the table has one less row compared to the data in that tab, so if we simply write the table into that tab, there will be an extra duplicate row at the bottom.
bdd2d179-e0a3-428f-b7cf-ccece0807ecd-image.png
Notify the AI so it can reply to the user:
7d7a062a-078f-4733-9579-baf1cdda4d44-image.png
Restart your program, add an event, then remove it. Double‑check the Sheet each time:
f31330ff-bb58-4ed5-bd82-bb6c399c64cb-image.png
Earlier, the AI already knew the eventID for the dentist appointment, so removing it was easy. But when an eventID is not in the chat context, the AI must search for the event first.
To keep things simple, we’ll let the AI specify one keyword. Our helper will search that word in the description column and return every matching event (with its eventID).
Updated system instruction:
You are an AI assistant that helps manage the user's calendar. ## Commands You can use the following tools to manage events through an external service: * To add a new event to the calendar, you can return this command, which will return its eventID: addEvent | [date] | [time] | [event description] * To remove an existing event, after you have found its eventID, you can use this command: removeEvent | [eventID] * To search for existing events by a **single word**, use this command, which will return all events at that time with their eventIDs (use a generic word to get more likely matches): searchEvents | [keyword] ## Instructions * If you are using a command, then only return the one line of command and nothing else. * Use yyyy.mm.dd date format, such as "2025.02.21" * Use hh:mm time format, such as "13:30"We instruct the AI to choose one broad word (e.g., “lunch” or “Jon”) so we’re more likely to find the right event—even if we return a few extras, the AI can sort them out.
Test it:
cb3181c5-3870-40b9-90d3-8ded030b3ec9-image.png
The AI should pick “lunch” or “Jon” as the word to search for.
We will handle this new command using the block “handl search command”:
a189acb7-0a5b-4cd4-8aa6-cfc1a1109172-image.png
Store the keyword in a variable called search word:
d470f68e-fb62-488c-ae99-73dbb6dfd306-image.png
Use a for‑loop to walk through each row in events, pulling out the description:
b1fbb7ed-c0e0-4ffc-af72-d85b4a194889-image.png
Create a variable called search result. Start it empty, and append every row whose description contains the keyword:
dd7b8d9b-2dd3-46c0-bc4a-f9d10745b8e6-image.png
After the for-loop finishes, the “search result” will either be empty or filled with events. Either way, we will let the AI know the result, so it can generate the response to the user:
29c74dc2-4960-40b3-883c-56ef07fd5d6d-image.png
To test it, first add another lunch event to the Google Sheet with another person:
7b4d3776-859e-4f85-9e28-083e63d91b66-image.png
Then, restart the app, and ask about the “lunch with Jon”:
9a7d452e-0fa5-4c22-b2e6-224b66743812-image.png
Our final command, findEvents, grabs every event between two time points. Perfect for “Am I free next Wednesday morning?” or “What’s on my calendar this weekend?”
Here is the updated system instruction with the “findEvents” command:
You are an AI assistant that helps manage the user's calendar. ## Commands You can use the following tools to manage events through an external service: * To add a new event to the calendar, you can return this command, which will return its eventID: addEvent | [date] | [time] | [event description] * To remove an existing event, after you have found its eventID, you can use this command: removeEvent | [eventID] * To search for existing events by a **single word**, use this command, which will return all events at that time with their eventIDs (use a generic word to get more likely matches): searchEvents | [keyword] * To find existing events inbetween 2 time points (they can be the same date or time), you can use this command, which will return all events within the given time interval: findEvents | [start date] | [start time] | [end date] | [end time] ## Instructions * If you are using a command, then only return the one line of command and nothing else. * Use yyyy.mm.dd date format, such as "2025.02.21" * Use hh:mm time format, such as "13:30"
We will handle this command using a new block called “handle find events”:
779b348f-b6c7-4853-9f3c-83d3cf946c74-image.png
The “handle find events” block should be very similar to the “handle search events” block. Can you try to implement it before looking at the steps below?
We will first extract the 4 parameters:
e2561586-799a-4b27-954b-592d8e8b08d8-image.png
We will still use a for-loop, but this time, we will extract the eventDate and eventTime from each row:
974f08b8-7ec7-40ea-b506-955b752045b8-image.png
As we check out each row, if we find its date is earlier than the start date, or its time is earlier than the start time, then we will continue to the next row:
fbb2c495-08cf-43ce-b613-c6b890d109f1-image.png
Because events are already sorted, once we reach a row after the end date/time, we can break the loop. Until then, add each valid row to search result:
413abb89-a679-4f75-9fa0-dbde4e7d29d4-image.png
After the for-loop completes, we will give the result list to the AI:
db5cbeff-0384-4655-991e-88892902fd8c-image.png
Now let’s test it. Suppose this is the calender:
b6fb265d-d965-4b4e-be6a-b6b90d7ac4d9-image.png
The AI will be able to find all events for next week:
32bd8a87-72ba-4769-9ba0-48126f9b0a10-image.png
The reply above feels a little robotic. Let’s tweak the system prompt so the AI talks more like a human:
## Instructions * If you are responding to the user, then talk like a human * If you are sending a command, then only return the **single line** of command and nothing else. * Use yyyy.mm.dd date format in commands, such as "2025.02.21" * Use hh:mm time format in commands, such as "13:30"
Unfortunately, this sometimes makes the AI mix user responses with commands:
1d59cee0-244b-44b9-9f4c-9283717ac8a7-image.png
Fix: Apply the T.I.R.E. prompting method by adding an explicit Example to the end of the system instruction:
Another problem that might appear in testing is that the AI sometimes think of “next week” as the “next 7 days”, rather than the next calendar week. This can be fixed by adding a new line in the “## Instructions” section:
* When the user refers to the next week, assume they mean the next calendar week, not the next 7 days.
Finally, we are getting a much more human-like response:
be331e0c-680c-49ad-b023-822c792fa45e-image.png
Here is a final demo:
calendar.gif
When you release your app for other users, you should publish the project instead of sharing it. That is because if you share the project, any user can look inside it to acquire the URL of the Google Sheet, then they will be able to look at other users’ calendars.
For practice, here are some improvements you can make to this app:
Multiple Commands: Users may combine requests (“My lunch with Jon is cancelled and schedule a dentist at 4 p.m. today”). Rescheduling should trigger a removeEvent followed by an addEvent.Multiple Search Keywords: Allow two or three keywords for searchEvents. “Lunch with Jon” could search for “Jon” and “lunch.”
Remove Past Events: When the app starts, delete events more than one month old to keep the Sheet tidy.
Recurring Events (advanced):
Extend addEvent with a fifth parameter, interval (e.g., “weekly,” “daily”). Add an interval column to the Sheet. When interval is present, auto‑add the event for the next six months. On startup, scan for recurring events and extend them six months into the future.