INTEGRITY Dokumentace

Opakování

Opakujte neúspěšné operace s exponenciálním odkladem a jitterem. Agents SDK poskytuje vestavěnou podporu opakování pro naplánované úlohy, zařazené úlohy a univerzální this.retry() metodu pro váš vlastní kód.

Přehled

Dočasné chyby jsou běžné při volání externích API, komunikaci s jinými službami nebo spouštění úloh na pozadí. Systém opakování je řeší automaticky:

Rychlý start

Použijte this.retry() k opakování libovolné asynchronní operace:

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;
	}
}

Standardně this.retry() opakuje pokusy až třikrát s exponenciálně narůstajícím prodlením a náhodnou odchylkou (jitter).

this.retry()

retry() metoda je k dispozici na každém Agent instance. Ve výchozím nastavení opakuje zadanou funkci při jakékoli vyvolané chybě.

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

Parametry:

Vrací: výsledek fn při úspěchu.

Vyhazuje: poslední chybu, pokud všechny pokusy selžou, nebo shouldRetry vrací false.

Příklady

Základní opakování:

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

Vlastní možnosti opakování:

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,
	},
);

Používání čísla pokusu:

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();
});

Selektivní opakování pokusu s shouldRetry:

Použijte shouldRetry a zastavit opakování u konkrétních chyb. Predikát dostává jak chybu, tak číslo dalšího pokusu:

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.)
		},
	},
);

Opakování v plánech

Při vytváření plánu předejte možnosti opakování:

// 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 },
	},
);

Pokud callback vyvolá chybu, zopakuje se podle nastavení opakování. Pokud selžou všechny pokusy, chyba se zaznamená do logu a přesměruje přes onError(). Plán se odstraní (u jednorázových plánů), nebo znovu naplánuje (u cron/interval), bez ohledu na to, zda skončil úspěchem, nebo chybou.

Opakování ve frontách

Při přidávání úlohy do fronty předejte možnosti opakování:

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,
	},
});

Pokud callback vyvolá chybu, zopakuje se ještě předtím, než je úloha vyřazena z fronty. Jakmile se vyčerpají všechny pokusy, úloha se vyřadí z fronty a chyba se zaznamená do logu.

Validace

Možnosti opakování se ověřují ihned při volání this.retry(), queue(), schedule(), nebo scheduleEvery(). Neplatné možnosti vyvolají chybu okamžitě, místo aby selhaly až později při spuštění:

// 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 },
});

Validace nejprve slučuje částečné možnosti s výchozími hodnotami na úrovni třídy nebo vestavěnými výchozími hodnotami a teprve poté kontroluje omezení napříč poli. To znamená { baseDelayMs: 5000 } se zachytí okamžitě, jakmile se vyřešený maxDelayMs je 3000, místo aby to selhalo až později při provádění.

Výchozí chování

I bez explicitních možností opakování se naplánované a zařazené callbacky opakují s rozumnými výchozími hodnotami:

Nastavení Výchozí
maxAttempts 3
baseDelayMs 100
maxDelayMs 3000

Tyto výchozí hodnoty platí pro this.retry(), queue(), schedule(), a scheduleEvery(). Možnosti nastavené pro konkrétní místo volání je přepíší.

Výchozí hodnoty na úrovni třídy

Přepište výchozí hodnoty pro celého agenta pomocí static options:

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 },
	};
}

Stačí zadat pouze pole, která chcete změnit, nenastavená pole se vrátí k výchozím vestavěným hodnotám:

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 },
	};
}

Výchozí hodnoty na úrovni třídy se použijí jako záložní řešení, pokud místo volání nezadá možnosti opakování. Možnosti zadané na úrovni jednotlivého volání mají vždy přednost:

// 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 });

Chcete-li u konkrétní úlohy vypnout opakování, nastavte 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;
}

Zpoždění mezi opakováními používá exponenciální odklad s plným jitterem:

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

To znamená, že první pokusy o opakování proběhnou rychle (často do 200ms), zatímco pozdější pokusy se postupně odkládají, aby nedošlo k přetížení selhávající služby. Náhodné rozptýlení (jitter) zabraňuje tomu, aby více agentů provádělo nový pokus přesně ve stejný okamžik.

Jak to funguje

Strategie prodlení

Systém opakování používá strategii „Full Jitter“ z AWS Architecture Blog ↗. Při 3 pokusech s výchozím nastavením:

Pokus Horní mez Skutečné zpoždění
1 min(2^1 * 100, 3000) = 200ms random(0, 200ms)
2 min(2^2 * 100, 3000) = 400ms random(0, 400ms)
3 (bez dalšího opakování, poslední pokus) ,

S maxAttempts: 5 a baseDelayMs: 500:

Pokus Horní mez Skutečné zpoždění
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 (bez dalšího opakování, poslední pokus) ,

Opakování serveru MCP

Při přidávání serveru MCP můžete nakonfigurovat možnosti opakování pro pokusy o připojení a opětovné připojení:

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 },
});

Tyto možnosti se ukládají trvale a použijí se, když:

Výchozí hodnota: 3 pokusy, základní prodleva 500 ms, maximální prodleva 5 s.

Vzory

Opakování s logováním

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);
		}
	}
}

Opakování s fallbackem

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 },
			);
		}
	}
}

Kombinace opakování s plánováním

Pro operace, jejichž zotavení může trvat dlouho (minuty až hodiny), zkombinujte this.retry() pro okamžitá opakování s this.schedule() pro odložená opakování:

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`);
		}
	}
}

Omezení

Další kroky

Plánování úloh

Plánujte úlohy k budoucímu spuštění.

Zařazování úloh do fronty

Fronta úloh na pozadí pro okamžité zpracování.

Spouštění Workflows

Trvalé vícekrokové zpracování s automatickým opakováním.