← Vytvářejte agenty na Cloudflare / agents / runtime / lifecycle
Vnitřní fungování třídy Agent
Jádro agents knihovna je Agent třída. Rozšíříte ji, přepíšete několik metod a získáte správu stavu, WebSockets, plánování, RPC a další věci zdarma. Tato stránka vysvětluje, jak Agent se vytváří vrstvu po vrstvě, abyste rozuměli tomu, co se děje pod kapotou.
Zde uvedené úryvky slouží jako ilustrace a nemusí nutně odpovídat osvědčeným postupům. Úplné API najdete v Referenční dokumentace API a zdrojový kód ↗.
Co je Agent?
Agent třída je rozšířením DurableObject : agents jsou Durable Objects. Pokud Durable Objects neznáte, přečtěte si Co jsou Durable Objects nejprve. V jádru jsou Durable Objects globálně adresovatelné (každá instance má unikátní ID), jednovláknové výpočetní instance s dlouhodobým úložištěm (typu klíč-hodnota a SQLite).
Agent nerozšiřuje DurableObject přímo. Rozšiřuje Server z partyserver ↗ balíček, který rozšiřuje DurableObject. Představte si to jako vrstvy: DurableObject > Server > Agent.
Vrstva 0: Durable Object
Krátce se podívejme, jaké primitivy Durable Objects nabízejí, abychom pochopili, jak je využívají vnější vrstvy. Třída Durable Object obsahuje:
constructor
constructor(ctx: DurableObjectState, env: Env) {}Runtime Workers vždy interně volá konstruktor. To znamená dvě věci:
- Konstruktor se sice volá pokaždé, když se Durable Object inicializuje, ale jeho signatura je pevně daná. Vývojáři nemohou parametry konstruktoru přidávat ani upravovat.
- Místo ručního vytváření instance třídy musí vývojáři použít binding API a provést to prostřednictvím DurableObjectNamespace.
RPC
Napsáním třídy Durable Object, která dědí od vestavěného typu DurableObject, veřejné metody se zpřístupní jako metody RPC, které mohou vývojáři volat pomocí DurableObjectStub z Workeru.
// This instance could've been active, hibernated,
// not initialized or maybe had never even been created!
const stub = env.MY_DO.getByName("foo");
// We can call any public method on the class. The runtime
// ensures the constructor is called if the instance was not active.
await stub.bar();fetch()
Durable Objects mohou k probuzení potřebovat Request z Workeru a odeslat Response zpět. To lze provést pouze prostřednictvím fetch metodu (kterou musí implementovat vývojář).
WebSockets
Durable Objects zahrnují plnohodnotnou podporu pro WebSockets. Durable Object může přijmout WebSocket, který obdrží od Request v fetch a dále se o to nestarat. Základní třída poskytuje metody, které mohou vývojáři implementovat a které se volají jako callbacky. Ty fakticky nahrazují potřebu posluchačů událostí.
Základní třída poskytuje webSocketMessage(ws, message), webSocketClose(ws, code, reason, wasClean) a webSocketError(ws , error) (API).
export class MyDurableObject extends DurableObject {
async fetch(request) {
// Creates two ends of a WebSocket connection.
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
// Calling `acceptWebSocket()` connects the WebSocket to the Durable Object, allowing the WebSocket to send and receive messages.
this.ctx.acceptWebSocket(server);
return new Response(null, {
status: 101,
webSocket: client,
});
}
async webSocketMessage(ws, message) {
ws.send(message);
}
}alarm()
Požadavky HTTP a RPC nejsou jediným vstupním bodem pro Durable Object. Alarmy umožňují vývojářům naplánovat spuštění události v pozdějším čase. Jakmile nastane čas dalšího alarmu, runtime zavolá alarm() metodu, jejíž implementace je ponechána na vývojáři.
Chcete-li naplánovat alarm, můžete použít this.ctx.storage.setAlarm() metodu. Další informace najdete na Alarmy.
this.ctx
Základní DurableObject třída nastavuje DurableObjectState do this.ctx. Nabízí spoustu zajímavých metod a vlastností, my se ale zaměříme na this.ctx.storage.
this.ctx.storage
DurableObjectStorage je hlavní rozhraní k mechanismům perzistence Durable Object, které zahrnují jak KV, tak SQLITE synchronní API.
const sql = this.ctx.storage.sql;
// Synchronous SQL query
const rows = sql.exec("SELECT * FROM contacts WHERE country = ?", "US");
// Key-value storage
const token = this.ctx.storage.get("someToken");this.ctx.env
Nakonec stojí za zmínku, že Durable Object má také Worker Env v this.env. Další informace najdete v Bindings.
Vrstva 1: Server (partyserver)
Nyní, když jste viděli, co Durable Objects nabízejí ihned po instalaci, Server třída z partyserver ↗ bude dávat větší smysl. Jde o účelově navržený DurableObject wrapper, který nahrazuje nízkoúrovňové primitivy callbacky přívětivými pro vývojáře.
Server nepřidává žádné vlastní operace ukládání, pouze obaluje životní cyklus Durable Object.
Adresování
partyserver zpřístupňuje pomocné funkce pro adresování Durable Objects podle jména namísto ručního procházení přes vazby. Patří sem i schéma směrování URL adres (<your-worker>/servers/:durableClass/:durableName) na které vrstva Agent staví.
// Note the await here!
const stub = await getServerByName(env.MY_DO, "foo");
// We can still call RPC methods.
await stub.bar();Schéma URL zároveň umožňuje směrování požadavků. Ve vrstvě Agent je toto opětovně exportováno jako routeAgentRequest:
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const res = await routeAgentRequest(request, env);
if (res) return res;
return new Response("Not found", { status: 404 });
}onStart
Adresovací vrstva umožňuje Server ke zpřístupnění onStart callback, který se spustí pokaždé, když se Durable Object spustí (po vyřazení z paměti, hibernaci nebo prvním vytvoření), a to ještě před jakýmkoli fetch nebo volání RPC.
class MyServer extends Server {
onStart() {
// Some initialization logic that you wish
// to run every time the DO is started up.
const sql = this.ctx.storage.sql;
sql.exec(`...`);
}
}onRequest a onConnect
Server již implementuje fetch pro podkladový Durable Object a poskytuje dva různé callbacky, které mohou vývojáři využít, onRequest a onConnect pro HTTP požadavky, respektive příchozí WS připojení (WebSocket připojení jsou ve výchozím nastavení přijímána).
class MyServer extends Server {
async onRequest(request: Request) {
const url = new URL(request.url);
return new Response(`Hello from ${url.origin}!`);
}
async onConnect(conn, ctx) {
const { request } = ctx;
const url = new URL(request.url);
// Connections are a WebSocket wrapper
conn.send(`Hello from ${url.origin}!`);
}
}WebSockets
Stejně jako onConnect je callback pro každé nové připojení, Server také poskytuje wrappery nad výchozími callbacky z DurableObject třída: onMessage, onClose a onError.
K dispozici je také this.broadcast která odešle WS zprávu všem připojeným klientům (žádná magie, jen cyklus přes this.getConnections()!).
this.name
Je obtížné zjistit u Durable Object name zevnitř něj. partyserver se snaží zpřístupnit ho v this.name ale není to dokonalé řešení. Více se dozvíte v tento GitHub issue ↗.
Vrstva 2: Agent
Nyní nakonec Agent třída. Agent rozšiřuje Server a poskytuje účelově navržené primitivy pro stavové, plánovatelné a pozorovatelné agenty, kteří mohou komunikovat přes RPC, WebSockets a (dokonce!) e-mail.
this.state a this.setState()
Jednou z klíčových funkcí Agent je automatické uchovávání stavu. Vývojáři definují tvar svého stavu pomocí generického parametru a initialState (ten se použije pouze tehdy, pokud v úložišti neexistuje žádný stav), a Agent se stará o načítání, ukládání a odesílání změn stavu (viz Server: this.broadcast() výše).
this.state je getter, který stav líně načítá z úložiště (SQL). Stav zůstává zachován i po vyřazení Durable Object z paměti, pokud je aktualizován pomocí this.setState(), který automaticky serializuje stav a zapisuje ho zpět do úložiště.
K dispozici je také this.onStateChanged kterou můžete přepsat, abyste reagovali na změny stavu.
class MyAgent extends Agent<Env, { count: number }> {
initialState = { count: 0 };
increment() {
this.setState({ count: this.state.count + 1 });
}
onStateChanged(state, source) {
console.log("State updated:", state);
}
}Stav se ukládá v cf_agents_state SQL tabulce. Stavové zprávy se odesílají s type: "cf_agent_state" (jak od klienta, tak od serveru). Protože agents poskytuje Klienti JS a React, aktualizace stavu v reálném čase jsou k dispozici bez dalšího nastavování.
this.sql
Agent poskytuje pohodlný sql tagged template pro spouštění dotazů nad SQL úložištěm Durable Object. Sestavuje parametrizované dotazy a spouští je. Používá se přitom synchronní SQL API z this.ctx.storage.sql.
class MyAgent extends Agent {
onStart() {
this.sql`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT
)
`;
const userId = "1";
const userName = "Alice";
this.sql`INSERT INTO users (id, name) VALUES (${userId}, ${userName})`;
const users = this.sql<{ id: string; name: string }>`
SELECT * FROM users WHERE id = ${userId}
`;
console.log(users); // [{ id: "1", name: "Alice" }]
}
}RPC a volatelné metody
agents posouvá RPC Durable Objects o krok dál tím, že implementuje RPC přes WebSockets, takže klienti mohou volat metody Agenta přímo. Aby byla metoda volatelná přes WebSocket, použijte @callable() dekorátor. Metody mohou vracet serializovatelnou hodnotu nebo stream (při použití @callable({ streaming: true })).
class MyAgent extends Agent {
@callable({ description: "Add two numbers" })
async add(a: number, b: number) {
return a + b;
}
}Klienti mohou tuto metodu vyvolat odesláním zprávy WebSocket:
{
"type": "rpc",
"id": "unique-request-id",
"method": "add",
"args": [2, 3]
}Například s poskytnutým React klienta je to stejně jednoduché jako:
const { stub } = useAgent({ name: "my-agent" });
const result = await stub.add(2, 3);
console.log(result); // 5this.queue a další podobné
Agents obsahují vestavěnou frontu úloh pro odložené provádění. To se hodí pro přenesení práce jinam nebo opakování operací. Dostupné metody jsou this.queue, this.dequeue, this.dequeueAll, this.dequeueAllByCallback, this.getQueue, a this.getQueues.
class MyAgent extends Agent {
async onConnect() {
// Queue a task to be executed later
await this.queue("processTask", { userId: "123" });
}
async processTask(payload: { userId: string }, queueItem: QueueItem) {
console.log("Processing task for user:", payload.userId);
}
}Úlohy se ukládají do cf_agents_queues SQL tabulce a automaticky se v pořadí zpracovávají. Pokud je úloha úspěšná, automaticky se odstraní z fronty.
this.schedule a další podobné
Agents podporují naplánované spouštění metod obalením alarmu Durable Object alarm(). Dostupné metody jsou this.schedule, this.getSchedule, this.getSchedules, this.cancelSchedule. Plány mohou být jednorázové, s odloženým spuštěním nebo opakující se (pomocí cron výrazů).
Protože Durable Objects povolují najednou jen jeden alarm, Agent třída to řeší tak, že spravuje více plánů v SQL a používá jediný alarm.
class MyAgent extends Agent {
async foo() {
// Schedule at a specific time
await this.schedule(new Date("2025-12-25T00:00:00Z"), "sendGreeting", {
message: "Merry Christmas!",
});
// Schedule with a delay (in seconds)
await this.schedule(60, "checkStatus", { check: "health" });
// Schedule with a cron expression
await this.schedule("0 0 * * *", "dailyTask", { type: "cleanup" });
}
async sendGreeting(payload: { message: string }) {
console.log(payload.message);
}
async checkStatus(payload: { check: string }) {
console.log("Running check:", payload.check);
}
async dailyTask(payload: { type: string }) {
console.log("Daily task:", payload.type);
}
}Plány jsou uloženy v cf_agents_schedules SQL tabulce. Plány typu cron se po spuštění automaticky znovu naplánují, zatímco jednorázové plány se po spuštění smažou.
this.mcp a další podobné
Agent zahrnuje klienta MCP pro více serverů. Díky tomu může váš agent komunikovat s externími službami, které zpřístupňují rozhraní MCP. Klient MCP je podrobně zdokumentován v API klienta MCP.
class MyAgent extends Agent {
async onStart() {
// Add an HTTP MCP server (callbackHost only needed for OAuth servers)
await this.addMcpServer("GitHub", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.example.workers.dev",
});
// Add an MCP server via RPC (Durable Object binding, no HTTP overhead)
await this.addMcpServer("internal-tools", this.env.MyMCP);
}
}Práce s e-maily
Agents mohou přijímat e-maily a odpovídat na ně pomocí Email Routing.
class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
console.log("Received email from:", email.from);
console.log("Subject:", email.headers.get("subject"));
const raw = await email.getRaw();
console.log("Raw email size:", raw.length);
// Reply to the email
await this.replyToEmail(email, {
fromName: "My Agent",
subject: "Re: " + email.headers.get("subject"),
body: "Thanks for your email!",
contentType: "text/plain",
});
}
}Chcete-li e-maily směrovat do svého Agenta, použijte routeAgentEmail v e-mailovém handleru vašeho Workeru:
export default {
async email(message, env, ctx) {
await routeAgentEmail(message, env, {
resolver: createAddressBasedEmailResolver("my-agent"),
});
},
} satisfies ExportedHandler<Env>;Správa kontextu
agents obaluje všechny vaše metody pomocí AsyncLocalStorage k udržování kontextu po celou dobu životního cyklu požadavku. Díky tomu můžete z libovolného místa ve svém kódu přistupovat k aktuálnímu agentovi, připojení, požadavku nebo e-mailu (v závislosti na tom, jaká událost se zpracovává):
import { getCurrentAgent } from "agents";
function someUtilityFunction() {
const { agent, connection, request, email } = getCurrentAgent();
if (agent) {
console.log("Current agent:", agent.name);
}
if (connection) {
console.log("WebSocket connection ID:", connection.id);
}
}this.onError
Agent rozšiřuje Server: onError aby ho bylo možné použít i pro zpracování chyb, které nejsou nutně chybami WebSocket. Volá se s Connection nebo unknown chyba.
class MyAgent extends Agent {
onError(connectionOrError: Connection | unknown, error?: unknown) {
if (error) {
// WebSocket connection error
console.error("Connection error:", error);
} else {
// Server error
console.error("Server error:", connectionOrError);
}
// Optionally throw to propagate the error
throw connectionOrError;
}
}this.destroy
this.destroy() odstraní všechny tabulky, smaže alarmy, vymaže úložiště a přeruší kontext. Aby bylo zajištěno úplné vyřazení Durable Object z paměti, this.ctx.abort() se volá asynchronně pomocí setTimeout() aby mohly aktuálně běžící handlery (například plánované úlohy) dokončit své operace čištění, než dojde k přerušení kontextu.
To znamená this.ctx.abort() vyvolá nezachytitelnou chybu, která se objeví ve vašich logách, ale stane se tak až po předání řízení event loopu (více se dočtete v abort()).
destroy() metodu lze bezpečně volat v rámci naplánovaných úloh. Pokud je zavolána uvnitř plánovacího callbacku, Agent nastaví interní příznak, aby přeskočil veškeré zbývající aktualizace databáze, a vrátí ctx.abort() do event loopu, aby obslužná rutina alarmu bezpečně doběhla dříve, než dojde k vystěhování Agenta.
class MyAgent extends Agent {
async onStart() {
console.log("Agent is starting up...");
// Initialize your agent
}
async cleanup() {
// This wipes everything!
await this.destroy();
}
async selfDestruct() {
// Safe to call from within a scheduled task
await this.schedule(60, "destroyAfterDelay", {});
}
async destroyAfterDelay() {
// This will safely destroy the Agent even when
// called from within the alarm handler
await this.destroy();
}
}static options
Nakonfigurujte chování agenta přepsáním static options na vaší třídě. Všechna pole jsou volitelná: výchozí hodnoty se použijí za běhu.
export class MyAgent extends Agent {
static options = {
hibernate: true,
sendIdentityOnConnect: false,
retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
};
}| Možnost | Typ | Výchozí | Popis |
|---|---|---|---|
hibernate |
boolean |
true |
Zda agent při nečinnosti hibernuje. Připojení WebSocket zůstávají otevřená, i když DO spí |
sendIdentityOnConnect |
boolean |
true |
Odešlete identitu (název agenta, název instance) klientům při připojení přes WebSocket. Nastavte na false ke skrytí citlivých názvů instancí |
hungScheduleTimeoutSeconds |
number |
30 |
Časový limit, po kterém je běžící intervalový plán považován za zaseknutý a vynuceně resetován. Pro dlouho běžící callbacky jej zvyšte |
keepAliveIntervalMs |
number |
30000 |
Interval v milisekundách pro keepAlive() alarmových heartbeatů. Nižší hodnoty znamenají rychlejší obnovení, ale častější alarmy |
retry |
RetryOptions |
{ maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 } |
Výchozí možnosti opakování pro schedule(), queue(), a this.retry(). Možnosti nastavené pro jednotlivé úlohy tyto výchozí hodnoty přepisují |
this.keepAlive() a this.keepAliveWhile()
Durable Objects se po období nečinnosti vyřadí z paměti (obvykle po 70 až 140 sekundách bez příchozích požadavků, zpráv WebSocketu nebo alarmů). Během dlouhotrvajících operací, jako je streamování odpovědí LLM, čekání na externí API nebo provádění vícekrokových výpočtů, může být agent vyřazen z paměti uprostřed práce.
keepAlive() vytvoří heartbeat alarmu, který zabraňuje vyřazení z paměti. keepAliveWhile() obaluje asynchronní funkci a zaručuje úklid.
class MyAgent extends Agent {
async handleLongTask() {
// Option 1: manual dispose
const dispose = await this.keepAlive();
try {
await longRunningComputation();
} finally {
dispose();
}
// Option 2: automatic cleanup (recommended)
const result = await this.keepAliveWhile(async () => {
return await longRunningComputation();
});
}
}AIChatAgent používá keepAliveWhile interně, aby agent zůstal aktivní během streamování odpovědí LLM. Podrobnosti najdete v Plánování úloh: Udržování agenta naživu.
Směrování
Agent třída znovu exportuje pomocné funkce pro adresování jako getAgentByName a routeAgentRequest.
const stub = await getAgentByName(env.MY_DO, "foo");
await stub.someMethod();
const res = await routeAgentRequest(request, env);
if (res) return res;
return new Response("Not found", { status: 404 });Vrstva 3: AIChatAgent
AIChatAgent třída z @cloudflare/ai-chat rozšiřuje Agent o účelově navrženou vrstvu pro AI chat. Přidává automatickou perzistenci zpráv do SQLite, obnovitelné streamování, podporu nástrojů (na straně serveru, na straně klienta i s lidským zásahem) a React hook (useAgentChat) pro tvorbu chatovacích uživatelských rozhraní.
Celá hierarchie je: DurableObject > Server > Agent > AIChatAgent.
Pokud vytváříte chatovacího agenta, začněte s AIChatAgent. Pokud potřebujete nižší úroveň kontroly nebo nevytváříte chatové rozhraní, použijte Agent přímo.