← Vytvářejte agenty na Cloudflare / agents / runtime / execution
Plánování úloh
Plánujte úlohy ke spuštění v budoucnu: ať už jde o sekundy od teď, konkrétní datum a čas, nebo opakující se plán typu cron. Naplánované úlohy přežijí restart agenta a jsou trvale uloženy v SQLite.
Naplánované úlohy dokážou vše, co požadavek nebo zpráva od uživatele: odesílat požadavky, dotazovat se databází, odesílat e-maily, číst a zapisovat stav. Naplánované úlohy mohou vyvolat libovolnou běžnou metodu vašeho Agentu.
Přehled
Systém plánování podporuje čtyři režimy:
| Režim | Syntaxe | Případ použití |
|---|---|---|
| Odložené | this.schedule(60, ...) |
Spuštění za 60 sekund |
| Naplánováno | this.schedule(new Date(...), ...) |
Spuštění v konkrétním čase |
| Cron | this.schedule("0 8 * * *", ...) |
Spouštění podle opakovaného plánu |
| Interval | this.scheduleEvery(30, ...) |
Spouštění každých 30 sekund |
Plánování pod kapotou využívá Alarmy Durable Object k probuzení agenta ve správný okamžik. Úlohy se ukládají do tabulky SQLite a spouštějí se v pořadí.
Rychlý start
import { Agent } from "agents";
export class ReminderAgent extends Agent {
async onRequest(request) {
const url = new URL(request.url);
// Schedule in 30 seconds
await this.schedule(30, "sendReminder", {
message: "Check your email",
});
// Schedule at specific time
await this.schedule(new Date("2025-02-01T09:00:00Z"), "sendReminder", {
message: "Monthly report due",
});
// Schedule recurring (every day at 8am)
await this.schedule("0 8 * * *", "dailyDigest", {
userId: url.searchParams.get("userId"),
});
return new Response("Scheduled!");
}
async sendReminder(payload) {
console.log(`Reminder: ${payload.message}`);
// Send notification, email, etc.
}
async dailyDigest(payload) {
console.log(`Sending daily digest to ${payload.userId}`);
// Generate and send digest
}
}import { Agent } from "agents";
export class ReminderAgent extends Agent {
async onRequest(request: Request) {
const url = new URL(request.url);
// Schedule in 30 seconds
await this.schedule(30, "sendReminder", {
message: "Check your email",
});
// Schedule at specific time
await this.schedule(new Date("2025-02-01T09:00:00Z"), "sendReminder", {
message: "Monthly report due",
});
// Schedule recurring (every day at 8am)
await this.schedule("0 8 * * *", "dailyDigest", {
userId: url.searchParams.get("userId"),
});
return new Response("Scheduled!");
}
async sendReminder(payload: { message: string }) {
console.log(`Reminder: ${payload.message}`);
// Send notification, email, etc.
}
async dailyDigest(payload: { userId: string }) {
console.log(`Sending daily digest to ${payload.userId}`);
// Generate and send digest
}
}Režimy plánování
Odložené spuštění
Předejte číslo, chcete-li naplánovat spuštění úlohy po prodlevě v sekundy:
// Run in 10 seconds
await this.schedule(10, "processTask", { taskId: "123" });
// Run in 5 minutes (300 seconds)
await this.schedule(300, "sendFollowUp", { email: "[email protected]" });
// Run in 1 hour
await this.schedule(3600, "checkStatus", { orderId: "abc" });// Run in 10 seconds
await this.schedule(10, "processTask", { taskId: "123" });
// Run in 5 minutes (300 seconds)
await this.schedule(300, "sendFollowUp", { email: "[email protected]" });
// Run in 1 hour
await this.schedule(3600, "checkStatus", { orderId: "abc" });Případy použití:
- Debouncing rychlých událostí
- Odložená oznámení („Ve vašem košíku zůstaly položky“)
- Opakování s odkladem
- Omezení četnosti požadavků
Naplánované spuštění
Předejte Date objekt pro naplánování úlohy na konkrétní čas:
// Run tomorrow at noon
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(12, 0, 0, 0);
await this.schedule(tomorrow, "sendReminder", { message: "Meeting time!" });
// Run at a specific timestamp
await this.schedule(new Date("2025-06-15T14:30:00Z"), "triggerEvent", {
eventId: "conference-2025",
});
// Run in 2 hours using Date math
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000);
await this.schedule(twoHoursFromNow, "checkIn", {});// Run tomorrow at noon
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(12, 0, 0, 0);
await this.schedule(tomorrow, "sendReminder", { message: "Meeting time!" });
// Run at a specific timestamp
await this.schedule(new Date("2025-06-15T14:30:00Z"), "triggerEvent", {
eventId: "conference-2025",
});
// Run in 2 hours using Date math
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000);
await this.schedule(twoHoursFromNow, "checkIn", {});Případy použití:
- Připomenutí schůzek
- Oznámení o termínech
- Naplánované publikování obsahu
- Časově řízené triggery
Opakující se (cron)
Předejte řetězec cron výrazu pro opakované plány:
// Every day at 8:00 AM
await this.schedule("0 8 * * *", "dailyReport", {});
// Every hour
await this.schedule("0 * * * *", "hourlyCheck", {});
// Every Monday at 9:00 AM
await this.schedule("0 9 * * 1", "weeklySync", {});
// Every 15 minutes
await this.schedule("*/15 * * * *", "pollForUpdates", {});
// First day of every month at midnight
await this.schedule("0 0 1 * *", "monthlyCleanup", {});// Every day at 8:00 AM
await this.schedule("0 8 * * *", "dailyReport", {});
// Every hour
await this.schedule("0 * * * *", "hourlyCheck", {});
// Every Monday at 9:00 AM
await this.schedule("0 9 * * 1", "weeklySync", {});
// Every 15 minutes
await this.schedule("*/15 * * * *", "pollForUpdates", {});
// First day of every month at midnight
await this.schedule("0 0 1 * *", "monthlyCleanup", {});Syntaxe cron: minute hour day month weekday
| Pole | Hodnoty | Speciální znaky |
|---|---|---|
| Minuta | 0-59 | * , - / |
| Hodina | 0-23 | * , - / |
| Den v měsíci | 1-31 | * , - / |
| Měsíc | 1-12 | * , - / |
| Den v týdnu | 0-6 (0=neděle) | * , - / |
Běžné vzory:
"* * * * *"; // Every minute
"*/5 * * * *"; // Every 5 minutes
"0 * * * *"; // Every hour (on the hour)
"0 0 * * *"; // Every day at midnight
"0 8 * * 1-5"; // Weekdays at 8am
"0 0 * * 0"; // Every Sunday at midnight
"0 0 1 * *"; // First of every month"* * * * *"; // Every minute
"*/5 * * * *"; // Every 5 minutes
"0 * * * *"; // Every hour (on the hour)
"0 0 * * *"; // Every day at midnight
"0 8 * * 1-5"; // Weekdays at 8am
"0 0 * * 0"; // Every Sunday at midnight
"0 0 1 * *"; // First of every monthPřípady použití:
- Denní/týdenní přehledy
- Pravidelné úlohy čištění
- Dotazování externích služeb
- Kontroly stavu
- Obnovení odběru
Plány cron jsou ve výchozím nastavení idempotentní: volání schedule() se stejným výrazem cron, callbackem a payloadem opakovaně vrátí existující plán místo vytvoření duplicity. Díky tomu je bezpečné nastavovat plány cron v onStart().
Interval
Použijte scheduleEvery() ke spouštění úlohy v pevných intervalech (v sekundách). Na rozdíl od cronu intervaly podporují přesnost pod jednu minutu i libovolnou délku trvání:
// Poll every 30 seconds
await this.scheduleEvery(30, "poll", { source: "api" });
// Health check every 45 seconds
await this.scheduleEvery(45, "healthCheck", {});
// Sync every 90 seconds (1.5 minutes - cannot be expressed in cron)
await this.scheduleEvery(90, "syncData", { destination: "warehouse" });// Poll every 30 seconds
await this.scheduleEvery(30, "poll", { source: "api" });
// Health check every 45 seconds
await this.scheduleEvery(45, "healthCheck", {});
// Sync every 90 seconds (1.5 minutes - cannot be expressed in cron)
await this.scheduleEvery(90, "syncData", { destination: "warehouse" });Klíčové rozdíly oproti cronu:
| Funkce | Cron | Interval |
|---|---|---|
| Minimální granularita | 1 minuta | 1 sekunda |
| Libovolné intervaly | Ne (musí odpovídat cron vzoru) | Ano |
| Pevný plán | Ano (například "každý den v 8 hodin ráno") | Ne (relativně k začátku) |
| Prevence překrývání | Ne | Ano (vestavěné) |
Idempotence:
scheduleEvery() je idempotentní vůči kombinaci názvu callbacku, intervalu a payloadu: opakované volání se stejnými argumenty nevytváří duplicitní plány. Díky tomu je bezpečné volat ji v onStart(), který se spouští při každém probuzení Durable Object:
class MyAgent extends Agent {
async onStart() {
// Safe to call on every wake — only one schedule is created
await this.scheduleEvery(30, "poll", { source: "api" });
}
}class MyAgent extends Agent {
async onStart() {
// Safe to call on every wake — only one schedule is created
await this.scheduleEvery(30, "poll", { source: "api" });
}
}Jiný interval nebo payload vytvoří nový, nezávislý plán.
Prevence překrývání:
Pokud callback trvá déle než daný interval, další spuštění se přeskočí (nezařadí se do fronty). Tím se zabrání nekontrolovanému růstu spotřeby zdrojů:
class PollingAgent extends Agent {
async poll() {
// If this takes 45 seconds and interval is 30 seconds,
// the next poll is skipped (with a warning logged)
const data = await slowExternalApi();
await this.processData(data);
}
}
// Set up 30-second interval
await this.scheduleEvery(30, "poll", {});class PollingAgent extends Agent {
async poll() {
// If this takes 45 seconds and interval is 30 seconds,
// the next poll is skipped (with a warning logged)
const data = await slowExternalApi();
await this.processData(data);
}
}
// Set up 30-second interval
await this.scheduleEvery(30, "poll", {});Když dojde k přeskočení, uvidíte v logu varování:
Skipping interval schedule abc123: previous execution still runningOdolnost vůči chybám:
Pokud callback vyvolá chybu, interval pokračuje, selže pouze toto spuštění:
class SyncAgent extends Agent {
async syncData() {
// Even if this throws, the interval keeps running
const response = await fetch("https://api.example.com/data");
if (!response.ok) throw new Error("Sync failed");
// ...
}
}class SyncAgent extends Agent {
async syncData() {
// Even if this throws, the interval keeps running
const response = await fetch("https://api.example.com/data");
if (!response.ok) throw new Error("Sync failed");
// ...
}
}Případy použití:
- Dotazování v intervalech kratších než minuta (každých 10, 30, 45 sekund)
- Intervaly, které neodpovídají cronu (každých 90 sekund, každých 7 minut)
- Dotazování API s omezenou rychlostí a přesnou kontrolou
- Synchronizace dat v reálném čase
Správa naplánovaných úloh
Získat plán
Načtěte naplánovanou úlohu podle jejího ID:
const schedule = await this.getScheduleById(scheduleId);
if (schedule) {
console.log(
`Task ${schedule.id} will run at ${new Date(schedule.time * 1000)}`,
);
console.log(`Callback: ${schedule.callback}`);
console.log(`Type: ${schedule.type}`); // "scheduled" | "delayed" | "cron" | "interval"
} else {
console.log("Schedule not found");
}const schedule = await this.getScheduleById(scheduleId);
if (schedule) {
console.log(
`Task ${schedule.id} will run at ${new Date(schedule.time * 1000)}`,
);
console.log(`Callback: ${schedule.callback}`);
console.log(`Type: ${schedule.type}`); // "scheduled" | "delayed" | "cron" | "interval"
} else {
console.log("Schedule not found");
}Vypsat plány
Dotázat se na naplánované úlohy s volitelnými filtry:
// Get all scheduled tasks
const allSchedules = await this.listSchedules();
// Get only cron jobs
const cronJobs = await this.listSchedules({ type: "cron" });
// Get tasks in the next hour
const upcoming = await this.listSchedules({
timeRange: {
start: new Date(),
end: new Date(Date.now() + 60 * 60 * 1000),
},
});
// Get a specific task by ID
const specific = await this.listSchedules({ id: "abc123" });
// Combine filters
const upcomingCronJobs = await this.listSchedules({
type: "cron",
timeRange: {
start: new Date(),
end: new Date(Date.now() + 24 * 60 * 60 * 1000),
},
});// Get all scheduled tasks
const allSchedules = await this.listSchedules();
// Get only cron jobs
const cronJobs = await this.listSchedules({ type: "cron" });
// Get tasks in the next hour
const upcoming = await this.listSchedules({
timeRange: {
start: new Date(),
end: new Date(Date.now() + 60 * 60 * 1000),
},
});
// Get a specific task by ID
const specific = await this.listSchedules({ id: "abc123" });
// Combine filters
const upcomingCronJobs = await this.listSchedules({
type: "cron",
timeRange: {
start: new Date(),
end: new Date(Date.now() + 24 * 60 * 60 * 1000),
},
});Zrušení plánu
Odeberte naplánovanou úlohu před jejím spuštěním:
const cancelled = await this.cancelSchedule(scheduleId);
if (cancelled) {
console.log("Schedule cancelled successfully");
} else {
console.log("Schedule not found (may have already executed)");
}const cancelled = await this.cancelSchedule(scheduleId);
if (cancelled) {
console.log("Schedule cancelled successfully");
} else {
console.log("Schedule not found (may have already executed)");
}Příklad: zrušitelné připomínky
class ReminderAgent extends Agent {
async setReminder(userId, message, delaySeconds) {
const schedule = await this.schedule(delaySeconds, "sendReminder", {
userId,
message,
});
// Store the schedule ID so user can cancel later
this.sql`
INSERT INTO user_reminders (user_id, schedule_id, message)
VALUES (${userId}, ${schedule.id}, ${message})
`;
return schedule.id;
}
async cancelReminder(scheduleId) {
const cancelled = await this.cancelSchedule(scheduleId);
if (cancelled) {
this.sql`DELETE FROM user_reminders WHERE schedule_id = ${scheduleId}`;
}
return cancelled;
}
async sendReminder(payload) {
// Send the reminder...
// Clean up the record
this.sql`DELETE FROM user_reminders WHERE user_id = ${payload.userId}`;
}
}class ReminderAgent extends Agent {
async setReminder(userId: string, message: string, delaySeconds: number) {
const schedule = await this.schedule(delaySeconds, "sendReminder", {
userId,
message,
});
// Store the schedule ID so user can cancel later
this.sql`
INSERT INTO user_reminders (user_id, schedule_id, message)
VALUES (${userId}, ${schedule.id}, ${message})
`;
return schedule.id;
}
async cancelReminder(scheduleId: string) {
const cancelled = await this.cancelSchedule(scheduleId);
if (cancelled) {
this.sql`DELETE FROM user_reminders WHERE schedule_id = ${scheduleId}`;
}
return cancelled;
}
async sendReminder(payload: { userId: string; message: string }) {
// Send the reminder...
// Clean up the record
this.sql`DELETE FROM user_reminders WHERE user_id = ${payload.userId}`;
}
}Objekt Schedule
Při vytvoření nebo načtení plánu získáte Schedule objekt:
type Schedule<T> = {
id: string; // Unique identifier
callback: string; // Method name to call
payload: T; // Data passed to the callback
time: number; // Unix timestamp (seconds) of next execution
} & (
| { type: "scheduled" } // One-time at specific date
| { type: "delayed"; delayInSeconds: number } // One-time after delay
| { type: "cron"; cron: string } // Recurring (cron expression)
| { type: "interval"; intervalSeconds: number } // Recurring (fixed interval)
);Příklad:
const schedule = await this.schedule(60, "myTask", { foo: "bar" });
console.log(schedule);
// {
// id: "abc123xyz",
// callback: "myTask",
// payload: { foo: "bar" },
// time: 1706745600,
// type: "delayed",
// delayInSeconds: 60
// }const schedule = await this.schedule(60, "myTask", { foo: "bar" });
console.log(schedule);
// {
// id: "abc123xyz",
// callback: "myTask",
// payload: { foo: "bar" },
// time: 1706745600,
// type: "delayed",
// delayInSeconds: 60
// }Vzory
Přeplánování z callbacků
Pro dynamické opakující se plány naplánujte další spuštění přímo v rámci callbacku:
class PollingAgent extends Agent {
async startPolling(intervalSeconds) {
await this.schedule(intervalSeconds, "poll", { interval: intervalSeconds });
}
async poll(payload) {
try {
const data = await fetch("https://api.example.com/updates");
await this.processUpdates(await data.json());
} catch (error) {
console.error("Polling failed:", error);
}
// Schedule the next poll (regardless of success/failure)
await this.schedule(payload.interval, "poll", payload);
}
async stopPolling() {
// Cancel all polling schedules
const schedules = await this.listSchedules({ type: "delayed" });
for (const schedule of schedules) {
if (schedule.callback === "poll") {
await this.cancelSchedule(schedule.id);
}
}
}
}class PollingAgent extends Agent {
async startPolling(intervalSeconds: number) {
await this.schedule(intervalSeconds, "poll", { interval: intervalSeconds });
}
async poll(payload: { interval: number }) {
try {
const data = await fetch("https://api.example.com/updates");
await this.processUpdates(await data.json());
} catch (error) {
console.error("Polling failed:", error);
}
// Schedule the next poll (regardless of success/failure)
await this.schedule(payload.interval, "poll", payload);
}
async stopPolling() {
// Cancel all polling schedules
const schedules = await this.listSchedules({ type: "delayed" });
for (const schedule of schedules) {
if (schedule.callback === "poll") {
await this.cancelSchedule(schedule.id);
}
}
}
}Opakování s exponenciálním prodlením
class RetryAgent extends Agent {
async attemptTask(payload) {
try {
await this.doWork(payload.taskId);
console.log(
`Task ${payload.taskId} succeeded on attempt ${payload.attempt}`,
);
} catch (error) {
if (payload.attempt >= payload.maxAttempts) {
console.error(
`Task ${payload.taskId} failed after ${payload.maxAttempts} attempts`,
);
return;
}
// Exponential backoff: 2^attempt seconds (2s, 4s, 8s, 16s...)
const delaySeconds = Math.pow(2, payload.attempt);
await this.schedule(delaySeconds, "attemptTask", {
...payload,
attempt: payload.attempt + 1,
});
console.log(`Retrying task ${payload.taskId} in ${delaySeconds}s`);
}
}
async doWork(taskId) {
// Your actual work here
}
}class RetryAgent extends Agent {
async attemptTask(payload: {
taskId: string;
attempt: number;
maxAttempts: number;
}) {
try {
await this.doWork(payload.taskId);
console.log(
`Task ${payload.taskId} succeeded on attempt ${payload.attempt}`,
);
} catch (error) {
if (payload.attempt >= payload.maxAttempts) {
console.error(
`Task ${payload.taskId} failed after ${payload.maxAttempts} attempts`,
);
return;
}
// Exponential backoff: 2^attempt seconds (2s, 4s, 8s, 16s...)
const delaySeconds = Math.pow(2, payload.attempt);
await this.schedule(delaySeconds, "attemptTask", {
...payload,
attempt: payload.attempt + 1,
});
console.log(`Retrying task ${payload.taskId} in ${delaySeconds}s`);
}
}
async doWork(taskId: string) {
// Your actual work here
}
}Agenti s automatickým sebezničením
Bezpečně můžete volat this.destroy() zevnitř naplánovaného callbacku:
class TemporaryAgent extends Agent {
async onStart() {
// Self-destruct in 24 hours
await this.schedule(24 * 60 * 60, "cleanup", {});
}
async cleanup() {
// Perform final cleanup
console.log("Agent lifetime expired, cleaning up...");
// This is safe to call from a scheduled callback
await this.destroy();
}
}class TemporaryAgent extends Agent {
async onStart() {
// Self-destruct in 24 hours
await this.schedule(24 * 60 * 60, "cleanup", {});
}
async cleanup() {
// Perform final cleanup
console.log("Agent lifetime expired, cleaning up...");
// This is safe to call from a scheduled callback
await this.destroy();
}
}plánování s podporou AI
SDK obsahuje nástroje pro rozpoznávání požadavků na plánování v přirozeném jazyce pomocí AI.
getSchedulePrompt()
Vrací systémový prompt pro parsování přirozeného jazyka do parametrů plánování:
import { getSchedulePrompt, scheduleSchema } from "agents";
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
class SmartScheduler extends Agent {
async parseScheduleRequest(userInput) {
const result = await generateObject({
model: openai("gpt-4o"),
system: getSchedulePrompt({ date: new Date() }),
prompt: userInput,
schema: scheduleSchema,
});
return result.object;
}
async handleUserRequest(input) {
// Parse: "remind me to call mom tomorrow at 3pm"
const parsed = await this.parseScheduleRequest(input);
// parsed = {
// description: "call mom",
// when: {
// type: "scheduled",
// date: "2025-01-30T15:00:00Z"
// }
// }
if (parsed.when.type === "scheduled" && parsed.when.date) {
await this.schedule(new Date(parsed.when.date), "sendReminder", {
message: parsed.description,
});
} else if (parsed.when.type === "delayed" && parsed.when.delayInSeconds) {
await this.schedule(parsed.when.delayInSeconds, "sendReminder", {
message: parsed.description,
});
} else if (parsed.when.type === "cron" && parsed.when.cron) {
await this.schedule(parsed.when.cron, "sendReminder", {
message: parsed.description,
});
}
}
async sendReminder(payload) {
console.log(`Reminder: ${payload.message}`);
}
}import { getSchedulePrompt, scheduleSchema } from "agents";
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
class SmartScheduler extends Agent {
async parseScheduleRequest(userInput: string) {
const result = await generateObject({
model: openai("gpt-4o"),
system: getSchedulePrompt({ date: new Date() }),
prompt: userInput,
schema: scheduleSchema,
});
return result.object;
}
async handleUserRequest(input: string) {
// Parse: "remind me to call mom tomorrow at 3pm"
const parsed = await this.parseScheduleRequest(input);
// parsed = {
// description: "call mom",
// when: {
// type: "scheduled",
// date: "2025-01-30T15:00:00Z"
// }
// }
if (parsed.when.type === "scheduled" && parsed.when.date) {
await this.schedule(new Date(parsed.when.date), "sendReminder", {
message: parsed.description,
});
} else if (parsed.when.type === "delayed" && parsed.when.delayInSeconds) {
await this.schedule(parsed.when.delayInSeconds, "sendReminder", {
message: parsed.description,
});
} else if (parsed.when.type === "cron" && parsed.when.cron) {
await this.schedule(parsed.when.cron, "sendReminder", {
message: parsed.description,
});
}
}
async sendReminder(payload: { message: string }) {
console.log(`Reminder: ${payload.message}`);
}
}scheduleSchema
Schéma Zod pro ověřování zpracovaných dat o plánování. Používá diskriminovanou unii podle when.type aby každá varianta obsahovala jen pole, která potřebuje:
import { scheduleSchema } from "agents";
// The schema is a discriminated union:
// {
// description: string,
// when:
// | { type: "scheduled", date: string } // ISO 8601 date string
// | { type: "delayed", delayInSeconds: number }
// | { type: "cron", cron: string }
// | { type: "no-schedule" }
// }import { scheduleSchema } from "agents";
// The schema is a discriminated union:
// {
// description: string,
// when:
// | { type: "scheduled", date: string } // ISO 8601 date string
// | { type: "delayed", delayInSeconds: number }
// | { type: "cron", cron: string }
// | { type: "no-schedule" }
// }Plánování vs. Queue vs. Workflows
| Funkce | Queue | Plánování | Workflows |
|---|---|---|---|
| Kdy | Ihned (FIFO) | Budoucí čas | Budoucí čas |
| Spuštění | Sekvenční | V naplánovaný čas | Vícekrokové |
| Opakování | Vestavěné | Vestavěné | Automaticky |
| Perzistence | SQLite | SQLite | Workflow engine |
| Opakující se | Ne | Ano (cron) | Ne (použijte plánování) |
| Komplexní logika | Ne | Ne | Ano |
| Lidské schválení | Ne | Ne | Ano |
Používejte Queue, když:
- Potřebujete zpracování na pozadí, aniž byste blokovali odpověď
- Úlohy by se měly spustit co nejdříve, ale nemusí blokovat
- Na pořadí záleží (FIFO)
Používejte Plánování, když:
- Úlohy je potřeba spustit v konkrétním čase
- Potřebujete opakující se úlohy (cron)
- Odložené spuštění (debouncing, opakování)
Používejte Workflows, když:
- Vícekrokové procesy se závislostmi
- Automatická opakování s prodlením
- Schválení s lidským zásahem
- Dlouhotrvající úlohy (minuty až hodiny)
Referenční dokumentace API
schedule()
async schedule<T>(
when: Date | string | number,
callback: keyof this,
payload?: T,
options?: { retry?: RetryOptions; idempotent?: boolean }
): Promise<Schedule<T>>Naplánujte úlohu k budoucímu spuštění.
Parametry:
when- Kdy se má spustit:number(zpoždění v sekundách),Date(konkrétní čas), nebostring(cron výraz)callback- Název volané metodypayload- Data, která se předají callbacku (musí být serializovatelná do JSON)options.retry- Volitelná konfigurace opakování. Viz Opakování pro podrobnostioptions.idempotent- Deduplikace podle callbacku a payloadu. Výchozí hodnota jetruepro plány cron,falsepro odložené a datumové plány
Vrací: Schedule objekt s podrobnostmi úlohy
Idempotence:
Plány cron jsou ve výchozím nastavení idempotentní: volání schedule("0 * * * *", "tick") vícekrát se stejným callbackem, cron výrazem a payloadem vrátí existující plán místo vytvoření duplicity. Nastavte idempotent: false k přepsání tohoto chování.
Pro odložené a na datu založené plány nastavte idempotent: true k zapojení do stejného chování deduplikace (shoda podle callbacku a payloadu). To je užitečné zejména při volání schedule() v onStart() abyste zabránili hromadění duplicitních řádků při restartech Durable Object:
class MyAgent extends Agent {
async onStart() {
// Without idempotent: true, this creates a new row on every DO restart
await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
}
}class MyAgent extends Agent {
async onStart() {
// Without idempotent: true, this creates a new row on every DO restart
await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
}
}scheduleEvery()
async scheduleEvery<T>(
intervalSeconds: number,
callback: keyof this,
payload?: T,
options?: { retry?: RetryOptions }
): Promise<Schedule<T>>Naplánujte úlohu, která se bude opakovaně spouštět v pevném intervalu.
Parametry:
intervalSeconds- Počet sekund mezi spuštěními (musí být větší než 0)callback- Název volané metodypayload- Data, která se předají callbacku (musí být serializovatelná do JSON)options.retry- Volitelná konfigurace opakování. Viz Opakování s podrobnostmi.
Vrací: Schedule objekt s type: "interval"
Chování:
- První spuštění proběhne po
intervalSeconds(ne okamžitě) - Pokud callback v okamžiku dalšího plánovaného spuštění stále běží, toto spuštění se přeskočí (prevence překrývání)
- Pokud callback vyvolá chybu, interval pokračuje
- Zrušení pomocí
cancelSchedule(id)a zastavit celý interval
getScheduleById()
async getScheduleById(id: string): Promise<Schedule<unknown> | undefined>Získat naplánovanou úlohu podle ID. Vrátí undefined pokud nebylo nalezeno. Tato metoda funguje jak u agentů nejvyšší úrovně, tak u podřízených agentů.
listSchedules()
async listSchedules(criteria?: {
id?: string;
type?: "scheduled" | "delayed" | "cron" | "interval";
timeRange?: { start?: Date; end?: Date };
}): Promise<Schedule<unknown>[]>Získat naplánované úlohy odpovídající zadaným kritériím. Tato metoda funguje jak u agentů nejvyšší úrovně, tak u podřízených agentů.
getSchedule()
getSchedule<T>(id: string): Schedule<T> | undefinedZastaralé. Synchronně získá naplánovanou úlohu podle ID. Tato metoda funguje pouze u agentů nejvyšší úrovně. Použijte await this.getScheduleById(id) místo toho.
getSchedules()
getSchedules<T>(criteria?: {
id?: string;
type?: "scheduled" | "delayed" | "cron" | "interval";
timeRange?: { start?: Date; end?: Date };
}): Schedule<T>[]Zastaralé. Synchronně získá naplánované úlohy odpovídající zadaným kritériím. Tato metoda funguje pouze u agentů nejvyšší úrovně. Použijte await this.listSchedules(criteria) místo toho.
cancelSchedule()
async cancelSchedule(id: string): Promise<boolean>Zruší naplánovanou úlohu. Vrátí true pokud je zrušeno, false pokud nebylo nalezeno.
keepAlive()
async keepAlive(): Promise<() => void>Zabránit vyřazení Durable Object z paměti kvůli nečinnosti podržením reference na 30sekundový heartbeat vázaný na alarm. Vrací funkci disposer, která po zavolání heartbeat uvolní. Disposer je idempotentní: jeho opakované volání je bezpečné.
Po dokončení práce vždy zavolejte disposer, jinak bude heartbeat pokračovat donekonečna.
const dispose = await this.keepAlive();
try {
// Long-running work that must not be interrupted
const result = await longRunningComputation();
await sendResults(result);
} finally {
dispose();
}const dispose = await this.keepAlive();
try {
// Long-running work that must not be interrupted
const result = await longRunningComputation();
await sendResults(result);
} finally {
dispose();
}keepAliveWhile()
async keepAliveWhile<T>(fn: () => Promise<T>): Promise<T>Spusťte asynchronní funkci a přitom udržujte Durable Object naživu. Heartbeat se automaticky spustí před spuštěním funkce a zastaví se po jejím dokončení (ať už uspěje, nebo vyvolá chybu). Vrací hodnotu, kterou funkce vrátí.
Toto je doporučený způsob, jak používat keepAlive : zaručuje úklid.
const result = await this.keepAliveWhile(async () => {
const data = await longRunningComputation();
return data;
});const result = await this.keepAliveWhile(async () => {
const data = await longRunningComputation();
return data;
});Udržování agenta naživu
Durable Objects se po období nečinnosti vyřadí z paměti (obvykle po 70 až 140 sekundách bez příchozích požadavků, zpráv WebSocketu nebo alarmů). Během dlouhotrvajících operací, jako je streamování odpovědí LLM, čekání na externí API nebo provádění vícekrokových výpočtů, může být agent vyřazen z paměti uprostřed práce.
keepAlive() tomu brání tím, že drží referenci na heartbeat v paměti a přímo využívá systém alarmů Durable Object. Samotné spuštění alarmu resetuje časovač nečinnosti.
- Heartbeat nekoliduje s vašimi vlastními plány, protože systém alarmů multiplexuje přes jediný slot pro alarm.
- Nevytvoří se žádné řádky plánu a heartbeat zůstává neviditelný pro
listSchedules(). - Více souběžných
keepAlive()volání využívají počítadlo odkazů, takže jeden disposer neuvolní heartbeat jiného volajícího. - Uvnitř dílčích agentů
keepAlive()deleguje tento odkaz na heartbeat na nadřazený objekt nejvyšší úrovně, protože facety nemají nezávislé sloty pro alarmy.
Více souběžných volajících
Každý keepAlive() volání vrátí nezávislý disposer:
const dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();
// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat
dispose2(); // Now the agent can go idleconst dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();
// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat
dispose2(); // Now the agent can go idleAIChatAgent
AIChatAgent automaticky volá keepAlive() během streamovaných odpovědí. Nemusíte jej přidávat sami, pokud používáte AIChatAgent : každý stream LLM je ve výchozím nastavení chráněn před vyřazením z paměti kvůli nečinnosti.
Kdy použít keepAlive
| Scénář | Použít keepAlive? |
|---|---|
Streamování odpovědí LLM přes AIChatAgent |
Ne: již je to vestavěné |
| Dlouhotrvající výpočet ve vlastním Agentovi | Ano |
| Čekání na pomalé externí volání API | Ano |
| Vícekrokové spouštění nástrojů | Ano |
| Krátké handlery typu request-response | Ne: není to potřeba |
| Práce na pozadí prostřednictvím plánování nebo workflow | Ne: alarmy již udržují DO aktivní |
Limity
- Maximální počet úloh: Omezeno úložištěm SQLite (každá úloha je jeden řádek). Praktický limit je desítky tisíc na agenta.
- Velikost úlohy: Každá úloha (včetně payloadu) může mít až 2 MB.
- Minimální zpoždění: 0 sekund (spustí se při dalším tiku alarmu)
- Přesnost cron: Na úrovni minut (ne sekund)
- Přesnost intervalu: Druhá úroveň
- Úlohy cron: Po provedení se automaticky naplánuje na další výskyt
- Intervalové úlohy: Po provedení naplánováno na
now + intervalSeconds; přeskočí se, pokud stále běží