跳转到内容
本站为社区维护的非官方中文翻译,与 Cloudflare, Inc. 无关联。本站不提供账户登录,请勿在任何非官方站点输入您的 Cloudflare 账户信息。官方文档:developers.cloudflare.com

调度器(Scheduler)

最后更新 查看 MarkdownAgent 设置

agents/schedules 中的 Scheduler 可在未来运行已注册的回调:延迟一段时间后、指定日期、按 cron 表达式或按固定间隔运行。计划会作为作业存储在 Lifecycle 作业队列中,因此对象被逐出后仍会保留,并与其他能力共享对象的闹钟。

安装 capability

在构造函数中按名称注册回调,然后将调度器安装到 Lifecycle 上:

import { DurableObject } from "cloudflare:workers";
import { getCurrentAgent, Lifecycle } from "agents/lifecycle";
import { Scheduler } from "agents/schedules";

export class ReminderAgent extends DurableObject {
	scheduler = new Scheduler({
		callbacks: {
			sendReminder: (payload, schedule) => {
				const { agent } = getCurrentAgent();
				console.log(
					`${agent?.lifecycle.name}: reminder ${schedule.id}: ${payload.message}`,
				);
			},
		},
	});
	lifecycle = Lifecycle.install(this).use(this.scheduler);

	async onStart() {
		// Cron schedules are idempotent, so this adds one row, not one per wake.
		await this.scheduler.set("0 8 * * 1-5", "sendReminder", {
			message: "Good morning",
		});
	}

	async onRequest() {
		const schedule = await this.scheduler.set(300, "sendReminder", {
			message: "Five minutes have passed",
		});
		return Response.json(schedule, { status: 201 });
	}
}
import { DurableObject } from "cloudflare:workers";
import { getCurrentAgent, Lifecycle } from "agents/lifecycle";
import { Scheduler, type Schedule } from "agents/schedules";

type Reminder = { message: string };

export class ReminderAgent extends DurableObject<Env> {
	readonly scheduler = new Scheduler({
		callbacks: {
			sendReminder: (payload: Reminder, schedule: Schedule<Reminder>) => {
				const { agent } = getCurrentAgent<ReminderAgent>();
				console.log(
					`${agent?.lifecycle.name}: reminder ${schedule.id}: ${payload.message}`,
				);
			},
		},
	});
	readonly lifecycle = Lifecycle.install(this).use(this.scheduler);

	async onStart(): Promise<void> {
		// Cron schedules are idempotent, so this adds one row, not one per wake.
		await this.scheduler.set("0 8 * * 1-5", "sendReminder", {
			message: "Good morning",
		});
	}

	async onRequest(): Promise<Response> {
		const schedule = await this.scheduler.set(300, "sendReminder", {
			message: "Five minutes have passed",
		});
		return Response.json(schedule, { status: 201 });
	}
}

每个计划只存储回调名称,不存储函数。对象每次启动时都会重建回调映射,因此逐出前创建的计划会在对象唤醒后找到对应回调。请在字段初始化器中无条件注册每个回调。

set() 和 every() 会根据映射检查回调名称和有效负载类型。回调会收到有效负载和触发该回调的 Schedule。它会在对象的调用上下文中运行,因此 getCurrentAgent() 会返回该对象。

选项

选项 说明
callbacks 命名回调 (payload, schedule) => unknown
retry 回调的默认重试策略:{ maxAttempts, baseDelayMs, maxDelayMs }。默认值依次为 3、100 和 3000。
hungScheduleTimeoutSeconds 运行中的间隔计划在被视为已放弃前等待的秒数。默认值为 30。
onError (error) => void。在回调最后一次重试失败后接收通知。

安排工作

set(when, callback, payload?, options?) 将时间作为第一个参数。when 的类型决定运行模式:

when 模式 示例
number delayed set(60, "sendReminder", payload) 会在 60 秒后运行。
Date scheduled set(new Date("2026-12-25T09:00:00Z"), ...)
string cron set("0 8 * * 1-5", ...) 会在工作日 UTC 时间 08:00 运行。

every(intervalSeconds, callback, payload?, options?) 会创建一个 interval 计划:

await this.scheduler.every(30, "sendReminder", { message: "Every 30 seconds" });

第一次间隔运行会在 intervalSeconds 秒后发生,而不是立即运行。如果上一次运行在下一次到期时仍未完成,调度器会跳过那次运行。

这两个方法都会返回已存储的 Schedule。它包含 id、callback 名称、payload、type 以及以 Unix 秒为单位的下一次运行 time。根据类型,它还会包含 delayInSeconds、cron 或 intervalSeconds。

单个计划的选项

将选项作为第四个参数传入:

选项 说明
retry 覆盖此计划的调度器重试策略
idempotent 若存在具有相同回调、时间和有效负载的计划,则返回该计划,而不是添加新记录。

idempotent 对 cron 和间隔计划默认为 true。你可以在 onStart() 中创建重复计划,而不会在每次唤醒时都添加新记录。对于延迟和定时计划,默认值为 false;每次调用都会添加一条记录,除非传入 { idempotent: true }。

管理计划

方法 说明
get(id) 返回一个计划,若不存在则返回 undefined。
list(criteria?) 返回计划列表,可按 id、type 或 timeRange: { start?, end? } 筛选。
cancel(id) 移除一个计划。若移除了计划则返回 true。
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { Scheduler } from "agents/schedules";

export class ReminderAgent extends DurableObject {
	scheduler = new Scheduler({
		callbacks: {
			sendReminder: (payload) => console.log(payload.message),
		},
		retry: { maxAttempts: 5, baseDelayMs: 500, maxDelayMs: 10_000 },
		onError: (error) => console.error("Reminder failed", error),
	});
	lifecycle = Lifecycle.install(this).use(this.scheduler);

	// An RPC method, so it starts the Lifecycle itself.
	async resetReminders() {
		await this.lifecycle.start();
		const crons = await this.scheduler.list({ type: "cron" });
		for (const schedule of crons) {
			await this.scheduler.cancel(schedule.id);
		}
		await this.scheduler.set(
			60,
			"sendReminder",
			{ message: "Reminders reset" },
			{ idempotent: true, retry: { maxAttempts: 1 } },
		);
		return crons.length;
	}
}
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { Scheduler } from "agents/schedules";

type Reminder = { message: string };

export class ReminderAgent extends DurableObject<Env> {
	readonly scheduler = new Scheduler({
		callbacks: {
			sendReminder: (payload: Reminder) => console.log(payload.message),
		},
		retry: { maxAttempts: 5, baseDelayMs: 500, maxDelayMs: 10_000 },
		onError: (error) => console.error("Reminder failed", error),
	});
	readonly lifecycle = Lifecycle.install(this).use(this.scheduler);

	// An RPC method, so it starts the Lifecycle itself.
	async resetReminders(): Promise<number> {
		await this.lifecycle.start();
		const crons = await this.scheduler.list({ type: "cron" });
		for (const schedule of crons) {
			await this.scheduler.cancel(schedule.id);
		}
		await this.scheduler.set(
			60,
			"sendReminder",
			{ message: "Reminders reset" },
			{ idempotent: true, retry: { maxAttempts: 1 } },
		);
		return crons.length;
	}
}

一次性计划运行后会被删除。cron 或间隔计划则会更新为下一次运行时间。

失败与重试

抛出异常的回调会按指数退避策略重试,最多达到 maxAttempts 次。最后一次尝试后会运行 onError。随后会移除一次性计划;重复计划则会继续安排下一次运行。

如果对象在回调运行期间被逐出,Lifecycle 闹钟会再次唤醒对象,并重新运行该回调。请确保外部操作具有幂等性。

限制

  • Cron 计划精确到分钟。间隔计划精确到秒。
  • 回调会与对象上的其他作业逐个运行。请保持回调简短。
  • 有效负载必须可序列化为 JSON。

从自然语言解析计划

agents/schedules/parser 导出一个提示和 Zod 架构,可将“每周一上午 9 点提醒我”之类的请求转换为结构化计划。请将它们与支持结构化输出的模型调用搭配使用:

import { getSchedulePrompt, scheduleSchema } from "agents/schedules/parser";
import { generateObject } from "ai";

export async function parseRequest(input) {
	const { object } = await generateObject({
		model,
		schema: scheduleSchema,
		system: getSchedulePrompt({ date: new Date() }),
		prompt: input,
	});
	return object; // { description, when: { type: "delayed", delayInSeconds } | ... }
}
import { getSchedulePrompt, scheduleSchema } from "agents/schedules/parser";
import { generateObject, type LanguageModel } from "ai";

declare const model: LanguageModel;

export async function parseRequest(input: string) {
	const { object } = await generateObject({
		model,
		schema: scheduleSchema,
		system: getSchedulePrompt({ date: new Date() }),
		prompt: input,
	});
	return object; // { description, when: { type: "delayed", delayInSeconds } | ... }
}

when.type 可以是 scheduled(带有 date)、delayed(带有 delayInSeconds)、cron(带有 cron 表达式)或 no-schedule。旧的 agents/schedule 导入路径仍可使用,但已弃用。

可观测性

调度器会向 agents:schedule 诊断通道发布 schedule:create、schedule:execute、schedule:retry、schedule:error 和 schedule:cancel 事件。

在 Agent 类上使用

Agent 类会安装 Scheduler。它的 this.schedule()、this.scheduleEvery()、this.getSchedule()、this.listSchedules() 和 this.cancelSchedule() 方法都会调用该能力,回调名称则对应 Agent 上的方法。更多信息,请参阅安排任务。

这篇文档对您有帮助吗?