In the previous chapter, Tooling System, we gave our bot "hands" to perform actions like searching the web or running code.
However, our bot is currently like a genie in a lamp. It has phenomenal cosmic power, but it spends 99% of its life sleeping. It only wakes up when you "rub the lamp" (send a message).
If you want the bot to "Check the news every morning at 8 AM" or "Remind me to buy milk in 20 minutes," the current system fails. The bot cannot start a conversation on its own.
In this final chapter, we will give the bot a Sense of Time.
Currently, your bot is Reactive:
Input (User Message) -> Processing -> Output
We want to make it Proactive:
Time Event -> Processing -> Output
To achieve this, we introduce two internal clocks:
Imagine you want the bot to research stock prices every morning and send you a summary, even if you haven't messaged it yet.
Think of the Heartbeat as a security guard doing rounds. Every 30 minutes (or whatever interval you set), the bot "wakes up" and looks at a specific file on your computer: HEARTBEAT.md.
Using a file (HEARTBEAT.md) allows you (the human) to edit the bot's background tasks simply by opening a text editor.
Let's look at nanobot/heartbeat/service.py. The core logic happens in the _tick method.
# nanobot/heartbeat/service.py
async def _tick(self) -> None:
# 1. Read the instructions from the file
content = self._read_heartbeat_file()
# 2. If the file is empty or just checkboxes, go back to sleep
if _is_heartbeat_empty(content):
return
# 3. Otherwise, wake up the Agent!
# We send a special system prompt to the bot.
await self.on_heartbeat(HEARTBEAT_PROMPT)
Explanation:
_read_heartbeat_file: Opens HEARTBEAT.md.on_heartbeat: This triggers the Agent Loop, just as if a user had sent a message. But instead of "Hi," the message is: "Read HEARTBEAT.md and follow instructions."When the Agent receives this prompt, it uses its Tooling System to read the file, processes the request (e.g., "Summarize unread emails"), and then marks the task as done.
The Heartbeat is great for general background tasks, but it's not precise. If you need a reminder exactly at 4:00 PM, checking every 30 minutes isn't good enough.
For this, we use a Cron Service.
User: "Remind me to call Mom in 10 minutes."
add_cron_job tool.
The Cron Service is a bit more complex because it has to calculate math for dates and times. It lives in nanobot/cron/service.py.
We don't keep jobs just in memory (RAM). If the bot restarts, we don't want to lose our reminders. We save them to a json file.
# nanobot/cron/service.py
def add_job(self, name, schedule, message, ...):
# 1. Create a Job object
job = CronJob(
id=str(uuid.uuid4())[:8],
name=name,
schedule=schedule, # e.g., "in 10 minutes"
payload=CronPayload(message=message)
)
# 2. Calculate when it should run
job.state.next_run_at_ms = _compute_next_run(schedule, _now_ms())
# 3. Save to disk
self._store.jobs.append(job)
self._save_store()
Explanation:
_compute_next_run: A helper that adds "10 minutes" to "Right Now" to get the exact timestamp._save_store: Writes the list of jobs to a JSON file.The Cron Service runs its own mini-loop to watch the time.
# nanobot/cron/service.py
async def _on_timer(self) -> None:
now = _now_ms()
# 1. Find jobs where "Next Run" is in the past
due_jobs = [
j for j in self._store.jobs
if j.state.next_run_at_ms and now >= j.state.next_run_at_ms
]
# 2. Execute them
for job in due_jobs:
await self._execute_job(job)
# 3. Reset the timer for the next job
self._arm_timer()
Explanation:
now vs. scheduled_time.now > scheduled_time, the job is due!_execute_job, which sends the message into the Agent Loop.So, how does the Agent actually use this? We need to expose the Cron Service via a Tool.
In Chapter 5, we learned how to make tools. Here is what the ScheduleTool looks like (simplified):
# nanobot/agent/tools/schedule.py
class ScheduleTool(Tool):
name = "schedule_reminder"
description = "Schedule a reminder for the future."
async def execute(self, message: str, minutes: int):
# 1. Calculate milliseconds
wait_ms = minutes * 60 * 1000
# 2. Call the Cron Service
cron_service.add_job(
name=f"reminder_{minutes}m",
schedule=CronSchedule(kind="every", every_ms=wait_ms),
message=message
)
return "Reminder set."
Now, when you say "Remind me in 5 minutes," the LLM selects this tool, and the Cron Service handles the waiting.
Let's trace the full experience of a Proactive Agent.
HEARTBEAT.md: Check NY weather and alert user if raining.weather_tool. Result: "Sunny."weather_tool. Result: "Raining."The user didn't do anything in step 12. The bot acted on its own.
In this chapter, we added the element of Time.
You have completed the nanobot tutorial series. Let's look at what you have built:
You now possess the blueprint for a modern, sophisticated AI Agent. The only limit left is your imagination. Go build something amazing!
Generated by Code IQ