← Vytvářejte agenty na Cloudflare / agents / getting-started
Přidání do stávajícího projektu
Tento návod ukazuje, jak přidat agenty do existujícího projektu Cloudflare Workers. Pokud začínáte úplně od začátku, přečtěte si Vytváření chatovacího agenta místo toho.
Předpoklady
- Existující projekt Cloudflare Workers s konfiguračním souborem Wrangler
- Node.js 18 nebo novější
1. Nainstalujte balíček
npm i agentsPro aplikace React nejsou potřeba žádné další balíčky: React bindings jsou součástí.
Pro aplikace Hono:
npm i agents hono-agents2. Vytvořte Agenta
Vytvořte nový soubor pro svého agenta (například src/agents/counter.ts):
import { Agent, callable } from "agents";
export class CounterAgent extends Agent {
initialState = { count: 0 };
@callable()
increment() {
this.setState({ count: this.state.count + 1 });
return this.state.count;
}
@callable()
decrement() {
this.setState({ count: this.state.count - 1 });
return this.state.count;
}
}import { Agent, callable } from "agents";
export type CounterState = {
count: number;
};
export class CounterAgent extends Agent<Env, CounterState> {
initialState: CounterState = { count: 0 };
@callable()
increment() {
this.setState({ count: this.state.count + 1 });
return this.state.count;
}
@callable()
decrement() {
this.setState({ count: this.state.count - 1 });
return this.state.count;
}
}3. Aktualizujte konfiguraci Wrangler
Přidejte vazbu Durable Object a migraci:
{
"name": "my-existing-project",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{
"name": "CounterAgent",
"class_name": "CounterAgent",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["CounterAgent"],
},
],
}name = "my-existing-project"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
compatibility_flags = [ "nodejs_compat" ]
[[durable_objects.bindings]]
name = "CounterAgent"
class_name = "CounterAgent"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "CounterAgent" ]Klíčové body:
nameve vazbách se stane vlastnostíenv(napříkladenv.CounterAgent)class_namemusí přesně odpovídat názvu vaší exportované třídynew_sqlite_classespovolí úložiště SQLite pro perzistenci stavunodejs_compatpříznak je pro balíček agents povinný
4. Nakonfigurujte TypeScript a Vite
Pokud používáte @callable() dekorátory (jako ve výše uvedeném příkladu), potřebujete dvě konfigurace sestavení.
tsconfig.json : rozšiřte agents/tsconfig (nebo nastavte "target": "ES2021" ručně):
{
"extends": "agents/tsconfig"
}Pokud již máte existující tsconfig.json s vlastním nastavením můžete rozšířit a přepsat:
{
"extends": "agents/tsconfig",
"compilerOptions": {
"paths": { "~/*": ["./src/*"] }
}
}vite.config.ts : Přidejte agents() plugin (řeší transformace dekorátorů TC39 pro Vite 8):
import agents from "agents/vite";
export default defineConfig({
plugins: [
agents(),
// ... your existing plugins
],
});import agents from "agents/vite";
export default defineConfig({
plugins: [
agents(),
// ... your existing plugins
],
});Pokud váš projekt nepoužívá Vite, tsconfig.json změna samotná stačí, ale váš bundler musí podporovat TC39 dekorátory (stage 3, verze 2023-11).
Podrobnosti najdete v Konfigurace TypeScript a Konfigurace Vite reference.
5. Exportujte třídu Agent
Třída vašeho agenta musí být exportována z hlavního vstupního bodu. Aktualizujte svůj src/index.ts:
// Export the agent class (required for Durable Objects)
export { CounterAgent } from "./agents/counter";
// Your existing exports...
export default {
// ...
};// Export the agent class (required for Durable Objects)
export { CounterAgent } from "./agents/counter";
// Your existing exports...
export default {
// ...
} satisfies ExportedHandler<Env>;6. Propojte směrování
Zvolte přístup, který odpovídá struktuře vašeho projektu:
Obyčejné Workers (fetch handler)
import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";
export default {
async fetch(request, env, ctx) {
// Try agent routing first
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// Your existing routing logic
const url = new URL(request.url);
if (url.pathname === "/api/hello") {
return Response.json({ message: "Hello!" });
}
return new Response("Not found", { status: 404 });
},
};import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Try agent routing first
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// Your existing routing logic
const url = new URL(request.url);
if (url.pathname === "/api/hello") {
return Response.json({ message: "Hello!" });
}
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;Hono
import { Hono } from "hono";
import { agentsMiddleware } from "hono-agents";
export { CounterAgent } from "./agents/counter";
const app = new Hono();
// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
app.use("*", agentsMiddleware());
// Your existing routes continue to work
app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
export default app;import { Hono } from "hono";
import { agentsMiddleware } from "hono-agents";
export { CounterAgent } from "./agents/counter";
const app = new Hono<{ Bindings: Env }>();
// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
app.use("*", agentsMiddleware());
// Your existing routes continue to work
app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
export default app;Se statickými assety
Pokud vedle agentů obsluhujete i statické assety, standardně se jako první obsluhují právě statické assety. Kód Workeru se spustí jen pro cesty, které neodpovídají žádnému statickému assetu:
import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";
export default {
async fetch(request, env, ctx) {
// Static assets are served automatically before this runs
// This only handles non-asset requests
// Route to agents
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
return new Response("Not found", { status: 404 });
},
};import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Static assets are served automatically before this runs
// This only handles non-asset requests
// Route to agents
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;Nakonfigurujte assety v konfiguračním souboru Wrangler:
{
"assets": {
"directory": "./public",
},
}[assets]
directory = "./public"7. Vygenerujte typy TypeScript
Nepište ručně svůj Env rozhraní. Spusťte wrangler types k vygenerování souboru s definicemi typů, který odpovídá vaší konfiguraci Wrangler. Díky tomu se nesoulad mezi konfigurací a kódem odhalí už v době kompilace, a ne až v době nasazení.
Znovu spusťte wrangler types kdykoli přidáte nebo přejmenujete binding.
npx wrangler typesTím se vytvoří soubor s definicemi typů, ve kterém jsou otypované všechny vaše bindings, včetně jmenných prostorů Durable Object patřících vašemu agentovi. Tento Agent třída ve výchozím nastavení používá vygenerovaný Env typ, takže ho není třeba předávat jako parametr typu: extends Agent stačí, pokud nepotřebujete předat druhý typový parametr pro stav (například Agent<Env, CounterState>).
Viz Konfigurace pro další podrobnosti o generování typů.
8. Připojte se z frontendu
React
import { useState } from "react";
import { useAgent } from "agents/react";
function CounterWidget() {
const [count, setCount] = useState(0);
const agent = useAgent({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
return (
<>
{count}
<button onClick={() => agent.stub.increment()}>+</button>
<button onClick={() => agent.stub.decrement()}>-</button>
</>
);
}import { useState } from "react";
import { useAgent } from "agents/react";
import type { CounterAgent, CounterState } from "./agents/counter";
function CounterWidget() {
const [count, setCount] = useState(0);
const agent = useAgent<CounterAgent, CounterState>({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
return (
<>
{count}
<button onClick={() => agent.stub.increment()}>+</button>
<button onClick={() => agent.stub.decrement()}>-</button>
</>
);
}Klíčové body:
useAgentse připojuje k vašemu agentovi přes WebSocketonStateUpdatese spustí vždy, když se změní stav agentaagent.stub.methodName()volá metody označené@callable()na vašem agentovi
Vanilla JavaScript
import { AgentClient } from "agents/client";
const agent = new AgentClient({
agent: "CounterAgent",
name: "user-123", // Optional: unique instance name
onStateUpdate: (state) => {
document.getElementById("count").textContent = state.count;
},
});
// Call methods
document.getElementById("increment").onclick = () => agent.call("increment");import { AgentClient } from "agents/client";
const agent = new AgentClient({
agent: "CounterAgent",
name: "user-123", // Optional: unique instance name
onStateUpdate: (state) => {
document.getElementById("count").textContent = state.count;
},
});
// Call methods
document.getElementById("increment").onclick = () => agent.call("increment");Jak to funguje
Když jste klikli na tlačítko:
- Klient nazvaný
agent.stub.increment()přes WebSocket - Agent spustil
increment(), aktualizovaný stav ssetState() - Stav se automaticky ukládá do SQLite
- Vysílání odeslané všem připojeným klientům
- React aktualizováno přes
onStateUpdate
flowchart LR
A["Browser<br/>(React)"] <-->|WebSocket| B["Agent<br/>(Counter)"]
B --> C["SQLite<br/>(State)"]
Klíčové koncepty
| Koncept | Co to znamená |
|---|---|
| Instance Agent | Každý jedinečný název získá vlastního agenta. CounterAgent:user-123 je oddělený od CounterAgent:user-456 |
| Trvalý stav | Stav přežije restarty, nasazení i hibernaci. Je uložen v SQLite |
| Synchronizace v reálném čase | Všichni klienti připojení ke stejnému agentovi dostávají aktualizace stavu okamžitě |
| Hibernace | Když není připojen žádný klient, agent hibernuje (bez nákladů). Probudí se při dalším požadavku |
Deploy to Cloudflare
npm run deployVáš agent je nyní aktivní v globální síti Cloudflare a běží blízko vašich uživatelů.
Běžné vzory integrace
Agents za autentizací
Před směrováním na agenty zkontrolujte ověření:
export default {
async fetch(request, env) {
// Check auth for agent routes
if (request.url.includes("/agents/")) {
const authResult = await checkAuth(request, env);
if (!authResult.valid) {
return new Response("Unauthorized", { status: 401 });
}
}
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// ... rest of routing
},
};export default {
async fetch(request: Request, env: Env) {
// Check auth for agent routes
if (request.url.includes("/agents/")) {
const authResult = await checkAuth(request, env);
if (!authResult.valid) {
return new Response("Unauthorized", { status: 401 });
}
}
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// ... rest of routing
},
} satisfies ExportedHandler<Env>;Vlastní prefix cesty agenta
Ve výchozím nastavení jsou agenti směrováni na /agents/{agent-name}/{instance-name}. Toto můžete přizpůsobit:
import { routeAgentRequest } from "agents";
const agentResponse = await routeAgentRequest(request, env, {
prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}
});import { routeAgentRequest } from "agents";
const agentResponse = await routeAgentRequest(request, env, {
prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}
});Viz Směrování pro další možnosti, včetně CORS, vlastního pojmenování instancí a nápověd k umístění.
Přístup k agentům ze serverového kódu
S agenty můžete pracovat přímo z kódu Worker:
import { getAgentByName } from "agents";
export default {
async fetch(request, env) {
if (request.url.endsWith("/api/increment")) {
// Get a specific agent instance
const counter = await getAgentByName(env.CounterAgent, "shared-counter");
const newCount = await counter.increment();
return Response.json({ count: newCount });
}
// ...
},
};import { getAgentByName } from "agents";
export default {
async fetch(request: Request, env: Env) {
if (request.url.endsWith("/api/increment")) {
// Get a specific agent instance
const counter = await getAgentByName(env.CounterAgent, "shared-counter");
const newCount = await counter.increment();
return Response.json({ count: newCount });
}
// ...
},
} satisfies ExportedHandler<Env>;Přidání více agentů
Přidejte další agenty rozšířením konfigurace:
// src/agents/chat.ts
export class Chat extends Agent {
// ...
}
// src/agents/scheduler.ts
export class Scheduler extends Agent {
// ...
}// src/agents/chat.ts
export class Chat extends Agent {
// ...
}
// src/agents/scheduler.ts
export class Scheduler extends Agent {
// ...
}Aktualizujte konfigurační soubor Wrangler:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"durable_objects": {
"bindings": [
{
"name": "CounterAgent",
"class_name": "CounterAgent"
},
{
"name": "Chat",
"class_name": "Chat"
},
{
"name": "Scheduler",
"class_name": "Scheduler"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"CounterAgent",
"Chat",
"Scheduler"
]
}
]
}[[durable_objects.bindings]]
name = "CounterAgent"
class_name = "CounterAgent"
[[durable_objects.bindings]]
name = "Chat"
class_name = "Chat"
[[durable_objects.bindings]]
name = "Scheduler"
class_name = "Scheduler"
[[migrations]]
tag = "v1"
new_sqlite_classes = ["CounterAgent", "Chat", "Scheduler"]Exportujte všechny agenty ze svého vstupního bodu:
export { CounterAgent } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";export { CounterAgent } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";Řešení potíží
Agent nenalezen nebo chyby 404
- Zkontrolujte export - Třída Agent musí být exportována z hlavního vstupního bodu.
- Zkontrolujte vazbu -
class_namev konfiguračním souboru Wrangler musí přesně odpovídat exportovanému názvu třídy. - Zkontrolujte trasu - Výchozí trasa je
/agents/{'{agent-name}'}/{'{instance-name}'}. Název agenta na klientovi odpovídá názvu třídy (nerozlišuje se velikost písmen).
Chyba No such Durable Object class
Přidejte migraci do konfiguračního souboru Wrangler:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"YourAgentClass"
]
}
]
}[[migrations]]
tag = "v1"
new_sqlite_classes = ["YourAgentClass"]Připojení WebSocket selže
Ujistěte se, že vaše směrování předává odpověď beze změny:
// Correct - return the response directly
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// Wrong - this breaks WebSocket connections
if (agentResponse) return new Response(agentResponse.body);// Correct - return the response directly
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// Wrong - this breaks WebSocket connections
if (agentResponse) return new Response(agentResponse.body);Stav se neukládá
Zkontrolujte, že:
- Voláte
this.setState(), nikoli úpravouthis.statepřímo. - Třída agenta je v
new_sqlite_classesv migracích. - Připojujete se ke stejnému názvu instance agenta.
-
onStateUpdatecallback je propojený ve vašem klientovi. - Připojení WebSocket je navázáno (zkontrolujte nástroje pro vývojáře v prohlížeči).
chyby typu "Method X is not callable"
Ujistěte se, že jsou vaše metody dekorovány pomocí @callable():
import { Agent, callable } from "agents";
export class MyAgent extends Agent {
@callable()
increment() {
// ...
}
}import { Agent, callable } from "agents";
export class MyAgent extends Agent {
@callable()
increment() {
// ...
}
}Typové chyby s agent.stub
Přidejte parametry typu agenta a stavu:
import { useAgent } from "agents/react";
// Pass the agent and state types to useAgent
const agent = useAgent({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
// Now agent.stub is fully typed
agent.stub.increment();import { useAgent } from "agents/react";
import type { CounterAgent, CounterState } from "./server";
// Pass the agent and state types to useAgent
const agent = useAgent<CounterAgent, CounterState>({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
// Now agent.stub is fully typed
agent.stub.increment();SyntaxError: Invalid or unexpected token hodnotou @callable()
Pokud váš vývojový server selže s SyntaxError: Invalid or unexpected token, nastavte "target": "ES2021" ve vašem tsconfig.json. Tím se zajistí, že transpiler esbuild v rámci Vite převede dekorátory TC39 do staršího zápisu, místo aby je ponechal jako nativní syntaxi.
{
"compilerOptions": {
"target": "ES2021"
}
}Další kroky
Nyní, když máte funkčního agenta, prozkoumejte tato témata:
Běžné další kroky
| Zjistěte, jak | Viz |
|---|---|
| Přidání AI/LLM funkcí | Používání modelů AI |
| Zpřístupnění nástrojů přes MCP | MCP servery |
| Spouštění úloh na pozadí | Plánování úloh |
| Zpracování e-mailů | Směrování e-mailů |
| Použití Cloudflare Workflows | Spouštění Workflows |