← Vytvářejte agenty na Cloudflare / agents / concepts / agentic-patterns
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:
- Agents jsou trvalé identity, nikoli neustále běžící procesy.
- Stav, data SQL, plány a kontrolní body fiberu přežijí hibernaci a restarty.
- Proměnné v paměti, časovače, otevřené požadavky fetch a lokální closures nepřežijí vyřazení z paměti.
- Použijte
keepAlive()pro aktivní práci měřenou v minutách,runFiber()když práce potřebuje obnovu,startFiber()když volající potřebují trvalé přijetí a stav, a Workflows pro náročné vícekrokové úlohy. - Použijte podřízené agenty, pokud jeden nadřazený agent koordinuje mnoho dlouhotrvajících kontextů potomků.
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
this.state: ukládá se do SQLite při každémsetState()voláníthis.sqldata : Všechny tabulky SQLite, které vytvoříte- Naplánované úlohy : ukládají se do SQLite, spouští alarmy, které agenta probudí
- Stav připojení,
connection.setState()data pro každého klienta WebSocket - Kontrolní body a ledgery fiberu,
stash()data zrunFiber()a zachoványstartFiber()stavové řádky
Přežijí i jakékoli vyšší abstrakce postavené na SQLite, protože sdílejí stejné trvalé úložiště.
Co nepřežije
- Proměnné v paměti : Pole třídy, která nejsou uložena pomocí
setState()nebothis.sql - Běžící časovače,
setTimeout,setIntervalse ztratí při hibernaci nebo evikci - Otevřené požadavky fetch : právě probíhající volání HTTP se zahodí
- Lokální uzávěry : Callbacky a řetězce promise se ztratí
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ý:
- Existuje po celou dobu trvání projektu (týdny až měsíce)
- Sleduje úkoly, přiděluje práci sub-agentům a hlásí průběh
- Probouzí se podle plánu, aby zkontroloval termíny a odeslal připomenutí
- Reaguje na externí události (webhooky z GitHubu, e-maily od členů týmu)
- Zpracovává dlouhotrvající operace (CI pipeline, revize kódu, nasazení)
- Přežije libovolný počet restartů a vyřazení z paměti po cestě
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 |
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:
- Obnovení je triviální : při restartu zkontrolujte
plan.currentStepa obnovit - Postup je viditelný : Klienti vidí, které kroky jsou dokončené a co bude následovat
- Přeplánování je možné : pokud krok selže nebo se změní požadavky, agent může upravit zbývající kroky, aniž by přišel o již dokončenou práci
- Lidský dohled : plán je přirozeným kontrolním bodem pro schválení („tady je to, co chci udělat, mám pokračovat?“)
- Rekonstrukce kontextu : plán říká LLM, kde se nachází, co se stalo a co má udělat dál, aniž by bylo nutné přehrávat celou konverzaci
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:
- Klouzavé okno : ponechává v aktivním kontextu jen posledních N zpráv. Jednoduché a předvídatelné.
- Shrnování : pravidelně shrnuje starší zprávy a nahrazuje je kompaktním souhrnem. Původní zprávy mohou pro účely auditu zůstat v SQLite.
- Selektivní uchovávání : ponechává zprávy obsahující rozhodnutí, schválení a klíčový kontext, zatímco běžnou komunikaci ořezává.
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ý:
- Plány : Rozděluje cíle na kroky, plán ukládá do stavu
- Spustí se : spouští kroky postupně, mezi nimi hibernuje
- Reaguje : probouzí se na základě webhooků, e-mailů a plánů
- Obnovuje : pokračuje od posledního kontrolního bodu po jakémkoli přerušení
- Deleguje : předává práci podřízeným agentům a Workflows
- Zachovává : ořezává stará data, archivuje dokončenou práci, řídí vlastní životní cyklus
- 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.
Související
- Durable Execution,
runFiber(),startFiber(),stash(), a obnovu po pádu - Plánování úloh : opožděné, cron a intervalové úlohy
- Opakování : možnosti a vzory opakování
- Workflows : trvalé vícekrokové zpracování
- Ukládání a synchronizace stavu,
setState()a perzistence - WebSockets : hooky životního cyklu a hibernace
- Volatelné metody : RPC přes
@callablea service bindings - Směrování e-mailů : přijímání příchozích e-mailů
- Webhooks : přijímání externích událostí
- Člověk ve smyčce : Schvalovací postupy