跳转到内容
搜索文档

重试

最后更新 查看 MarkdownAgent 设置

使用指数退避与 jitter 重试失败操作。Agents SDK 为调度任务、队列任务以及通用的 this.retry() 方法提供内置重试支持。

概览

调用外部 API、与其他服务交互或运行后台任务时,瞬态失败很常见。重试系统会自动处理:

  • Exponential backoff — 每次重试等待时间更长
  • Jitter — 随机延迟防止 thundering herd 问题
  • 可配置 — 按调用点调整尝试次数、延迟与上限
  • 内置 — schedule、queue 与 workflow 操作自动重试

快速入门

使用 this.retry() 重试任意 async 操作:

import { Agent } from "agents";

export class MyAgent extends Agent {
	async fetchWithRetry(url) {
		const response = await this.retry(async () => {
			const res = await fetch(url);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			return res.json();
		});

		return response;
	}
}
import { Agent } from "agents";

export class MyAgent extends Agent {
	async fetchWithRetry(url: string) {
		const response = await this.retry(async () => {
			const res = await fetch(url);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			return res.json();
		});

		return response;
	}
}

默认情况下,this.retry() 最多重试三次,并使用带 jitter 的指数退避。

this.retry()

每个 Agent 实例都有 retry() 方法。默认情况下,对任何抛出的错误重试提供的函数。

async retry<T>(
  fn: (attempt: number) => Promise<T>,
  options?: RetryOptions & {
    shouldRetry?: (err: unknown, nextAttempt: number) => boolean;
  }
): Promise<T>

参数:

  • fn — 要重试的 async 函数。接收当前尝试次数(从 1 开始)。
  • options — 可选重试配置(见下方 RetryOptions)。选项会立即验证——无效值会立即抛出。
  • options.shouldRetry — 可选谓词,以抛出的错误与下次尝试次数为参数调用。返回 false 可立即停止重试。未提供时,所有错误都会重试。

返回值: 成功时返回 fn 的结果。

抛出: 若所有尝试失败或 shouldRetry 返回 false,则抛出最后一次错误。

示例

基本重试:

const data = await this.retry(() => fetch("https://api.example.com/data"));
const data = await this.retry(() => fetch("https://api.example.com/data"));

自定义重试选项:

const data = await this.retry(
	async () => {
		const res = await fetch("https://slow-api.example.com/data");
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
		return res.json();
	},
	{
		maxAttempts: 5,
		baseDelayMs: 500,
		maxDelayMs: 10000,
	},
);
const data = await this.retry(
	async () => {
		const res = await fetch("https://slow-api.example.com/data");
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
		return res.json();
	},
	{
		maxAttempts: 5,
		baseDelayMs: 500,
		maxDelayMs: 10000,
	},
);

使用 attempt 编号:

const result = await this.retry(async (attempt) => {
	console.log(`Attempt ${attempt}...`);
	return await this.callExternalService();
});
const result = await this.retry(async (attempt) => {
	console.log(`Attempt ${attempt}...`);
	return await this.callExternalService();
});

使用 shouldRetry 选择性重试:

使用 shouldRetry 可在特定错误上停止重试。谓词同时接收错误与下次尝试次数:

const data = await this.retry(
	async () => {
		const res = await fetch("https://api.example.com/data");
		if (!res.ok) throw new HttpError(res.status, await res.text());
		return res.json();
	},
	{
		maxAttempts: 5,
		shouldRetry: (err, nextAttempt) => {
			// Do not retry 4xx client errors — our request is wrong
			if (err instanceof HttpError && err.status >= 400 && err.status < 500) {
				return false;
			}
			return true; // retry everything else (5xx, network errors, etc.)
		},
	},
);
const data = await this.retry(
	async () => {
		const res = await fetch("https://api.example.com/data");
		if (!res.ok) throw new HttpError(res.status, await res.text());
		return res.json();
	},
	{
		maxAttempts: 5,
		shouldRetry: (err, nextAttempt) => {
			// Do not retry 4xx client errors — our request is wrong
			if (err instanceof HttpError && err.status >= 400 && err.status < 500) {
				return false;
			}
			return true; // retry everything else (5xx, network errors, etc.)
		},
	},
);

调度中的重试

创建 schedule 时传入 retry 选项:

// Retry up to 5 times if the callback fails
await this.schedule(
	"processTask",
	60,
	{ taskId: "123" },
	{
		retry: { maxAttempts: 5 },
	},
);

// Retry with custom backoff
await this.schedule(
	new Date("2026-03-01T09:00:00Z"),
	"sendReport",
	{},
	{
		retry: {
			maxAttempts: 3,
			baseDelayMs: 1000,
			maxDelayMs: 30000,
		},
	},
);

// Cron with retries
await this.schedule(
	"0 8 * * *",
	"dailyDigest",
	{},
	{
		retry: { maxAttempts: 3 },
	},
);

// Interval with retries
await this.scheduleEvery(
	30,
	"poll",
	{ source: "api" },
	{
		retry: { maxAttempts: 5, baseDelayMs: 200 },
	},
);
// Retry up to 5 times if the callback fails
await this.schedule(
	"processTask",
	60,
	{ taskId: "123" },
	{
		retry: { maxAttempts: 5 },
	},
);

// Retry with custom backoff
await this.schedule(
	new Date("2026-03-01T09:00:00Z"),
	"sendReport",
	{},
	{
		retry: {
			maxAttempts: 3,
			baseDelayMs: 1000,
			maxDelayMs: 30000,
		},
	},
);

// Cron with retries
await this.schedule(
	"0 8 * * *",
	"dailyDigest",
	{},
	{
		retry: { maxAttempts: 3 },
	},
);

// Interval with retries
await this.scheduleEvery(
	30,
	"poll",
	{ source: "api" },
	{
		retry: { maxAttempts: 5, baseDelayMs: 200 },
	},
);

若回调抛出异常,将按重试选项重试。若所有尝试失败,错误会被记录并通过 onError() 路由。无论成功或失败,调度仍会被移除(一次性调度)或重新调度(cron/间隔)。

队列中的重试

向队列添加任务时传入重试选项:

await this.queue(
	"sendEmail",
	{ to: "user@example.com" },
	{
		retry: { maxAttempts: 5 },
	},
);

await this.queue("processWebhook", webhookData, {
	retry: {
		maxAttempts: 3,
		baseDelayMs: 500,
		maxDelayMs: 5000,
	},
});
await this.queue(
	"sendEmail",
	{ to: "user@example.com" },
	{
		retry: { maxAttempts: 5 },
	},
);

await this.queue("processWebhook", webhookData, {
	retry: {
		maxAttempts: 3,
		baseDelayMs: 500,
		maxDelayMs: 5000,
	},
});

若回调抛出异常,会在任务出队前重试。所有尝试耗尽后,任务会被出队并记录错误。

验证

调用 this.retry()queue()schedule()scheduleEvery() 时会立即验证重试选项。无效选项会立即抛出,而非延迟到执行时失败:

// Throws immediately: "retry.maxAttempts must be >= 1"
await this.queue("sendEmail", data, {
	retry: { maxAttempts: 0 },
});

// Throws immediately: "retry.baseDelayMs must be > 0"
await this.schedule(
	60,
	"process",
	{},
	{
		retry: { baseDelayMs: -100 },
	},
);

// Throws immediately: "retry.maxAttempts must be an integer"
await this.retry(() => fetch(url), { maxAttempts: 2.5 });

// Throws immediately: "retry.baseDelayMs must be <= retry.maxDelayMs"
// because baseDelayMs: 5000 exceeds the default maxDelayMs: 3000
await this.queue("sendEmail", data, {
	retry: { baseDelayMs: 5000 },
});
// Throws immediately: "retry.maxAttempts must be >= 1"
await this.queue("sendEmail", data, {
	retry: { maxAttempts: 0 },
});

// Throws immediately: "retry.baseDelayMs must be > 0"
await this.schedule(
	60,
	"process",
	{},
	{
		retry: { baseDelayMs: -100 },
	},
);

// Throws immediately: "retry.maxAttempts must be an integer"
await this.retry(() => fetch(url), { maxAttempts: 2.5 });

// Throws immediately: "retry.baseDelayMs must be <= retry.maxDelayMs"
// because baseDelayMs: 5000 exceeds the default maxDelayMs: 3000
await this.queue("sendEmail", data, {
	retry: { baseDelayMs: 5000 },
});

验证会将部分选项与类级别或内置默认值合并后再检查跨字段约束。这意味着当解析后的 maxDelayMs 为 3000 时,{ baseDelayMs: 5000 } 会立即被捕获,而非延迟到执行时才失败。

默认行为

即使没有显式重试选项,调度与队列的回调也会以合理默认值重试:

设置 默认值
maxAttempts 3
baseDelayMs 100
maxDelayMs 3000

这些默认值适用于 this.retry()queue()schedule()scheduleEvery()。每个调用点的选项会覆盖它们。

类级别默认值

通过 static options 为整个 Agent 覆盖默认值:

class MyAgent extends Agent {
	static options = {
		retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
	};
}
class MyAgent extends Agent {
	static options = {
		retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
	};
}

只需指定要更改的字段——未设置字段回退到内置默认值:

class MyAgent extends Agent {
	// Only override maxAttempts; baseDelayMs (100) and maxDelayMs (3000) stay default
	static options = {
		retry: { maxAttempts: 10 },
	};
}
class MyAgent extends Agent {
	// Only override maxAttempts; baseDelayMs (100) and maxDelayMs (3000) stay default
	static options = {
		retry: { maxAttempts: 10 },
	};
}

当调用点未指定 retry 选项时,使用类级别默认值。每个调用点的选项始终优先:

// Uses class-level defaults (10 attempts)
await this.retry(() => fetch(url));

// Overrides to 2 attempts for this specific call
await this.retry(() => fetch(url), { maxAttempts: 2 });
// Uses class-level defaults (10 attempts)
await this.retry(() => fetch(url));

// Overrides to 2 attempts for this specific call
await this.retry(() => fetch(url), { maxAttempts: 2 });

要禁用特定任务的重试,请设置 maxAttempts: 1

await this.schedule(
	60,
	"oneShot",
	{},
	{
		retry: { maxAttempts: 1 },
	},
);
await this.schedule(
	60,
	"oneShot",
	{},
	{
		retry: { maxAttempts: 1 },
	},
);

RetryOptions

interface RetryOptions {
	/** Maximum number of attempts (including the first). Must be an integer >= 1. Default: 3 */
	maxAttempts?: number;
	/** Base delay in milliseconds for exponential backoff. Must be > 0 and <= maxDelayMs. Default: 100 */
	baseDelayMs?: number;
	/** Maximum delay cap in milliseconds. Must be > 0. Default: 3000 */
	maxDelayMs?: number;
}

重试间隔使用全 jitter 指数退避

delay = random(0, min(2^attempt * baseDelayMs, maxDelayMs))

这意味着早期重试很快(通常低于 200ms),后续重试会退避以避免压垮失败的服务。随机化(jitter)可防止多个 Agent 在同一时刻重试。

工作原理

Backoff 策略

重试系统使用 AWS Architecture Blog 的「Full Jitter」策略。默认设置下 3 次尝试:

尝试 上限 实际延迟
1 min(2^1 * 100, 3000) = 200ms random(0, 200ms)
2 min(2^2 * 100, 3000) = 400ms random(0, 400ms)
3 (无重试 — 最后一次尝试)

maxAttempts: 5baseDelayMs: 500 时:

尝试 上限 实际延迟
1 min(2 * 500, 3000) = 1000ms random(0, 1000ms)
2 min(4 * 500, 3000) = 2000ms random(0, 2000ms)
3 min(8 * 500, 3000) = 3000ms random(0, 3000ms)
4 min(16 * 500, 3000) = 3000ms random(0, 3000ms)
5 (无重试 — 最后一次尝试)

MCP 服务器重试

添加 MCP 服务器时,可配置连接与重连尝试的重试选项:

await this.addMcpServer("github", "https://mcp.github.com", {
	retry: { maxAttempts: 5, baseDelayMs: 1000, maxDelayMs: 10000 },
});
await this.addMcpServer("github", "https://mcp.github.com", {
	retry: { maxAttempts: 5, baseDelayMs: 1000, maxDelayMs: 10000 },
});

这些选项会被持久化,并在以下情况使用:

  • 休眠后恢复服务器连接
  • OAuth 完成后建立连接

默认:3 次尝试,500ms base delay,5s max delay。

模式

带日志的重试

class MyAgent extends Agent {
	async resilientTask(payload) {
		try {
			const result = await this.retry(
				async (attempt) => {
					if (attempt > 1) {
						console.log(`Retrying ${payload.url} (attempt ${attempt})...`);
					}
					const res = await fetch(payload.url);
					if (!res.ok) throw new Error(`HTTP ${res.status}`);
					return res.json();
				},
				{ maxAttempts: 5 },
			);
			console.log("Success:", result);
		} catch (e) {
			console.error("All retries failed:", e);
		}
	}
}
class MyAgent extends Agent {
	async resilientTask(payload: { url: string }) {
		try {
			const result = await this.retry(
				async (attempt) => {
					if (attempt > 1) {
						console.log(`Retrying ${payload.url} (attempt ${attempt})...`);
					}
					const res = await fetch(payload.url);
					if (!res.ok) throw new Error(`HTTP ${res.status}`);
					return res.json();
				},
				{ maxAttempts: 5 },
			);
			console.log("Success:", result);
		} catch (e) {
			console.error("All retries failed:", e);
		}
	}
}

带降级的重试

class MyAgent extends Agent {
	async fetchData() {
		try {
			return await this.retry(
				() => fetch("https://primary-api.example.com/data"),
				{ maxAttempts: 3, baseDelayMs: 200 },
			);
		} catch {
			// Primary failed, try fallback
			return await this.retry(
				() => fetch("https://fallback-api.example.com/data"),
				{ maxAttempts: 2 },
			);
		}
	}
}
class MyAgent extends Agent {
	async fetchData() {
		try {
			return await this.retry(
				() => fetch("https://primary-api.example.com/data"),
				{ maxAttempts: 3, baseDelayMs: 200 },
			);
		} catch {
			// Primary failed, try fallback
			return await this.retry(
				() => fetch("https://fallback-api.example.com/data"),
				{ maxAttempts: 2 },
			);
		}
	}
}

将重试与调度结合

对于可能需要较长时间恢复的操作(数分钟或数小时),可将 this.retry() 的即时重试与 this.schedule() 的延迟重试结合:

class MyAgent extends Agent {
	async syncData(payload) {
		const attempt = payload.attempt ?? 1;

		try {
			// Immediate retries for transient failures (seconds)
			await this.retry(() => this.fetchAndProcess(payload.source), {
				maxAttempts: 3,
				baseDelayMs: 1000,
			});
		} catch (e) {
			if (attempt >= 5) {
				console.error("Giving up after 5 scheduled attempts");
				return;
			}

			// Schedule a retry in 5 minutes for longer outages
			const delaySeconds = 300 * attempt;
			await this.schedule(delaySeconds, "syncData", {
				source: payload.source,
				attempt: attempt + 1,
			});
			console.log(`Scheduled retry ${attempt + 1} in ${delaySeconds}s`);
		}
	}
}
class MyAgent extends Agent {
	async syncData(payload: { source: string; attempt?: number }) {
		const attempt = payload.attempt ?? 1;

		try {
			// Immediate retries for transient failures (seconds)
			await this.retry(() => this.fetchAndProcess(payload.source), {
				maxAttempts: 3,
				baseDelayMs: 1000,
			});
		} catch (e) {
			if (attempt >= 5) {
				console.error("Giving up after 5 scheduled attempts");
				return;
			}

			// Schedule a retry in 5 minutes for longer outages
			const delaySeconds = 300 * attempt;
			await this.schedule(delaySeconds, "syncData", {
				source: payload.source,
				attempt: attempt + 1,
			});
			console.log(`Scheduled retry ${attempt + 1} in ${delaySeconds}s`);
		}
	}
}

限制

  • 无死信队列。 若已入队或已调度任务耗尽所有重试,会被移除。若需跟踪失败任务,请自行实现持久化。
  • 重试延迟会阻塞 agent。 在退避延迟期间,Durable Object 处于唤醒但空闲状态。短延迟(3 秒以内)通常没问题。较长恢复时间请改用 this.schedule()
  • 队列重试会造成队头阻塞。 队列项按顺序处理。若某项以长延迟重试,会阻塞后续所有项。若需要独立重试行为,请在回调内使用 this.retry(),而非在 queue() 上使用每任务重试选项。
  • 无熔断器。 重试系统不会跨调用跟踪失败率。若服务持续不可用,每个任务会独立耗尽重试预算。
  • shouldRetry 仅可用于 this.retry() shouldRetry 谓词无法用于 schedule()queue(),因为函数无法序列化到数据库。对于已调度/已入队任务,请在回调内自行处理不可重试错误。

后续步骤

这篇文档对您有帮助吗?