INTEGRITY Dokumentace

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

1. Nainstalujte balíček

npm i agents

Pro aplikace React nejsou potřeba žádné další balíčky: React bindings jsou součástí.

Pro aplikace Hono:

npm i agents hono-agents

2. 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:

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 types

Tí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:

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:

  1. Klient nazvaný agent.stub.increment() přes WebSocket
  2. Agent spustil increment(), aktualizovaný stav s setState()
  3. Stav se automaticky ukládá do SQLite
  4. Vysílání odeslané všem připojeným klientům
  5. 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 deploy

Váš 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

  1. Zkontrolujte export - Třída Agent musí být exportována z hlavního vstupního bodu.
  2. Zkontrolujte vazbu - class_name v konfiguračním souboru Wrangler musí přesně odpovídat exportovanému názvu třídy.
  3. 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:

  1. Voláte this.setState(), nikoli úpravou this.state přímo.
  2. Třída agenta je v new_sqlite_classes v migracích.
  3. Připojujete se ke stejnému názvu instance agenta.
  4. onStateUpdate callback je propojený ve vašem klientovi.
  5. 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

Zjistit více

Správa stavu

Bližší pohled na setState(), initialState a onStateChanged().

Client SDK

Úplná referenční příručka API pro useAgent a AgentClient.

Volatelné metody

Zpřístupní metody klientům pomocí @callable().

Plánování úloh

Spouštějte úlohy s prodlevou, podle plánu nebo pomocí cronu.

Vnitřní fungování třídy Agent

Referenční příručka pro celý životní cyklus a metody.

Agents API

Kompletní referenční dokumentace API pro Agents SDK.