Welcome back! in Asynchronous Flow Control, we built the engine that allows our tool to wait for a specific amount of time without freezing the whole computer.
Now, we need to teach our AI how to wait intelligently.
Imagine a lifeguard at a busy pool.
Scenario A: The Deep Sleep The lifeguard decides to rest for 60 minutes. They put on noise-canceling headphones and close their eyes.
Scenario B: The Heartbeat (Polling) The lifeguard rests, but every 30 seconds, they open their eyes, scan the pool, and check for trouble. If everything is fine, they close their eyes for another 30 seconds.
In software, we call this Polling or a Heartbeat. We don't want the AI to sleep for an hour straight; we want it to sleep in short bursts so it can "check the pool" (look for new files, errors, or commands).
To implement this, we use a concept called a "Tick." A Tick is simply the moment the AI wakes up, looks around, and decides what to do next.
We define a special marker for this in our code to ensure the AI recognizes this moment.
We use a constant called TICK_TAG. This is a string literal (like <tick>) that acts as a visual cue for the AI model.
// In constants/xml.ts (simulated)
export const TICK_TAG = 'tick'
Explanation: This is just a label. Like a sticky note that says "CHECK POOL."
Periodic Heartbeat Handling isn't just one function; it is a Cycle of Behavior that the AI performs.
Sleep(seconds).
We need to make sure the AI knows that when the Sleep tool finishes, it shouldn't just say "I'm done." It should treat that wake-up as a prompt to look for work.
We reinforce this in our prompt logic (which we touched on in Tool Behavior Definition).
// prompt.ts
import { TICK_TAG } from '../../constants/xml.js'
// We insert the tag into the instructions
const instructions = `You may receive <${TICK_TAG}> prompts.
Look for useful work to do before sleeping.`
Explanation:
When the SleepTool finishes its timer (from Chapter 3), it returns a value to the AI.
// handler.ts
// When the timer finishes, we can return a status
return `Woke up after ${seconds}s. <${TICK_TAG}> Check for changes.`
Explanation:
Instead of just returning "Done," we return a message containing the TICK_TAG. This triggers the "Lifeguard" behavior in the AI's brain.
Let's look at how the System and the AI interact in a loop.
<tick> tag.Why do we need a specific abstraction for this? Why not just let the AI figure it out?
Large Language Models (LLMs) can get "lazy" or "hallucinate" if they don't receive clear feedback. If the tool just returns an empty string "", the AI might think the tool failed or get confused.
By providing a structured Heartbeat, we keep the AI aligned.
Here is a simplified example of how the AI logic processes this heartbeat internally.
// conceptual_ai_logic.ts
async function runAgentLoop() {
while (true) {
// 1. Check for events (The Scan)
const work = checkForNewFiles();
if (work) {
// 2. Act immediately
await processWork(work);
} else {
// 3. Resting State (The Heartbeat)
console.log("Nothing to do. Sleeping...");
await callTool('Sleep', { seconds: 5 });
}
}
}
Explanation:
If the AI calls the tool:
Sleep(10)"Check complete. <tick> Status: Nominal."The AI sees this output, realizes nothing is burning down, and feels safe to sleep again.
You have learned how to turn a "dumb wait" into a Smart Heartbeat.
TICK_TAG to signal a check-in.However, constant waking and sleeping has a cost. Every time the AI wakes up, it has to "remember" what it was doing. If we aren't careful, the AI might forget its instructions or cost us a lot of money in API tokens.
We will learn how to balance these costs in the final chapter.
Next Chapter: Cache Lifecycle Management
Generated by Code IQ