INTEGRITY Dokumentace

Směrování

Tento návod vysvětluje, jak se požadavky směrují k agentům, jak funguje pojmenovávání a jaké existují vzory pro organizaci vašich agentů.

Jak funguje routing

Když přijde požadavek, routeAgentRequest() prozkoumá URL adresu a přesměruje ji na příslušnou instanci agenta:

https://your-worker.dev/agents/{agent-name}/{instance-name}
                               └────┬────┘   └─────┬─────┘
                               Class name     Unique instance ID
                              (kebab-case)

Příklady URL adres:

URL Agent Class Instance
/agents/counter/user-123 Counter user-123
/agents/chat-room/lobby ChatRoom lobby
/agents/my-agent/default MyAgent default

Rozlišení názvů

Názvy tříd Agent se pro URL automaticky převádějí na kebab-case:

Název třídy URL Path
Counter /agents/counter/...
MyAgent /agents/my-agent/...
ChatRoom /agents/chat-room/...
AIAssistant /agents/ai-assistant/...

Router odpovídá jak originálnímu názvu, tak verzi v kebab-case, takže můžete použít kterýkoli z nich:

Používání routeAgentRequest()

routeAgentRequest() funkce je hlavním vstupním bodem pro směrování agenta:

import { routeAgentRequest } from "agents";

export default {
	async fetch(request, env, ctx) {
		// Route to agents - returns Response or undefined
		const agentResponse = await routeAgentRequest(request, env);

		if (agentResponse) {
			return agentResponse;
		}

		// No agent matched - handle other routes
		return new Response("Not found", { status: 404 });
	},
};
import { routeAgentRequest } from "agents";

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		// Route to agents - returns Response or undefined
		const agentResponse = await routeAgentRequest(request, env);

		if (agentResponse) {
			return agentResponse;
		}

		// No agent matched - handle other routes
		return new Response("Not found", { status: 404 });
	},
} satisfies ExportedHandler<Env>;

Vzory pojmenování instancí

Název instance (poslední část URL adresy) určuje, která instance agenta požadavek zpracuje. Každý jedinečný název získá vlastního izolovaného agenta s vlastním stavem.

Agenti pro jednotlivé uživatele

Každý uživatel získá vlastní instanci agenta:

// Client
const agent = useAgent({
	agent: "UserProfile",
	name: `user-${userId}`, // e.g., "user-abc123"
});
// Client
const agent = useAgent({
	agent: "UserProfile",
	name: `user-${userId}`, // e.g., "user-abc123"
});
/agents/user-profile/user-abc123 → User abc123's agent
/agents/user-profile/user-xyz789 → User xyz789's agent (separate instance)

Sdílené místnosti

Více uživatelů sdílí stejnou instanci agenta:

// Client
const agent = useAgent({
	agent: "ChatRoom",
	name: roomId, // e.g., "general" or "room-42"
});
// Client
const agent = useAgent({
	agent: "ChatRoom",
	name: roomId, // e.g., "general" or "room-42"
});
/agents/chat-room/general → All users in "general" share this agent

Globální singleton

Jedna instance pro celou aplikaci:

// Client
const agent = useAgent({
	agent: "AppConfig",
	name: "default", // Or any consistent name
});
// Client
const agent = useAgent({
	agent: "AppConfig",
	name: "default", // Or any consistent name
});

Dynamické pojmenování

Generujte názvy instancí na základě kontextu:

// Per-session
const agent = useAgent({
	agent: "Session",
	name: sessionId,
});

// Per-document
const agent = useAgent({
	agent: "Document",
	name: `doc-${documentId}`,
});

// Per-game
const agent = useAgent({
	agent: "Game",
	name: `game-${gameId}-${Date.now()}`,
});
// Per-session
const agent = useAgent({
	agent: "Session",
	name: sessionId,
});

// Per-document
const agent = useAgent({
	agent: "Document",
	name: `doc-${documentId}`,
});

// Per-game
const agent = useAgent({
	agent: "Game",
	name: `game-${gameId}-${Date.now()}`,
});

Vlastní směrování URL

Pro pokročilé případy použití, kde potřebujete kontrolu nad strukturou URL, můžete obejít výchozí /agents/{agent}/{name} vzor.

Používání basePath (na straně klienta)

basePath možnost umožňuje klientům připojit se k jakékoli cestě URL:

// Client connects to /user instead of /agents/user-agent/...
const agent = useAgent({
	agent: "UserAgent", // Required but ignored when basePath is set
	basePath: "user", // → connects to /user
});
// Client connects to /user instead of /agents/user-agent/...
const agent = useAgent({
	agent: "UserAgent", // Required but ignored when basePath is set
	basePath: "user", // → connects to /user
});

Toto se hodí, když:

Výběr instance na straně serveru

Při použití basePath, server musí zajistit směrování. Použijte getAgentByName() k získání instance agenta, poté předejte požadavek pomocí fetch():

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		// Custom routing - server determines instance from session
		if (url.pathname.startsWith("/user/")) {
			const session = await getSession(request);
			const agent = await getAgentByName(env.UserAgent, session.userId);
			return agent.fetch(request); // Forward request directly to agent
		}

		// Default routing for standard /agents/... paths
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
export default {
	async fetch(request: Request, env: Env) {
		const url = new URL(request.url);

		// Custom routing - server determines instance from session
		if (url.pathname.startsWith("/user/")) {
			const session = await getSession(request);
			const agent = await getAgentByName(env.UserAgent, session.userId);
			return agent.fetch(request); // Forward request directly to agent
		}

		// Default routing for standard /agents/... paths
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

Vlastní cesta s dynamickou instancí

Směrujte různé cesty na různé instance:

// Route /chat/{room} to ChatRoom agent
if (url.pathname.startsWith("/chat/")) {
	const roomId = url.pathname.replace("/chat/", "");
	const agent = await getAgentByName(env.ChatRoom, roomId);
	return agent.fetch(request);
}

// Route /doc/{id} to Document agent
if (url.pathname.startsWith("/doc/")) {
	const docId = url.pathname.replace("/doc/", "");
	const agent = await getAgentByName(env.Document, docId);
	return agent.fetch(request);
}
// Route /chat/{room} to ChatRoom agent
if (url.pathname.startsWith("/chat/")) {
	const roomId = url.pathname.replace("/chat/", "");
	const agent = await getAgentByName(env.ChatRoom, roomId);
	return agent.fetch(request);
}

// Route /doc/{id} to Document agent
if (url.pathname.startsWith("/doc/")) {
	const docId = url.pathname.replace("/doc/", "");
	const agent = await getAgentByName(env.Document, docId);
	return agent.fetch(request);
}

Přijímání identity instance (na straně klienta)

Při použití basePath, klient neví, ke které instanci se připojil, dokud mu server tuto informaci nevrátí. Agent svou identitu odešle automaticky při připojení:

const agent = useAgent({
	agent: "UserAgent",
	basePath: "user",
	onIdentity: (name, agentType) => {
		console.log(`Connected to ${agentType} instance: ${name}`);
		// e.g., "Connected to user-agent instance: user-123"
	},
});

// Reactive state - re-renders when identity is received
return (
	<div>
		{agent.identified ? `Connected to: ${agent.name}` : "Connecting..."}
	</div>
);
const agent = useAgent({
	agent: "UserAgent",
	basePath: "user",
	onIdentity: (name, agentType) => {
		console.log(`Connected to ${agentType} instance: ${name}`);
		// e.g., "Connected to user-agent instance: user-123"
	},
});

// Reactive state - re-renders when identity is received
return (
	<div>
		{agent.identified ? `Connected to: ${agent.name}` : "Connecting..."}
	</div>
);

Pro AgentClient:

const agent = new AgentClient({
	agent: "UserAgent",
	basePath: "user",
	host: "example.com",
	onIdentity: (name, agentType) => {
		// Update UI with actual instance name
		setInstanceName(name);
	},
});

// Wait for identity before proceeding
await agent.ready;
console.log(agent.name); // Now has the server-determined name
const agent = new AgentClient({
	agent: "UserAgent",
	basePath: "user",
	host: "example.com",
	onIdentity: (name, agentType) => {
		// Update UI with actual instance name
		setInstanceName(name);
	},
});

// Wait for identity before proceeding
await agent.ready;
console.log(agent.name); // Now has the server-determined name

Zpracování změn identity při opětovném připojení

Pokud se identita při novém připojení změní (například relace vypršela a uživatel se přihlásí jako někdo jiný), můžete to ošetřit pomocí onIdentityChange:

const agent = useAgent({
	agent: "UserAgent",
	basePath: "user",
	onIdentityChange: (oldName, newName, oldAgent, newAgent) => {
		console.log(`Session changed: ${oldName} → ${newName}`);
		// Refresh state, show notification, etc.
	},
});
const agent = useAgent({
	agent: "UserAgent",
	basePath: "user",
	onIdentityChange: (oldName, newName, oldAgent, newAgent) => {
		console.log(`Session changed: ${oldName} → ${newName}`);
		// Refresh state, show notification, etc.
	},
});

Pokud onIdentityChange není zadán a identita se změní, zaznamená se varování, které pomůže odhalit neočekávané změny relace.

Zakázání identity z bezpečnostních důvodů

Pokud názvy instancí obsahují citlivá data (ID relací, interní ID uživatelů), můžete odesílání identity vypnout:

class SecureAgent extends Agent {
	// Do not expose instance names to clients
	static options = { sendIdentityOnConnect: false };
}
class SecureAgent extends Agent {
	// Do not expose instance names to clients
	static options = { sendIdentityOnConnect: false };
}

Když je identita vypnutá:

Kdy použít vlastní směrování

Scénář Přístup
Standardní přístup agenta Výchozí /agents/{agent}/{name}
Instance z auth/session basePath + getAgentByName + fetch
Čisté adresy URL (bez /agents/ prefix) basePath + vlastní směrování
Starší struktura URL basePath + vlastní směrování
Komplexní logika směrování Vlastní směrování ve Workeru

Možnosti směrování

Obojí routeAgentRequest() a getAgentByName() přijímají možnosti pro přizpůsobení chování směrování.

CORS

Pro cross-origin požadavky (běžné, pokud je váš frontend na jiné doméně):

const response = await routeAgentRequest(request, env, {
	cors: true, // Enable default CORS headers
});
const response = await routeAgentRequest(request, env, {
	cors: true, // Enable default CORS headers
});

Nebo s vlastními hlavičkami CORS:

const response = await routeAgentRequest(request, env, {
	cors: {
		"Access-Control-Allow-Origin": "https://myapp.com",
		"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
		"Access-Control-Allow-Headers": "Content-Type, Authorization",
	},
});
const response = await routeAgentRequest(request, env, {
	cors: {
		"Access-Control-Allow-Origin": "https://myapp.com",
		"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
		"Access-Control-Allow-Headers": "Content-Type, Authorization",
	},
});

Location hints

U aplikací citlivých na latenci naznačte, kde má agent běžet:

// With getAgentByName
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	locationHint: "enam", // Eastern North America
});

// With routeAgentRequest (applies to all matched agents)
const response = await routeAgentRequest(request, env, {
	locationHint: "enam",
});
// With getAgentByName
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	locationHint: "enam", // Eastern North America
});

// With routeAgentRequest (applies to all matched agents)
const response = await routeAgentRequest(request, env, {
	locationHint: "enam",
});

Dostupné nápovědy k umístění: wnam, enam, sam, weur, eeur, apac, oc, afr, me

Jurisdikce

Pro požadavky na umístění dat:

// With getAgentByName
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	jurisdiction: "eu", // EU jurisdiction
});

// With routeAgentRequest (applies to all matched agents)
const response = await routeAgentRequest(request, env, {
	jurisdiction: "eu",
});
// With getAgentByName
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	jurisdiction: "eu", // EU jurisdiction
});

// With routeAgentRequest (applies to all matched agents)
const response = await routeAgentRequest(request, env, {
	jurisdiction: "eu",
});

Props

Vzhledem k tomu, že agenty instancuje runtime a nejsou konstruovány přímo, props poskytuje způsob, jak předat inicializační argumenty:

const agent = await getAgentByName(env.MyAgent, "instance-name", {
	props: {
		userId: session.userId,
		config: { maxRetries: 3 },
	},
});
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	props: {
		userId: session.userId,
		config: { maxRetries: 3 },
	},
});

Props se předávají do agentova onStart metoda životního cyklu:

class MyAgent extends Agent {
	userId;
	config;

	async onStart(props) {
		this.userId = props?.userId;
		this.config = props?.config;
	}
}
class MyAgent extends Agent<Env, State> {
	private userId?: string;
	private config?: { maxRetries: number };

	async onStart(props?: { userId: string; config: { maxRetries: number } }) {
		this.userId = props?.userId;
		this.config = props?.config;
	}
}

Při použití props hodnotou routeAgentRequest, stejné props se předají tomu agentovi, který odpovídá dané URL. To se dobře hodí pro univerzální kontext, jako je autentizace:

export default {
	async fetch(request, env) {
		const session = await getSession(request);
		return routeAgentRequest(request, env, {
			props: { userId: session.userId, role: session.role },
		});
	},
};
export default {
	async fetch(request, env) {
		const session = await getSession(request);
		return routeAgentRequest(request, env, {
			props: { userId: session.userId, role: session.role },
		});
	},
} satisfies ExportedHandler<Env>;

Pro inicializaci specifickou pro agenta použijte getAgentByName místo toho tam, kde přesně určujete, který agent props obdrží.

Opakování směrování

Použijte routingRetry hodnotou getAgentByName() když má kód na straně serveru opakovat přechodná selhání směrování Durable Object:

const agent = await getAgentByName(env.MyAgent, "instance-name", {
	routingRetry: {
		maxAttempts: 3,
	},
});
const agent = await getAgentByName(env.MyAgent, "instance-name", {
	routingRetry: {
		maxAttempts: 3,
	},
});

Tato možnost se hodí pro přeposílání požadavků a RPC cesty, kde by se krátkodobé selhání směrování mělo před vrácením chyby volajícímu nejprve zopakovat.

Hooky

routeAgentRequest podporuje hooky pro zachytávání požadavků, než se dostanou k agentům:

const response = await routeAgentRequest(request, env, {
	onBeforeConnect: (req, lobby) => {
		// Called before WebSocket connections
		// Return a Response to reject, Request to modify, or void to continue
	},
	onBeforeRequest: (req, lobby) => {
		// Called before HTTP requests
		// Return a Response to reject, Request to modify, or void to continue
	},
});
const response = await routeAgentRequest(request, env, {
	onBeforeConnect: (req, lobby) => {
		// Called before WebSocket connections
		// Return a Response to reject, Request to modify, or void to continue
	},
	onBeforeRequest: (req, lobby) => {
		// Called before HTTP requests
		// Return a Response to reject, Request to modify, or void to continue
	},
});

Tyto hooky se hodí pro ověřování a validaci. Podrobnosti najdete v Autentizace mezi doménami pro podrobné příklady.

Přístup k agentovi na straně serveru

K agentům máte přístup z kódu Worker pomocí getAgentByName() pro volání RPC:

import { getAgentByName, routeAgentRequest } from "agents";

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		// API endpoint that interacts with an agent
		if (url.pathname === "/api/increment") {
			const counter = await getAgentByName(env.Counter, "global-counter");
			const newCount = await counter.increment();
			return Response.json({ count: newCount });
		}

		// Regular agent routing
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
import { getAgentByName, routeAgentRequest } from "agents";

export default {
	async fetch(request: Request, env: Env) {
		const url = new URL(request.url);

		// API endpoint that interacts with an agent
		if (url.pathname === "/api/increment") {
			const counter = await getAgentByName(env.Counter, "global-counter");
			const newCount = await counter.increment();
			return Response.json({ count: newCount });
		}

		// Regular agent routing
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

Pro možnosti jako locationHint, jurisdiction, a props, přečtěte si Možnosti směrování.

Podcesty a metody HTTP

Požadavky mohou za názvem instance obsahovat dílčí cesty. Ty se předávají do vašeho agenta onRequest() handler:

/agents/api/v1/users     → agent: "api", instance: "v1", path: "/users"
/agents/api/v1/users/123 → agent: "api", instance: "v1", path: "/users/123"

Zpracování podcest ve vašem agentovi:

export class API extends Agent {
	async onRequest(request) {
		const url = new URL(request.url);

		// url.pathname contains the full path including /agents/api/v1/...
		// Extract the sub-path after your agent's base path
		const path = url.pathname.replace(/^\/agents\/api\/[^/]+/, "");

		if (request.method === "GET" && path === "/users") {
			return Response.json(await this.getUsers());
		}

		if (request.method === "POST" && path === "/users") {
			const data = await request.json();
			return Response.json(await this.createUser(data));
		}

		return new Response("Not found", { status: 404 });
	}
}
export class API extends Agent {
	async onRequest(request: Request): Promise<Response> {
		const url = new URL(request.url);

		// url.pathname contains the full path including /agents/api/v1/...
		// Extract the sub-path after your agent's base path
		const path = url.pathname.replace(/^\/agents\/api\/[^/]+/, "");

		if (request.method === "GET" && path === "/users") {
			return Response.json(await this.getUsers());
		}

		if (request.method === "POST" && path === "/users") {
			const data = await request.json();
			return Response.json(await this.createUser(data));
		}

		return new Response("Not found", { status: 404 });
	}
}

Více agentů

V jednom projektu můžete mít více tříd agentů. Každá má svůj vlastní jmenný prostor:

// server.ts
export { Counter } from "./agents/counter";
export { ChatRoom } from "./agents/chat-room";
export { UserProfile } from "./agents/user-profile";

export default {
	async fetch(request, env) {
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
// server.ts
export { Counter } from "./agents/counter";
export { ChatRoom } from "./agents/chat-room";
export { UserProfile } from "./agents/user-profile";

export default {
	async fetch(request: Request, env: Env) {
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;
{
	"durable_objects": {
		"bindings": [
			{ "name": "Counter", "class_name": "Counter" },
			{ "name": "ChatRoom", "class_name": "ChatRoom" },
			{ "name": "UserProfile", "class_name": "UserProfile" },
		],
	},
	"migrations": [
		{
			"tag": "v1",
			"new_sqlite_classes": ["Counter", "ChatRoom", "UserProfile"],
		},
	],
}
[[durable_objects.bindings]]
name = "Counter"
class_name = "Counter"

[[durable_objects.bindings]]
name = "ChatRoom"
class_name = "ChatRoom"

[[durable_objects.bindings]]
name = "UserProfile"
class_name = "UserProfile"

[[migrations]]
tag = "v1"
new_sqlite_classes = [ "Counter", "ChatRoom", "UserProfile" ]

Ke každému agentovi se přistupuje přes jeho vlastní cestu:

/agents/counter/...
/agents/chat-room/...
/agents/user-profile/...

Tok požadavku

Zde je znázorněno, jak požadavek prochází systémem:

flowchart TD
    A["HTTP Request<br/>or WebSocket"] --> B["routeAgentRequest<br/>Parse URL path"]
    B --> C["Find binding in<br/>env by name"]
    C --> D["Get/create DO<br/>by instance ID"]
    D --> E["Agent Instance"]
    E --> F{"Protocol?"}
    F -->|WebSocket| G["onConnect(), onMessage"]
    F -->|HTTP| H["onRequest()"]

Směrování s ověřováním

Požadavky můžete ověřit několika způsoby ještě předtím, než dorazí k vašemu agentovi.

Používání autentizačních hooků

routeAgentRequest() funkce poskytuje onBeforeConnect a onBeforeRequest hooky pro ověřování:

import { Agent, routeAgentRequest } from "agents";

export default {
	async fetch(request, env) {
		return (
			(await routeAgentRequest(request, env, {
				// Run before WebSocket connections
				onBeforeConnect: async (request) => {
					const token = new URL(request.url).searchParams.get("token");
					if (!(await verifyToken(token, env))) {
						// Return a response to reject the connection
						return new Response("Unauthorized", { status: 401 });
					}
					// Return nothing to allow the connection
				},
				// Run before HTTP requests
				onBeforeRequest: async (request) => {
					const auth = request.headers.get("Authorization");
					if (!auth || !(await verifyAuth(auth, env))) {
						return new Response("Unauthorized", { status: 401 });
					}
				},
				// Optional: prepend a prefix to agent instance names
				prefix: "user-",
			})) ?? new Response("Not found", { status: 404 })
		);
	},
};
import { Agent, routeAgentRequest } from "agents";

export default {
	async fetch(request: Request, env: Env) {
		return (
			(await routeAgentRequest(request, env, {
				// Run before WebSocket connections
				onBeforeConnect: async (request) => {
					const token = new URL(request.url).searchParams.get("token");
					if (!(await verifyToken(token, env))) {
						// Return a response to reject the connection
						return new Response("Unauthorized", { status: 401 });
					}
					// Return nothing to allow the connection
				},
				// Run before HTTP requests
				onBeforeRequest: async (request) => {
					const auth = request.headers.get("Authorization");
					if (!auth || !(await verifyAuth(auth, env))) {
						return new Response("Unauthorized", { status: 401 });
					}
				},
				// Optional: prepend a prefix to agent instance names
				prefix: "user-",
			})) ?? new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

Ruční ověřování

Zkontrolujte ověření před voláním routeAgentRequest():

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		// Protect agent routes
		if (url.pathname.startsWith("/agents/")) {
			const user = await authenticate(request, env);
			if (!user) {
				return new Response("Unauthorized", { status: 401 });
			}

			// Optionally, enforce that users can only access their own agents
			const instanceName = url.pathname.split("/")[3];
			if (instanceName !== `user-${user.id}`) {
				return new Response("Forbidden", { status: 403 });
			}
		}

		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
export default {
	async fetch(request: Request, env: Env) {
		const url = new URL(request.url);

		// Protect agent routes
		if (url.pathname.startsWith("/agents/")) {
			const user = await authenticate(request, env);
			if (!user) {
				return new Response("Unauthorized", { status: 401 });
			}

			// Optionally, enforce that users can only access their own agents
			const instanceName = url.pathname.split("/")[3];
			if (instanceName !== `user-${user.id}`) {
				return new Response("Forbidden", { status: 403 });
			}
		}

		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

Používání frameworku (Hono)

Pokud používáte framework, jako je Hono ↗, se ověřte v middlewaru před voláním agenta:

import { Agent, getAgentByName } from "agents";
import { Hono } from "hono";

const app = new Hono();

// Authentication middleware
app.use("/agents/*", async (c, next) => {
	const token = c.req.header("Authorization")?.replace("Bearer ", "");
	if (!token || !(await verifyToken(token, c.env))) {
		return c.json({ error: "Unauthorized" }, 401);
	}
	await next();
});

// Route to a specific agent
app.all("/agents/code-review/:id/*", async (c) => {
	const id = c.req.param("id");
	const agent = await getAgentByName(c.env.CodeReviewAgent, id);
	return agent.fetch(c.req.raw);
});

export default app;
import { Agent, getAgentByName } from "agents";
import { Hono } from "hono";

const app = new Hono<{ Bindings: Env }>();

// Authentication middleware
app.use("/agents/*", async (c, next) => {
	const token = c.req.header("Authorization")?.replace("Bearer ", "");
	if (!token || !(await verifyToken(token, c.env))) {
		return c.json({ error: "Unauthorized" }, 401);
	}
	await next();
});

// Route to a specific agent
app.all("/agents/code-review/:id/*", async (c) => {
	const id = c.req.param("id");
	const agent = await getAgentByName(c.env.CodeReviewAgent, id);
	return agent.fetch(c.req.raw);
});

export default app;

Vzory autentizace přes WebSocket (tokeny v URL, obnovení JWT) najdete v Autentizace mezi doménami.

Řešení potíží

Jmenný prostor agenta nebyl nalezen

Chybová zpráva uvádí dostupné agenty. Zkontrolujte:

  1. Třída Agent je exportována z vašeho vstupního bodu.
  2. Název třídy v kódu odpovídá class_name v wrangler.jsonc.
  3. URL adresa používá správný název ve formátu kebab-case.

Požadavek vrátí 404

  1. Ověřte vzor URL adresy: /agents/{agent-name}/{instance-name}.
  2. Zkontrolujte, že routeAgentRequest() se volá před vaším handlerem pro 404.
  3. Ujistěte se, že odpověď z routeAgentRequest() se vrací (nejen volá).

Připojení WebSocket selže

  1. Neupravujte odpověď od routeAgentRequest() pro upgrady WebSocket.
  2. Ujistěte se, že je povoleno CORS, pokud se připojujete z jiného origin.
  3. Skutečnou chybu zkontrolujte v nástrojích pro vývojáře v prohlížeči.

basePath nefunguje

  1. Ujistěte se, že váš Worker zpracovává vlastní cestu a přeposílá ji agentovi.
  2. Použijte getAgentByName() + agent.fetch(request) k předávání požadavků.
  3. agent parametr je stále povinný, ale ignoruje se, když basePath je nastaven.
  4. Zkontrolujte, zda trasa na straně serveru odpovídá klientské basePath.

Referenční dokumentace API

routeAgentRequest(request, env, options?)

Směruje požadavek k příslušnému agentovi.

Parametr Typ Popis
request Request Příchozí požadavek
env Env Prostředí s vazbami agenta
options.cors boolean | HeadersInit Povolte hlavičky CORS
options.props Record<string, unknown> Props předané agentovi, který požadavek zpracovává
options.locationHint string Preferovaná lokalita pro instance agenta
options.jurisdiction string Jurisdikce dat pro instance agentů
options.onBeforeConnect Function Callback před WebSocket připojeními
options.onBeforeRequest Function Callback před HTTP požadavky

Vrací: Promise<Response | undefined> - Odpověď při shodě, undefined, pokud neexistuje odpovídající trasa agenta.

getAgentByName(namespace, name, options?)

Získat instanci agenta podle názvu pro RPC na straně serveru nebo přeposílání požadavků.

Parametr Typ Popis
namespace DurableObjectNamespace<T> Vazba Agent z env
name string Název instance
options.locationHint string Preferovaná lokalita
options.jurisdiction string Jurisdikce dat
options.props Record<string, unknown> Inicializační vlastnosti pro onStart
options.routingRetry object Konfigurace opakování pro přechodná selhání směrování Durable Object

Vrací: Promise<DurableObjectStub<T>> - Typovaný stub pro volání metod agenta nebo předávání požadavků.

useAgent(options) / AgentClient možnosti

Možnosti připojení klienta pro vlastní směrování:

Možnost Typ Popis
agent string Název třídy Agent (povinné)
name string Název instance (výchozí: "default")
basePath string Úplná cesta URL: obchází konstrukci URL adresy agent/name
path string Doplňková cesta, která se připojí k URL
onIdentity (name, agent) => void Volá se, když server odešle identitu
onIdentityChange (oldName, newName, oldAgent, newAgent) => void Volá se při změně identity při opětovném připojení

Vlastnosti návratové hodnoty (React hook):

Vlastnost Typ Popis
name string Aktuální název instance (reaktivní)
agent string Aktuální název třídy agenta (reaktivní)
identified boolean Zda byla přijata identita (reaktivní)
ready Promise<void> Vyřeší se po přijetí identity

Agent.options (server)

Statické možnosti konfigurace agenta:

Možnost Typ Výchozí Popis
hibernate boolean true Zda by měl agent při nečinnosti hibernovat
sendIdentityOnConnect boolean true Zda při připojení odeslat identitu klientům
hungScheduleTimeoutSeconds number 30 Časový limit, po kterém je běžící plán považován za zaseknutý
class SecureAgent extends Agent {
	static options = { sendIdentityOnConnect: false };
}
class SecureAgent extends Agent {
	static options = { sendIdentityOnConnect: false };
}

Další kroky

Client SDK

Připojení z prohlížečů pomocí useAgent a AgentClient.

Autentizace mezi doménami

Vzory autentizace WebSocket.

Volatelné metody

RPC od klientů přes WebSocket.

Konfigurace

Nastavte vazby agenta v souboru wrangler.jsonc.