使用指数退避与 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 的指数退避。
每个 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: "[email protected]" },
{
retry: { maxAttempts: 5 },
},
);
await this.queue("processWebhook", webhookData, {
retry: {
maxAttempts: 3,
baseDelayMs: 500,
maxDelayMs: 5000,
},
});await this.queue(
"sendEmail",
{ to: "[email protected]" },
{
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 },
},
);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 在同一时刻重试。
重试系统使用 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: 5 且 baseDelayMs: 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 服务器时,可配置连接与重连尝试的重试选项:
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(),因为函数无法序列化到数据库。对于已调度/已入队任务,请在回调内自行处理不可重试错误。