← Создание агентов на Cloudflare / agents / runtime / communication
Маршрутизация
Это руководство объясняет, как запросы направляются к агентам, как работает присвоение имён и какие есть паттерны для организации ваших агентов.
Как работает маршрутизация
Когда поступает запрос, routeAgentRequest() анализирует URL и перенаправляет его на соответствующий экземпляр агента:
https://your-worker.dev/agents/{agent-name}/{instance-name}
└────┬────┘ └─────┬─────┘
Class name Unique instance ID
(kebab-case)Примеры URL:
| URL | Класс Agent | Экземпляр |
|---|---|---|
/agents/counter/user-123 |
Counter |
user-123 |
/agents/chat-room/lobby |
ChatRoom |
lobby |
/agents/my-agent/default |
MyAgent |
default |
Разрешение имён
Имена классов Agent автоматически преобразуются в kebab-case для URL:
| Имя класса | URL Path |
|---|---|
Counter |
/agents/counter/... |
MyAgent |
/agents/my-agent/... |
ChatRoom |
/agents/chat-room/... |
AIAssistant |
/agents/ai-assistant/... |
Роутер сопоставляет как исходное имя, так и его kebab-case версию, поэтому можно использовать любое из них:
useAgent({ agent: "Counter" })→/agents/counter/...useAgent({ agent: "counter" })→/agents/counter/...
Использование routeAgentRequest()
routeAgentRequest() функция является основной точкой входа для маршрутизации агента:
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>;Шаблоны именования экземпляров
Имя экземпляра (последняя часть URL) определяет, какой экземпляр агента обрабатывает запрос. Каждое уникальное имя получает собственный изолированный агент с собственным состоянием.
Агенты для каждого пользователя
Каждый пользователь получает собственный экземпляр агента:
// 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)Общие комнаты
Несколько пользователей используют один и тот же экземпляр агента:
// 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Глобальный синглтон
Один экземпляр для всего приложения:
// Client
const agent = useAgent({
agent: "AppConfig",
name: "default", // Or any consistent name
});// Client
const agent = useAgent({
agent: "AppConfig",
name: "default", // Or any consistent name
});Динамическое именование
Генерируйте имена экземпляров на основе контекста:
// 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()}`,
});Пользовательская маршрутизация URL
Для сложных сценариев, где нужен контроль над структурой URL, можно обойти стандартный /agents/{agent}/{name} шаблоном.
Использование basePath (на стороне клиента)
basePath опция позволяет клиентам подключаться к любому пути 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
});Это полезно, когда:
- Вы хотите получить чистые URL-адреса без
/agents/префикс - Имя экземпляра определяется на стороне сервера (например, на основе аутентификации или сессии)
- Вы интегрируетесь с существующей структурой URL
Выбор экземпляра на стороне сервера
При использовании basePath, сервер должен сам обрабатывать маршрутизацию. Используйте getAgentByName() чтобы получить экземпляр агента, а затем передать запрос с 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>;Пользовательский путь с динамическим экземпляром
Маршрутизируйте разные пути к разным инстансам:
// 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);
}Получение идентичности экземпляра (на стороне клиента)
При использовании basePath, клиент не знает, к какому экземпляру он подключился, пока сервер не вернёт эту информацию. Агент автоматически отправляет свою идентичность при подключении:
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>
);Для 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 nameconst 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Обработка изменений идентичности при переподключении
Если при повторном подключении идентификация меняется (например, сессия истекла и пользователь входит под другим аккаунтом), это можно обработать с помощью 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.
},
});Если onIdentityChange не задан и идентичность меняется, записывается предупреждение, которое помогает выявить неожиданные изменения сессии.
Отключение идентификации из соображений безопасности
Если имена ваших экземпляров содержат чувствительные данные (идентификаторы сессий, внутренние идентификаторы пользователей), можно отключить отправку идентификатора:
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 };
}Когда идентификация отключена:
agent.identifiedостаётсяfalseagent.readyникогда не разрешается (используйте вместо этого обновления состояния)onIdentityиonIdentityChangeникогда не вызываются
Когда использовать собственную маршрутизацию
| Сценарий | Подход |
|---|---|
| Стандартный доступ к агенту | По умолчанию /agents/{agent}/{name} |
| Экземпляр из auth/session | basePath + getAgentByName + fetch |
Чистые URL-адреса (без /agents/ префикс) |
basePath + собственная маршрутизация |
| Устаревшая структура URL | basePath + собственная маршрутизация |
| Сложная логика маршрутизации | Пользовательская маршрутизация в Worker |
Параметры маршрутизации
Оба routeAgentRequest() и getAgentByName() принимают параметры для настройки поведения маршрутизации.
CORS
Для межсайтовых запросов (частый случай, когда фронтенд размещён на другом домене):
const response = await routeAgentRequest(request, env, {
cors: true, // Enable default CORS headers
});const response = await routeAgentRequest(request, env, {
cors: true, // Enable default CORS headers
});Или с пользовательскими заголовками 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",
},
});Подсказки о местоположении
Для приложений, чувствительных к задержкам, подскажите, где должен выполняться агент:
// 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",
});Доступные подсказки о местоположении: wnam, enam, sam, weur, eeur, apac, oc, afr, me
Юрисдикция
Для требований к размещению данных:
// 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
Поскольку агенты создаются рантаймом, а не конструируются напрямую, props предоставляет способ передать аргументы инициализации:
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 передаются агенту в onStart метод жизненного цикла:
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;
}
}При использовании props с routeAgentRequest, одни и те же props передаются любому агенту, который соответствует URL. Это хорошо подходит для универсального контекста, например для аутентификации:
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>;Для инициализации, специфичной для агента, используйте getAgentByName вместо этого, если вы хотите точно контролировать, какой агент получит props.
Повтор маршрутизации
Используйте routingRetry с getAgentByName() когда серверный код должен повторять попытки при временных сбоях маршрутизации Durable Object:
const agent = await getAgentByName(env.MyAgent, "instance-name", {
routingRetry: {
maxAttempts: 3,
},
});const agent = await getAgentByName(env.MyAgent, "instance-name", {
routingRetry: {
maxAttempts: 3,
},
});Этот параметр полезен для переадресации запросов и путей RPC, где кратковременный сбой маршрутизации стоит повторить, прежде чем возвращать ошибку вызывающей стороне.
Хуки
routeAgentRequest поддерживает хуки для перехвата запросов до того, как они достигнут агентов:
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
},
});Эти хуки полезны для аутентификации и валидации. Подробнее см. в Аутентификация между доменами для подробных примеров.
Доступ к агенту на стороне сервера
Вы можете обращаться к агентам из кода Worker с помощью getAgentByName() для вызовов 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>;Для таких опций, как locationHint, jurisdiction, а также props, см. Параметры маршрутизации.
Подпути и методы HTTP
Запросы могут включать подпути после имени экземпляра. Они передаются в onRequest() обработчик:
/agents/api/v1/users → agent: "api", instance: "v1", path: "/users"
/agents/api/v1/users/123 → agent: "api", instance: "v1", path: "/users/123"Обрабатывайте подпути в своём агенте:
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 });
}
}Несколько агентов
В одном проекте может быть несколько классов агентов. Каждый получает собственное пространство имён:
// 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" ]К каждому агенту обращаются по его собственному пути:
/agents/counter/...
/agents/chat-room/...
/agents/user-profile/...Поток запроса
Ниже показано, как запрос проходит через систему:
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()"]
Маршрутизация с аутентификацией
Существует несколько способов аутентифицировать запросы до того, как они достигнут вашего агента.
Использование хуков аутентификации
routeAgentRequest() функция предоставляет onBeforeConnect и onBeforeRequest хуки для аутентификации:
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>;Ручная аутентификация
Проверьте аутентификацию перед вызовом 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>;Использование фреймворка (Hono)
Если вы используете такой фреймворк, как Hono ↗, авторизуйтесь в middleware перед вызовом агента:
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;Про шаблоны аутентификации WebSocket (токены в URL, обновление JWT) см. в Аутентификация между доменами.
Устранение неполадок
Пространство имён Agent не найдено
Сообщение об ошибке перечисляет доступных агентов. Проверьте:
- Класс Agent экспортируется из вашей точки входа.
- Имя класса в коде соответствует
class_nameвwrangler.jsonc. - URL использует корректное имя в формате kebab-case.
Запрос возвращает 404
- Проверьте шаблон URL:
/agents/{agent-name}/{instance-name}. - Проверьте, что
routeAgentRequest()вызывается до вашего обработчика 404. - Убедитесь, что ответ от
routeAgentRequest()возвращается (а не просто вызывается).
Соединение WebSocket не устанавливается
- Не изменяйте ответ от
routeAgentRequest()для апгрейдов WebSocket. - Убедитесь, что CORS включён, если подключение выполняется с другого источника.
- Фактическую ошибку проверьте в инструментах разработчика браузера.
basePath не работает
- Убедитесь, что ваш Worker обрабатывает пользовательский путь и перенаправляет запрос агенту.
- Используйте
getAgentByName()+agent.fetch(request)чтобы перенаправлять запросы. -
agentпараметр по-прежнему обязателен, но игнорируется, еслиbasePathзадан. - Проверьте, что маршрут на стороне сервера соответствует клиентскому
basePath.
Справочник по API
routeAgentRequest(request, env, options?)
Маршрутизирует запрос к подходящему агенту.
| Параметр | Тип | Описание |
|---|---|---|
request |
Request |
Входящий запрос |
env |
Env |
Окружение с привязками агента |
options.cors |
boolean | HeadersInit |
Включите заголовки CORS |
options.props |
Record<string, unknown> |
Props, передаваемые агенту, который обрабатывает запрос |
options.locationHint |
string |
Предпочтительное расположение для экземпляров агента |
options.jurisdiction |
string |
Юрисдикция данных для экземпляров агентов |
options.onBeforeConnect |
Function |
Колбэк перед подключениями WebSocket |
options.onBeforeRequest |
Function |
Колбэк перед HTTP-запросами |
Возвращает: Promise<Response | undefined> - Ответ при совпадении, undefined, если маршрут агента не найден.
getAgentByName(namespace, name, options?)
Получить экземпляр агента по имени для RPC на стороне сервера или переадресации запросов.
| Параметр | Тип | Описание |
|---|---|---|
namespace |
DurableObjectNamespace<T> |
Привязка Agent из env |
name |
string |
Имя экземпляра |
options.locationHint |
string |
Предпочитаемое расположение |
options.jurisdiction |
string |
Юрисдикция данных |
options.props |
Record<string, unknown> |
Свойства инициализации для onStart |
options.routingRetry |
object |
Конфигурация повторных попыток для временных сбоев маршрутизации Durable Object |
Возвращает: Promise<DurableObjectStub<T>> - Типизированная заглушка для вызова методов агента или пересылки запросов.
useAgent(options) / AgentClient параметры
Параметры клиентского подключения для пользовательской маршрутизации:
| Опция | Тип | Описание |
|---|---|---|
agent |
string |
Имя класса Agent (обязательно) |
name |
string |
Имя экземпляра (по умолчанию: "default") |
basePath |
string |
Полный путь URL: обходит построение URL agent/name |
path |
string |
Дополнительный путь, добавляемый к URL |
onIdentity |
(name, agent) => void |
Вызывается, когда сервер отправляет идентичность |
onIdentityChange |
(oldName, newName, oldAgent, newAgent) => void |
Вызывается при изменении идентичности при переподключении |
Свойства возвращаемого значения (React-хук):
| Свойство | Тип | Описание |
|---|---|---|
name |
string |
Имя текущего экземпляра (реактивное) |
agent |
string |
Имя текущего класса агента (реактивное) |
identified |
boolean |
Получена ли идентичность (реактивно) |
ready |
Promise<void> |
Разрешается при получении identity |
Agent.options (сервер)
Статические параметры конфигурации агента:
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
hibernate |
boolean |
true |
Должен ли агент переходить в спящий режим при простое |
sendIdentityOnConnect |
boolean |
true |
Нужно ли отправлять идентичность клиентам при подключении |
hungScheduleTimeoutSeconds |
number |
30 |
Тайм-аут, после которого работающее расписание считается зависшим |
class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
}class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
}