INTEGRITY Dokumentace

Dlouhodobě běžící agenti

Vytvářejte agenty, kteří přetrvávají po dny, týdny nebo měsíce: přežívají restarty, probouzejí se na vyžádání a spravují práci, která trvá mnohem déle než jediný požadavek.

Stručná verze:

Proč Cloudflare pro dlouhotrvající agenty

Agents tráví většinu času čekáním: na vstup uživatele (sekundy až dny), na odpovědi LLM (sekundy až minuty), na výsledky nástrojů (sekundy až hodiny), na schválení od člověka (hodiny až dny) nebo na naplánovaná probuzení (minuty až měsíce). Na tradičním VM nebo v kontejneru platíte za veškerý tento nečinný čas. Agent, který je z 99 % v klidu a jen z 1 % aktivní, vás přesto stojí 100 % ceny serveru.

Durable Objects tento model obracejí. Agent existuje jako adresovatelná entita s trvalým stavem, ale v hibernaci nespotřebovává žádný výpočetní výkon. Jakmile něco nastane, ať už jde o HTTP požadavek, zprávu WebSocketu, naplánovaný alarm nebo příchozí e-mail, platforma agenta probudí, načte jeho stav z SQLite a předá mu danou událost. Agent provede svou práci a poté opět usne.

Toto je aktorový model ↗: každý agent má identitu, trvalý stav a probouzí se při příchodu zprávy. Nespravujete servery, směrování, kontroly stavu ani logiku restartu, o umístění, škálování a obnovu se stará platforma.

Ekonomické důsledky jsou přímočaré:

VM / kontejnery Durable Objects
Náklady za nečinnost Plné výpočetní náklady, vždy Nula (v hibernaci)
Škálování Zajištění a správa kapacity Automaticky, pro každého agenta
Stav Vyžadována externí databáze Vestavěné SQLite
Obnova Budujete to sami (process manažery, health checks) Restart platformy, stav přetrvá
Identita / směrování Budujete to sami (load balancery, sticky sessions) Vestavěné (mapování názvu na agenta)
10,000 agentů, každý aktivní 1 % času 10,000 neustále běžících instancí ~100 aktivních v libovolném okamžiku

Pro agenty, kteří jsou svou povahou nárazoví, stavoví a dlouhodobě běžící, je to přirozeně vhodné řešení.

Životní cyklus dlouhodobě běžícího agenta

Dlouhotrvající agent není proces, který běží nepřetržitě. Je to entita, která existuje nepřetržitě, ale běhy přerušovaně. Pochopení životního cyklu je klíčové pro vytváření agentů, kteří spolehlivě fungují v dlouhém časovém horizontu.

Wake → onStart() → handle events → idle (~2 min) → hibernation
  ▲                                                      │
  └──────────────── alarm or request wakes agent ────────┘

Eviction (crash / redeploy) can happen at any point.
State persists in SQLite. Agent restarts on next event.

Co přežije

Přežijí i jakékoli vyšší abstrakce postavené na SQLite, protože sdílejí stejné trvalé úložiště.

Co nepřežije

Důsledek: veškerá důležitá práce musí být perzistentní nebo obnovitelná. SDK k tomu poskytuje primitiva, totiž plány, fibery a fronty, ale zásadní je pochopit hranici mezi „in-memory“ a „durable“.

Průběžný příklad: agent projektového manažera

V celé této dokumentaci postupně budujeme agenta pro řízení projektů, který:

import { Agent } from "agents";

type ProjectState = {
	name: string;
	status: "planning" | "active" | "review" | "complete";
	tasks: Task[];
	plan: Plan | null;
};

type Task = {
	id: string;
	title: string;
	status: "pending" | "in_progress" | "blocked" | "complete";
	assignee?: string;
	dueDate?: string;
	completedAt?: number;
	externalJobId?: string;
};

export class ProjectManager extends Agent<Env, ProjectState> {
	initialState: ProjectState = {
		name: "",
		status: "planning",
		tasks: [],
		plan: null,
	};
}

Plan typ byl zaveden v Plánování jako strategie trvalosti. K tomuto agentovi přidáváme schopnosti postupně, sekci po sekci.

Probouzení: jak se agenti aktivují

Agenta v hibernaci může probudit kterýkoli z těchto zdrojů:

Zdroj probuzení Jak to funguje Příklad
Požadavek HTTP Jakýkoli požadavek na URL agenta spustí onRequest() Webhook z GitHubu
Připojení WebSocket Klient se připojí, což spustí onConnect() Člen týmu otevře dashboard
Volání RPC Jiný Worker nebo agent zavolá metodu přes service binding nebo @callable Koordinátorský agent deleguje úkol
Naplánovaný alarm Uložený plán se spustí a vyvolá váš callback Připomínka denního standupu v 9:00
E-mail Příchozí e-mail spustí onEmail() Člen týmu odpoví na stavový e-mail

Tento vzor se přirozeně rozšiřuje na jakýkoli zdroj událostí, který dokáže oslovit Worker: od telefonních webhooků až po roboty chatovacích platforem. Přijde externí signál, platforma agenta probudí a agent ho zpracuje.

Agent nemusí být pro každý zdroj probuzení zvlášť „spouštěn“ nebo „nasazován“: všechny vedou ke stejné instanci Durable Object. Směrovacím klíčem je identita agenta, tedy jeho název.

export class ProjectManager extends Agent<Env, ProjectState> {
	async onStart() {
		// Daily deadline check at 9am UTC — idempotent, safe across restarts
		await this.schedule(
			"0 9 * * *",
			"checkDeadlines",
			{},
			{
				idempotent: true,
			},
		);

		// Progress sync every 30 minutes
		await this.scheduleEvery(1800, "syncProgress");
	}

	async onRequest(request: Request): Promise<Response> {
		const url = new URL(request.url);

		if (url.pathname.endsWith("/github-webhook")) {
			const event = await request.json();
			await this.handleGitHubEvent(event);
			return new Response("OK");
		}

		return Response.json({
			project: this.state.name,
			status: this.state.status,
		});
	}

	async checkDeadlines() {
		/* ... find overdue tasks, broadcast alerts ... */
	}
	async syncProgress() {
		/* ... check on sub-agents, update task statuses ... */
	}
}

Udržení aktivity při dlouhotrvající práci

Agent někdy potřebuje provést práci, která trvá déle než okno vyřazení z paměti při nečinnosti (přibližně 70 až 140 sekund). Streamování odpovědi LLM, orchestrace vícekrokového řetězce nástrojů nebo čekání na pomalé API, to vše s sebou nese riziko, že agent bude vyřazen z paměti uprostřed práce.

keepAlive() tomu brání vytvořením heartbeatu, který resetuje časovač nečinnosti:

export class ProjectManager extends Agent<Env, ProjectState> {
	async generateProjectPlan(goal: string) {
		const result = await this.keepAliveWhile(async () => {
			const plan = await this.callLLM(`Create a project plan for: ${goal}`);
			const tasks = await this.callLLM(
				`Break this into tasks: ${JSON.stringify(plan)}`,
			);
			return { plan, tasks };
		});

		this.setState({
			...this.state,
			status: "active",
			plan: result.plan,
			tasks: result.tasks,
		});
	}
}

keepAliveWhile() je doporučený postup: zaručuje, že se heartbeat ukončí, jakmile práce skončí (nebo vyvolá výjimku). Pro ruční řízení keepAlive() vrátí disposer:

const dispose = await this.keepAlive();
try {
	await longWork();
} finally {
	dispose();
}

Když keepAlive nestačí

keepAlive je určen pro práci v řádu minut, ne hodin. Pro skutečně dlouho běžící operace použijte jinou strategii:

Doba trvání Strategie
Sekundy Běžné zpracování požadavků
Minuty keepAlive() / keepAliveWhile()
Minuty startFiber() když záleží na opakovatelném přijetí
Minuty až hodiny Workflows
Hodiny až dny Asynchronní vzor: spusťte úlohu, přejděte do hibernace, probuďte se po dokončení

Přežití pádů: fibery a zotavení

Agent může být kdykoli vyřazen z paměti: nasazením, restartem platformy nebo dosažením limitů zdrojů. Pokud byl agent v polovině úlohy, tato práce se ztratí, pokud nebyla uložena pomocí kontrolního bodu.

runFiber() poskytuje provádění, které lze po pádu obnovit. Po dobu trvání práce ukládá řádek do SQLite a umožňuje vám stash() mezistav. Pokud je agent vyřazen z paměti, řádek fiber zůstává zachován a onFiberRecovered() se volá při další aktivaci.

Použijte startFiber() když důležitou hranicí je trvalé přijetí. Přidává idempotentní klíč, zachované záznamy stavu, kontrolu, zrušení a úklid nad rámec stejného mechanismu fiberu. Ve výchozím nastavení se vrací po přijetí; předejte waitForCompletion: true když má požadavek zůstat otevřený, dokud přijatá úloha nedosáhne konečného stavu. To se hodí pro webhooky, u kterých může poskytovatel opakovat doručení a agent se musí vyhnout spuštění duplicitních viditelných vedlejších efektů.

export class ProjectManager extends Agent<Env, ProjectState> {
	async executeTask(task: Task) {
		await this.runFiber(`task:${task.id}`, async (ctx) => {
			const resources = await this.gatherResources(task);
			ctx.stash({ phase: "prepared", resources, task });

			const result = await this.runSubAgent(task, resources);
			ctx.stash({ phase: "executed", result, task });

			await this.updateTaskStatus(task.id, "complete", result);
		});
	}

	async onFiberRecovered(ctx: FiberRecoveryContext) {
		if (!ctx.name.startsWith("task:")) return;
		const { phase, task } = ctx.snapshot as { phase: string; task: Task };

		if (phase === "prepared") {
			await this.executeTask(task);
		} else if (phase === "executed") {
			await this.updateTaskStatus(
				task.id,
				"complete",
				(ctx.snapshot as { result: unknown }).result,
			);
		}
	}
}

Vzor je: vytvořit kontrolní bod před nákladnou prací, obnovit se z posledního kontrolního bodu. Nejde o automatické přehrání: o tom, co obnovení ve vaší doméně znamená, rozhodujete sami.

Úplnou referenci API najdete zde: FiberContext, FiberRecoveryContext, souběžné fibery, vzory inline oproti fire-and-forget, viz Durable Execution.

Zpracování dlouhých asynchronních operací

Projektový manažer často spouští práci, která trvá mnohem déle než jedna jediná aktivace: CI pipeline běží 20 minut, revize návrhu trvá den, generování video assetu trvá hodiny. Agent by kvůli tomu neměl zůstávat aktivní. Místo toho práci spustí, uloží ID úlohy do stavu a přejde do hibernace. Jakmile výsledek dorazí, ať už prostřednictvím callbacku, dotazu nebo dokončení workflow, agent se probudí, přiřadí výsledek a pokračuje dál.

Vzor: webhookový callback

Projektový manažer spustí pro úkol CI pipeline. Pipeline trvá 20 minut. Namísto udržování otevřeného spojení agent zaregistruje svou vlastní URL jako callback a přejde do spánku:

export class ProjectManager extends Agent<Env, ProjectState> {
	async startCIPipeline(task: Task) {
		const response = await fetch("https://ci.example.com/api/pipelines", {
			method: "POST",
			body: JSON.stringify({
				repo: "org/project",
				branch: "main",
				callback_url: `${this.url}/ci-callback?taskId=${task.id}`,
			}),
		});

		const { pipelineId } = await response.json();
		this.updateTask(task.id, {
			status: "in_progress",
			externalJobId: pipelineId,
		});
	}

	async onRequest(request: Request): Promise<Response> {
		const url = new URL(request.url);
		if (url.pathname.endsWith("/ci-callback")) {
			const taskId = url.searchParams.get("taskId");
			const result = await request.json();
			this.updateTask(taskId, {
				status: result.status === "success" ? "complete" : "blocked",
			});
			return new Response("OK");
		}
		// ... other routes
	}
}

Vzor: dotazování pomocí plánu

Ne každá externí služba podporuje callbacky. Když projektový manažer odešle video k vygenerování, musí pravidelně kontrolovat stav, dokud úloha není dokončená:

export class ProjectManager extends Agent<Env, ProjectState> {
	async startVideoGeneration(task: Task) {
		const response = await fetch("https://video-api.example.com/generate", {
			method: "POST",
			body: JSON.stringify({ prompt: task.title }),
		});
		const { jobId } = await response.json();
		this.updateTask(task.id, { status: "in_progress", externalJobId: jobId });
		await this.schedule(60, "pollExternalJob", {
			taskId: task.id,
			jobId,
			attempt: 1,
		});
	}

	async pollExternalJob(payload: {
		taskId: string;
		jobId: string;
		attempt: number;
	}) {
		const response = await fetch(
			`https://video-api.example.com/status/${payload.jobId}`,
		);
		const status = await response.json();

		if (status.state === "complete" || status.state === "failed") {
			this.updateTask(payload.taskId, {
				status: status.state === "complete" ? "complete" : "blocked",
			});
			return;
		}

		const nextDelay = Math.min(60 * payload.attempt, 600);
		await this.schedule(nextDelay, "pollExternalJob", {
			...payload,
			attempt: payload.attempt + 1,
		});
	}
}

Vzor: delegování na workflow

Produkční nasazení zahrnuje více kroků, které se musí opakovat nezávisle na sobě: sestavení, testování, staging, promotion. Projektový manažer by tyto kroky neměl řídit interně, deleguje je na Workflow která zajišťuje opakování pokusů a řazení kroků:

export class ProjectManager extends Agent<Env, ProjectState> {
	async startDeployment(task: Task) {
		const instanceId = await this.runWorkflow("DEPLOY_WORKFLOW", {
			taskId: task.id,
			environment: "production",
		});
		this.updateTask(task.id, {
			status: "in_progress",
			externalJobId: instanceId,
		});
	}

	async onWorkflowComplete(
		workflowName: string,
		instanceId: string,
		result?: unknown,
	) {
		const task = this.state.tasks.find((t) => t.externalJobId === instanceId);
		if (task) this.updateTask(task.id, { status: "complete" });
	}
}

Rekonstrukce kontextu po dlouhém čekání

Pipeline CI doběhne o 20 minut později. Webhook probudí projektového manažera. Stav úlohy se aktualizuje. Co ale dál? Pokud agent používal LLM k orchestraci práce, tedy k rozhodování, kterou úlohu spustit jako další, k sepsání stavové zprávy nebo k uvažování o blokujících problémech, potřebuje na tuto myšlenkovou linku navázat. Původní prompt, rozpracované volání nástroje i řetězec úvah, to vše zmizelo z paměti.

Toto je zásadní problém dlouhotrvajících AI agentů. Většina frameworků předpokládá, že volání nástrojů proběhne v rámci timeoutu LLM, a tímto problémem se přímo nezabývá.

V současnosti fungují tři přístupy:

Přehrajte celou historii konverzace. AIChatAgent ukládá všechny zprávy do SQLite. Jakmile výsledek dorazí, připojte ho k historii a znovu zavolejte LLM. Toto je nejjednodušší přístup, ale znovu zpracovává celé kontextové okno.

Uložte souhrn pokračování do stashe. Před přechodem do hibernace uložte stručný popis toho, co agent dělal a co se má provést s výsledkem:

ctx.stash({
	task: "Waiting for CI results",
	onSuccess: "Mark task complete, move to next step in plan",
	onFailure: "Notify team, schedule retry in 1 hour",
	relevantContext: { taskId, planStep: 3 },
});

Při obnově použijte stash k sestavení cíleného promptu, místo přehrávání úplně všeho.

Použijte plán jako kontext. Pokud má agent strukturovaný plán, poskytuje samotný plán dostatečný kontext: "Jsem na kroku 3 ze 7, krok byl 'spustit CI pipeline', výsledek právě dorazil." Toto je nejrobustnější přístup pro dlouhotrvající agenty: plán slouží zároveň jako mechanismus obnovy i strategie rekonstrukce kontextu. Viz další část.

Plánování jako strategie trvalosti

Strukturovaný plán není užitečný jen pro zobrazování průběhu uživatelům: je to mechanismus trvanlivosti. Agent s plánem se dokáže zotavit z jakéhokoli přerušení tím, že zjistí, kde přestal.

type Plan = {
	goal: string;
	steps: PlanStep[];
	currentStep: number;
	createdAt: string;
	updatedAt: string;
};

type PlanStep = {
	id: string;
	description: string;
	status: "pending" | "in_progress" | "complete" | "failed" | "skipped";
	result?: unknown;
};

export class ProjectManager extends Agent<Env, ProjectState> {
	async createPlan(goal: string) {
		const steps = await this.keepAliveWhile(async () => {
			return this.callLLM(`
        Break down this project goal into concrete steps.
        Return a JSON array of { id, description } objects.
        Goal: ${goal}
      `);
		});

		this.setState({
			...this.state,
			plan: {
				goal,
				steps: steps.map((s: { id: string; description: string }) => ({
					...s,
					status: "pending" as const,
				})),
				currentStep: 0,
				createdAt: new Date().toISOString(),
				updatedAt: new Date().toISOString(),
			},
		});

		await this.schedule(0, "executeNextStep");
	}

	async executeNextStep() {
		const { plan } = this.state;
		if (!plan || plan.currentStep >= plan.steps.length) {
			this.setState({ ...this.state, status: "complete" });
			return;
		}

		const step = plan.steps[plan.currentStep];

		try {
			const result = await this.keepAliveWhile(() => this.executeStep(step));

			const updatedSteps = plan.steps.map((s) =>
				s.id === step.id ? { ...s, status: "complete" as const, result } : s,
			);
			this.setState({
				...this.state,
				plan: {
					...plan,
					steps: updatedSteps,
					currentStep: plan.currentStep + 1,
					updatedAt: new Date().toISOString(),
				},
			});

			await this.schedule(0, "executeNextStep");
		} catch (error) {
			const updatedSteps = plan.steps.map((s) =>
				s.id === step.id ? { ...s, status: "failed" as const } : s,
			);
			this.setState({
				...this.state,
				plan: {
					...plan,
					steps: updatedSteps,
					updatedAt: new Date().toISOString(),
				},
			});
		}
	}
}

Tento vzor má pro dlouhoběžící agenty několik výhod:

Delegování na podřízené agenty

Projektový manažer nedělá vše sám. Specializovanou práci deleguje na podřízené agenty, z nichž každý má vlastní identitu, stav a životní cyklus.

export class ProjectManager extends Agent<Env, ProjectState> {
	async delegateTask(task: Task) {
		const researcher = await this.subAgent(
			ResearchAgent,
			`research-${task.id}`,
		);

		const findings = await researcher.research(task.title);

		this.updateTask(task.id, { status: "complete" });
		return findings;
	}
}

Podřízení agenti mají vlastní stav, plány, trvalé fibery a životní cyklus. Sdílejí stejný stroj jako nadřazený agent, ale každý podřízený agent ukládá vlastní data SQLite a spouští callbacky s podřízeným agentem jako this.

Protože facety nemají nezávislé sloty pro alarmy, fyzický alarm Durable Object vlastní nadřazený objekt nejvyšší úrovně. Agents SDK eviduje, který podřízený agent vlastní jednotlivý plán nebo právo na obnovu fiberu, probouzí nadřazený objekt a směruje callback zpět k potomkovi. Nadřazený objekt nemusí zůstat aktivní po dobu, kdy podřízený agent pracuje: může práci spustit, hibernovat a nechat se probudit plánem nebo kontrolou obnovy vlastněnými potomkem.

Úplný subAgent() API: typované stuby RPC, směrování klientů, řízení přístupu, izolaci úložiště a rozhraní API podložená alarmy, viz Podřízení agenti. Pro streamování specifické pro AI mezi podřízenými agenty (spouštění celých kol LLM prostřednictvím podřízeného agenta) si přečtěte Think: RPC podřízeného agenta.

Obnovování přerušených streamů LLM

Výše uvedené vzory řeší koordinační práci projektového manažera: plánování, delegování, dotazování. Projektový manažer ale také přímo využívá LLM, a to při generování plánů, shrnování postupu a psaní stavových e-mailů. Tato volání LLM streamují tokeny přes spojení, které nelze obnovit, pokud je agent uprostřed odpovědi odstaven.

Pro agenty orientované na chat postavené na AIChatAgent nebo Think, to je ještě citelnější problém: uživatel sleduje stream odpovědi v reálném čase a vidí, jak se uprostřed věty zastaví. Durable recovery obaluje každé kolo chatu do runFiber. Tím se automaticky zajistí keepAlive během streamování a hook pro obnovení při restartu agenta:

import { AIChatAgent } from "@cloudflare/ai-chat";
import type {
	ChatRecoveryContext,
	ChatRecoveryOptions,
} from "@cloudflare/ai-chat";

class ProjectChat extends AIChatAgent<Env> {
	override async onChatRecovery(
		ctx: ChatRecoveryContext,
	): Promise<ChatRecoveryOptions> {
		// ctx.partialText    — text generated before eviction
		// ctx.recoveryData   — whatever you stashed via this.stash()
		// ctx.messages        — full conversation history
		// ctx.createdAt       — when the interrupted turn started
		return {};
	}
}

Správná strategie obnovení závisí na poskytovateli LLM:

Poskytovatel Strategie Jak to funguje Náklady na tokeny
Workers AI Pokračovat od částečného continueLastTurn() : model pokračuje pomocí assistant prefill Low
OpenAI (Responses API) Načíst dokončenou odpověď Stash responseId během streamování, načtení při obnovení Nula
Anthropic Syntetické pokračování Uložte částečný výsledek a odešlete syntetickou uživatelskou zprávu s žádostí, aby model pokračoval Medium
Ostatní Zkuste prefill, jinak přejděte na syntetické continueLastTurn() pokud to poskytovatel podporuje, jinak syntetickou zprávu Liší se

Použijte ctx.createdAt a potlačit zastaralá obnovení. Pokud je například obnovený tah chatu starší než několik minut, můžete částečnou odpověď uložit, ale přeskočit automatické pokračování, abyste uživatele nepřekvapili starou odpovědí.

AIChatAgent a Think vždy používají trvalé obnovení. Výchozí postup uloží částečný výstup a tah, pokud je to bezpečné, buď pokračuje, nebo ho zopakuje. Přepište onChatRecovery když má poskytovatel lepší strategii obnovy. Nakonfigurujte chatRecovery = { maxAttempts, terminalMessage, onExhausted } k doladění zážitku z terminálu.

Pokud je agent přerušen dříve, než se zapíší jakékoli části streamu odpovědi asistenta, neexistuje žádná částečná zpráva asistenta, na kterou by šlo navázat. Pokud je poslední uložená zpráva stále nezodpovězenou zprávou uživatele z daného kola, obnovení chatu toto kolo automaticky zopakuje, pokud onChatRecovery vrací { continue: false }.

Správa stavu v průběhu času

Agent, který běží měsíce, hromadí data: historii konverzace, události časové osy, dokončené úlohy a záznamy plánů. Bez správy tato data neomezeně rostou.

Údržba

Naplánujte pravidelné čištění, které odstraní stará data a archivuje dokončenou práci:

export class ProjectManager extends Agent<Env, ProjectState> {
	async onStart() {
		await this.schedule("0 0 * * *", "housekeeping", {}, { idempotent: true });
	}

	async housekeeping() {
		const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
		const toArchive = this.state.tasks.filter(
			(t) => t.status === "complete" && (t.completedAt ?? 0) < cutoff,
		);
		for (const task of toArchive) {
			this
				.sql`INSERT INTO archived_tasks (id, data) VALUES (${task.id}, ${JSON.stringify(task)})`;
		}
		this.setState({
			...this.state,
			tasks: this.state.tasks.filter(
				(t) => !toArchive.some((a) => a.id === t.id),
			),
		});

		this.deleteWorkflows({
			status: ["complete", "errored"],
			createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
		});
	}
}

Správa historie konverzace

Pro agenty, kteří používají AIChatAgent, historie konverzace může při delší životnosti výrazně narůst. Bez správy vyčerpá 3měsíční konverzace kontextové okno LLM ještě dlouho před koncem projektu.

Strategie pro správu velikosti konverzace:

Konec životnosti

Dlouhotrvající agent nakonec naplní svůj účel. Projekt se dokončí, vyšetřování skončí, okno pro monitorování se uzavře. Proveďte explicitní úklid:

export class ProjectManager extends Agent<Env, ProjectState> {
	async completeProject() {
		const schedules = await this.listSchedules();
		for (const schedule of schedules) {
			await this.cancelSchedule(schedule.id);
		}

		this.setState({ ...this.state, status: "complete" });

		// All SQLite data, schedules, and state are permanently deleted
		await this.destroy();
	}
}

this.destroy() je trvalé. Pokud byste mohli data agenta později potřebovat, před zničením je archivujte do externího úložiště (R2, D1 nebo prostřednictvím volání API). U agentů, které se mohou znovu aktivovat, je stačí označit jako dokončené a nechat je přejít do hibernace: v nečinnosti nic nestojí.

Kdy použít Workflows oproti vzorům uvnitř agenta

Jak Workflows, tak interní primitiva agenta (plány, fibery, fronty) podporují dlouhotrvající práci. Správná volba závisí na povaze dané práce:

Interní pro Agent Workflows
Vhodné pro Práce zaměřená na agenta: plánování, dotazování (polling), aktualizace stavu Nezávislé vícekrokové pipeline
Trvanlivost SQLite (přežije vyřazení z paměti) Workflow engine (přežije všechno)
Opakování this.retry(), opakování na úrovni plánu Opakování jednotlivých kroků s prodlením
Maximální doba trvání Minuty na aktivaci (s keepAlive) 30 minut na krok, neomezený počet kroků
Lidské schválení Sestavte si to sami (stav + WebSocket) Vestavěné waitForApproval()
Komplexnost Nižší: vše je v agentovi Vyšší: samostatná třída, konfigurace wrangler

Praktické pravidlo: pokud práce spočívá v tom, že agent spravuje svůj vlastní životní cyklus (kontrola termínů, synchronizace stavu, odesílání připomínek), použijte plány a fibery. Pokud jde o samostatný pipeline, který může selhat a nezávisle se opakovat (nasazení, zpracování dat, generování reportu), použijte Workflow.

Agent projektového manažera využívá obojí: plánování pro vlastní rytmus (denní standupy, synchronizace postupu) a Workflows pro náročné operace (nasazení, CI pipeline).

Souhrn

Dlouhodobě běžící agenti na Cloudflare nejsou dlouhodobě běžícími procesy. Jsou to trvalé entity, které se probouzí, pracují a usínají, a to klidně i po dobu týdnů nebo měsíců. Klíčové primitivy:

Primitivum Účel
setState() / this.sql Uložit stav napříč aktivacemi
schedule() / scheduleEvery() Probuďte agenta v budoucích časech
keepAlive() / keepAliveWhile() Prevence vyřazení z paměti během aktivní práce
runFiber() / stash() Vytvořte kontrolní bod a obnovte dlouhotrvající úlohy
startFiber() Trvale přijímejte, kontrolujte a rušte úlohy
chatRecovery Obnovte přerušené streamy LLM
onRequest() / onEmail() / RPC Probuzení při externích událostech
runWorkflow() Delegujte náročnou vícekrokovou práci
subAgent() Delegujte specializovanou práci na podřízené agenty
Strukturované plány ve stavu Povolte obnovení, viditelnost a přeplánování

U agenta projektového manažera se tyto prvky skládají do agenta, který:

  1. Plány : Rozděluje cíle na kroky, plán ukládá do stavu
  2. Spustí se : spouští kroky postupně, mezi nimi hibernuje
  3. Reaguje : probouzí se na základě webhooků, e-mailů a plánů
  4. Obnovuje : pokračuje od posledního kontrolního bodu po jakémkoli přerušení
  5. Deleguje : předává práci podřízeným agentům a Workflows
  6. Zachovává : ořezává stará data, archivuje dokončenou práci, řídí vlastní životní cyklus
  7. Konec : Po dokončení projektu se sám vyčistí a zničí

Agent kvůli tomu nemusí běžet nepřetržitě, stačí, když existuje.